diff --git a/.gitignore b/.gitignore index b2265095..05eba2f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,28 @@ .?* +!.prettierrc +!.solhint.json __pycache__ **/.DS_Store **/*.iml build/ -bin/ \ No newline at end of file +bin/ +**/.idea +**/**/.idea +**/**/node_modules/ + + +/docker-compose/goloop2goloop/config +/docker-compose/goloop2goloop/data +/docker-compose/goloop2moonbeam/config +/docker-compose/goloop2moonbeam/data + +.gradle/* +javascore/bmv/godWallet.json +**/**/Module/* +EventDecoder.java +TypeDecoder.java +metaData.json +BMVInitializeData.json +paraMetaData.json +relayMetaData.json +sovereignMetaData.json diff --git a/Makefile b/Makefile index b3f6031f..23c43462 100644 --- a/Makefile +++ b/Makefile @@ -52,10 +52,15 @@ $(foreach M,$(CMDS),$(eval $(call CMD_template,$(M)))) # Build flags for each command btpsimple_LDFLAGS = -X 'main.version=$(GL_VERSION)' -X 'main.build=$(BUILD_INFO)' BUILD_TARGETS += btpsimple +BUILD_TARGETS += iconvalidators linux : $(addsuffix -linux,$(BUILD_TARGETS)) -PYSCORE_DIST_DIR = $(BUILD_ROOT)/build/pyscore +export CONTRACT_DIST_DIR=$(BUILD_ROOT)/build/contracts +export PYSCORE_DIST_DIR=$(CONTRACT_DIST_DIR)/pyscore +export JAVASCORE_DIST_DIR=$(CONTRACT_DIST_DIR)/javascore +export SOLIDITY_DIST_DIR=$(CONTRACT_DIST_DIR)/solidity +export PYSCORE_TESTNET_DIR=${BUILD_ROOT}/testnet/goloop2goloop/pyscore $(PYSCORE_DIST_DIR)/%: $(eval MODULE := $(patsubst $(PYSCORE_DIST_DIR)/%,%,$@)) @@ -80,21 +85,29 @@ dist-py-irc2: $(PYSCORE_DIST_DIR)/token_bsh zip -r -v $(PYSCORE_DIST_DIR)/irc2_token.zip * -x *__pycache__* -x *tests* dist-py: dist-py-bmc dist-py-bmv dist-py-irc2 +dist-java: + bash ./scripts/dist_javascore.sh +dist-sol: + bash ./scripts/dist_solidity.sh clean-dist-py: rm -rf $(PYSCORE_DIST_DIR)/* +clean-dist-sol: + rm -rf $(SOLIDITY_DIST_DIR) +clean-dist-java: + rm -rf $(JAVASCORE_DIST_DIR) BTPSIMPLE_IMAGE = btpsimple:$(GL_TAG) BTPSIMPLE_DOCKER_DIR = $(BUILD_ROOT)/build/btpsimple -btpsimple-image: btpsimple-linux dist-py +btpsimple-image: btpsimple-linux dist-py dist-java dist-sol @ echo "[#] Building image $(BTPSIMPLE_IMAGE) for $(GL_VERSION)" @ rm -rf $(BTPSIMPLE_DOCKER_DIR) @ \ BIN_DIR=$(abspath $(LINUX_BIN_DIR)) \ BIN_VERSION=$(GL_VERSION) \ BUILD_TAGS="$(GOBUILD_TAGS)" \ - DIST_DIR="$(PYSCORE_DIST_DIR)" \ + DIST_DIR="$(CONTRACT_DIST_DIR)" \ $(BUILD_ROOT)/docker/btpsimple/build.sh $(BTPSIMPLE_IMAGE) $(BUILD_ROOT) $(BTPSIMPLE_DOCKER_DIR) .PHONY: test diff --git a/README.md b/README.md index fee71bf0..4a4baff8 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,9 @@ Terminologies - Send BTP Relay Message ### Blockchain specifics + * [ICON](doc/icon.md) +* [Polkadot parachain with frontier](doc/polkadot_parachain_with_frontier.md) ## BTP Project @@ -57,12 +59,12 @@ Terminologies |:--------------------|:-------| | /cmd | Root of implement of BMR | | /cmd/btpsimple | Reference implement of BMR. only provide unidirectional relay. (golang) | -| /cmd/btpsimple/chain | Implement of common logic of BMR, use module | -| /cmd/btpsimple/module | BMR module interface and common codes | -| /cmd/btpsimple/module/`` | Implement of BMR module (`Sender`,`Receiver`), `` is name of blockchain | +| /chain/ | BMR module interface and common codes | +| /chain/`` | Implement of BMR module (`Sender`,`Receiver`), `` is name of blockchain | | /common | Common codes (golang) | | /doc | Documents | | /docker | Docker related resources | +| /docker-compose | Setup of local network for running e2e of BTP | | /`` | Root of implement of BTP smart contracts, `` is name of smart contract execution environment | | /``/bmc | Implement of BMC smart contract | | /``/bmv | Root of implement of BMV smart contract | @@ -77,7 +79,10 @@ Terminologies #### BMR module | Directory | Description | |:--------------------|:-------| -| /cmd/btpsimple/module/icon | BMR module for ICON blockchain | +| /chain/icon | BMR module for ICON blockchain | +| /chain/pra | BMR module for Polkadot Parachain blockchain | +| /chain/pra/receiver_relay* | BMR module to listen for Polkadot Relay blockchain | +| *_test.go | BMR files to perform unit test for the *.go file | #### Python SCORE of ICON | Directory | Description | @@ -86,3 +91,30 @@ Terminologies | /pyscore/bmv/icon | Implement of BMV smart contract for ICON blockchain | | /pyscore/lib | BTP interface and common codes for Python SCORE | | /pyscore/lib/icon | ICON related common codes | + +#### Java SCORE of ICON +| Directory | Description | +|:--------------------|:-------| +| /javascore | Implement of BTP smart contracts for Java SCORE of ICON blockchain | +| /javascore/bmc | Implement of BMC smart contract | +| /javascore/bmv/icon | Implement of BMV smart contract for ICON blockchain | +| /javascore/lib | BTP interface and common codes for Java SCORE | + +#### Java SCORE of Parachain + +| Directory | Description | +|:--------------------|:-------| +| /javascore/bmv/parachain | Implement of BMV smart contract for moonbeam parachain | +| /javascore/bmv/sovereignChain | Implement of BMV smart contract for edgeware parachain | +| /javascore/bmv/eventDecoder | Implement of event decoder for substrate chain | +| /javascore/bmv/helper | Script to get parachain initialize parameters and deploy contract | +| /javascore/lib | BTP interface and common codes for Java SCORE | + +#### Solidity of ICON + +| Directory | Description | +|:--------------------|:-------| +| /solidity | Implement of BTP solidity smart contracts of ICON blockchain | +| /solidity/bmc | Implement of BMC solidity smart contract | +| /solidity/bmv | Implement of BMV solidity smart contract for ICON blockchain | +| /solidity/bsh | Implement of BMC solidity smart contract | \ No newline at end of file diff --git a/btp/btp.go b/btp/btp.go new file mode 100644 index 00000000..051b2af0 --- /dev/null +++ b/btp/btp.go @@ -0,0 +1,492 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package btp + +import ( + "math" + "sync" + "sync/atomic" + "time" + + "github.com/gammazero/workerpool" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/icon" + "github.com/icon-project/btp/chain/pra" + "github.com/icon-project/btp/common/db" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/mta" + "github.com/icon-project/btp/common/wallet" +) + +const ( + DefaultDBDir = "db" + DefaultDBType = db.GoLevelDBBackend + //Base64 in:out = 6:8 + DefaultBufferScaleOfBlockProof = 0.5 + DefaultBufferNumberOfBlockProof = 100 + DefaultBufferInterval = 5 * time.Second + DefaultReconnectDelay = time.Second + DefaultRelayReSendInterval = time.Second + DefaultMaxWorkers = 100 +) + +// BTP is the core to manage relaying messages +type BTP struct { + cfg *Config + wallet wallet.Wallet + dir string + log log.Logger + sender chain.Sender + receiver chain.Receiver + bmcLinkStatus *chain.BMCLinkStatus + heightOfDst int64 + relaySignal chan bool + bmcSrcBtpAddress chain.BtpAddress + bmcDstBtpAddress chain.BtpAddress + store *mta.ExtAccumulator + rms []*chain.RelayMessage + rmsMutex *sync.RWMutex + rmSeq uint64 + lastBlockUpdate *chain.BlockUpdate + wp *workerpool.WorkerPool // Use worker pool to avoid too many open files error +} + +// New creates a new BTP object +func New(cfg *Config, w wallet.Wallet, l log.Logger) (*BTP, error) { + + var sender chain.Sender + var receiver chain.Receiver + + switch cfg.Dst.Address.BlockChain() { + case "icon": + sender = icon.NewSender(cfg.Src.Address, cfg.Dst.Address, w, cfg.Dst.Endpoint, cfg.Dst.Options, l) + case "pra": + sender = pra.NewSender(cfg.Src.Address, cfg.Dst.Address, w, cfg.Dst.Endpoint, cfg.Dst.Options, l) + default: + return nil, errors.New("Chain not supported yet") + } + + switch cfg.Src.Address.BlockChain() { + case "icon": + receiver = icon.NewReceiver(cfg.Src.Address, cfg.Dst.Address, cfg.Src.Endpoint, cfg.Src.Options, l) + case "pra": + receiver = pra.NewReceiver(cfg.Src.Address, cfg.Dst.Address, cfg.Src.Endpoint, cfg.Src.Options, l, cfg.AbsBaseDir(), cfg.Dst.Endpoint) + default: + return nil, errors.New("Chain not supported yet") + } + + return &BTP{ + cfg: cfg, + wallet: w, + dir: cfg.BaseDir, + log: l, + sender: sender, + receiver: receiver, + relaySignal: make(chan bool, 2), + bmcSrcBtpAddress: cfg.Src.Address, + bmcDstBtpAddress: cfg.Dst.Address, + rms: []*chain.RelayMessage{}, + rmsMutex: &sync.RWMutex{}, + wp: workerpool.New(DefaultMaxWorkers), + }, nil +} + +func (b *BTP) init() error { + if err := b.refreshStatus(); err != nil { + return err + } + + if err := b.prepareDatabase(b.bmcLinkStatus.Verifier.Offset); err != nil { + return err + } + atomic.StoreInt64(&b.heightOfDst, b.bmcLinkStatus.CurrentHeight) + b.relayLoop() + b.newRelayMessage() + return nil +} + +// Serve starts the BTP +func (b *BTP) Serve() error { + log.Info("Starting BTP...") + if err := b.init(); err != nil { + return err + } + + errCh := make(chan error) + // Update bufferd rms + go func() { + err := b.sender.MonitorLoop( + b.bmcLinkStatus.CurrentHeight, + b.OnBlockOfDst, + func() { + b.log.Debugf("Connect MonitorLoop") + errCh <- nil + }) + select { + case errCh <- err: + default: + } + }() + // Add relay message to buffer rms + go func() { + h := b.receiveHeight() + b.log.Debugf("start receiveloop from height: %v", h) + + err := b.receiver.ReceiveLoop( + h, + b.bmcLinkStatus.RxSeq, + b.OnBlockOfSrc, + func() { + b.log.Debug("Connect ReceiveLoop") + errCh <- nil + }) + select { + case errCh <- err: + default: + } + }() + + for { + select { + case err := <-errCh: + if err != nil { + return err + } + } + } +} + +// OnBlockOfDst callback when got a new update from the BMC source +func (b *BTP) OnBlockOfSrc(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + b.log.Tracef("OnBlockOfSrc bu.Height:%d", bu.Height) + // BMV.Offset > Next BTP Message block => throw out of bound + if len(rps) > 0 && b.bmcLinkStatus.Verifier.Height > rps[0].Height { + b.log.Panicf("OnBlockOfSrc: out of bound Msg %d at Height %d lower than BMV.Height:%d", rps[0].Events[0].Sequence, rps[0].Height, b.bmcLinkStatus.Verifier.Height) + } + b.updateMTA(bu) + b.addRelayMessage(bu, rps) + + if b.store.Height() >= b.bmcLinkStatus.Verifier.Height { + b.relaySignal <- true + } else { + b.log.Debugf("OnBlockOfSrc syncing Merkle Tree Accumulator at %d", bu.Height) + } +} + +// OnBlockOfDst callback when got a new update from the BMC destination +func (b *BTP) OnBlockOfDst(height int64) error { + b.log.Debugf("OnBlockOfDst height:%d", height) + atomic.StoreInt64(&b.heightOfDst, height) + + h, seq := b.bmcLinkStatus.Verifier.Height, b.bmcLinkStatus.RxSeq + if err := b.refreshStatus(); err != nil { + return err + } + + if h != b.bmcLinkStatus.Verifier.Height || seq != b.bmcLinkStatus.RxSeq { + if err := b.updateRelayMessages(b.bmcLinkStatus.Verifier.Height, b.bmcLinkStatus.RxSeq); err != nil { + return err + } + b.relaySignal <- true + } + return nil +} + +// refreshStatus update current status of BMCLink +func (b *BTP) refreshStatus() error { + bmcStatus, err := b.sender.GetStatus() + if err != nil { + return err + } + b.bmcLinkStatus = bmcStatus + return nil +} + +// relayLoop listen a signal to relay messages +func (b *BTP) relayLoop() { + go func() { + b.log.Debugln("start relayLoop") + for { + _, ok := <-b.relaySignal + if !ok { + break + } + b.relay() + } + b.log.Debugln("stop relayLoop") + }() +} + +func (b *BTP) canRelay(rm *chain.RelayMessage) bool { + hasWait := rm.HasWait() + skippable := b.skippable(rm) + relayable := b.relayble(rm) + + b.log.Debugf("canRelay rms:%v has_wait:%v skippable:%v relayable:%v", len(b.rms), hasWait, skippable, relayable) + return !(hasWait || (!skippable && !relayable)) +} + +// relay relays messages are in the buffered rms +func (b *BTP) relay() { + b.rmsMutex.Lock() + defer b.rmsMutex.Unlock() + + if err := b.refreshStatus(); err != nil { + b.log.Panicf("fail to refresh status err:%+v", err) + } + + for _, rm := range b.rms { + if !b.canRelay(rm) { + break + } else { + b.logRelaying("before segment", rm, nil, -1) + if len(rm.Segments) == 0 { + if segments, err := b.sender.Segment(rm, b.bmcLinkStatus.Verifier.Height); err != nil { + b.log.Panicf("fail to segment err:%+v", err) + } else { + rm.Segments = segments + } + } + + b.logRelaying("before relay", rm, nil, -1) + reSegment := true + for j, segment := range rm.Segments { + reSegment = false + + if segment.GetResultParam == nil { + segment.TransactionResult = nil + if resultParam, err := b.sender.Relay(segment); err != nil { + b.log.Panicf("fail to Relay err:%+v", err) + } else { + segment.GetResultParam = resultParam + } + + b.logRelaying("after relay", rm, segment, j) + b.updateResult(rm, segment) + + } + } + + if reSegment { + rm.Segments = make([]*chain.Segment, 0) + } + } + } + +} + +// addRelayMessage adds messages to the buffered rms +func (b *BTP) addRelayMessage(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + b.log.Debugf("addRelayMessage bu.Height:%v b.bmcLinkStatus.Verifier.Height:%v", bu.Height, b.bmcLinkStatus.Verifier.Height) + b.rmsMutex.Lock() + defer b.rmsMutex.Unlock() + + if b.lastBlockUpdate != nil { + //TODO consider remained bu when reconnect + if b.lastBlockUpdate.Height+1 != bu.Height { + b.log.Panicf("invalid bu") + } + } + b.lastBlockUpdate = bu + + rm := b.rms[len(b.rms)-1] + if len(rm.Segments) > 0 { + rm = b.newRelayMessage() + } + + if len(rps) > 0 { + rm.BlockUpdates = append(rm.BlockUpdates, bu) + + rm.ReceiptProofs = rps + rm.HeightOfDst = b.HeightOfDst() + if b.bmcLinkStatus.BlockIntervalDst > 0 { + scale := float64(b.bmcLinkStatus.BlockIntervalSrc) / float64(b.bmcLinkStatus.BlockIntervalDst) + guessHeightOfDst := b.bmcLinkStatus.RxHeight + int64(math.Ceil(float64(bu.Height-b.bmcLinkStatus.RxHeightSrc)/scale)) - 1 + if guessHeightOfDst < rm.HeightOfDst { + rm.HeightOfDst = guessHeightOfDst + } + } + + if rps[0].Height < b.bmcLinkStatus.Verifier.Height { + var err error + if rm.BlockProof, err = b.newBlockProof(rps[0].Height, bu.Header); err != nil { + b.log.Panicf("addRelayMessage: bp at %+v", err) + } + } + + b.log.Debugf("addRelayMessage rms:%d bu:%d rps:%d HeightOfDst:%d", len(b.rms), bu.Height, len(rps), rm.HeightOfDst) + rm = b.newRelayMessage() + } else { + if bu.Height <= b.bmcLinkStatus.Verifier.Height { + return + } + rm.BlockUpdates = append(rm.BlockUpdates, bu) + b.log.Debugf("addRelayMessage rms:%d bu:%d ~ %d", len(b.rms), rm.BlockUpdates[0].Height, bu.Height) + } +} + +// updateRelayMessage updates messages in the buffered rms +func (b *BTP) updateRelayMessages(verifierHeight int64, rxSeq int64) (err error) { + b.rmsMutex.Lock() + defer b.rmsMutex.Unlock() + + rrm := 0 + for i, rm := range b.rms { + if len(rm.ReceiptProofs) > 0 { + b.updateReceiptProofs(rm, rxSeq) + } + + if rm.BlockProof != nil { + // BTP.store.MTA < BMCStatus.Link.Verifier.Height + // buffered rm ReceiptProof.Height < BMCStatus.Link.Verifier.Height + // can't use BlockProof at ReceiptProof.Height + if len(rm.ReceiptProofs) > 0 { + if rm.BlockProof, err = b.newBlockProof(rm.BlockProof.BlockWitness.Height, rm.BlockProof.Header); err != nil { + b.log.Tracef("updateRelayMessages: rm: %d bp at %d", i, rm.BlockProof.BlockWitness.Height) + return + } + } else { + rrm = i + 1 + } + } + + if len(rm.BlockUpdates) > 0 { + rbu := verifierHeight - rm.BlockUpdates[0].Height + 1 + if rbu < 1 { + break + } + + if rbu >= int64(len(rm.BlockUpdates)) { + if len(rm.ReceiptProofs) > 0 { + lbu := rm.BlockUpdates[len(rm.BlockUpdates)-1] + if rm.BlockProof, err = b.newBlockProof(lbu.Height, lbu.Header); err != nil { + b.log.Tracef("updateRelayMessages: fails rm: %d bp at %d", i, lbu.Height) + return + } + b.log.Debugf("updateRelayMessage rm:%d removeBlockUpdates %d ~ %d", + rm.Seq, + rm.BlockUpdates[0].Height, + lbu.Height) + + rm.BlockUpdates = make([]*chain.BlockUpdate, 0) + } else { + rrm = i + 1 + } + } else { + b.log.Debugf("updateRelayMessage rm:%d removeBlockUpdates %d ~ %d", + rm.Seq, + rm.BlockUpdates[0].Height, + rm.BlockUpdates[rbu-1].Height) + + rm.BlockUpdates = rm.BlockUpdates[rbu:] + } + } + } + + if rrm > 0 { + b.log.Debugf("updateRelayMessage rms:%d removeRelayMessage %d ~ %d", + len(b.rms), + b.rms[0].Seq, + b.rms[rrm-1].Seq) + + b.rms = b.rms[rrm:] + if len(b.rms) == 0 { + b.newRelayMessage() + } + } + return +} + +// updateMTA updates Merkle Tree Accumulator +func (b *BTP) updateMTA(bu *chain.BlockUpdate) { + next := b.store.Height() + 1 + if next < bu.Height { + b.log.Fatalf("found missing block next:%d bu:%d", next, bu.Height) + return + } + + if next == bu.Height { + b.store.AddHash(bu.BlockHash) + if err := b.store.Flush(); err != nil { + //TODO MTA Flush error handling + b.log.Fatalf("fail to MTA Flush err:%+v", err) + } + } +} + +// updateResult updates TransactionResult of the segment +func (b *BTP) updateResult(rm *chain.RelayMessage, segment *chain.Segment) (err error) { + + b.wp.Submit(func() { + segment.TransactionResult, err = b.sender.GetResult(segment.GetResultParam) + if err != nil { + if ec, ok := errors.CoderOf(err); ok { + b.log.Debugf("fail to GetResult GetResultParam:%v ErrorCoder:%+v", segment.GetResultParam, ec) + switch ec.ErrorCode() { + case chain.BMVRevertInvalidSequence, chain.BMVRevertInvalidBlockUpdateLower: + // FIXME this can cause BMR run with error for very longtime and still cost transaction fee + for i := 0; i < len(rm.Segments); i++ { + if rm.Segments[i] == segment { + rm.RemoveSegment(i) + break + } + } + case chain.BMVRevertInvalidBlockWitnessOld: + rm.BlockProof, err = b.newBlockProof(rm.BlockProof.BlockWitness.Height, rm.BlockProof.Header) + b.sender.UpdateSegment(rm.BlockProof, segment) + segment.GetResultParam = nil + case chain.BMVRevertInvalidSequenceHigher, chain.BMVRevertInvalidBlockUpdateHigher, chain.BMVRevertInvalidBlockProofHigher: + segment.GetResultParam = nil + case chain.BMCRevertUnauthorized: + segment.GetResultParam = nil + default: + b.log.Panicf("fail to GetResult GetResultParam:%v ErrorCoder:%+v", segment.GetResultParam, ec) + } + } else { + b.log.Panicf("fail to GetResult GetResultParam:%v err:%+v", segment.GetResultParam, err) + } + } + }) + return nil +} + +// dst.BMCStatusLink.RxSequence = 5 +// btp.rms[0].ReceiptProofs[0].TxSeq = 4 +// remove btp.rms[0] +func (b *BTP) updateReceiptProofs(rm *chain.RelayMessage, rxSeq int64) { + rrp := 0 + for j, rp := range rm.ReceiptProofs { + revt := rxSeq - rp.Events[0].Sequence + 1 + if revt < 1 { + break + } + + if revt >= int64(len(rp.Events)) { + rrp = j + 1 + } else { + rp.Events = rp.Events[revt:] + if len(rp.EventProofs) > 0 { + rp.EventProofs = rp.EventProofs[revt:] + } + } + } + + if rrp > 0 { + rm.ReceiptProofs = rm.ReceiptProofs[rrp:] + } +} diff --git a/btp/btp_test.go b/btp/btp_test.go new file mode 100644 index 00000000..507ba33e --- /dev/null +++ b/btp/btp_test.go @@ -0,0 +1,62 @@ +package btp + +import ( + "errors" + "testing" + + "github.com/gammazero/workerpool" + "github.com/icon-project/btp/chain" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestBTP_UpdateResult(t *testing.T) { + mockSender := &chain.MockSender{} + + btp := &BTP{ + sender: mockSender, + wp: workerpool.New(DefaultMaxWorkers), + } + + t.Run("should panic if error in unknown", func(t *testing.T) { + t.Skip() + }) + + t.Run("should panic if error is not decodable", func(t *testing.T) { + t.Skip() + }) + + t.Run("should remove segment when receive BMVRevertInvalidSequence, BMVRevertInvalidBlockUpdateLower", func(t *testing.T) { + t.Skip() + segment := &chain.Segment{ + GetResultParam: mock.AnythingOfType("chain.GetResultParam"), + } + rm := &chain.RelayMessage{ + Segments: make([]*chain.Segment, 3), + } + rm.Segments = append(rm.Segments, segment) + + mockSender.On( + "GetResult", + segment.GetResultParam, + ).Return( + mock.AnythingOfType("chain.TransactionResult"), + errors.New(chain.BMVRevertCodeNames[chain.BMVRevertInvalidSequence]), + ).Once() + + btp.updateResult(rm, segment) + assert.Len(t, rm.Segments, 3) + }) + + t.Run("should call senderUpdateSegment when receive BMVRevertInvalidBlockWitnessOld", func(t *testing.T) { + t.Skip() + }) + + t.Run("should call only set segment.GetResultParam = nil when receive BMVRevertInvalidSequenceHigher or BMVRevertInvalidBlockUpdateHigher, BMVRevertInvalidBlockProofHigher", func(t *testing.T) { + t.Skip() + }) + + t.Run("should call only set segment.GetResultParam = nil when receiver BMCRevertUnauthorized", func(t *testing.T) { + t.Skip() + }) +} diff --git a/cmd/btpsimple/chain/config.go b/btp/config.go similarity index 73% rename from cmd/btpsimple/chain/config.go rename to btp/config.go index 91eb6bf4..b83b3582 100644 --- a/cmd/btpsimple/chain/config.go +++ b/btp/config.go @@ -14,23 +14,23 @@ * limitations under the License. */ -package chain +package btp import ( - "github.com/icon-project/btp/cmd/btpsimple/module" + "github.com/icon-project/btp/chain" "github.com/icon-project/btp/common/config" ) type BaseConfig struct { - Address module.BtpAddress `json:"address"` - Endpoint string `json:"endpoint"` + Address chain.BtpAddress `json:"address"` + Endpoint string `json:"endpoint"` Options map[string]interface{} `json:"options,omitempty"` } type Config struct { config.FileConfig `json:",squash"` //instead of `mapstructure:",squash"` - Src BaseConfig `json:"src"` - Dst BaseConfig `json:"dst"` + Src BaseConfig `json:"src"` + Dst BaseConfig `json:"dst"` - Offset int64 `json:"offset"` + syncWithBmvHeight bool `json:"sync_with_bmv_height` } diff --git a/btp/rotate.go b/btp/rotate.go new file mode 100644 index 00000000..a7fefa80 --- /dev/null +++ b/btp/rotate.go @@ -0,0 +1,95 @@ +package btp + +import ( + "math" + "sync/atomic" + + "github.com/icon-project/btp/chain" +) + +// bmrIndex returns the index of the current BRM in BMCStatusLink +func (b *BTP) bmrIndex() int { + for i, bmr := range b.bmcLinkStatus.BMRs { + if bmr.Address == b.wallet.Address() { + return i + } + } + + b.log.Warn("BMR not found") + return -1 +} + +// HeightOfDst return turn the current heigh of the BMC destination +func (b *BTP) HeightOfDst() int64 { + return atomic.LoadInt64(&b.heightOfDst) +} + +func (b *BTP) relayableIndex(rotate int) int { + bs := b.bmcLinkStatus + relaybleIndex := bs.BMRIndex + + if rotate > 0 { + relaybleIndex += rotate + if relaybleIndex >= len(bs.BMRs) { + relaybleIndex = relaybleIndex % len(bs.BMRs) + } + } + + return relaybleIndex +} + +func (b *BTP) overMaxAggregation(rm *chain.RelayMessage) bool { + return len(rm.BlockUpdates) >= b.bmcLinkStatus.MaxAggregation +} + +func (b *BTP) relayble(rm *chain.RelayMessage) bool { + bs := b.bmcLinkStatus + if bs.RotateTerm <= 0 { + b.log.Debugf("bs.RotateTerm:%v", bs.RotateTerm) + return false + } + + rotate := int(math.Ceil(float64(b.HeightOfDst()+1-bs.RotateHeight) / float64(bs.RotateTerm))) + relayableIndex := b.relayableIndex(rotate) + relaybleHeightEnd := bs.RotateHeight + if rotate > 0 { + relaybleHeightEnd += int64(bs.RotateTerm * rotate) + } + + b.log.Tracef("relayableIndex:%v b.bmrIndex:%v b.overMaxAggregation(rm):%v", relayableIndex, b.bmrIndex(), b.overMaxAggregation(rm)) + + prevFinalizeHeight := relaybleHeightEnd - int64(bs.RotateTerm) + int64(b.sender.FinalizeLatency()) + return (relayableIndex == b.bmrIndex()) && + (prevFinalizeHeight <= b.HeightOfDst()) && + (relaybleHeightEnd == b.HeightOfDst()+1 || b.overMaxAggregation(rm)) +} + +func (b *BTP) skippable(rm *chain.RelayMessage) bool { + if len(rm.ReceiptProofs) < 0 { + return false + } + + bs := b.bmcLinkStatus + if bs.RotateTerm <= 0 { + return true + } + + rotate := 0 + relaybleHeightStart := bs.RotateHeight - int64(bs.RotateTerm+1) + if rm.HeightOfDst > bs.RotateHeight { + rotate = int(math.Ceil(float64(rm.HeightOfDst-bs.RotateHeight) / float64(bs.RotateTerm))) + if rotate > 0 { + relaybleHeightStart += int64(bs.RotateTerm * (rotate - 1)) + } + } + + skip := int(math.Ceil(float64(b.HeightOfDst()+1-rm.HeightOfDst)/float64(bs.DelayLimit))) - 1 + if skip > 0 { + rotate += skip + relaybleHeightStart = rm.HeightOfDst + int64(bs.DelayLimit*skip) + } + + relaybleIndex := b.relayableIndex(rotate) + prevFinalizeHeight := relaybleHeightStart + int64(b.sender.FinalizeLatency()) + return (relaybleIndex == b.bmrIndex()) && (prevFinalizeHeight <= b.HeightOfDst()) +} diff --git a/btp/util.go b/btp/util.go new file mode 100644 index 00000000..5a05929e --- /dev/null +++ b/btp/util.go @@ -0,0 +1,129 @@ +package btp + +import ( + "path/filepath" + + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/common/db" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/mta" +) + +func (b *BTP) prepareDatabase(offset int64) error { + b.log.Debugln("open database", filepath.Join(b.cfg.AbsBaseDir(), b.bmcDstBtpAddress.NetworkAddress())) + database, err := db.Open(b.cfg.AbsBaseDir(), string(DefaultDBType), b.bmcDstBtpAddress.NetworkAddress()) + if err != nil { + return errors.Wrap(err, "fail to open database") + } + defer func() { + if err != nil { + database.Close() + } + }() + + var bk db.Bucket + if bk, err = database.GetBucket("Accumulator"); err != nil { + return err + } + k := []byte("Accumulator") + if offset < 0 { + offset = 0 + } + b.store = mta.NewExtAccumulator(k, bk, offset) + if bk.Has(k) { + if err = b.store.Recover(); err != nil { + b.log.Panicf("fail to acc.Recover cause:%v", err) + } + b.log.Debugf("recover Accumulator offset:%d, height:%d", b.store.Offset(), b.store.Height()) + // b.log.Fatal("Stop") + //TODO sync offset + } + + return nil +} + +func (b *BTP) receiveHeight() int64 { + mtaHeight := b.store.Height() + if b.cfg.syncWithBmvHeight { + if b.store.Height() < b.bmcLinkStatus.Verifier.Height { + b.log.Panicf("not allow to sycn with BMV height, local mta height %d not catched up", mtaHeight) + } + b.log.Warnf("sync with BMV height, might skip next BTP message") + return b.bmcLinkStatus.Verifier.Height + } + + if mtaHeight < b.bmcLinkStatus.Verifier.Offset { + mtaHeight = b.bmcLinkStatus.Verifier.Offset + } + + mtaHeight += 1 + receiveHeight := b.bmcLinkStatus.Verifier.LastHeight + if mtaHeight < receiveHeight { + receiveHeight = mtaHeight + } + + return receiveHeight +} + +// newRelayMessage initializes an empty RelayMessage into the bufferred rms +func (b *BTP) newRelayMessage() *chain.RelayMessage { + rm := &chain.RelayMessage{ + From: b.bmcSrcBtpAddress, + BlockUpdates: make([]*chain.BlockUpdate, 0), + Seq: b.rmSeq, + } + b.rms = append(b.rms, rm) + b.rmSeq++ + + return rm +} + +// newBlockProof creates a new BlockProof +func (b *BTP) newBlockProof(height int64, header []byte) (*chain.BlockProof, error) { + at, w, err := b.store.WitnessForAt(height, b.bmcLinkStatus.Verifier.Height, b.bmcLinkStatus.Verifier.Offset) + if err != nil { + return nil, err + } + + bp := &chain.BlockProof{ + Header: header, + BlockWitness: &chain.BlockWitness{ + Height: at, + Witness: mta.WitnessesToHashes(w), + }, + } + + b.log.Debugf("newBlockProof height:%d, at:%d, w:%x", height, at, bp.BlockWitness.Witness) + return bp, nil +} + +func (b *BTP) logRelaying(prefix string, rm *chain.RelayMessage, segment *chain.Segment, segmentIdx int) { + if segment == nil { + if len(rm.BlockUpdates) > 0 { + b.log.Debugf("%s rm:%d bu:%d ~ %d rps:%d", + prefix, + rm.Seq, + rm.BlockUpdates[0].Height, + rm.BlockUpdates[len(rm.BlockUpdates)-1].Height, + len(rm.ReceiptProofs)) + } else { + b.log.Debugf("%s rm:%d bp:%d ~ %d rps:%d", + prefix, + rm.Seq, + rm.BlockProof.BlockWitness.Height, + rm.ReceiptProofs[0], + len(rm.ReceiptProofs)) + } + + } else { + b.log.Debugf("%s rm:%d [i:%d,h:%d,bu:%d,seq:%d,evt:%d,txh:%v]", + prefix, + rm.Seq, + segmentIdx, + segment.Height, + segment.NumberOfBlockUpdate, + segment.EventSequence, + segment.NumberOfEvent, + segment.GetResultParam) + } +} diff --git a/btp/util_test.go b/btp/util_test.go new file mode 100644 index 00000000..bc02203f --- /dev/null +++ b/btp/util_test.go @@ -0,0 +1,22 @@ +package btp + +import ( + "testing" +) + +func TestBTP_PrepareDatabaset(t *testing.T) { + // mockSender := &chain.MockSender{} + + // btp := &BTP{ + // sender: mockSender, + // wp: workerpool.New(DefaultMaxWorkers), + // } + + t.Run("should call database.Open() and defer call database.Close()", func(t *testing.T) { + t.Skip() + }) + + t.Run("should assign BTP.store with ExtAccumulator", func(t *testing.T) { + t.Skip() + }) +} diff --git a/cmd/btpsimple/module/btpaddress.go b/chain/btpaddress.go similarity index 99% rename from cmd/btpsimple/module/btpaddress.go rename to chain/btpaddress.go index a723a932..099da087 100644 --- a/cmd/btpsimple/module/btpaddress.go +++ b/chain/btpaddress.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package module +package chain import ( "fmt" @@ -98,4 +98,3 @@ func ValidateBtpAddress(ba BtpAddress) error { } return nil } - diff --git a/cmd/btpsimple/module/error.go b/chain/error.go similarity index 99% rename from cmd/btpsimple/module/error.go rename to chain/error.go index 606bc1c2..58a16547 100644 --- a/cmd/btpsimple/module/error.go +++ b/chain/error.go @@ -14,7 +14,7 @@ * limitations under the License. */ -package module +package chain import ( "fmt" diff --git a/cmd/btpsimple/module/icon/client.go b/chain/icon/client.go similarity index 68% rename from cmd/btpsimple/module/icon/client.go rename to chain/icon/client.go index d42e95f4..a82617ee 100644 --- a/cmd/btpsimple/module/icon/client.go +++ b/chain/icon/client.go @@ -17,10 +17,10 @@ package icon import ( + "bytes" "encoding/base64" "encoding/json" "fmt" - "io" "net/http" "reflect" "strconv" @@ -29,8 +29,8 @@ import ( "time" "github.com/gorilla/websocket" - - "github.com/icon-project/btp/cmd/btpsimple/module" + "github.com/hashicorp/go-retryablehttp" + "github.com/icon-project/btp/chain" "github.com/icon-project/btp/common/crypto" "github.com/icon-project/btp/common/jsonrpc" "github.com/icon-project/btp/common/log" @@ -39,8 +39,8 @@ import ( const ( DefaultSendTransactionRetryInterval = 3 * time.Second //3sec DefaultGetTransactionResultPollingInterval = 1500 * time.Millisecond //1.5sec - DefaultKeepAliveInterval = 10 * time.Second - DefaultPingWait = 3 * time.Second + maxRetryReadMethod = 10 + maxRetryReadMethodInterval = time.Second ) type Wallet interface { @@ -50,13 +50,49 @@ type Wallet interface { type Client struct { *jsonrpc.Client - conns map[string]*websocket.Conn + conns map[string]*jsonrpc.RecConn l log.Logger mtx sync.Mutex } +func countBytesOfCompactJSON(jsonData interface{}) int { + data, err := json.Marshal(jsonData) + if err != nil { + return txMaxDataSize + } + + if len(data) == 0 { + return txMaxDataSize + } + b := bytes.NewBuffer(nil) + if err := json.Compact(b, data); err != nil { + return txMaxDataSize + } + return b.Len() +} + var txSerializeExcludes = map[string]bool{"signature": true} +func (c *Client) do(method string, reqPtr, respPtr interface{}) (jrResp *jsonrpc.Response, err error) { + var resp *jsonrpc.Response + tries := 0 + for { + tries++ + resp, err = c.Do(method, reqPtr, respPtr) + if err != nil && tries < maxRetryReadMethod && strings.HasPrefix(method, "icx_get") { + if je, ok := err.(*jsonrpc.Error); ok { + switch je.Code { + case JsonrpcErrorCodePending, JsonrpcErrorCodeExecuting, JsonrpcErrorCodeNotFound: + <-time.After(maxRetryReadMethodInterval) + c.l.Tracef("do: retry %d with method %s err:%+v", tries, method, err) + continue + } + } + } + return resp, err + } +} + func (c *Client) SignTransaction(w Wallet, p *TransactionParam) error { p.Timestamp = NewHexInt(time.Now().UnixNano() / int64(time.Microsecond)) js, err := json.Marshal(p) @@ -80,34 +116,34 @@ func (c *Client) SignTransaction(w Wallet, p *TransactionParam) error { } func (c *Client) SendTransaction(p *TransactionParam) (*HexBytes, error) { var result HexBytes - if _, err := c.Do("icx_sendTransaction", p, &result); err != nil { + if _, err := c.do("icx_sendTransaction", p, &result); err != nil { return nil, err } return &result, nil } func (c *Client) SendTransactionAndWait(p *TransactionParam) (*HexBytes, error) { var result HexBytes - if _, err := c.Do("icx_sendTransactionAndWait", p, &result); err != nil { + if _, err := c.do("icx_sendTransactionAndWait", p, &result); err != nil { return nil, err } return &result, nil } func (c *Client) GetTransactionResult(p *TransactionHashParam) (*TransactionResult, error) { tr := &TransactionResult{} - if _, err := c.Do("icx_getTransactionResult", p, tr); err != nil { + if _, err := c.do("icx_getTransactionResult", p, tr); err != nil { return nil, err } return tr, nil } func (c *Client) WaitTransactionResult(p *TransactionHashParam) (*TransactionResult, error) { tr := &TransactionResult{} - if _, err := c.Do("icx_waitTransactionResult", p, tr); err != nil { + if _, err := c.do("icx_waitTransactionResult", p, tr); err != nil { return nil, err } return tr, nil } func (c *Client) Call(p *CallParam, r interface{}) error { - _, err := c.Do("icx_call", p, r) + _, err := c.do("icx_call", p, r) return err } func (c *Client) SendTransactionAndGetResult(p *TransactionParam) (*HexBytes, *TransactionResult, error) { @@ -117,7 +153,7 @@ txLoop: txh, err := c.SendTransaction(p) if err != nil { switch err { - case module.ErrSendFailByOverflow: + case chain.ErrSendFailByOverflow: //TODO Retry max time.Sleep(DefaultSendTransactionRetryInterval) c.l.Debugf("Retry SendTransaction") @@ -168,7 +204,7 @@ txrLoop: func (c *Client) GetBlockByHeight(p *BlockHeightParam) (*Block, error) { result := &Block{} - if _, err := c.Do("icx_getBlockByHeight", p, &result); err != nil { + if _, err := c.do("icx_getBlockByHeight", p, &result); err != nil { return nil, err } return result, nil @@ -176,21 +212,21 @@ func (c *Client) GetBlockByHeight(p *BlockHeightParam) (*Block, error) { func (c *Client) GetBlockHeaderByHeight(p *BlockHeightParam) ([]byte, error) { var result []byte - if _, err := c.Do("icx_getBlockHeaderByHeight", p, &result); err != nil { + if _, err := c.do("icx_getBlockHeaderByHeight", p, &result); err != nil { return nil, err } return result, nil } func (c *Client) GetVotesByHeight(p *BlockHeightParam) ([]byte, error) { var result []byte - if _, err := c.Do("icx_getVotesByHeight", p, &result); err != nil { + if _, err := c.do("icx_getVotesByHeight", p, &result); err != nil { return nil, err } return result, nil } func (c *Client) GetDataByHash(p *DataHashParam) ([]byte, error) { var result []byte - _, err := c.Do("icx_getDataByHash", p, &result) + _, err := c.do("icx_getDataByHash", p, &result) if err != nil { return nil, err } @@ -198,29 +234,33 @@ func (c *Client) GetDataByHash(p *DataHashParam) ([]byte, error) { } func (c *Client) GetProofForResult(p *ProofResultParam) ([][]byte, error) { var result [][]byte - if _, err := c.Do("icx_getProofForResult", p, &result); err != nil { + if _, err := c.do("icx_getProofForResult", p, &result); err != nil { return nil, err } return result, nil } func (c *Client) GetProofForEvents(p *ProofEventsParam) ([][][]byte, error) { var result [][][]byte - if _, err := c.Do("icx_getProofForEvents", p, &result); err != nil { + if _, err := c.do("icx_getProofForEvents", p, &result); err != nil { return nil, err } return result, nil } -func (c *Client) MonitorBlock(p *BlockRequest, cb func(conn *websocket.Conn, v *BlockNotification) error, scb func(conn *websocket.Conn), errCb func(*websocket.Conn, error)) error { +func (c *Client) MonitorBlock(p *BlockRequest, cb func(conn *jsonrpc.RecConn, v *BlockNotification) error, scb func(conn *jsonrpc.RecConn), errCb func(*jsonrpc.RecConn, error)) error { resp := &BlockNotification{} - return c.Monitor("/block", p, resp, func(conn *websocket.Conn, v interface{}) { + reconP := p + + return c.Monitor("/block", p, resp, reconP, func(conn *jsonrpc.RecConn, v interface{}) { switch t := v.(type) { case *BlockNotification: + h, _ := t.Height.Int() + reconP.Height = NewHexInt(int64(h + 1)) if err := cb(conn, t); err != nil { c.l.Debugf("MonitorBlock callback return err:%+v", err) } case WSEvent: - c.l.Debugf("MonitorBlock WSEvent %s %+v", conn.LocalAddr().String(), t) + c.l.Debugf("MonitorBlock WSEvent %s %+v", conn.Id, t) switch t { case WSEventInit: if scb != nil { @@ -235,9 +275,9 @@ func (c *Client) MonitorBlock(p *BlockRequest, cb func(conn *websocket.Conn, v * }) } -func (c *Client) MonitorEvent(p *EventRequest, cb func(conn *websocket.Conn, v *EventNotification) error, errCb func(*websocket.Conn, error)) error { +func (c *Client) MonitorEvent(p *EventRequest, cb func(conn *jsonrpc.RecConn, v *EventNotification) error, errCb func(*jsonrpc.RecConn, error)) error { resp := &EventNotification{} - return c.Monitor("/event", p, resp, func(conn *websocket.Conn, v interface{}) { + return c.Monitor("/event", p, resp, nil, func(conn *jsonrpc.RecConn, v interface{}) { switch t := v.(type) { case *EventNotification: if err := cb(conn, t); err != nil { @@ -251,87 +291,95 @@ func (c *Client) MonitorEvent(p *EventRequest, cb func(conn *websocket.Conn, v * }) } -func (c *Client) Monitor(reqUrl string, reqPtr, respPtr interface{}, cb wsReadCallback) error { +func (c *Client) Monitor(reqUrl string, reqPtr, respPtr, reconReqPtr interface{}, cb wsReadCallback) error { if cb == nil { return fmt.Errorf("callback function cannot be nil") } conn, err := c.wsConnect(reqUrl, nil) if err != nil { - return module.ErrConnectFail + return chain.ErrConnectFail } defer func() { - c.l.Debugf("Monitor finish %s", conn.LocalAddr().String()) + c.l.Debugf("Monitor finish %s", conn.Id) c.wsClose(conn) }() if err = c.wsRequest(conn, reqPtr); err != nil { return err } + firstCbCalled := false cb(conn, WSEventInit) - return c.wsReadJSONLoop(conn, respPtr, cb) + firstCbCalled = true + elem := reflect.ValueOf(respPtr).Elem() + for { + v := reflect.New(elem.Type()) + ptr := v.Interface() + if _, ok := c.conns[conn.Id]; !ok { + c.l.Debugf("Monitor c.conns[%s] is nil", conn.Id) + return nil + } + + if !conn.IsConnected() { + c.l.Debugf("Monitor c.conns[%s] not connected", conn.Id) + firstCbCalled = false + time.Sleep(conn.RecIntvlMin) + continue + } + + if firstCbCalled { + if err := conn.ReadJSON(ptr); err != nil { + c.l.Debugf("Monitor c.conns[%s] ReadJSON err:%+v", conn.Id, err) + continue + } + cb(conn, ptr) + } else { + c.l.Debugf("Monitor c.conns[%s] reconnecting", conn.Id) + if err = c.wsRequest(conn, reconReqPtr); err != nil { + c.l.Debugf("Monitor c.conns[%s] request monitor err:%+v", conn.Id, err) + continue + } + cb(conn, WSEventInit) + firstCbCalled = true + } + } } -func (c *Client) CloseMonitor(conn *websocket.Conn) { - c.l.Debugf("CloseMonitor %s", conn.LocalAddr().String()) +func (c *Client) CloseMonitor(conn *jsonrpc.RecConn) { + c.l.Debugf("CloseMonitor %s", conn.Id) c.wsClose(conn) } func (c *Client) CloseAllMonitor() { for _, conn := range c.conns { - c.l.Debugf("CloseAllMonitor %s", conn.LocalAddr().String()) + c.l.Debugf("CloseAllMonitor %s", conn.Id) c.wsClose(conn) } } -type wsReadCallback func(*websocket.Conn, interface{}) +type wsReadCallback func(*jsonrpc.RecConn, interface{}) -func (c *Client) _addWsConn(conn *websocket.Conn) { +func (c *Client) _addWsConn(conn *jsonrpc.RecConn) { c.mtx.Lock() defer c.mtx.Unlock() - la := conn.LocalAddr().String() + la := conn.Id c.conns[la] = conn } -func (c *Client) _removeWsConn(conn *websocket.Conn) { +func (c *Client) _removeWsConn(conn *jsonrpc.RecConn) { c.mtx.Lock() defer c.mtx.Unlock() - la := conn.LocalAddr().String() + la := conn.Id _, ok := c.conns[la] if ok { delete(c.conns, la) } } -type wsConnectError struct { - error - httpResp *http.Response -} - -func (c *Client) keepAlive(conn *websocket.Conn) { - go func() { - ticker := time.NewTicker(DefaultKeepAliveInterval) - defer ticker.Stop() - - for { - <-ticker.C - if err := conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(DefaultPingWait)); err != nil { - c.l.Warn("fail to WriteControl err: %v", err) - return - } - } - }() -} - -func (c *Client) wsConnect(reqUrl string, reqHeader http.Header) (*websocket.Conn, error) { +func (c *Client) wsConnect(reqUrl string, reqHeader http.Header) (*jsonrpc.RecConn, error) { wsEndpoint := strings.Replace(c.Endpoint, "http", "ws", 1) - conn, httpResp, err := websocket.DefaultDialer.Dial(wsEndpoint+reqUrl, reqHeader) - if err != nil { - wsErr := wsConnectError{error: err} - wsErr.httpResp = httpResp - return nil, wsErr - } - c.keepAlive(conn) + conn := &jsonrpc.RecConn{} + conn.Dial(wsEndpoint+reqUrl, reqHeader) c._addWsConn(conn) return conn, nil } @@ -341,7 +389,7 @@ type wsRequestError struct { wsResp *WSResponse } -func (c *Client) wsRequest(conn *websocket.Conn, reqPtr interface{}) error { +func (c *Client) wsRequest(conn *jsonrpc.RecConn, reqPtr interface{}) error { if reqPtr == nil { log.Panicf("reqPtr cannot be nil") } @@ -363,53 +411,27 @@ func (c *Client) wsRequest(conn *websocket.Conn, reqPtr interface{}) error { return nil } -func (c *Client) wsClose(conn *websocket.Conn) { +func (c *Client) wsClose(conn *jsonrpc.RecConn) { c._removeWsConn(conn) if err := conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")); err != nil { c.l.Debugf("fail to WriteMessage CloseNormalClosure err:%+v", err) } - if err := conn.Close(); err != nil { - c.l.Debugf("fail to Close err:%+v", err) - } -} - -func (c *Client) wsRead(conn *websocket.Conn, respPtr interface{}) error { - mt, r, err := conn.NextReader() - if err != nil { - return err - } - if mt == websocket.CloseMessage { - return io.EOF - } - return json.NewDecoder(r).Decode(respPtr) -} -func (c *Client) wsReadJSONLoop(conn *websocket.Conn, respPtr interface{}, cb wsReadCallback) error { - elem := reflect.ValueOf(respPtr).Elem() - for { - v := reflect.New(elem.Type()) - ptr := v.Interface() - if _, ok := c.conns[conn.LocalAddr().String()]; !ok { - c.l.Debugf("wsReadJSONLoop c.conns[%s] is nil", conn.LocalAddr().String()) - return nil - } - if err := c.wsRead(conn, ptr); err != nil { - c.l.Debugf("wsReadJSONLoop c.conns[%s] ReadJSON err:%+v", conn.LocalAddr().String(), err) - if cErr, ok := err.(*websocket.CloseError); !ok || cErr.Code != websocket.CloseNormalClosure { - cb(conn, err) - } - return err - } - cb(conn, ptr) - } + conn.Close() } func NewClient(uri string, l log.Logger) *Client { + //TODO options {MaxRetrySendTx, MaxRetryGetResult, MaxIdleConnsPerHost, Debug, Dump} - tr := &http.Transport{MaxIdleConnsPerHost: 1000} + + retryClient := retryablehttp.NewClient() + retryClient.RetryMax = 10 + standardClient := retryClient.StandardClient() + standardClient.Transport = &http.Transport{MaxIdleConnsPerHost: 1000} + c := &Client{ - Client: jsonrpc.NewJsonRpcClient(&http.Client{Transport: tr}, uri), - conns: make(map[string]*websocket.Conn), + Client: jsonrpc.NewJsonRpcClient(standardClient, uri), + conns: make(map[string]*jsonrpc.RecConn), l: l, } opts := IconOptions{} diff --git a/chain/icon/error.go b/chain/icon/error.go new file mode 100644 index 00000000..101c2789 --- /dev/null +++ b/chain/icon/error.go @@ -0,0 +1,13 @@ +package icon + +import "errors" + +var ( + ErrBlockNotReady = errors.New("required result to be 32 bytes, but got 0") + ErrInvalidBlockUpdateProofSize = errors.New("invalid BlockUpdate.Proof size") + ErrInvalidBlockProofSize = errors.New("invalid BlockProof size") + ErrInvalidStateProofSize = errors.New("invalid StateProof.Proof size") + ErrInvalidReceiptProofSize = errors.New("invalid ReceiptProof.Proof size") + ErrInvalidEventProofProofSize = errors.New("invalid EventProof.Proof size") + ErrNotSupportedSrcChain = errors.New("not supported source chain") +) diff --git a/chain/icon/pra_bmv.go b/chain/icon/pra_bmv.go new file mode 100644 index 00000000..e0843e04 --- /dev/null +++ b/chain/icon/pra_bmv.go @@ -0,0 +1,185 @@ +package icon + +import ( + "time" + + "github.com/icon-project/btp/common/log" +) + +const ( + maxDefaultRetries = 10 + defaultRetryDelay = time.Second +) + +type PraBmvClient struct { + *Client + address string + log log.Logger +} + +type PraBmvStatus struct { + RelayMtaHeight int64 + RelayMtaOffset int64 + RelaySetId int64 + ParaMtaHeight int64 +} + +func NewPraBmvClient(url string, address string, log log.Logger) PraBmvClient { + pC := PraBmvClient{ + Client: NewClient(url, log), + address: address, + log: log, + } + + return pC +} + +func (c *PraBmvClient) GetRelayMtaHeight() uint64 { + p := &CallParam{ + ToAddress: Address(c.address), + DataType: "call", + Data: CallData{ + Method: "relayMtaHeight", + }, + } + + tries := 0 + for { + tries++ + var height HexInt + err := mapError(c.Call(p, &height)) + if err != nil && tries < maxDefaultRetries { + c.log.Debugf("getRelayMtaHeight: retry %d", tries) + <-time.After(defaultRetryDelay) + continue + } + + value, err := height.Value() + if err != nil { + c.log.Debugf("getRelayMtaHeight: failed") + } + + return uint64(value) + } +} + +func (c *PraBmvClient) GetRelayMtaOffset() uint64 { + p := &CallParam{ + ToAddress: Address(c.address), + DataType: "call", + Data: CallData{ + Method: "relayMtaOffset", + }, + } + + tries := 0 + for { + tries++ + var height HexInt + err := mapError(c.Call(p, &height)) + if err != nil && tries < maxDefaultRetries { + c.log.Debugf("getRelayMtaOffset: failed retry %d", tries) + <-time.After(defaultRetryDelay) + continue + } + + value, err := height.Value() + if err != nil { + c.log.Debugf("getRelayMtaOffset: failed") + } + + return uint64(value) + } +} + +func (c *PraBmvClient) GetParaMtaHeight() uint64 { + p := &CallParam{ + ToAddress: Address(c.address), + DataType: "call", + Data: CallData{ + Method: "paraMtaHeight", + }, + } + + tries := 0 + for { + tries++ + var height HexInt + err := mapError(c.Call(p, &height)) + if err != nil && tries < maxDefaultRetries { + c.log.Debugf("getParaMtaHeight: failed retry %d", tries) + <-time.After(defaultRetryDelay) + continue + } + + value, err := height.Value() + if err != nil { + c.log.Debugf("getParaMtaHeight: failed") + } + + return uint64(value) + } +} + +func (c *PraBmvClient) GetSetId() uint64 { + p := &CallParam{ + ToAddress: Address(c.address), + DataType: "call", + Data: CallData{ + Method: "setId", + }, + } + + tries := 0 + for { + tries++ + var setId HexInt + err := mapError(c.Call(p, &setId)) + if err != nil && tries < maxDefaultRetries { + c.log.Debugf("getSetId: failed retry %d", tries) + <-time.After(defaultRetryDelay) + continue + } + + value, err := setId.Value() + if err != nil { + c.log.Debugf("getSetId: failed") + } + + return uint64(value) + } +} + +func (c *PraBmvClient) GetPraBmvStatus() PraBmvStatus { + relayMtaHeightChan := make(chan int64) + relayMtaOffsetChan := make(chan int64) + relaySetIdChan := make(chan int64) + paraMtaHeightChan := make(chan int64) + + for i := 0; i < 4; i++ { + index := i + go func() { + switch index { + case 0: + defer close(relayMtaHeightChan) + relayMtaHeightChan <- int64(c.GetRelayMtaHeight()) + case 1: + defer close(relayMtaOffsetChan) + relayMtaOffsetChan <- int64(c.GetRelayMtaOffset()) + case 2: + defer close(relaySetIdChan) + relaySetIdChan <- int64(c.GetSetId()) + case 3: + defer close(paraMtaHeightChan) + paraMtaHeightChan <- int64(c.GetParaMtaHeight()) + } + }() + } + + return PraBmvStatus{ + RelayMtaHeight: <-relayMtaHeightChan, + RelayMtaOffset: <-relayMtaOffsetChan, + RelaySetId: <-relaySetIdChan, + ParaMtaHeight: <-paraMtaHeightChan, + } +} diff --git a/chain/icon/pra_bmv_test.go b/chain/icon/pra_bmv_test.go new file mode 100644 index 00000000..9458a07d --- /dev/null +++ b/chain/icon/pra_bmv_test.go @@ -0,0 +1,28 @@ +package icon + +import ( + "testing" + "time" + + "github.com/icon-project/btp/common/log" + "github.com/stretchr/testify/assert" +) + +func TestGetPraBmvStatus(t *testing.T) { + t.Skip("Manual test only") + c := NewPraBmvClient("https://btp.net.solidwallet.io/api/v3/icon_dex", "cx88fe82e2427432bb0b2ba6d75b3ef4e25eb9d085", log.New()) + + start1 := time.Now() + c.GetRelayMtaHeight() + c.GetRelayMtaOffset() + c.GetSetId() + c.GetParaMtaHeight() + elapsed1 := time.Since(start1) + + start2 := time.Now() + c.GetPraBmvStatus() + elapsed2 := time.Since(start2) + + t.Logf("%f %f", elapsed1.Seconds(), elapsed2.Seconds()) + assert.Greater(t, elapsed1, elapsed2) +} diff --git a/cmd/btpsimple/module/icon/receiver.go b/chain/icon/receiver.go similarity index 81% rename from cmd/btpsimple/module/icon/receiver.go rename to chain/icon/receiver.go index 99160f52..c69443ab 100644 --- a/cmd/btpsimple/module/icon/receiver.go +++ b/chain/icon/receiver.go @@ -21,12 +21,11 @@ import ( "encoding/json" "fmt" - "github.com/gorilla/websocket" - - "github.com/icon-project/btp/cmd/btpsimple/module" + "github.com/icon-project/btp/chain" "github.com/icon-project/btp/common" "github.com/icon-project/btp/common/codec" "github.com/icon-project/btp/common/crypto" + "github.com/icon-project/btp/common/jsonrpc" "github.com/icon-project/btp/common/log" "github.com/icon-project/btp/common/mpt" ) @@ -40,8 +39,8 @@ const ( type receiver struct { c *Client - src module.BtpAddress - dst module.BtpAddress + src chain.BtpAddress + dst chain.BtpAddress l log.Logger opt struct { } @@ -72,7 +71,7 @@ func (r *receiver) getBlockHeader(height HexInt) (*BlockHeader, error) { return &bh, nil } -func (r *receiver) newBlockUpdate(v *BlockNotification) (*module.BlockUpdate, error) { +func (r *receiver) newBlockUpdate(v *BlockNotification) (*chain.BlockUpdate, error) { bh, err := r.getBlockHeader(v.Height) if err != nil { return nil, err @@ -99,7 +98,7 @@ func (r *receiver) newBlockUpdate(v *BlockNotification) (*module.BlockUpdate, er update.Validators = nvb } - bu := &module.BlockUpdate{ + bu := &chain.BlockUpdate{ BlockHash: blkHash, Height: bh.Height, Header: bh.serialized, @@ -112,9 +111,9 @@ func (r *receiver) newBlockUpdate(v *BlockNotification) (*module.BlockUpdate, er return bu, nil } -func (r *receiver) newReceiptProofs(v *BlockNotification) ([]*module.ReceiptProof, error) { +func (r *receiver) newReceiptProofs(v *BlockNotification) ([]*chain.ReceiptProof, error) { nextEp := 0 - rps := make([]*module.ReceiptProof, 0) + rps := make([]*chain.ReceiptProof, 0) if len(v.Indexes) > 0 { l := v.Indexes[0] RpLoop: @@ -134,7 +133,7 @@ func (r *receiver) newReceiptProofs(v *BlockNotification) ([]*module.ReceiptProo bytes.Equal(el.Indexed[EventIndexNext], r.evtLogRawFilter.next) && bytes.Equal(el.Indexed[EventIndexSequence], r.evtLogRawFilter.seq) { r.isFoundOffsetBySeq = true - r.l.Debugln("onCatchUp found offset sequence", j, v) + r.l.Debugf("onCatchUp found offset sequence %d at %d event on block %s", r.evtLogRawFilter.seq, j, v.Height) if (j + 1) < len(p.Events) { nextEp = j + 1 break EpLoop @@ -146,22 +145,24 @@ func (r *receiver) newReceiptProofs(v *BlockNotification) ([]*module.ReceiptProo } } idx, _ := index.Value() - rp := &module.ReceiptProof{ + h, _ := v.Height.Value() + rp := &chain.ReceiptProof{ + Height: h, Index: int(idx), - EventProofs: make([]*module.EventProof, 0), + EventProofs: make([]*chain.EventProof, 0), } if rp.Proof, err = codec.RLP.MarshalToBytes(proofs[0]); err != nil { return nil, err } for k := nextEp; k < len(p.Events); k++ { eIdx, _ := p.Events[k].Value() - ep := &module.EventProof{ + ep := &chain.EventProof{ Index: int(eIdx), } if ep.Proof, err = codec.RLP.MarshalToBytes(proofs[k+1]); err != nil { return nil, err } - var evt *module.Event + var evt *chain.Event if evt, err = r.toEvent(proofs[k+1]); err != nil { return nil, err } @@ -175,7 +176,7 @@ func (r *receiver) newReceiptProofs(v *BlockNotification) ([]*module.ReceiptProo return rps, nil } -func (r *receiver) toEvent(proof [][]byte) (*module.Event, error) { +func (r *receiver) toEvent(proof [][]byte) (*chain.Event, error) { el, err := toEventLog(proof) if err != nil { return nil, err @@ -185,10 +186,10 @@ func (r *receiver) toEvent(proof [][]byte) (*module.Event, error) { bytes.Equal(el.Indexed[EventIndexNext], r.evtLogRawFilter.next) { var i common.HexInt i.SetBytes(el.Indexed[EventIndexSequence]) - evt := &module.Event{ - Next: module.BtpAddress(el.Indexed[EventIndexNext]), + evt := &chain.Event{ + Next: chain.BtpAddress(el.Indexed[EventIndexNext]), Sequence: i.Int64(), - Message: el.Data[0], + Message: el.Data[0], } return evt, nil } @@ -207,7 +208,7 @@ func toEventLog(proof [][]byte) (*EventLog, error) { return el, nil } -func (r *receiver) ReceiveLoop(height int64, seq int64, cb module.ReceiveCallback, scb func()) error { +func (r *receiver) ReceiveLoop(height int64, seq int64, cb chain.ReceiveCallback, scb func()) error { s := r.dst.String() ef := &EventFilter{ Addr: Address(r.src.ContractAddress()), @@ -236,10 +237,10 @@ func (r *receiver) ReceiveLoop(height int64, seq int64, cb module.ReceiveCallbac r.evtLogRawFilter.next = []byte(s) r.evtLogRawFilter.seq = common.NewHexInt(seq).Bytes() return r.c.MonitorBlock(r.evtReq, - func(conn *websocket.Conn, v *BlockNotification) error { + func(conn *jsonrpc.RecConn, v *BlockNotification) error { var err error - var bu *module.BlockUpdate - var rps []*module.ReceiptProof + var bu *chain.BlockUpdate + var rps []*chain.ReceiptProof if bu, err = r.newBlockUpdate(v); err != nil { return err } @@ -252,15 +253,15 @@ func (r *receiver) ReceiveLoop(height int64, seq int64, cb module.ReceiveCallbac } return nil }, - func(conn *websocket.Conn) { - r.l.Debugf("ReceiveLoop connected %s", conn.LocalAddr().String()) + func(conn *jsonrpc.RecConn) { + r.l.Debugf("ReceiveLoop connected %s", conn.Id) if scb != nil { scb() } }, - func(conn *websocket.Conn, err error) { - r.l.Debugf("onError %s err:%+v", conn.LocalAddr().String(), err) - _ = conn.Close() + func(conn *jsonrpc.RecConn, err error) { + r.l.Debugf("onError %s err:%+v", conn.Id, err) + conn.CloseAndReconnect() }) } @@ -268,7 +269,7 @@ func (r *receiver) StopReceiveLoop() { r.c.CloseAllMonitor() } -func NewReceiver(src, dst module.BtpAddress, endpoint string, opt map[string]interface{}, l log.Logger) module.Receiver { +func NewReceiver(src, dst chain.BtpAddress, endpoint string, opt map[string]interface{}, l log.Logger) chain.Receiver { r := &receiver{ src: src, dst: dst, diff --git a/chain/icon/receiver_test.go b/chain/icon/receiver_test.go new file mode 100644 index 00000000..35ca9b1a --- /dev/null +++ b/chain/icon/receiver_test.go @@ -0,0 +1,32 @@ +package icon + +import ( + "fmt" + "testing" + + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/common/log" + "github.com/stretchr/testify/assert" +) + +func TestReceiver_ReceiveLoop(t *testing.T) { + t.Run("should monitor from the given height", func(t *testing.T) { + t.Skip("This is for manual run") + r := NewReceiver("btp://0x53.icon/cxdc2a468aada7a4826176e87ae72d6ee24c50df0a", "btp://0x507.pra/0x6a436465184fA9b0b5f20fbeFADaF83CaC466ACD", "https://sejong.net.solidwallet.io/api/v3/icon_dex", nil, log.New()) + err := r.ReceiveLoop(560733, 1, func(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + assert.EqualValues(t, bu.Height, 560733) + assert.Equal(t, "0x6ee5f7dfc438f30818fe31351328bf3f84bb5791a77d1bba127fb7f7651f2b9f", fmt.Sprintf("0x%x", bu.BlockHash)) + assert.Equal(t, "0xf901bd0283088e5d8705c66fb357515295002ad7b8a8735bd42288a3536f84ef48ce3dc422daa0270495df6d85da73327b3f8325a2b1f1a2a56c55aec634404f1f46b543a769dca00d62cd556ad88c68cfc217ee5d58de14bd0de74d3dc05660a40d58753efc45d7a057b858ded1d7ccdd8ad8cebee01d7c6215e70ab90f14463084d48766de76d421f800a04901d63e276865e3f521de904daa1d1a8dc939e0989dbc800194b89df5d5c4f9a201002070482c1a0f0304410030886c3a1f100042a230689c522f188cc1c811a81c04b8eff8eda029edd8cf1ece7b28976cc8e545cfe93e55eb795388dfe3297c3c48a754c12deaf800a0a086ce7ac3c1950c2036186f951002476f20ebdb52aeecaef83bdee5bc44a8ebb8a7f8a5a0e9f536d238573c7221403cb7556abe727b25e97d7d19bc8c857b741c4fcc4eada01b36e9ff5b7b6a90d60e7e7668df097ca0f43cc897396e6d1b57634a7ffff581a0019768905b77979d96437fcfc2382f6bde9694799006d94e6932c9f7387c2d39a08e34656b341d36ab016e1dac35eafcb9d493075f40c02e617cf5065679336ef2a06496ce45c101862923051f8dd4cd5ddf8e608029672f5ab44191f71135ea0127", + fmt.Sprintf("0x%x", bu.Header), + ) + assert.Equal(t, "0xf907a9b901c0f901bd0283088e5d8705c66fb357515295002ad7b8a8735bd42288a3536f84ef48ce3dc422daa0270495df6d85da73327b3f8325a2b1f1a2a56c55aec634404f1f46b543a769dca00d62cd556ad88c68cfc217ee5d58de14bd0de74d3dc05660a40d58753efc45d7a057b858ded1d7ccdd8ad8cebee01d7c6215e70ab90f14463084d48766de76d421f800a04901d63e276865e3f521de904daa1d1a8dc939e0989dbc800194b89df5d5c4f9a201002070482c1a0f0304410030886c3a1f100042a230689c522f188cc1c811a81c04b8eff8eda029edd8cf1ece7b28976cc8e545cfe93e55eb795388dfe3297c3c48a754c12deaf800a0a086ce7ac3c1950c2036186f951002476f20ebdb52aeecaef83bdee5bc44a8ebb8a7f8a5a0e9f536d238573c7221403cb7556abe727b25e97d7d19bc8c857b741c4fcc4eada01b36e9ff5b7b6a90d60e7e7668df097ca0f43cc897396e6d1b57634a7ffff581a0019768905b77979d96437fcfc2382f6bde9694799006d94e6932c9f7387c2d39a08e34656b341d36ab016e1dac35eafcb9d493075f40c02e617cf5065679336ef2a06496ce45c101862923051f8dd4cd5ddf8e608029672f5ab44191f71135ea0127b905e1f905de00e201a03ada4f90c4f324224a7c54a4a5777e2dc9c629d9298928df6f49397acf033eaef905b7f84b8705c66fb37617a1b84137f006ff9265585a4e45d102cc7c2bae41fc37904d28f8887cf94e00a6ce85fc7571f873ce2bbe846e809115a5f95c6ad7e21a4daa77f078e40dea0f755adbf300f84b8705c66fb3761958b84112dd304b499e997e5854758dc372d75017842d4d424039509a6bb0359e36a538673c709012550407074b786711f0a96391d2b56ea5205ca3efecbbab6da6b0f401f84b8705c66fb3761a52b841bf1e793e6fa390ceae77d36110e6bc921955e5b0e751a801016dd49146fcceed1c1eb51b128655e3b8b4c34dbe31cce94943cc90cdf5aab48facf88ec1cfdc8b00f84b8705c66fb3763514b8410da02a11947f835e3c7dd4b66133daf032ec8c63ad53340292d709820b11b8892f855e67162c2dd394eddc4639f7187edb8fa70ab1909b3cd77d555cf92ca07000f84b8705c66fb3761c86b8414873d374c85312c8210dbf5c31c0058232882bae0c8358c4855610546a297e9120a6a0580ff55b909ab937f5cf9c3bfe8f64691e3a1eed50ce3446e0e75fb03701f84b8705c66fb37618d9b8419714dcdfbb974bcce996da28cdfe1249c486ef668f923b3a17e8e54ce08eaf3f5078a6b825185b592e4850127c627360b346673bc99070a72767e874b2a78f7a01f84b8705c66fb3761a22b841e7d810cd727d5fa321cbfb147d4c854bb9a3254d85881f92601224097f6d73ca1ad3bf305445981d191c3b7f349f7b25a567abef37538e1caab71df4ed5a973d01f84b8705c66fb3761996b841ddff929c23a764c1b1247e29e9c9d97d9f6f9f1e59dcc603c73b49ca9be7fdef311b075bf29a4f33c83d32b7dbe65a7e6efadfb9f0d921ba103455b576519eb001f84b8705c66fb37622beb841e6ffb3c09af9f8c52c9d597635b173e4c76c439dd59534e030d61d00240aa1cd0f7c944e28e47957b75119ed894d23639f7846cb6ee6fe187ed0345ec661420a01f84b8705c66fb3761f39b841ce6c7ab08833ec04dad5360ecbb4c861fa0db5f30a1bc38832ccff2339e6aab85758df0805c8f36a524014e58b57cba86abb393339a3258c5fb93508cd2b624400f84b8705c66fb3761a62b841b93c90a2cbc28d1be5fdebf70301647b3a86e986e2d03e3d250cb67d2fe8ced2484d10507c097a79e3175d12cf2673891f775e2a54e62760043362919532c3ea01f84b8705c66fb37627bcb841e40a474731ad3a3d4ccc12d170b648aba65c10acfbfcad9b52dfecfb5fa99dd43e4d0648c8fc6d0b8825f4ceaf478e0094d0a4394fc433a381427b05ad686fd200f84b8705c66fb3762c1db841a61ad1586b3c63d6f6c05cbdadc354b4127ad33e36238dfbc7893d3f29dddf99470ef001e70ee8e2aaa6975bc2a27d7f3834bd17ee2441edbb3798c0f90d8b7200f84b8705c66fb37619d6b8412c543e1ae5c8b9106cd58e53edd24def35f5bbf75a8dd5ee57a73b960abb008730442d2a4df539ad717bb52ae74220399c1f2051fb9972ff6a684859f1ff86af00f84b8705c66fb3761a63b841487986d56378e4b19c3b2ad2c04f05e8332c1c9c440e103c0cdbc16c2794261124115fec08c132daa27c015bae72b25ac6cfb327a95b6ccd36719b875736628f01f84b8705c66fb37619a6b8414c001acf02e4ab1ead4969d10cc8e357ffb2a4f0cf56b0876dbc5a373f1132a109a508fd12f309bf2b95e6e644ae039d02846011ac7f4f12e8d3e654009df72d01f84b8705c66fb3761777b841e708a0a89d31dd825df718eaaab211f7201312170c662ebc614c3229f30d2f3c1d81e408f4454c6af1ccf732b45272a926ec85ebbe63d3947c68a23a3682cdb001f84b8705c66fb37618e2b8416a650e64a20141e6ce8c1eebee8071a6be7384deb329993bd4ac6137d9183133406ce79cb8819b31312946b8031d2f3532cd7bed1f0e7f71239aa4e02cb4e88d00f84b8705c66fb3762452b841e60969ff8033e5442aa9f47a8cef8d795b4080e13cbf4945df92b42dd9bb0cf85a489d31c31473188bc908b06b491ecc8ae6aa399ed6af7d4552f4ab97c4e36801f800", + fmt.Sprintf("0x%x", bu.Proof), + ) + r.StopReceiveLoop() + }, func() {}) + + assert.NoError(t, err) + }) + + // t.Run("should build ReceiptProofs when events contains BMC SendMessage Event") +} diff --git a/cmd/btpsimple/module/icon/sender.go b/chain/icon/sender.go similarity index 51% rename from cmd/btpsimple/module/icon/sender.go rename to chain/icon/sender.go index 2641ce9b..87117e13 100644 --- a/cmd/btpsimple/module/icon/sender.go +++ b/chain/icon/sender.go @@ -20,13 +20,13 @@ import ( "encoding/base64" "encoding/json" "fmt" + "math" "net/url" + "regexp" "strconv" "time" - "github.com/gorilla/websocket" - - "github.com/icon-project/btp/cmd/btpsimple/module" + "github.com/icon-project/btp/chain" "github.com/icon-project/btp/common" "github.com/icon-project/btp/common/codec" "github.com/icon-project/btp/common/jsonrpc" @@ -34,33 +34,28 @@ import ( ) const ( - txMaxDataSize = 524288 //512 * 1024 // 512kB - txOverheadScale = 0.37 //base64 encoding overhead 0.36, rlp and other fields 0.01 - txSizeLimit = txMaxDataSize / (1 + txOverheadScale) - DefaultGetRelayResultInterval = time.Second - DefaultRelayReSendInterval = time.Second + txMaxDataSize = 524288 //512 * 1024 // 512kB + txOverheadScale = 0.37 //base64 encoding overhead 0.36, rlp and other fields 0.01 + txSizeLimit = txMaxDataSize / (1 + txOverheadScale) + DefaultGetRelayResultInterval = time.Second + DefaultRelayReSendInterval = time.Second * 3 + MaxDefaultGetRelayResultRetries = int((time.Minute * 5) / (DefaultGetRelayResultInterval)) // Pending or stale transaction timeout is 5 minute + DefaultStepLimit = 2500000000 // estimated step limit for 10 blockupdates per segment ) +var RetryHTTPError = regexp.MustCompile(`connection reset by peer|EOF`) + +type SenderOptions struct { + StepLimit int64 `json:"stepLimit"` +} + type sender struct { c *Client - src module.BtpAddress - dst module.BtpAddress + src chain.BtpAddress + dst chain.BtpAddress w Wallet l log.Logger - opt struct { - StepLimit int64 - } - - evtLogRawFilter struct { - addr []byte - signature []byte - next []byte - seq []byte - } - evtReq *BlockRequest - bh *BlockHeader - isFoundOffsetBySeq bool - cb module.ReceiveCallback + opt SenderOptions } func (s *sender) newTransactionParam(prev string, rm *RelayMessage) (*TransactionParam, error) { @@ -68,27 +63,63 @@ func (s *sender) newTransactionParam(prev string, rm *RelayMessage) (*Transactio if err != nil { return nil, err } + rmp := BMCRelayMethodParams{ Prev: prev, Messages: base64.URLEncoding.EncodeToString(b), } + + sl := NewHexInt(s.opt.StepLimit) + if s.opt.StepLimit == 0 { + sl = NewHexInt(DefaultStepLimit) + } + p := &TransactionParam{ Version: NewHexInt(JsonrpcApiVersion), FromAddress: Address(s.w.Address()), ToAddress: Address(s.dst.ContractAddress()), NetworkID: HexInt(s.dst.NetworkID()), - StepLimit: NewHexInt(s.opt.StepLimit), + StepLimit: sl, DataType: "call", Data: CallData{ Method: BMCRelayMethod, Params: rmp, }, } + + // s.l.Tracef("newTransactionParam RLPEncodedRelayMessage: %x\n", b) + // s.l.Tracef("newTransactionParam Base64EncodedRLPEncodedRelayMessage: %s\n", rmp.Messages) + return p, nil +} + +// TODO refactor with newTransactionParam +func (s *sender) newFragmentationsTransactionParam(prev string, msg string, idx int) (*TransactionParam, error) { + sl := NewHexInt(s.opt.StepLimit) + if s.opt.StepLimit == 0 { + sl = NewHexInt(DefaultStepLimit) + } + + p := &TransactionParam{ + Version: NewHexInt(JsonrpcApiVersion), + FromAddress: Address(s.w.Address()), + ToAddress: Address(s.dst.ContractAddress()), + NetworkID: HexInt(s.dst.NetworkID()), + StepLimit: sl, + DataType: "call", + Data: CallData{ + Method: BMCFragmentMethod, + Params: BMCFragmentMethodParams{ + Prev: prev, + Messages: msg, + Index: NewHexInt(int64(idx)), + }, + }, + } return p, nil } -func (s *sender) Segment(rm *module.RelayMessage, height int64) ([]*module.Segment, error) { - segments := make([]*module.Segment, 0) +func (s *sender) iconSegment(rm *chain.RelayMessage, height int64) ([]*chain.Segment, error) { + segments := make([]*chain.Segment, 0) var err error msg := &RelayMessage{ BlockUpdates: make([][]byte, 0), @@ -102,18 +133,20 @@ func (s *sender) Segment(rm *module.RelayMessage, height int64) ([]*module.Segme continue } buSize := len(bu.Proof) - if s.isOverLimit(buSize) { - return nil, fmt.Errorf("invalid BlockUpdate.Proof size") + if s.isOverSizeLimit(buSize) { + return nil, ErrInvalidBlockUpdateProofSize } size += buSize - if s.isOverLimit(size) { - segment := &module.Segment{ - Height: msg.height, + if s.isOverSizeLimit(size) { + segment := &chain.Segment{ + Height: msg.height, NumberOfBlockUpdate: msg.numberOfBlockUpdate, } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { return nil, err } + segments = append(segments, segment) msg = &RelayMessage{ BlockUpdates: make([][]byte, 0), @@ -130,92 +163,134 @@ func (s *sender) Segment(rm *module.RelayMessage, height int64) ([]*module.Segme if bp, err = codec.RLP.MarshalToBytes(rm.BlockProof); err != nil { return nil, err } - if s.isOverLimit(len(bp)) { + + if s.isOverSizeLimit(len(bp)) { return nil, fmt.Errorf("invalid BlockProof size") } - var b []byte - for _, rp := range rm.ReceiptProofs { - if s.isOverLimit(len(rp.Proof)) { - return nil, fmt.Errorf("invalid ReceiptProof.Proof size") + for i, rp := range rm.ReceiptProofs { + if s.isOverSizeLimit(len(rp.Proof)) { + return nil, ErrInvalidReceiptProofSize } if len(msg.BlockUpdates) == 0 { size += len(bp) msg.BlockProof = bp msg.height = rm.BlockProof.BlockWitness.Height + s.l.Tracef("Segment: at %d BlockProof: %x", msg.height, msg.BlockProof) } + size += len(rp.Proof) trp := &ReceiptProof{ Index: rp.Index, Proof: rp.Proof, - EventProofs: make([]*module.EventProof, 0), + EventProofs: make([]*chain.EventProof, 0), } + for j, ep := range rp.EventProofs { - if s.isOverLimit(len(ep.Proof)) { - return nil, fmt.Errorf("invalid EventProof.Proof size") + if s.isOverSizeLimit(len(ep.Proof)) { + return nil, ErrInvalidEventProofProofSize } + size += len(ep.Proof) - if s.isOverLimit(size) { - if j == 0 && len(msg.BlockUpdates) == 0 { - return nil, fmt.Errorf("BlockProof + ReceiptProof + EventProof > limit") + if s.isOverSizeLimit(size) { + if i == 0 && j == 0 && len(msg.BlockUpdates) == 0 { + return nil, fmt.Errorf("BlockProof + ReceiptProof + EventProof > limit %v", i) } - // - segment := &module.Segment{ - Height: msg.height, + + segment := &chain.Segment{ + Height: msg.height, NumberOfBlockUpdate: msg.numberOfBlockUpdate, - EventSequence: msg.eventSequence, - NumberOfEvent: msg.numberOfEvent, + EventSequence: msg.eventSequence, + NumberOfEvent: msg.numberOfEvent, } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { return nil, err } + segments = append(segments, segment) msg = &RelayMessage{ BlockUpdates: make([][]byte, 0), - ReceiptProofs: make([][]byte, 0), BlockProof: bp, + ReceiptProofs: make([][]byte, 0), } - size = len(ep.Proof) - size += len(rp.Proof) - size += len(bp) trp = &ReceiptProof{ Index: rp.Index, Proof: rp.Proof, - EventProofs: make([]*module.EventProof, 0), + EventProofs: make([]*chain.EventProof, 0), } + + size = len(ep.Proof) + size += len(rp.Proof) + size += len(bp) } + trp.EventProofs = append(trp.EventProofs, ep) msg.eventSequence = rp.Events[j].Sequence msg.numberOfEvent += 1 } - if b, err = codec.RLP.MarshalToBytes(trp); err != nil { + if b, err := codec.RLP.MarshalToBytes(trp); err != nil { return nil, err + } else { + msg.ReceiptProofs = append(msg.ReceiptProofs, b) } - msg.ReceiptProofs = append(msg.ReceiptProofs, b) } - // - segment := &module.Segment{ - Height: msg.height, + + segment := &chain.Segment{ + Height: msg.height, NumberOfBlockUpdate: msg.numberOfBlockUpdate, - EventSequence: msg.eventSequence, - NumberOfEvent: msg.numberOfEvent, + EventSequence: msg.eventSequence, + NumberOfEvent: msg.numberOfEvent, } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { return nil, err } + segments = append(segments, segment) return segments, nil } -func (s *sender) UpdateSegment(bp *module.BlockProof, segment *module.Segment) error { +// Can't import pra package because of cycle import +type parachainBlockUpdateExtra struct { + ScaleEncodedBlockHeader []byte + FinalityProofs [][]byte +} + +type parachainBlockUpdate struct { + ScaleEncodedBlockHeader []byte + FinalityProof []byte +} + +func (s *sender) Segment(rm *chain.RelayMessage, height int64) ([]*chain.Segment, error) { + s.l.Tracef("Segments: create Segment for height %d", height) + switch s.src.BlockChain() { + case "icon": + return s.iconSegment(rm, height) + case "pra": + return s.praSegment(rm, height) + default: + return nil, ErrNotSupportedSrcChain + } +} + +func (s *sender) isOverSizeLimit(size int) bool { + return txSizeLimit < float64(size) +} + +// UpdateSegment updates segment +func (s *sender) UpdateSegment(bp *chain.BlockProof, segment *chain.Segment) error { p := segment.TransactionParam.(*TransactionParam) cd := p.Data.(CallData) rmp := cd.Params.(BMCRelayMethodParams) msg := &RelayMessage{} b, err := base64.URLEncoding.DecodeString(rmp.Messages) + if err != nil { + return err + } if _, err = codec.RLP.UnmarshalFromBytes(b, msg); err != nil { return err } @@ -226,11 +301,7 @@ func (s *sender) UpdateSegment(bp *module.BlockProof, segment *module.Segment) e return err } -func (s *sender) Relay(segment *module.Segment) (module.GetResultParam, error) { - p, ok := segment.TransactionParam.(*TransactionParam) - if !ok { - return nil, fmt.Errorf("casting failure") - } +func (s *sender) signAndSendTransaction(p *TransactionParam) (chain.GetResultParam, error) { thp := &TransactionHashParam{} SignLoop: for { @@ -239,6 +310,7 @@ SignLoop: } SendLoop: for { + // s.l.Tracef("signAndSendTransaction: TransactionParam %+v\n", p) txh, err := s.c.SendTransaction(p) if txh != nil { thp.Hash = *txh @@ -268,15 +340,112 @@ SignLoop: } } -func (s *sender) GetResult(p module.GetResultParam) (module.TransactionResult, error) { +func (s *sender) sendFragmentations(tps []*TransactionParam, idxs []int) (chain.GetResultParam, error) { + s.l.Tracef("sendFragmentations: start") + var grp chain.GetResultParam + var err error + + for i := 0; i < len(tps); i++ { + grp, err = s.signAndSendTransaction(tps[i]) + s.l.Debugf("sendFragmentations: fragment %d hash %+v", idxs[i], grp) + if err != nil { + s.l.Panicf("sendFragmentations: fails result %+v error%+v", grp, err) + } + // TODO use (finalizelatency * blockinterval / appropriateratio) * time.Second + time.Sleep(time.Duration(s.FinalizeLatency()) * time.Second) + } + + s.l.Tracef("sendFragmentations: end") + return grp, err +} + +func (s *sender) relayByFragments(p *TransactionParam, dataSize int) (chain.GetResultParam, error) { + callData, ok1 := p.Data.(CallData) + if !ok1 { + return nil, fmt.Errorf("casting failure") + } + + bmcRelayMessageParams, ok2 := callData.Params.(BMCRelayMethodParams) + if !ok2 { + return nil, fmt.Errorf("casting failure") + } + + prev := bmcRelayMessageParams.Prev + msg := bmcRelayMessageParams.Messages + tps := make([]*TransactionParam, 0) + idxs := make([]int, 0) + // + 1 is safeguard against transaction limit + nuFragments := int(math.Ceil(float64(dataSize)/float64(txMaxDataSize))) + 1 + fragmentStringSize := len(msg) / nuFragments + if (len(msg) % nuFragments) != 0 { + nuFragments += 1 + } + + // Index will be negative for the first index, and 0 for the last index + // Ex: 4 indexes -> -3 2 1 0 + s.l.Debugf("relayByFragments: fragments: %d size: %d", nuFragments, fragmentStringSize) + for i := 0; i < len(msg); i += fragmentStringSize { + end := i + fragmentStringSize + + if end > len(msg) { + end = len(msg) + } + + var index int + if i == 0 { + index = ^nuFragments + 2 + } else if end >= len(msg) { + index = 0 + } else { + index = (nuFragments - 1 - (i / fragmentStringSize)) + } + + tp, err := s.newFragmentationsTransactionParam(prev, msg[i:end], index) + if err != nil { + return nil, err + } + + tps = append(tps, tp) + idxs = append(idxs, index) + } + return s.sendFragmentations(tps, idxs) +} + +func (s *sender) Relay(segment *chain.Segment) (chain.GetResultParam, error) { + p, ok := segment.TransactionParam.(*TransactionParam) + if !ok { + return nil, fmt.Errorf("casting failure") + } + + // Each Segmentation is a valid RelayMessage, the smallest RelayMessage contains 1 BlockUpdate or 1 ReceiptProof + // However, if a RelayMessage contains 1 BlockUpdate and 1 ReceiptProof has data size is over the transaction max data size + // We must split this message into Fragmentations and send by handleFragment + dataSize := countBytesOfCompactJSON(p.Data) + if dataSize > txMaxDataSize { + return s.relayByFragments(p, dataSize) + } else { + return s.signAndSendTransaction(p) + } +} + +func (s *sender) GetResult(p chain.GetResultParam) (chain.TransactionResult, error) { if txh, ok := p.(*TransactionHashParam); ok { + tries := 0 for { + tries++ txr, err := s.c.GetTransactionResult(txh) - if err != nil { + if err != nil && tries < MaxDefaultGetRelayResultRetries { + if RetryHTTPError.MatchString(err.Error()) { + <-time.After(DefaultGetRelayResultInterval) + s.l.Tracef("GetResult: retry %d with GetResult %s err:%+v", tries, txh.Hash, err) + continue + } + if je, ok := err.(*jsonrpc.Error); ok { switch je.Code { - case JsonrpcErrorCodePending, JsonrpcErrorCodeExecuting: + case JsonrpcErrorCodePending, JsonrpcErrorCodeExecuting, JsonrpcErrorCodeNotFound: <-time.After(DefaultGetRelayResultInterval) + s.l.Tracef("GetResult: retry %d with GetResult %s err:%+v", tries, txh.Hash, err) continue } } @@ -288,7 +457,7 @@ func (s *sender) GetResult(p module.GetResultParam) (module.TransactionResult, e } } -func (s *sender) GetStatus() (*module.BMCLinkStatus, error) { +func (s *sender) GetStatus() (*chain.BMCLinkStatus, error) { p := &CallParam{ FromAddress: Address(s.w.Address()), ToAddress: Address(s.dst.ContractAddress()), @@ -305,7 +474,7 @@ func (s *sender) GetStatus() (*module.BMCLinkStatus, error) { if err != nil { return nil, err } - ls := &module.BMCLinkStatus{} + ls := &chain.BMCLinkStatus{} ls.TxSeq, err = bs.TxSeq.Value() ls.RxSeq, err = bs.RxSeq.Value() ls.Verifier.Height, err = bs.Verifier.Height.Value() @@ -329,6 +498,8 @@ func (s *sender) GetStatus() (*module.BMCLinkStatus, error) { ls.CurrentHeight, err = bs.CurrentHeight.Value() ls.RxHeight, err = bs.RxHeight.Value() ls.RxHeightSrc, err = bs.RxHeightSrc.Value() + ls.BlockIntervalSrc, err = bs.BlockIntervalSrc.Int() + ls.BlockIntervalDst, err = bs.BlockIntervalDst.Int() return ls, nil } @@ -336,39 +507,41 @@ func (s *sender) isOverLimit(size int) bool { return txSizeLimit < float64(size) } -func (s *sender) MonitorLoop(height int64, cb module.MonitorCallback, scb func()) error { +func (s *sender) MonitorLoop(height int64, cb chain.MonitorCallback, scb func()) error { br := &BlockRequest{ Height: NewHexInt(height), } return s.c.MonitorBlock(br, - func(conn *websocket.Conn, v *BlockNotification) error { + func(conn *jsonrpc.RecConn, v *BlockNotification) error { if h, err := v.Height.Value(); err != nil { return err } else { return cb(h) } }, - func(conn *websocket.Conn) { - s.l.Debugf("MonitorLoop connected %s", conn.LocalAddr().String()) + func(conn *jsonrpc.RecConn) { + s.l.Debugf("MonitorLoop connected %s", conn.Id) if scb != nil { scb() } }, - func(conn *websocket.Conn, err error) { - s.l.Debugf("onError %s err:%+v", conn.LocalAddr().String(), err) - _ = conn.Close() + func(conn *jsonrpc.RecConn, err error) { + s.l.Debugf("onError %s err:%+v", conn.Id, err) + conn.CloseAndReconnect() }) } func (s *sender) StopMonitorLoop() { s.c.CloseAllMonitor() } + +// the unit is block func (s *sender) FinalizeLatency() int { //on-the-next return 1 } -func NewSender(src, dst module.BtpAddress, w Wallet, endpoint string, opt map[string]interface{}, l log.Logger) module.Sender { +func NewSender(src, dst chain.BtpAddress, w Wallet, endpoint string, opt map[string]interface{}, l log.Logger) chain.Sender { s := &sender{ src: src, dst: dst, @@ -393,29 +566,29 @@ func mapError(err error) error { //fmt.Printf("jrResp.Error:%+v", re) switch re.Code { case JsonrpcErrorCodeTxPoolOverflow: - return module.ErrSendFailByOverflow + return chain.ErrSendFailByOverflow case JsonrpcErrorCodeSystem: if subEc, err := strconv.ParseInt(re.Message[1:5], 0, 32); err == nil { //TODO return JsonRPC Error switch subEc { case ExpiredTransactionError: - return module.ErrSendFailByExpired + return chain.ErrSendFailByExpired case FutureTransactionError: - return module.ErrSendFailByFuture + return chain.ErrSendFailByFuture case TransactionPoolOverflowError: - return module.ErrSendFailByOverflow + return chain.ErrSendFailByOverflow } } case JsonrpcErrorCodePending, JsonrpcErrorCodeExecuting: - return module.ErrGetResultFailByPending + return chain.ErrGetResultFailByPending } case *common.HttpError: fmt.Printf("*common.HttpError:%+v", re) - return module.ErrConnectFail + return chain.ErrConnectFail case *url.Error: if common.IsConnectRefusedError(re.Err) { //fmt.Printf("*url.Error:%+v", re) - return module.ErrConnectFail + return chain.ErrConnectFail } } } @@ -430,7 +603,7 @@ func mapErrorWithTransactionResult(txr *TransactionResult, err error) error { err = fmt.Errorf("failure with code:%s, message:%s", txr.Failure.CodeValue, txr.Failure.MessageValue) } else { - err = module.NewRevertError(int(fc - ResultStatusFailureCodeRevert)) + err = chain.NewRevertError(int(fc - ResultStatusFailureCodeRevert)) } } return err diff --git a/chain/icon/sender_pra.go b/chain/icon/sender_pra.go new file mode 100644 index 00000000..c7c84db2 --- /dev/null +++ b/chain/icon/sender_pra.go @@ -0,0 +1,182 @@ +package icon + +import ( + "errors" + + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/common/codec" +) + +var ( + paraChainMaxBlockUpdatePerSegment = 10 + ErrInvalidParaChainFinalityProofsLength = errors.New("FinalityProofs of ParaChainBlockUpdates must greater than 0") +) + +func (s *sender) praSegment(rm *chain.RelayMessage, height int64) ([]*chain.Segment, error) { + var err error + segments := make([]*chain.Segment, 0) + + msg := &RelayMessage{ + BlockUpdates: make([][]byte, 0), + ReceiptProofs: make([][]byte, 0), + } + + for i, bu := range rm.BlockUpdates { + if bu.Height <= height { + continue + } + + var paraBuExtra chain.ParaChainBlockUpdateExtra + if _, err := codec.RLP.UnmarshalFromBytes(bu.Proof, ¶BuExtra); err != nil { + return nil, err + } + + if len(paraBuExtra.FinalityProofs) == 0 { + return nil, ErrInvalidParaChainFinalityProofsLength + } + + encodedParaBu, err := codec.RLP.MarshalToBytes(chain.ParaChainBlockUpdate{ + ScaleEncodedBlockHeader: paraBuExtra.ScaleEncodedBlockHeader, + FinalityProof: paraBuExtra.FinalityProofs[len(paraBuExtra.FinalityProofs)-1], + }) + + if err != nil { + return nil, err + } + + // Check a Single ParaBlockUpdate requires submit multiple RelayMessages + if len(paraBuExtra.FinalityProofs) > 1 { + // We can't submit ParaBlockUpdates contains non-empty ScaleEncodedBlockHeader + // with ParaBlockUpdates contains empty ScaleEncodedBlockHeader + // So, if message contains previous ParaBlockUpdates of non-empty ScaleEncodedBlockHeader, split them into a Segment/RelayMessage + if len(msg.BlockUpdates) > 0 { + s.l.Debug("Segment: send non-empty ScaleEncodedBlockHeader") + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + } + + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + + segments = append(segments, segment) + // Reset message + msg = &RelayMessage{ + BlockUpdates: make([][]byte, 0), + ReceiptProofs: make([][]byte, 0), + } + } + + // 2 cases of RelayChainData + // - Justifications Block greater than ParaInclusionCandidateIncluded Block -> 1 RelayMessage for RelayChainData + // - Justifications Block greater than GrandpaNewAuthorities Block + // And GrandpaNewAuthorities Block greater than ParaInclusionCandidateIncluded Block + // -> 2 RelayMessages for Relaychain Data + for j := 0; j < len(paraBuExtra.FinalityProofs)-1; j++ { + s.l.Debugf("Segment: send RelayChainData") + encodedRelayChainDataBu, err := codec.RLP.MarshalToBytes(chain.ParaChainBlockUpdate{ + ScaleEncodedBlockHeader: nil, + FinalityProof: paraBuExtra.FinalityProofs[j], + }) + + if err != nil { + return nil, err + } + + relayChainDataMsg := &RelayMessage{ + BlockUpdates: [][]byte{encodedRelayChainDataBu}, + ReceiptProofs: make([][]byte, 0), + } + + var transParams *TransactionParam + if transParams, err = s.newTransactionParam(rm.From.String(), relayChainDataMsg); err != nil { + return nil, err + } + + segments = append(segments, &chain.Segment{ + // Height at the same height with the non-empty ScaleEncodedBlockHeader + // This is to prove that submit the non-empty ScaleEncodedBlockHeader requires RelayChain Blocks prior to + Height: bu.Height, + TransactionParam: transParams, + // No Parachain BlockUpdate + NumberOfBlockUpdate: 0, + }) + } + } + + // If the remaining BlockUpdates over limit + if msg.numberOfBlockUpdate > paraChainMaxBlockUpdatePerSegment { + s.l.Debugf("Segment: split by maximum number of blockupdate per Segment") + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + segments = append(segments, segment) + // Reset message + msg = &RelayMessage{ + BlockUpdates: make([][]byte, 0), + ReceiptProofs: make([][]byte, 0), + } + } + + s.l.Tracef("Segment: at %d BlockUpdates[%d]", bu.Height, i) + msg.BlockUpdates = append(msg.BlockUpdates, encodedParaBu) + msg.height = bu.Height + msg.numberOfBlockUpdate += 1 + } + + var bp []byte + if bp, err = codec.RLP.MarshalToBytes(rm.BlockProof); err != nil { + return nil, err + } + + for i, rp := range rm.ReceiptProofs { + if len(rp.Proof) > 0 && len(msg.BlockUpdates) == 0 { + if rm.BlockProof == nil { + return segments, nil + } + msg.BlockProof = bp + msg.ReceiptProofs = append(msg.ReceiptProofs, rp.Proof) + + segment := &chain.Segment{ + Height: rm.BlockProof.BlockWitness.Height, + EventSequence: rp.Events[len(rp.Events)-1].Sequence, + NumberOfEvent: len(rp.Events), + NumberOfBlockUpdate: 0, + } + + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + + segments = append(segments, segment) + s.l.Tracef("Segment: at %d StateProofs[%d]", rp.Height, i) + } else { + msg.eventSequence = rp.Events[len(rp.Events)-1].Sequence + msg.numberOfEvent = len(rp.Events) + msg.ReceiptProofs = append(msg.ReceiptProofs, rp.Proof) + s.l.Tracef("Segment: at %d StateProofs[%d]", rp.Height, i) + } + } + + if len(msg.BlockUpdates) > 0 || len(msg.ReceiptProofs) > 0 { + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + EventSequence: msg.eventSequence, + NumberOfEvent: msg.numberOfEvent, + } + + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + + segments = append(segments, segment) + } + + return segments, nil +} diff --git a/chain/icon/sender_test.go b/chain/icon/sender_test.go new file mode 100644 index 00000000..630bbdf5 --- /dev/null +++ b/chain/icon/sender_test.go @@ -0,0 +1,55 @@ +package icon + +import ( + "encoding/base64" + "testing" + + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/crypto" + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/wallet" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func fakeWallet() wallet.Wallet { + priKey, _ := crypto.GenerateKeyPair() + + w, err := wallet.NewIcxWalletFromPrivateKey(priKey) + if err != nil { + panic(err) + } + return w +} + +func TestSender_newTransactionParam(t *testing.T) { + prev := "btp://0x3.icon/cx0c59775cb325c1aea9c872546dbe8365af7c9d07" + rm := &RelayMessage{ + BlockUpdates: [][]byte{{1, 2, 3, 4}}, + BlockProof: []byte{1, 2, 3, 4}, + ReceiptProofs: [][]byte{{1, 2, 3, 4}}, + } + + sender := &sender{l: log.New(), w: fakeWallet(), dst: "btp://0x42.icon/cx1f304314d232db2ea504e84607e8926ed6fa8d91"} + p, err := sender.newTransactionParam(prev, rm) + require.NoError(t, err) + require.NotNil(t, p) + + callData, ok := p.Data.(CallData) + require.True(t, ok) + params, ok := callData.Params.(BMCRelayMethodParams) + require.True(t, ok) + assert.Equal(t, prev, params.Prev) + + rlpEncoded, err := base64.URLEncoding.DecodeString(params.Messages) + require.NoError(t, err) + + expected := &RelayMessage{} + _, err = codec.RLP.UnmarshalFromBytes(rlpEncoded, expected) + require.NoError(t, err) + assert.Equal(t, expected, rm) +} + +func TestSender_praSegment(t *testing.T) { + t.Skip() +} diff --git a/cmd/btpsimple/module/icon/serialize.go b/chain/icon/serialize.go similarity index 98% rename from cmd/btpsimple/module/icon/serialize.go rename to chain/icon/serialize.go index b03d4477..838b08aa 100644 --- a/cmd/btpsimple/module/icon/serialize.go +++ b/chain/icon/serialize.go @@ -62,7 +62,7 @@ func serializeList(v_list []interface{}) ([]byte, *SerializeError) { for idx, v := range v_list { frag, err := serializeValue(v) if err != nil { - err.position = "[" + string(idx) + "]." + err.position + err.position = "[" + string(rune(idx)) + "]." + err.position return nil, err } if buf.Len() > 0 { diff --git a/cmd/btpsimple/module/icon/type.go b/chain/icon/type.go similarity index 95% rename from cmd/btpsimple/module/icon/type.go rename to chain/icon/type.go index c2f68e46..1e926851 100644 --- a/cmd/btpsimple/module/icon/type.go +++ b/chain/icon/type.go @@ -23,7 +23,8 @@ import ( "strconv" "strings" - "github.com/icon-project/btp/cmd/btpsimple/module" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/common/intconv" "github.com/icon-project/btp/common/jsonrpc" ) @@ -56,14 +57,15 @@ const ( ) const ( - ResultStatusSuccess = "0x1" + ResultStatusSuccess = "0x1" ResultStatusFailureCodeRevert = 32 - ResultStatusFailureCodeEnd = 99 + ResultStatusFailureCodeEnd = 99 ) const ( - BMCRelayMethod = "handleRelayMessage" - BMCGetStatusMethod = "getStatus" + BMCRelayMethod = "handleRelayMessage" + BMCFragmentMethod = "handleFragment" + BMCGetStatusMethod = "getStatus" ) type BlockHeader struct { @@ -163,6 +165,12 @@ type BMCRelayMethodParams struct { Messages string `json:"_msg"` } +type BMCFragmentMethodParams struct { + Prev string `json:"_prev"` + Messages string `json:"_msg"` + Index HexInt `json:"_idx"` +} + type BMCLinkMethodParams struct { Target string `json:"_link"` } @@ -308,7 +316,7 @@ func (i HexInt) Int() (int, error) { } func NewHexInt(v int64) HexInt { - return HexInt("0x" + strconv.FormatInt(v, 16)) + return HexInt(intconv.FormatInt(v)) } //T_ADDR_EOA, T_ADDR_SCORE @@ -370,11 +378,11 @@ type RelayMessage struct { type ReceiptProof struct { Index int Proof []byte - EventProofs []*module.EventProof + EventProofs []*chain.EventProof } type Block struct { - //BlockHash HexBytes `json:"block_hash" validate:"required,t_hash"` + BlockHash HexBytes `json:"block_hash" validate:"required,t_hash"` //Version HexInt `json:"version" validate:"required,t_int"` Height int64 `json:"height" validate:"required,t_int"` //Timestamp int64 `json:"time_stamp" validate:"required,t_int"` diff --git a/chain/mock_Sender.go b/chain/mock_Sender.go new file mode 100644 index 00000000..8f689b1e --- /dev/null +++ b/chain/mock_Sender.go @@ -0,0 +1,149 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package chain + +import mock "github.com/stretchr/testify/mock" + +// MockSender is an autogenerated mock type for the Sender type +type MockSender struct { + mock.Mock +} + +// FinalizeLatency provides a mock function with given fields: +func (_m *MockSender) FinalizeLatency() int { + ret := _m.Called() + + var r0 int + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// GetResult provides a mock function with given fields: p +func (_m *MockSender) GetResult(p GetResultParam) (TransactionResult, error) { + ret := _m.Called(p) + + var r0 TransactionResult + if rf, ok := ret.Get(0).(func(GetResultParam) TransactionResult); ok { + r0 = rf(p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(TransactionResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(GetResultParam) error); ok { + r1 = rf(p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetStatus provides a mock function with given fields: +func (_m *MockSender) GetStatus() (*BMCLinkStatus, error) { + ret := _m.Called() + + var r0 *BMCLinkStatus + if rf, ok := ret.Get(0).(func() *BMCLinkStatus); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*BMCLinkStatus) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MonitorLoop provides a mock function with given fields: height, cb, scb +func (_m *MockSender) MonitorLoop(height int64, cb MonitorCallback, scb func()) error { + ret := _m.Called(height, cb, scb) + + var r0 error + if rf, ok := ret.Get(0).(func(int64, MonitorCallback, func()) error); ok { + r0 = rf(height, cb, scb) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Relay provides a mock function with given fields: segment +func (_m *MockSender) Relay(segment *Segment) (GetResultParam, error) { + ret := _m.Called(segment) + + var r0 GetResultParam + if rf, ok := ret.Get(0).(func(*Segment) GetResultParam); ok { + r0 = rf(segment) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(GetResultParam) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*Segment) error); ok { + r1 = rf(segment) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Segment provides a mock function with given fields: rm, height +func (_m *MockSender) Segment(rm *RelayMessage, height int64) ([]*Segment, error) { + ret := _m.Called(rm, height) + + var r0 []*Segment + if rf, ok := ret.Get(0).(func(*RelayMessage, int64) []*Segment); ok { + r0 = rf(rm, height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*Segment) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*RelayMessage, int64) error); ok { + r1 = rf(rm, height) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StopMonitorLoop provides a mock function with given fields: +func (_m *MockSender) StopMonitorLoop() { + _m.Called() +} + +// UpdateSegment provides a mock function with given fields: bp, segment +func (_m *MockSender) UpdateSegment(bp *BlockProof, segment *Segment) error { + ret := _m.Called(bp, segment) + + var r0 error + if rf, ok := ret.Get(0).(func(*BlockProof, *Segment) error); ok { + r0 = rf(bp, segment) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/chain/pra/assets/moonbase_blockinfo_243221.json b/chain/pra/assets/moonbase_blockinfo_243221.json new file mode 100644 index 00000000..18995945 --- /dev/null +++ b/chain/pra/assets/moonbase_blockinfo_243221.json @@ -0,0 +1 @@ +{"BlockNumber":243221,"Hash":"0xed27ad8166e3d8a3ff54a4687547b77ef0d95600d94b2521af9f700665100bb3","Header":{"parentHash":"0x4b6ca5b74e19d4bc04280edf20a53a4ebe1402cbb2ef7ed9a1611fb8411a33ca","number":"3b615","stateRoot":"0x6415ef11020701d83ae5456ccecb3685eb39acc6b52d3e481214d8f1aa6b5465","extrinsicsRoot":"0xe347562ba9c7c993862047e1d6f020c25ab4139676afbf7170c254e2f36cefe7","digest":{"logs":["0x046e6d62738060eed538a43e6738f4c560c5d950be96c72ad591f0c16f564c003b5c7b895c0e","0x0466726f6e890101f7b9c5fb3f5b72f937ed511b173e5a39b9fb3ffaa1cc4dd024a4c7c36c7da8610847fec28647d5f0806548f385257170d76cf6e890f7467ef33b36dcc5b9be1b15d0fdb267aa2fce057cc81a0e2397f5a32ffd8637753f7d0c0c0b7289b002dc3f","0x056e6d627301019a1b7069e8aa71015a15925595589999890dba42cb87dae2b5aabfee791bad47d380392c626eef34701ea3a95d7dd4fc891759cb375991aacbc1ee3663742289"]}},"ScaleEncodedHeader":"S2ylt04Z1LwEKA7fIKU6Tr4UAsuy737ZoWEfuEEaM8pW2A4AZBXvEQIHAdg65UVszss2hes5rMa1LT5IEhTY8aprVGXjR1YrqcfJk4YgR+HW8CDCWrQTlnavv3FwwlTi82zv5wwEbm1ic4Bg7tU4pD5nOPTFYMXZUL6WxyrVkfDBb1ZMADtce4lcDgRmcm9uiQEB97nF+z9bcvk37VEbFz5aObn7P/qhzE3QJKTHw2x9qGEIR/7ChkfV8IBlSPOFJXFw12z26JD3Rn7zOzbcxbm+GxXQ/bJnqi/OBXzIGg4jl/WjL/2GN3U/fQwMC3KJsALcPwVubWJzAQGaG3Bp6KpxAVoVklWVWJmZiQ26QsuH2uK1qr/ueRutR9OAOSxibu80cB6jqV191PyJF1nLN1mRqsvB7jZjdCKJ","MetaData":{"MagicNumber":1635018093,"Version":13,"IsMetadataV4":false,"AsMetadataV4":{"Modules":null},"IsMetadataV7":false,"AsMetadataV7":{"Modules":null},"IsMetadataV8":false,"AsMetadataV8":{"Modules":null},"IsMetadataV9":false,"AsMetadataV9":{"Modules":null},"IsMetadataV10":false,"AsMetadataV10":{"Modules":null},"IsMetadataV11":false,"AsMetadataV11":{"Modules":null,"Extrinsic":{"Version":0,"SignedExtensions":null}},"IsMetadataV12":false,"AsMetadataV12":{"Modules":null,"Extrinsic":{"Version":0,"SignedExtensions":null}},"IsMetadataV13":true,"AsMetadataV13":{"Modules":[{"Name":"System","HasStorage":true,"Storage":{"Prefix":"System","Items":[{"Name":"Account","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"AccountInfo\u003cT::Index, T::AccountData\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The full account information for a particular account ID."]},{"Name":"ExtrinsicCount","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Total extrinsics count for the current block."]},{"Name":"BlockWeight","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ConsumedWeight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" The current weight for the block."]},{"Name":"AllExtrinsicsLen","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Total length (in bytes) for all extrinsics put together, for the current block."]},{"Name":"BlockHash","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::BlockNumber","Value":"T::Hash","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" Map of block numbers to block hashes."]},{"Name":"ExtrinsicData","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"u32","Value":"Vec\u003cu8\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Extrinsics data for the current block (maps an extrinsic's index to its data)."]},{"Name":"Number","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The current block number being processed. Set by `execute_block`."]},{"Name":"ParentHash","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Hash","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" Hash of the previous block."]},{"Name":"Digest","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"DigestOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Digest of the current block, also part of the block header."]},{"Name":"Events","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cEventRecord\u003cT::Event, T::Hash\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Events deposited for the current block."]},{"Name":"EventCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"EventIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of events in the `Events\u003cT\u003e` list."]},{"Name":"EventTopics","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::Hash","Value":"Vec\u003c(T::BlockNumber, EventIndex)\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Mapping between a topic (represented by T::Hash) and a vector of indexes"," of events in the `\u003cEvents\u003cT\u003e\u003e` list.",""," All topic vectors have deterministic storage locations depending on the topic. This"," allows light-clients to leverage the changes trie storage tracking mechanism and"," in case of changes fetch the list of events of interest.",""," The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"," the `EventIndex` then in case if the topic has the same contents on the next block"," no notification will be triggered thus the event might be lost."]},{"Name":"LastRuntimeUpgrade","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"LastRuntimeUpgradeInfo","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened."]},{"Name":"UpgradedToU32RefCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if we have upgraded so that `type RefCount` is `u32`. False (default) if not."]},{"Name":"UpgradedToTripleRefCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if we have upgraded so that AccountInfo contains three types of `RefCount`. False"," (default) if not."]},{"Name":"ExecutionPhase","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Phase","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The execution phase of the block."]}]},"HasCalls":true,"Calls":[{"Name":"fill_block","Args":[{"Name":"_ratio","Type":"Perbill"}],"Documentation":[" A dispatch that will fill the block weight up to the given ratio."]},{"Name":"remark","Args":[{"Name":"_remark","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Make some on-chain remark.",""," # \u003cweight\u003e"," - `O(1)`"," # \u003c/weight\u003e"]},{"Name":"set_heap_pages","Args":[{"Name":"pages","Type":"u64"}],"Documentation":[" Set the number of pages in the WebAssembly environment's heap.",""," # \u003cweight\u003e"," - `O(1)`"," - 1 storage write."," - Base Weight: 1.405 µs"," - 1 write to HEAP_PAGES"," # \u003c/weight\u003e"]},{"Name":"set_code","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Set the new runtime code.",""," # \u003cweight\u003e"," - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"," - 1 storage write (codec `O(C)`)."," - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is expensive)."," - 1 event."," The weight of this function is dependent on the runtime, but generally this is very expensive."," We will treat this as a full block."," # \u003c/weight\u003e"]},{"Name":"set_code_without_checks","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Set the new runtime code without doing any checks of the given `code`.",""," # \u003cweight\u003e"," - `O(C)` where `C` length of `code`"," - 1 storage write (codec `O(C)`)."," - 1 event."," The weight of this function is dependent on the runtime. We will treat this as a full block."," # \u003c/weight\u003e"]},{"Name":"set_changes_trie_config","Args":[{"Name":"changes_trie_config","Type":"Option\u003cChangesTrieConfiguration\u003e"}],"Documentation":[" Set the new changes trie configuration.",""," # \u003cweight\u003e"," - `O(1)`"," - 1 storage write or delete (codec `O(1)`)."," - 1 call to `deposit_log`: Uses `append` API, so O(1)"," - Base Weight: 7.218 µs"," - DB Weight:"," - Writes: Changes Trie, System Digest"," # \u003c/weight\u003e"]},{"Name":"set_storage","Args":[{"Name":"items","Type":"Vec\u003cKeyValue\u003e"}],"Documentation":[" Set some items of storage.",""," # \u003cweight\u003e"," - `O(I)` where `I` length of `items`"," - `I` storage writes (`O(1)`)."," - Base Weight: 0.568 * i µs"," - Writes: Number of items"," # \u003c/weight\u003e"]},{"Name":"kill_storage","Args":[{"Name":"keys","Type":"Vec\u003cKey\u003e"}],"Documentation":[" Kill some items from storage.",""," # \u003cweight\u003e"," - `O(IK)` where `I` length of `keys` and `K` length of one key"," - `I` storage deletions."," - Base Weight: .378 * i µs"," - Writes: Number of items"," # \u003c/weight\u003e"]},{"Name":"kill_prefix","Args":[{"Name":"prefix","Type":"Key"},{"Name":"_subkeys","Type":"u32"}],"Documentation":[" Kill all storage items with a key that starts with the given prefix.",""," **NOTE:** We rely on the Root origin to provide us the number of subkeys under"," the prefix we are removing to accurately calculate the weight of this function.",""," # \u003cweight\u003e"," - `O(P)` where `P` amount of keys with prefix `prefix`"," - `P` storage deletions."," - Base Weight: 0.834 * P µs"," - Writes: Number of subkeys + 1"," # \u003c/weight\u003e"]},{"Name":"remark_with_event","Args":[{"Name":"remark","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Make some on-chain remark and emit event.",""," # \u003cweight\u003e"," - `O(b)` where b is the length of the remark."," - 1 event."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"ExtrinsicSuccess","Args":["DispatchInfo"],"Documentation":[" An extrinsic completed successfully. \\[info\\]"]},{"Name":"ExtrinsicFailed","Args":["DispatchError","DispatchInfo"],"Documentation":[" An extrinsic failed. \\[error, info\\]"]},{"Name":"CodeUpdated","Args":null,"Documentation":[" `:code` was updated."]},{"Name":"NewAccount","Args":["AccountId"],"Documentation":[" A new \\[account\\] was created."]},{"Name":"KilledAccount","Args":["AccountId"],"Documentation":[" An \\[account\\] was reaped."]},{"Name":"Remarked","Args":["AccountId","Hash"],"Documentation":[" On on-chain remark happened. \\[origin, remark_hash\\]"]}],"Constants":[{"Name":"BlockWeights","Type":"limits::BlockWeights","Value":"APIFKgEAAAAAiFJqdAAAAEBZcwcAAAAAAcAYD6RLAAAAAQDmvU9XAAAAAQAAAAAAAAAAQFlzBwAAAAABwLqjvmgAAAABAIhSanQAAAABAKKUGh0AAABAWXMHAAAAAAAAAA==","Documentation":[" Block \u0026 extrinsics weights: base values and limits."]},{"Name":"BlockLength","Type":"limits::BlockLength","Value":"AAA8AAAAUAAAAFAA","Documentation":[" The maximum length of a block (in bytes)."]},{"Name":"BlockHashCount","Type":"T::BlockNumber","Value":"AAEAAA==","Documentation":[" Maximum number of block number to block hash mappings to keep (oldest pruned first)."]},{"Name":"DbWeight","Type":"RuntimeDbWeight","Value":"QHh9AQAAAAAA4fUFAAAAAA==","Documentation":[" The weight of runtime database operations the runtime can invoke."]},{"Name":"Version","Type":"RuntimeVersion","Value":"IG1vb25iYXNlIG1vb25iYXNlAwAAADQAAAAAAAAANNK8mJfu0I8VAgAAAN9qy2iZB2CbAwAAADfjl/x8kfXkAQAAAED+OtQB+JWaBQAAAPeLJ4vlP0VMAgAAAKs8BXIpH+uLAQAAALydiZBPW5I/AQAAAL14JV1P7uofAQAAAKM9Q/WHMa2EAQAAAFgiEfZbsUuJAQAAADfIuxNQqaKoAQAAAB+6P/u34H6NAQAAAOqT4/FvPWliAQAAAAIAAAA=","Documentation":[" Get the chain's current version."]},{"Name":"SS58Prefix","Type":"u8","Value":"Kg==","Documentation":[" The designated SS85 prefix of this chain.",""," This replaces the \"ss58Format\" property declared in the chain spec. Reason is"," that the runtime should know about the prefix in order to make use of it as"," an identifier of the chain."]}],"Errors":[{"Name":"InvalidSpecName","Documentation":[" The name of specification does not match between the current runtime"," and the new runtime."]},{"Name":"SpecVersionNeedsToIncrease","Documentation":[" The specification version is not allowed to decrease between the current runtime"," and the new runtime."]},{"Name":"FailedToExtractRuntimeVersion","Documentation":[" Failed to extract the runtime version from the new runtime.",""," Either calling `Core_version` or decoding `RuntimeVersion` failed."]},{"Name":"NonDefaultComposite","Documentation":[" Suicide called when the account has non-default composite data."]},{"Name":"NonZeroRefCount","Documentation":[" There is a non-zero reference count preventing the account from being purged."]}],"Index":0},{"Name":"Utility","HasStorage":false,"Storage":{"Prefix":"","Items":null},"HasCalls":true,"Calls":[{"Name":"batch","Args":[{"Name":"calls","Type":"Vec\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a batch of dispatch calls.",""," May be called from any origin.",""," - `calls`: The calls to be dispatched from the same origin.",""," If origin is root then call are dispatch without checking origin filter. (This includes"," bypassing `frame_system::Config::BaseCallFilter`).",""," # \u003cweight\u003e"," - Complexity: O(C) where C is the number of calls to be batched."," # \u003c/weight\u003e",""," This will return `Ok` in all circumstances. To determine the success of the batch, an"," event is deposited. If a call failed and the batch was interrupted, then the"," `BatchInterrupted` event is deposited, along with the number of successful calls made"," and the error of the failed call. If all were successful, then the `BatchCompleted`"," event is deposited."]},{"Name":"as_derivative","Args":[{"Name":"index","Type":"u16"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a call through an indexed pseudonym of the sender.",""," Filter from origin are passed along. The call will be dispatched with an origin which"," use the same filter as the origin of this call.",""," NOTE: If you need to ensure that any account-based filtering is not honored (i.e."," because you expect `proxy` to have been used prior in the call stack and you do not want"," the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`"," in the Multisig pallet instead.",""," NOTE: Prior to version *12, this was called `as_limited_sub`.",""," The dispatch origin for this call must be _Signed_."]},{"Name":"batch_all","Args":[{"Name":"calls","Type":"Vec\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a batch of dispatch calls and atomically execute them."," The whole transaction will rollback and fail if any of the calls failed.",""," May be called from any origin.",""," - `calls`: The calls to be dispatched from the same origin.",""," If origin is root then call are dispatch without checking origin filter. (This includes"," bypassing `frame_system::Config::BaseCallFilter`).",""," # \u003cweight\u003e"," - Complexity: O(C) where C is the number of calls to be batched."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"BatchInterrupted","Args":["u32","DispatchError"],"Documentation":[" Batch of dispatches did not complete fully. Index of first failing dispatch given, as"," well as the error. \\[index, error\\]"]},{"Name":"BatchCompleted","Args":null,"Documentation":[" Batch of dispatches completed fully with no error."]}],"Constants":null,"Errors":null,"Index":1},{"Name":"Timestamp","HasStorage":true,"Storage":{"Prefix":"Timestamp","Items":[{"Name":"Now","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Moment","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAA=","Documentation":[" Current time for the current block."]},{"Name":"DidUpdate","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Did the timestamp get updated in this block?"]}]},"HasCalls":true,"Calls":[{"Name":"set","Args":[{"Name":"now","Type":"Compact\u003cT::Moment\u003e"}],"Documentation":[" Set the current time.",""," This call should be invoked exactly once per block. It will panic at the finalization"," phase, if this call hasn't been invoked by that time.",""," The timestamp should be greater than the previous one by the amount specified by"," `MinimumPeriod`.",""," The dispatch origin for this call must be `Inherent`.",""," # \u003cweight\u003e"," - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"," - 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in `on_finalize`)"," - 1 event handler `on_timestamp_set`. Must be `O(1)`."," # \u003c/weight\u003e"]}],"HasEvents":false,"Events":null,"Constants":[{"Name":"MinimumPeriod","Type":"T::Moment","Value":"AQAAAAAAAAA=","Documentation":[" The minimum period between blocks. Beware that this is different to the *expected* period"," that the block production apparatus provides. Your chosen consensus system will generally"," work with this to determine a sensible block time. e.g. For Aura, it will be double this"," period on default settings."]}],"Errors":null,"Index":2},{"Name":"Balances","HasStorage":true,"Storage":{"Prefix":"Balances","Items":[{"Name":"TotalIssuance","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Balance","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The total units issued in the system."]},{"Name":"Account","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"AccountData\u003cT::Balance\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The balance of an account.",""," NOTE: This is only used in the case that this pallet is used to store balances."]},{"Name":"Locks","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"WeakBoundedVec\u003cBalanceLock\u003cT::Balance\u003e, T::MaxLocks\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Any liquidity locks on some account balances."," NOTE: Should only be accessed when setting, changing and freeing a lock."]},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," This is set to v2.0.0 for new networks."]}]},"HasCalls":true,"Calls":[{"Name":"transfer","Args":[{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Transfer some liquid free balance to another account.",""," `transfer` will set the `FreeBalance` of the sender and receiver."," It will decrease the total issuance of the system by the `TransferFee`."," If the sender's account is below the existential deposit as a result"," of the transfer, the account will be reaped.",""," The dispatch origin for this call must be `Signed` by the transactor.",""," # \u003cweight\u003e"," - Dependent on arguments but not critical, given proper implementations for"," input config types. See related functions below."," - It contains a limited number of reads and writes internally and no complex computation.",""," Related functions:",""," - `ensure_can_withdraw` is always called internally but has a bounded complexity."," - Transferring balances to accounts that did not exist before will cause"," `T::OnNewAccount::on_new_account` to be called."," - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."," - `transfer_keep_alive` works the same way as `transfer`, but has an additional"," check that the transfer will not kill the origin account."," ---------------------------------"," - Base Weight: 73.64 µs, worst case scenario (account created, account removed)"," - DB Weight: 1 Read and 1 Write to destination account"," - Origin account is already in memory, so no DB operations for them."," # \u003c/weight\u003e"]},{"Name":"set_balance","Args":[{"Name":"who","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"new_free","Type":"Compact\u003cT::Balance\u003e"},{"Name":"new_reserved","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Set the balances of a given account.",""," This will alter `FreeBalance` and `ReservedBalance` in storage. it will"," also decrease the total issuance of the system (`TotalIssuance`)."," If the new free or reserved balance is below the existential deposit,"," it will reset the account nonce (`frame_system::AccountNonce`).",""," The dispatch origin for this call is `root`.",""," # \u003cweight\u003e"," - Independent of the arguments."," - Contains a limited number of reads and writes."," ---------------------"," - Base Weight:"," - Creating: 27.56 µs"," - Killing: 35.11 µs"," - DB Weight: 1 Read, 1 Write to `who`"," # \u003c/weight\u003e"]},{"Name":"force_transfer","Args":[{"Name":"source","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Exactly as `transfer`, except the origin must be root and the source account may be"," specified."," # \u003cweight\u003e"," - Same as transfer, but additional read and write because the source account is"," not assumed to be in the overlay."," # \u003c/weight\u003e"]},{"Name":"transfer_keep_alive","Args":[{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Same as the [`transfer`] call, but with a check that the transfer will not kill the"," origin account.",""," 99% of the time you want [`transfer`] instead.",""," [`transfer`]: struct.Pallet.html#method.transfer"," # \u003cweight\u003e"," - Cheaper than transfer because account cannot be killed."," - Base Weight: 51.4 µs"," - DB Weight: 1 Read and 1 Write to dest (sender is in overlay already)"," #\u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Endowed","Args":["AccountId","Balance"],"Documentation":[" An account was created with some free balance. \\[account, free_balance\\]"]},{"Name":"DustLost","Args":["AccountId","Balance"],"Documentation":[" An account was removed whose balance was non-zero but below ExistentialDeposit,"," resulting in an outright loss. \\[account, balance\\]"]},{"Name":"Transfer","Args":["AccountId","AccountId","Balance"],"Documentation":[" Transfer succeeded. \\[from, to, value\\]"]},{"Name":"BalanceSet","Args":["AccountId","Balance","Balance"],"Documentation":[" A balance was set by root. \\[who, free, reserved\\]"]},{"Name":"Deposit","Args":["AccountId","Balance"],"Documentation":[" Some amount was deposited (e.g. for transaction fees). \\[who, deposit\\]"]},{"Name":"Reserved","Args":["AccountId","Balance"],"Documentation":[" Some balance was reserved (moved from free to reserved). \\[who, value\\]"]},{"Name":"Unreserved","Args":["AccountId","Balance"],"Documentation":[" Some balance was unreserved (moved from reserved to free). \\[who, value\\]"]},{"Name":"ReserveRepatriated","Args":["AccountId","AccountId","Balance","Status"],"Documentation":[" Some balance was moved from the reserve of the first account to the second account."," Final argument indicates the destination balance type."," \\[from, to, balance, destination_status\\]"]}],"Constants":[{"Name":"ExistentialDeposit","Type":"T::Balance","Value":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The minimum amount required to keep an account open."]}],"Errors":[{"Name":"VestingBalance","Documentation":[" Vesting balance too high to send value"]},{"Name":"LiquidityRestrictions","Documentation":[" Account liquidity restrictions prevent withdrawal"]},{"Name":"InsufficientBalance","Documentation":[" Balance too low to send value"]},{"Name":"ExistentialDeposit","Documentation":[" Value too low to create account due to existential deposit"]},{"Name":"KeepAlive","Documentation":[" Transfer/payment would kill account"]},{"Name":"ExistingVestingSchedule","Documentation":[" A vesting schedule already exists for this account"]},{"Name":"DeadAccount","Documentation":[" Beneficiary account must pre-exist"]}],"Index":3},{"Name":"Sudo","HasStorage":true,"Storage":{"Prefix":"Sudo","Items":[{"Name":"Key","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The `AccountId` of the sudo key."]}]},"HasCalls":true,"Calls":[{"Name":"sudo","Args":[{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Root` origin.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB write (event)."," - Weight of derivative `call` execution + 10,000."," # \u003c/weight\u003e"]},{"Name":"sudo_unchecked_weight","Args":[{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"},{"Name":"_weight","Type":"Weight"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Root` origin."," This function does not check the weight of the call, and instead allows the"," Sudo user to specify the weight of the call.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - The weight of this call is defined by the caller."," # \u003c/weight\u003e"]},{"Name":"set_key","Args":[{"Name":"new","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"}],"Documentation":[" Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB change."," # \u003c/weight\u003e"]},{"Name":"sudo_as","Args":[{"Name":"who","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Signed` origin from"," a given account.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB write (event)."," - Weight of derivative `call` execution + 10,000."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Sudid","Args":["DispatchResult"],"Documentation":[" A sudo just took place. \\[result\\]"]},{"Name":"KeyChanged","Args":["AccountId"],"Documentation":[" The \\[sudoer\\] just switched identity; the old key is supplied."]},{"Name":"SudoAsDone","Args":["DispatchResult"],"Documentation":[" A sudo just took place. \\[result\\]"]}],"Constants":null,"Errors":[{"Name":"RequireSudo","Documentation":[" Sender must be the Sudo account"]}],"Index":4},{"Name":"RandomnessCollectiveFlip","HasStorage":true,"Storage":{"Prefix":"RandomnessCollectiveFlip","Items":[{"Name":"RandomMaterial","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::Hash\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Series of block headers from the last 81 blocks that acts as random seed material. This"," is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of"," the oldest hash."]}]},"HasCalls":true,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":5},{"Name":"ParachainSystem","HasStorage":true,"Storage":{"Prefix":"ParachainSystem","Items":[{"Name":"PendingRelayChainBlockNumber","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"RelayChainBlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" We need to store the new validation function for the span between"," setting it and applying it. If it has a"," value, then [`PendingValidationCode`] must have a real value, and"," together will coordinate the block number where the upgrade will happen."]},{"Name":"PendingValidationCode","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cu8\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The new validation function we will upgrade to when the relay chain"," reaches [`PendingRelayChainBlockNumber`]. A real validation function must"," exist here as long as [`PendingRelayChainBlockNumber`] is set."]},{"Name":"ValidationData","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"PersistedValidationData","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The [`PersistedValidationData`] set for this block."]},{"Name":"DidSetValidationCode","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Were the validation data set to notify the relay chain?"]},{"Name":"LastUpgrade","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The last relay parent block number at which we signalled the code upgrade."]},{"Name":"RelevantMessagingState","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"MessagingStateSnapshot","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The snapshot of some state related to messaging relevant to the current parachain as per"," the relay parent.",""," This field is meant to be updated each block with the validation data inherent. Therefore,"," before processing of the inherent, e.g. in `on_initialize` this data may be stale.",""," This data is also absent from the genesis."]},{"Name":"HostConfiguration","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"AbridgedHostConfiguration","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The parachain host configuration that was obtained from the relay parent.",""," This field is meant to be updated each block with the validation data inherent. Therefore,"," before processing of the inherent, e.g. in `on_initialize` this data may be stale.",""," This data is also absent from the genesis."]},{"Name":"LastDmqMqcHead","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"MessageQueueChain","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The last downward message queue chain head we have observed.",""," This value is loaded before and saved after processing inbound downward messages carried"," by the system inherent."]},{"Name":"LastHrmpMqcHeads","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BTreeMap\u003cParaId, MessageQueueChain\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The message queue chain heads we have observed per each channel incoming channel.",""," This value is loaded before and saved after processing inbound downward messages carried"," by the system inherent."]},{"Name":"ProcessedDownwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Number of downward messages processed in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"NewValidationCode","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cu8\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" New validation code that was set in a block.",""," This will be cleared in `on_initialize` of each new block if no other pallet already set"," the value."]},{"Name":"HrmpWatermark","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::v1::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" HRMP watermark that was set in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"HrmpOutboundMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cOutboundHrmpMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" HRMP messages that were sent in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"UpwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cUpwardMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Upward messages that were sent in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"PendingUpwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cUpwardMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Upward messages that are still pending and not yet send to the relay chain."]},{"Name":"AnnouncedHrmpMessagesPerCandidate","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of HRMP messages we observed in `on_initialize` and thus used that number for"," announcing the weight of `on_initialize` and `on_finalize`."]},{"Name":"ReservedXcmpWeightOverride","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Weight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The weight we reserve at the beginning of the block for processing XCMP messages. This"," overrides the amount set in the Config trait."]},{"Name":"ReservedDmpWeightOverride","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Weight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The weight we reserve at the beginning of the block for processing DMP messages. This"," overrides the amount set in the Config trait."]},{"Name":"AuthorizedUpgrade","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Hash","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The next authorized upgrade, if there is one."]}]},"HasCalls":true,"Calls":[{"Name":"set_upgrade_block","Args":[{"Name":"relay_chain_block","Type":"RelayChainBlockNumber"}],"Documentation":[" Force an already scheduled validation function upgrade to happen on a particular block.",""," Note that coordinating this block for the upgrade has to happen independently on the"," relay chain and this parachain. Synchronizing the block for the upgrade is sensitive,"," and this bypasses all checks and and normal protocols. Very easy to brick your chain"," if done wrong."]},{"Name":"set_validation_data","Args":[{"Name":"data","Type":"ParachainInherentData"}],"Documentation":[" Set the current validation data.",""," This should be invoked exactly once per block. It will panic at the finalization"," phase if the call was not invoked.",""," The dispatch origin for this call must be `Inherent`",""," As a side effect, this function upgrades the current validation function"," if the appropriate time has come."]},{"Name":"sudo_send_upward_message","Args":[{"Name":"message","Type":"UpwardMessage"}],"Documentation":null},{"Name":"authorize_upgrade","Args":[{"Name":"code_hash","Type":"T::Hash"}],"Documentation":null},{"Name":"enact_authorized_upgrade","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":null}],"HasEvents":true,"Events":[{"Name":"ValidationFunctionStored","Args":["RelayChainBlockNumber"],"Documentation":[" The validation function has been scheduled to apply as of the contained relay chain"," block number."]},{"Name":"ValidationFunctionApplied","Args":["RelayChainBlockNumber"],"Documentation":[" The validation function was applied as of the contained relay chain block number."]},{"Name":"UpgradeAuthorized","Args":["Hash"],"Documentation":[" An upgrade has been authorized."]},{"Name":"DownwardMessagesReceived","Args":["u32"],"Documentation":[" Some downward messages have been received and will be processed."," \\[ count \\]"]},{"Name":"DownwardMessagesProcessed","Args":["Weight","relay_chain::Hash"],"Documentation":[" Downward messages were processed using the given weight."," \\[ weight_used, result_mqc_head \\]"]}],"Constants":null,"Errors":[{"Name":"OverlappingUpgrades","Documentation":[" Attempt to upgrade validation function while existing upgrade pending"]},{"Name":"ProhibitedByPolkadot","Documentation":[" Polkadot currently prohibits this parachain from upgrading its validation function"]},{"Name":"TooBig","Documentation":[" The supplied validation function has compiled into a blob larger than Polkadot is"," willing to run"]},{"Name":"ValidationDataNotAvailable","Documentation":[" The inherent which supplies the validation data did not run this block"]},{"Name":"HostConfigurationNotAvailable","Documentation":[" The inherent which supplies the host configuration did not run this block"]},{"Name":"NotScheduled","Documentation":[" No validation function upgrade is currently scheduled."]},{"Name":"NothingAuthorized","Documentation":[" No code upgrade has been authorized."]},{"Name":"Unauthorized","Documentation":[" The given code upgrade has not been authorized."]}],"Index":6},{"Name":"TransactionPayment","HasStorage":true,"Storage":{"Prefix":"TransactionPayment","Items":[{"Name":"NextFeeMultiplier","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Multiplier","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AABkp7O24A0AAAAAAAAAAA==","Documentation":null},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":[{"Name":"TransactionByteFee","Type":"BalanceOf\u003cT\u003e","Value":"AEB6EPNaAAAAAAAAAAAAAA==","Documentation":[" The fee to be paid for making a transaction; the per-byte portion."]},{"Name":"WeightToFee","Type":"Vec\u003cWeightToFeeCoefficient\u003cBalanceOf\u003cT\u003e\u003e\u003e","Value":"BAEAAAAAAAAAAAAAAAAAAAAAAAAAAAE=","Documentation":[" The polynomial that is applied in order to derive fee from weight."]}],"Errors":null,"Index":7},{"Name":"ParachainInfo","HasStorage":true,"Storage":{"Prefix":"ParachainInfo","Items":[{"Name":"ParachainId","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ParaId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"ZAAAAA==","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":8},{"Name":"EthereumChainId","HasStorage":true,"Storage":{"Prefix":"EthereumChainId","Items":[{"Name":"ChainId","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u64","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAA=","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":9},{"Name":"EVM","HasStorage":true,"Storage":{"Prefix":"EVM","Items":[{"Name":"AccountCodes","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"H160","Value":"Vec\u003cu8\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"AccountStorages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"H160","Key2":"H256","Value":"H256","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":null}]},"HasCalls":true,"Calls":[{"Name":"withdraw","Args":[{"Name":"address","Type":"H160"},{"Name":"value","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Withdraw balance from EVM into currency/balances pallet."]},{"Name":"call","Args":[{"Name":"source","Type":"H160"},{"Name":"target","Type":"H160"},{"Name":"input","Type":"Vec\u003cu8\u003e"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM call operation. This is similar to a message call transaction in Ethereum."]},{"Name":"create","Args":[{"Name":"source","Type":"H160"},{"Name":"init","Type":"Vec\u003cu8\u003e"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM create operation. This is similar to a contract creation transaction in"," Ethereum."]},{"Name":"create2","Args":[{"Name":"source","Type":"H160"},{"Name":"init","Type":"Vec\u003cu8\u003e"},{"Name":"salt","Type":"H256"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM create2 operation."]}],"HasEvents":true,"Events":[{"Name":"Log","Args":["Log"],"Documentation":[" Ethereum events from contracts."]},{"Name":"Created","Args":["H160"],"Documentation":[" A contract has been created at given \\[address\\]."]},{"Name":"CreatedFailed","Args":["H160"],"Documentation":[" A \\[contract\\] was attempted to be created, but the execution failed."]},{"Name":"Executed","Args":["H160"],"Documentation":[" A \\[contract\\] has been executed successfully with states applied."]},{"Name":"ExecutedFailed","Args":["H160"],"Documentation":[" A \\[contract\\] has been executed with errors. States are reverted with only gas fees applied."]},{"Name":"BalanceDeposit","Args":["AccountId","H160","U256"],"Documentation":[" A deposit has been made at a given address. \\[sender, address, value\\]"]},{"Name":"BalanceWithdraw","Args":["AccountId","H160","U256"],"Documentation":[" A withdrawal has been made from a given address. \\[sender, address, value\\]"]}],"Constants":null,"Errors":[{"Name":"BalanceLow","Documentation":[" Not enough balance to perform action"]},{"Name":"FeeOverflow","Documentation":[" Calculating total fee overflowed"]},{"Name":"PaymentOverflow","Documentation":[" Calculating total payment overflowed"]},{"Name":"WithdrawFailed","Documentation":[" Withdraw fee failed"]},{"Name":"GasPriceTooLow","Documentation":[" Gas price is too low."]},{"Name":"InvalidNonce","Documentation":[" Nonce is invalid"]}],"Index":10},{"Name":"Ethereum","HasStorage":true,"Storage":{"Prefix":"Ethereum","Items":[{"Name":"Pending","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003c(ethereum::Transaction, TransactionStatus, ethereum::Receipt)\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Current building block's transactions and receipts."]},{"Name":"CurrentBlock","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"ethereum::Block","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current Ethereum block."]},{"Name":"CurrentReceipts","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cethereum::Receipt\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current Ethereum receipts."]},{"Name":"CurrentTransactionStatuses","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cTransactionStatus\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current transaction statuses."]}]},"HasCalls":true,"Calls":[{"Name":"transact","Args":[{"Name":"transaction","Type":"ethereum::Transaction"}],"Documentation":[" Transact an Ethereum transaction."]}],"HasEvents":true,"Events":[{"Name":"Executed","Args":["H160","H160","H256","ExitReason"],"Documentation":[" An ethereum transaction was successfully executed. [from, to/contract_address, transaction_hash, exit_reason]"]}],"Constants":null,"Errors":null,"Index":11},{"Name":"ParachainStaking","HasStorage":true,"Storage":{"Prefix":"ParachainStaking","Items":[{"Name":"CollatorCommission","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Perbill","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Commission percent taken off of rewards for all collators"]},{"Name":"TotalSelected","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The total candidates selected every round"]},{"Name":"ParachainBondInfo","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ParachainBondConfig\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Parachain bond config info { account, percent_of_inflation }"]},{"Name":"Round","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"RoundInfo\u003cT::BlockNumber\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AQAAAAEAAAAUAAAA","Documentation":[" Current round index and next round scheduled transition"]},{"Name":"NominatorState","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Nominator\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Get nominator state associated with an account if account is nominating else None"]},{"Name":"CollatorState","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Collator\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" DEPRECATED: This is the old storage item. It is retained for purposes of storage migration"," and should be removed in the future."]},{"Name":"CollatorState2","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Collator2\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Get collator state associated with an account if account is collating else None"]},{"Name":"SelectedCandidates","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The collator candidates selected for the current round"]},{"Name":"Total","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BalanceOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total capital locked by this staking pallet"]},{"Name":"CandidatePool","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"OrderedSet\u003cBond\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The pool of collator candidates, each with their total backing stake"]},{"Name":"ExitQueue","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"OrderedSet\u003cBond\u003cT::AccountId, RoundIndex\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" A queue of collators awaiting exit `BondDuration` delay after request"]},{"Name":"AtStake","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key1":"RoundIndex","Key2":"T::AccountId","Value":"CollatorSnapshot\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Snapshot of collator nomination stake at the start of the round"]},{"Name":"Staked","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"RoundIndex","Value":"BalanceOf\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total backing stake for selected candidates in the round"]},{"Name":"InflationConfig","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"InflationInfo\u003cBalanceOf\u003cT\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Inflation configuration"]},{"Name":"Points","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"RoundIndex","Value":"RewardPoint","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Total points awarded to collators for block production in the round"]},{"Name":"AwardedPts","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key1":"RoundIndex","Key2":"T::AccountId","Value":"RewardPoint","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Points for each collator per round"]}]},"HasCalls":true,"Calls":[{"Name":"set_staking_expectations","Args":[{"Name":"expectations","Type":"Range\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Set the expectations for total staked. These expectations determine the issuance for"," the round according to logic in `fn compute_issuance`"]},{"Name":"set_inflation","Args":[{"Name":"schedule","Type":"Range\u003cPerbill\u003e"}],"Documentation":[" Set the annual inflation rate to derive per-round inflation"]},{"Name":"set_parachain_bond_account","Args":[{"Name":"new","Type":"T::AccountId"}],"Documentation":[" Set the account that will hold funds set aside for parachain bond"]},{"Name":"set_parachain_bond_reserve_percent","Args":[{"Name":"new","Type":"Percent"}],"Documentation":[" Set the percent of inflation set aside for parachain bond"]},{"Name":"set_total_selected","Args":[{"Name":"new","Type":"u32"}],"Documentation":[" Set the total number of collator candidates selected per round"," - changes are not applied until the start of the next round"]},{"Name":"set_collator_commission","Args":[{"Name":"new","Type":"Perbill"}],"Documentation":[" Set the commission for all collators"]},{"Name":"set_blocks_per_round","Args":[{"Name":"new","Type":"u32"}],"Documentation":[" Set blocks per round"," - if called with `new` less than length of current round, will transition immediately"," in the next block"," - also updates per-round inflation config"]},{"Name":"join_candidates","Args":[{"Name":"bond","Type":"BalanceOf\u003cT\u003e"},{"Name":"candidate_count","Type":"u32"}],"Documentation":[" Join the set of collator candidates"]},{"Name":"leave_candidates","Args":[{"Name":"candidate_count","Type":"u32"}],"Documentation":[" Request to leave the set of candidates. If successful, the account is immediately"," removed from the candidate pool to prevent selection as a collator, but unbonding is"," executed with a delay of `BondDuration` rounds."]},{"Name":"go_offline","Args":null,"Documentation":[" Temporarily leave the set of collator candidates without unbonding"]},{"Name":"go_online","Args":null,"Documentation":[" Rejoin the set of collator candidates if previously had called `go_offline`"]},{"Name":"candidate_bond_more","Args":[{"Name":"more","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond more for collator candidates"]},{"Name":"candidate_bond_less","Args":[{"Name":"less","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond less for collator candidates"]},{"Name":"nominate","Args":[{"Name":"collator","Type":"T::AccountId"},{"Name":"amount","Type":"BalanceOf\u003cT\u003e"},{"Name":"collator_nominator_count","Type":"u32"},{"Name":"nomination_count","Type":"u32"}],"Documentation":[" If caller is not a nominator, then join the set of nominators"," If caller is a nominator, then makes nomination to change their nomination state"]},{"Name":"leave_nominators","Args":[{"Name":"nomination_count","Type":"u32"}],"Documentation":[" Leave the set of nominators and, by implication, revoke all ongoing nominations"]},{"Name":"revoke_nomination","Args":[{"Name":"collator","Type":"T::AccountId"}],"Documentation":[" Revoke an existing nomination"]},{"Name":"nominator_bond_more","Args":[{"Name":"candidate","Type":"T::AccountId"},{"Name":"more","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond more for nominators with respect to a specific collator candidate"]},{"Name":"nominator_bond_less","Args":[{"Name":"candidate","Type":"T::AccountId"},{"Name":"less","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond less for nominators with respect to a specific nominator candidate"]}],"HasEvents":true,"Events":[{"Name":"NewRound","Args":["T::BlockNumber","RoundIndex","u32","BalanceOf\u003cT\u003e"],"Documentation":[" Starting Block, Round, Number of Collators Selected, Total Balance"]},{"Name":"JoinedCollatorCandidates","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Account, Amount Locked, New Total Amt Locked"]},{"Name":"CollatorChosen","Args":["RoundIndex","T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Round, Collator Account, Total Exposed Amount (includes all nominations)"]},{"Name":"CollatorBondedMore","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Collator Account, Old Bond, New Bond"]},{"Name":"CollatorBondedLess","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Collator Account, Old Bond, New Bond"]},{"Name":"CollatorWentOffline","Args":["RoundIndex","T::AccountId"],"Documentation":null},{"Name":"CollatorBackOnline","Args":["RoundIndex","T::AccountId"],"Documentation":null},{"Name":"CollatorScheduledExit","Args":["RoundIndex","T::AccountId","RoundIndex"],"Documentation":[" Round, Collator Account, Scheduled Exit"]},{"Name":"CollatorLeft","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Account, Amount Unlocked, New Total Amt Locked"]},{"Name":"NominationIncreased","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","bool","BalanceOf\u003cT\u003e"],"Documentation":null},{"Name":"NominationDecreased","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","bool","BalanceOf\u003cT\u003e"],"Documentation":null},{"Name":"NominatorLeft","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Nominator, Amount Unstaked"]},{"Name":"Nomination","Args":["T::AccountId","BalanceOf\u003cT\u003e","T::AccountId","NominatorAdded\u003cBalanceOf\u003cT\u003e\u003e"],"Documentation":[" Nominator, Amount Locked, Collator, Nominator Position with New Total Backing if in Top"]},{"Name":"NominatorLeftCollator","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Nominator, Collator, Amount Unstaked, New Total Amt Staked for Collator"]},{"Name":"Rewarded","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Paid the account (nominator or collator) the balance as liquid rewards"]},{"Name":"ReservedForParachainBond","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Transferred to account which holds funds reserved for parachain bond"]},{"Name":"ParachainBondAccountSet","Args":["T::AccountId","T::AccountId"],"Documentation":[" Account (re)set for parachain bond treasury [old, new]"]},{"Name":"ParachainBondReservePercentSet","Args":["Percent","Percent"],"Documentation":[" Percent of inflation reserved for parachain bond (re)set [old, new]"]},{"Name":"InflationSet","Args":["Perbill","Perbill","Perbill","Perbill","Perbill","Perbill"],"Documentation":[" Annual inflation input (first 3) was used to derive new per-round inflation (last 3)"]},{"Name":"StakeExpectationsSet","Args":["BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Staking expectations set"]},{"Name":"TotalSelectedSet","Args":["u32","u32"],"Documentation":[" Set total selected candidates to this value [old, new]"]},{"Name":"CollatorCommissionSet","Args":["Perbill","Perbill"],"Documentation":[" Set collator commission to this value [old, new]"]},{"Name":"BlocksPerRoundSet","Args":["RoundIndex","T::BlockNumber","u32","u32","Perbill","Perbill","Perbill"],"Documentation":[" Set blocks per round [current_round, first_block, old, new, new_per_round_inflation]"]}],"Constants":null,"Errors":[{"Name":"NominatorDNE","Documentation":null},{"Name":"CandidateDNE","Documentation":null},{"Name":"NominatorExists","Documentation":null},{"Name":"CandidateExists","Documentation":null},{"Name":"ValBondBelowMin","Documentation":null},{"Name":"NomBondBelowMin","Documentation":null},{"Name":"NominationBelowMin","Documentation":null},{"Name":"AlreadyOffline","Documentation":null},{"Name":"AlreadyActive","Documentation":null},{"Name":"AlreadyLeaving","Documentation":null},{"Name":"CannotActivateIfLeaving","Documentation":null},{"Name":"ExceedMaxCollatorsPerNom","Documentation":null},{"Name":"AlreadyNominatedCollator","Documentation":null},{"Name":"NominationDNE","Documentation":null},{"Name":"CannotBondLessGEQTotalBond","Documentation":null},{"Name":"InvalidSchedule","Documentation":null},{"Name":"CannotSetBelowMin","Documentation":null},{"Name":"NoWritingSameValue","Documentation":null},{"Name":"TooLowCandidateCountWeightHintJoinCandidates","Documentation":null},{"Name":"TooLowCollatorCandidateCountToLeaveCandidates","Documentation":null},{"Name":"TooLowNominationCountToNominate","Documentation":null},{"Name":"TooLowCollatorNominationCountToNominate","Documentation":null},{"Name":"TooLowNominationCountToLeaveNominators","Documentation":null}],"Index":12},{"Name":"Scheduler","HasStorage":true,"Storage":{"Prefix":"Scheduler","Items":[{"Name":"Agenda","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::BlockNumber","Value":"Vec\u003cOption\u003cScheduled\u003c\u003cT as Config\u003e::Call, T::BlockNumber, T::\nPalletsOrigin, T::AccountId\u003e\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Items to be executed, indexed by the block number that they should be executed on."]},{"Name":"Lookup","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"Vec\u003cu8\u003e","Value":"TaskAddress\u003cT::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Lookup from identity to the block number and index of the task."]},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," New networks start with last version."]}]},"HasCalls":true,"Calls":[{"Name":"schedule","Args":[{"Name":"when","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Anonymously schedule a task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 22.29 + .126 * S µs"," - DB Weight:"," - Read: Agenda"," - Write: Agenda"," - Will use base weight of 25 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"cancel","Args":[{"Name":"when","Type":"T::BlockNumber"},{"Name":"index","Type":"u32"}],"Documentation":[" Cancel an anonymously scheduled task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 22.15 + 2.869 * S µs"," - DB Weight:"," - Read: Agenda"," - Write: Agenda, Lookup"," - Will use base weight of 100 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"schedule_named","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"},{"Name":"when","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Schedule a named task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 29.6 + .159 * S µs"," - DB Weight:"," - Read: Agenda, Lookup"," - Write: Agenda, Lookup"," - Will use base weight of 35 which should be good for more than 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"cancel_named","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Cancel a named scheduled task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 24.91 + 2.907 * S µs"," - DB Weight:"," - Read: Agenda, Lookup"," - Write: Agenda, Lookup"," - Will use base weight of 100 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"schedule_after","Args":[{"Name":"after","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Anonymously schedule a task after a delay.",""," # \u003cweight\u003e"," Same as [`schedule`]."," # \u003c/weight\u003e"]},{"Name":"schedule_named_after","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"},{"Name":"after","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Schedule a named task after a delay.",""," # \u003cweight\u003e"," Same as [`schedule_named`]."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Scheduled","Args":["BlockNumber","u32"],"Documentation":[" Scheduled some task. \\[when, index\\]"]},{"Name":"Canceled","Args":["BlockNumber","u32"],"Documentation":[" Canceled some task. \\[when, index\\]"]},{"Name":"Dispatched","Args":["TaskAddress\u003cBlockNumber\u003e","Option\u003cVec\u003cu8\u003e\u003e","DispatchResult"],"Documentation":[" Dispatched some task. \\[task, id, result\\]"]}],"Constants":null,"Errors":[{"Name":"FailedToSchedule","Documentation":[" Failed to schedule a call"]},{"Name":"NotFound","Documentation":[" Cannot find the scheduled call."]},{"Name":"TargetBlockNumberInPast","Documentation":[" Given target block number is in the past."]},{"Name":"RescheduleNoChange","Documentation":[" Reschedule failed because it does not change scheduled time."]}],"Index":13},{"Name":"Democracy","HasStorage":true,"Storage":{"Prefix":"Democracy","Items":[{"Name":"PublicPropCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"PropIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of (public) proposals that have been made so far."]},{"Name":"PublicProps","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003c(PropIndex, T::Hash, T::AccountId)\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The public proposals. Unsorted. The second item is the proposal's hash."]},{"Name":"DepositOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"PropIndex","Value":"(Vec\u003cT::AccountId\u003e, BalanceOf\u003cT\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Those who have locked a deposit.",""," TWOX-NOTE: Safe, as increasing integer keys are safe."]},{"Name":"Preimages","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"PreimageStatus\u003cT::AccountId, BalanceOf\u003cT\u003e, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Map of hashes to the proposal preimage, along with who registered it and their deposit."," The block number is the block at which it was deposited."]},{"Name":"ReferendumCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ReferendumIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The next free referendum index, aka the number of referenda started so far."]},{"Name":"LowestUnbaked","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ReferendumIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The lowest referendum index representing an unbaked referendum. Equal to"," `ReferendumCount` if there isn't a unbaked referendum."]},{"Name":"ReferendumInfoOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"ReferendumIndex","Value":"ReferendumInfo\u003cT::BlockNumber, T::Hash, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Information concerning any given referendum.",""," TWOX-NOTE: SAFE as indexes are not under an attacker’s control."]},{"Name":"VotingOf","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Voting\u003cBalanceOf\u003cT\u003e, T::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" All votes for a particular voter. We store the balance for the number of votes that we"," have recorded. The second item is the total amount of delegations, that will be added.",""," TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway."]},{"Name":"Locks","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"T::BlockNumber","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Accounts for which there are locks in action which may be removed at some point in the"," future. The value is the block number at which the lock expires and may be removed.",""," TWOX-NOTE: OK ― `AccountId` is a secure hash."]},{"Name":"LastTabledWasExternal","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if the last referendum tabled was submitted externally. False if it was a public"," proposal."]},{"Name":"NextExternal","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"(T::Hash, VoteThreshold)","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The referendum to be tabled whenever it would be valid to table an external proposal."," This happens when a referendum needs to be tabled and one of two conditions are met:"," - `LastTabledWasExternal` is `false`; or"," - `PublicProps` is empty."]},{"Name":"Blacklist","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"(T::BlockNumber, Vec\u003cT::AccountId\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" A record of who vetoed what. Maps proposal hash to a possible existent block number"," (until when it may not be resubmitted) and who vetoed it."]},{"Name":"Cancellations","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"bool","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Record of all proposals that have been subject to emergency cancellation."]},{"Name":"StorageVersion","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," New networks start with last version."]}]},"HasCalls":true,"Calls":[{"Name":"propose","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"value","Type":"Compact\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Propose a sensitive action to be taken.",""," The dispatch origin of this call must be _Signed_ and the sender must"," have funds to cover the deposit.",""," - `proposal_hash`: The hash of the proposal preimage."," - `value`: The amount of deposit (must be at least `MinimumDeposit`).",""," Emits `Proposed`.",""," Weight: `O(p)`"]},{"Name":"second","Args":[{"Name":"proposal","Type":"Compact\u003cPropIndex\u003e"},{"Name":"seconds_upper_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Signals agreement with a particular proposal.",""," The dispatch origin of this call must be _Signed_ and the sender"," must have funds to cover the deposit, equal to the original deposit.",""," - `proposal`: The index of the proposal to second."," - `seconds_upper_bound`: an upper bound on the current number of seconds on this"," proposal. Extrinsic is weighted according to this value with no refund.",""," Weight: `O(S)` where S is the number of seconds a proposal already has."]},{"Name":"vote","Args":[{"Name":"ref_index","Type":"Compact\u003cReferendumIndex\u003e"},{"Name":"vote","Type":"AccountVote\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;"," otherwise it is a vote to keep the status quo.",""," The dispatch origin of this call must be _Signed_.",""," - `ref_index`: The index of the referendum to vote for."," - `vote`: The vote configuration.",""," Weight: `O(R)` where R is the number of referendums the voter has voted on."]},{"Name":"emergency_cancel","Args":[{"Name":"ref_index","Type":"ReferendumIndex"}],"Documentation":[" Schedule an emergency cancellation of a referendum. Cannot happen twice to the same"," referendum.",""," The dispatch origin of this call must be `CancellationOrigin`.",""," -`ref_index`: The index of the referendum to cancel.",""," Weight: `O(1)`."]},{"Name":"external_propose","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a referendum to be tabled once it is legal to schedule an external"," referendum.",""," The dispatch origin of this call must be `ExternalOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Weight: `O(V)` with V number of vetoers in the blacklist of proposal."," Decoding vec of length V. Charged as maximum"]},{"Name":"external_propose_majority","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a majority-carries referendum to be tabled next once it is legal to schedule"," an external referendum.",""," The dispatch of this call must be `ExternalMajorityOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Unlike `external_propose`, blacklisting has no effect on this and it may replace a"," pre-scheduled `external_propose` call.",""," Weight: `O(1)`"]},{"Name":"external_propose_default","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a negative-turnout-bias referendum to be tabled next once it is legal to"," schedule an external referendum.",""," The dispatch of this call must be `ExternalDefaultOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Unlike `external_propose`, blacklisting has no effect on this and it may replace a"," pre-scheduled `external_propose` call.",""," Weight: `O(1)`"]},{"Name":"fast_track","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"voting_period","Type":"T::BlockNumber"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Schedule the currently externally-proposed majority-carries referendum to be tabled"," immediately. If there is no externally-proposed referendum currently, or if there is one"," but it is not a majority-carries referendum then it fails.",""," The dispatch of this call must be `FastTrackOrigin`.",""," - `proposal_hash`: The hash of the current external proposal."," - `voting_period`: The period that is allowed for voting on this proposal. Increased to"," `FastTrackVotingPeriod` if too low."," - `delay`: The number of block after voting has ended in approval and this should be"," enacted. This doesn't have a minimum amount.",""," Emits `Started`.",""," Weight: `O(1)`"]},{"Name":"veto_external","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Veto and blacklist the external proposal hash.",""," The dispatch origin of this call must be `VetoOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.",""," Emits `Vetoed`.",""," Weight: `O(V + log(V))` where V is number of `existing vetoers`"]},{"Name":"cancel_referendum","Args":[{"Name":"ref_index","Type":"Compact\u003cReferendumIndex\u003e"}],"Documentation":[" Remove a referendum.",""," The dispatch origin of this call must be _Root_.",""," - `ref_index`: The index of the referendum to cancel.",""," # Weight: `O(1)`."]},{"Name":"cancel_queued","Args":[{"Name":"which","Type":"ReferendumIndex"}],"Documentation":[" Cancel a proposal queued for enactment.",""," The dispatch origin of this call must be _Root_.",""," - `which`: The index of the referendum to cancel.",""," Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`."]},{"Name":"delegate","Args":[{"Name":"to","Type":"T::AccountId"},{"Name":"conviction","Type":"Conviction"},{"Name":"balance","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Delegate the voting power (with some given conviction) of the sending account.",""," The balance delegated is locked for as long as it's delegated, and thereafter for the"," time appropriate for the conviction's lock period.",""," The dispatch origin of this call must be _Signed_, and the signing account must either:"," - be delegating already; or"," - have no voting activity (if there is, then it will need to be removed/consolidated"," through `reap_vote` or `unvote`).",""," - `to`: The account whose voting the `target` account's voting power will follow."," - `conviction`: The conviction that will be attached to the delegated votes. When the"," account is undelegated, the funds will be locked for the corresponding period."," - `balance`: The amount of the account's balance to be used in delegating. This must"," not be more than the account's current balance.",""," Emits `Delegated`.",""," Weight: `O(R)` where R is the number of referendums the voter delegating to has"," voted on. Weight is charged as if maximum votes."]},{"Name":"undelegate","Args":null,"Documentation":[" Undelegate the voting power of the sending account.",""," Tokens may be unlocked following once an amount of time consistent with the lock period"," of the conviction with which the delegation was issued.",""," The dispatch origin of this call must be _Signed_ and the signing account must be"," currently delegating.",""," Emits `Undelegated`.",""," Weight: `O(R)` where R is the number of referendums the voter delegating to has"," voted on. Weight is charged as if maximum votes."]},{"Name":"clear_public_proposals","Args":null,"Documentation":[" Clears all public proposals.",""," The dispatch origin of this call must be _Root_.",""," Weight: `O(1)`."]},{"Name":"note_preimage","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Register the preimage for an upcoming proposal. This doesn't require the proposal to be"," in the dispatch queue but does require a deposit, returned once enacted.",""," The dispatch origin of this call must be _Signed_.",""," - `encoded_proposal`: The preimage of a proposal.",""," Emits `PreimageNoted`.",""," Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)."]},{"Name":"note_preimage_operational","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Same as `note_preimage` but origin is `OperationalPreimageOrigin`."]},{"Name":"note_imminent_preimage","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Register the preimage for an upcoming proposal. This requires the proposal to be"," in the dispatch queue. No deposit is needed. When this call is successful, i.e."," the preimage has not been uploaded before and matches some imminent proposal,"," no fee is paid.",""," The dispatch origin of this call must be _Signed_.",""," - `encoded_proposal`: The preimage of a proposal.",""," Emits `PreimageNoted`.",""," Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)."]},{"Name":"note_imminent_preimage_operational","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`."]},{"Name":"reap_preimage","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"proposal_len_upper_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Remove an expired proposal preimage and collect the deposit.",""," The dispatch origin of this call must be _Signed_.",""," - `proposal_hash`: The preimage hash of a proposal."," - `proposal_length_upper_bound`: an upper bound on length of the proposal."," Extrinsic is weighted according to this value with no refund.",""," This will only work after `VotingPeriod` blocks from the time that the preimage was"," noted, if it's the same account doing it. If it's a different account, then it'll only"," work an additional `EnactmentPeriod` later.",""," Emits `PreimageReaped`.",""," Weight: `O(D)` where D is length of proposal."]},{"Name":"unlock","Args":[{"Name":"target","Type":"T::AccountId"}],"Documentation":[" Unlock tokens that have an expired lock.",""," The dispatch origin of this call must be _Signed_.",""," - `target`: The account to remove the lock on.",""," Weight: `O(R)` with R number of vote of target."]},{"Name":"remove_vote","Args":[{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Remove a vote for a referendum.",""," If:"," - the referendum was cancelled, or"," - the referendum is ongoing, or"," - the referendum has ended such that"," - the vote of the account was in opposition to the result; or"," - there was no conviction to the account's vote; or"," - the account made a split vote"," ...then the vote is removed cleanly and a following call to `unlock` may result in more"," funds being available.",""," If, however, the referendum has ended and:"," - it finished corresponding to the vote of the account, and"," - the account made a standard vote with conviction, and"," - the lock period of the conviction is not over"," ...then the lock will be aggregated into the overall account's lock, which may involve"," *overlocking* (where the two locks are combined into a single lock that is the maximum"," of both the amount locked and the time is it locked for).",""," The dispatch origin of this call must be _Signed_, and the signer must have a vote"," registered for referendum `index`.",""," - `index`: The index of referendum of the vote to be removed.",""," Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on."," Weight is calculated for the maximum number of vote."]},{"Name":"remove_other_vote","Args":[{"Name":"target","Type":"T::AccountId"},{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Remove a vote for a referendum.",""," If the `target` is equal to the signer, then this function is exactly equivalent to"," `remove_vote`. If not equal to the signer, then the vote must have expired,"," either because the referendum was cancelled, because the voter lost the referendum or"," because the conviction period is over.",""," The dispatch origin of this call must be _Signed_.",""," - `target`: The account of the vote to be removed; this account must have voted for"," referendum `index`."," - `index`: The index of referendum of the vote to be removed.",""," Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on."," Weight is calculated for the maximum number of vote."]},{"Name":"enact_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Enact a proposal from a referendum. For now we just make the weight be the maximum."]},{"Name":"blacklist","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"maybe_ref_index","Type":"Option\u003cReferendumIndex\u003e"}],"Documentation":[" Permanently place a proposal into the blacklist. This prevents it from ever being"," proposed again.",""," If called on a queued public or external proposal, then this will result in it being"," removed. If the `ref_index` supplied is an active referendum with the proposal hash,"," then it will be cancelled.",""," The dispatch origin of this call must be `BlacklistOrigin`.",""," - `proposal_hash`: The proposal hash to blacklist permanently."," - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be"," cancelled.",""," Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a"," reasonable value)."]},{"Name":"cancel_proposal","Args":[{"Name":"prop_index","Type":"Compact\u003cPropIndex\u003e"}],"Documentation":[" Remove a proposal.",""," The dispatch origin of this call must be `CancelProposalOrigin`.",""," - `prop_index`: The index of the proposal to cancel.",""," Weight: `O(p)` where `p = PublicProps::\u003cT\u003e::decode_len()`"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["PropIndex","Balance"],"Documentation":[" A motion has been proposed by a public account. \\[proposal_index, deposit\\]"]},{"Name":"Tabled","Args":["PropIndex","Balance","Vec\u003cAccountId\u003e"],"Documentation":[" A public proposal has been tabled for referendum vote. \\[proposal_index, deposit, depositors\\]"]},{"Name":"ExternalTabled","Args":null,"Documentation":[" An external proposal has been tabled."]},{"Name":"Started","Args":["ReferendumIndex","VoteThreshold"],"Documentation":[" A referendum has begun. \\[ref_index, threshold\\]"]},{"Name":"Passed","Args":["ReferendumIndex"],"Documentation":[" A proposal has been approved by referendum. \\[ref_index\\]"]},{"Name":"NotPassed","Args":["ReferendumIndex"],"Documentation":[" A proposal has been rejected by referendum. \\[ref_index\\]"]},{"Name":"Cancelled","Args":["ReferendumIndex"],"Documentation":[" A referendum has been cancelled. \\[ref_index\\]"]},{"Name":"Executed","Args":["ReferendumIndex","bool"],"Documentation":[" A proposal has been enacted. \\[ref_index, is_ok\\]"]},{"Name":"Delegated","Args":["AccountId","AccountId"],"Documentation":[" An account has delegated their vote to another account. \\[who, target\\]"]},{"Name":"Undelegated","Args":["AccountId"],"Documentation":[" An \\[account\\] has cancelled a previous delegation operation."]},{"Name":"Vetoed","Args":["AccountId","Hash","BlockNumber"],"Documentation":[" An external proposal has been vetoed. \\[who, proposal_hash, until\\]"]},{"Name":"PreimageNoted","Args":["Hash","AccountId","Balance"],"Documentation":[" A proposal's preimage was noted, and the deposit taken. \\[proposal_hash, who, deposit\\]"]},{"Name":"PreimageUsed","Args":["Hash","AccountId","Balance"],"Documentation":[" A proposal preimage was removed and used (the deposit was returned)."," \\[proposal_hash, provider, deposit\\]"]},{"Name":"PreimageInvalid","Args":["Hash","ReferendumIndex"],"Documentation":[" A proposal could not be executed because its preimage was invalid."," \\[proposal_hash, ref_index\\]"]},{"Name":"PreimageMissing","Args":["Hash","ReferendumIndex"],"Documentation":[" A proposal could not be executed because its preimage was missing."," \\[proposal_hash, ref_index\\]"]},{"Name":"PreimageReaped","Args":["Hash","AccountId","Balance","AccountId"],"Documentation":[" A registered preimage was removed and the deposit collected by the reaper."," \\[proposal_hash, provider, deposit, reaper\\]"]},{"Name":"Unlocked","Args":["AccountId"],"Documentation":[" An \\[account\\] has been unlocked successfully."]},{"Name":"Blacklisted","Args":["Hash"],"Documentation":[" A proposal \\[hash\\] has been blacklisted permanently."]}],"Constants":[{"Name":"EnactmentPeriod","Type":"T::BlockNumber","Value":"IBwAAA==","Documentation":[" The minimum period of locking and the period between a proposal being approved and enacted.",""," It should generally be a little more than the unstake period to ensure that"," voting stakers have an opportunity to remove themselves from the system in the case where"," they are on the losing side of a vote."]},{"Name":"LaunchPeriod","Type":"T::BlockNumber","Value":"IBwAAA==","Documentation":[" How often (in blocks) new public referenda are launched."]},{"Name":"VotingPeriod","Type":"T::BlockNumber","Value":"oIwAAA==","Documentation":[" How often (in blocks) to check for new votes."]},{"Name":"MinimumDeposit","Type":"BalanceOf\u003cT\u003e","Value":"AACQnc7agjcAAAAAAAAAAA==","Documentation":[" The minimum amount to be used as a deposit for a public referendum proposal."]},{"Name":"FastTrackVotingPeriod","Type":"T::BlockNumber","Value":"sAQAAA==","Documentation":[" Minimum voting period allowed for a fast-track referendum."]},{"Name":"CooloffPeriod","Type":"T::BlockNumber","Value":"4MQAAA==","Documentation":[" Period in blocks where an external proposal may not be re-submitted after being vetoed."]},{"Name":"PreimageByteDeposit","Type":"BalanceOf\u003cT\u003e","Value":"AEB6EPNaAAAAAAAAAAAAAA==","Documentation":[" The amount of balance that must be deposited per byte of preimage stored."]},{"Name":"MaxVotes","Type":"u32","Value":"ZAAAAA==","Documentation":[" The maximum number of votes for an account.",""," Also used to compute weight, an overly big value can"," lead to extrinsic with very big weight: see `delegate` for instance."]}],"Errors":[{"Name":"ValueLow","Documentation":[" Value too low"]},{"Name":"ProposalMissing","Documentation":[" Proposal does not exist"]},{"Name":"BadIndex","Documentation":[" Unknown index"]},{"Name":"AlreadyCanceled","Documentation":[" Cannot cancel the same proposal twice"]},{"Name":"DuplicateProposal","Documentation":[" Proposal already made"]},{"Name":"ProposalBlacklisted","Documentation":[" Proposal still blacklisted"]},{"Name":"NotSimpleMajority","Documentation":[" Next external proposal not simple majority"]},{"Name":"InvalidHash","Documentation":[" Invalid hash"]},{"Name":"NoProposal","Documentation":[" No external proposal"]},{"Name":"AlreadyVetoed","Documentation":[" Identity may not veto a proposal twice"]},{"Name":"NotDelegated","Documentation":[" Not delegated"]},{"Name":"DuplicatePreimage","Documentation":[" Preimage already noted"]},{"Name":"NotImminent","Documentation":[" Not imminent"]},{"Name":"TooEarly","Documentation":[" Too early"]},{"Name":"Imminent","Documentation":[" Imminent"]},{"Name":"PreimageMissing","Documentation":[" Preimage not found"]},{"Name":"ReferendumInvalid","Documentation":[" Vote given for invalid referendum"]},{"Name":"PreimageInvalid","Documentation":[" Invalid preimage"]},{"Name":"NoneWaiting","Documentation":[" No proposals waiting"]},{"Name":"NotLocked","Documentation":[" The target account does not have a lock."]},{"Name":"NotExpired","Documentation":[" The lock on the account to be unlocked has not yet expired."]},{"Name":"NotVoter","Documentation":[" The given account did not vote on the referendum."]},{"Name":"NoPermission","Documentation":[" The actor has no permission to conduct the action."]},{"Name":"AlreadyDelegating","Documentation":[" The account is already delegating."]},{"Name":"InsufficientFunds","Documentation":[" Too high a balance was provided that the account cannot afford."]},{"Name":"NotDelegating","Documentation":[" The account is not currently delegating."]},{"Name":"VotesExist","Documentation":[" The account currently has votes attached to it and the operation cannot succeed until"," these are removed, either through `unvote` or `reap_vote`."]},{"Name":"InstantNotAllowed","Documentation":[" The instant referendum origin is currently disallowed."]},{"Name":"Nonsense","Documentation":[" Delegation to oneself makes no sense."]},{"Name":"WrongUpperBound","Documentation":[" Invalid upper bound."]},{"Name":"MaxVotesReached","Documentation":[" Maximum number of votes reached."]},{"Name":"InvalidWitness","Documentation":[" The provided witness data is wrong."]},{"Name":"TooManyProposals","Documentation":[" Maximum number of proposals reached."]}],"Index":14},{"Name":"CouncilCollective","HasStorage":true,"Storage":{"Prefix":"Instance1Collective","Items":[{"Name":"Proposals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cT::Hash, T::MaxProposals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The hashes of the active proposals."]},{"Name":"ProposalOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"\u003cT as Config\u003cI\u003e\u003e::Proposal","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Actual proposal for a given hash, if it's current."]},{"Name":"Voting","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"Votes\u003cT::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Votes on a given proposal, if it is ongoing."]},{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Proposals so far."]},{"Name":"Members","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current members of the collective. This is stored sorted (just by value)."]},{"Name":"Prime","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The prime member that helps determine the default vote behavior in case of absentations."]}]},"HasCalls":true,"Calls":[{"Name":"set_members","Args":[{"Name":"new_members","Type":"Vec\u003cT::AccountId\u003e"},{"Name":"prime","Type":"Option\u003cT::AccountId\u003e"},{"Name":"old_count","Type":"MemberCount"}],"Documentation":[" Set the collective's membership.",""," - `new_members`: The new member list. Be nice to the chain and provide it sorted."," - `prime`: The prime member whose vote sets the default."," - `old_count`: The upper bound for the previous number of members in storage."," Used for weight estimation.",""," Requires root origin.",""," NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but"," the weight estimations rely on it to estimate dispatchable weight.",""," # \u003cweight\u003e"," ## Weight"," - `O(MP + N)` where:"," - `M` old-members-count (code- and governance-bounded)"," - `N` new-members-count (code- and governance-bounded)"," - `P` proposals-count (code-bounded)"," - DB:"," - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members"," - 1 storage read (codec `O(P)`) for reading the proposals"," - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal"," - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one"," # \u003c/weight\u003e"]},{"Name":"execute","Args":[{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Dispatch a proposal from a member using the `Member` origin.",""," Origin must be a member of the collective.",""," # \u003cweight\u003e"," ## Weight"," - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`"," - DB: 1 read (codec `O(M)`) + DB access of `proposal`"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"propose","Args":[{"Name":"threshold","Type":"Compact\u003cMemberCount\u003e"},{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Add a new proposal to either be voted on or executed directly.",""," Requires the sender to be member.",""," `threshold` determines whether `proposal` is executed directly (`threshold \u003c 2`)"," or put up for voting.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1)` or `O(B + M + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - branching is influenced by `threshold` where:"," - `P1` is proposal execution complexity (`threshold \u003c 2`)"," - `P2` is proposals-count (code-bounded) (`threshold \u003e= 2`)"," - DB:"," - 1 storage read `is_member` (codec `O(M)`)"," - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)"," - DB accesses influenced by `threshold`:"," - EITHER storage accesses done by `proposal` (`threshold \u003c 2`)"," - OR proposal insertion (`threshold \u003c= 2`)"," - 1 storage mutation `Proposals` (codec `O(P2)`)"," - 1 storage mutation `ProposalCount` (codec `O(1)`)"," - 1 storage write `ProposalOf` (codec `O(B)`)"," - 1 storage write `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"vote","Args":[{"Name":"proposal","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"approve","Type":"bool"}],"Documentation":[" Add an aye or nay vote for the sender to the given proposal.",""," Requires the sender to be a member.",""," Transaction fees will be waived if the member is voting on any particular proposal"," for the first time and the call is successful. Subsequent vote changes will charge a fee."," # \u003cweight\u003e"," ## Weight"," - `O(M)` where `M` is members-count (code- and governance-bounded)"," - DB:"," - 1 storage read `Members` (codec `O(M)`)"," - 1 storage mutation `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"close","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"proposal_weight_bound","Type":"Compact\u003cWeight\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Close a vote that is either approved, disapproved or whose voting period has ended.",""," May be called by any signed account in order to finish voting and close the proposal.",""," If called before the end of the voting period it will only close the vote if it is"," has enough votes to be approved or disapproved.",""," If called after the end of the voting period abstentions are counted as rejections"," unless there is a prime member set and the prime member cast an approval.",""," If the close operation completes successfully with disapproval, the transaction fee will"," be waived. Otherwise execution of the approved operation will be charged to the caller.",""," + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal."," + `length_bound`: The upper bound for the length of the proposal in storage. Checked via"," `storage::read` so it is `size_of::\u003cu32\u003e() == 4` larger than the pure length.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1 + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - `P1` is the complexity of `proposal` preimage."," - `P2` is proposal-count (code-bounded)"," - DB:"," - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)"," - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)"," - any mutations done while executing `proposal` (`P1`)"," - up to 3 events"," # \u003c/weight\u003e"]},{"Name":"disapprove_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Disapprove a proposal, close, and remove it from the system, regardless of its current state.",""," Must be called by the Root origin.",""," Parameters:"," * `proposal_hash`: The hash of the proposal that should be disapproved.",""," # \u003cweight\u003e"," Complexity: O(P) where P is the number of max proposals"," DB Weight:"," * Reads: Proposals"," * Writes: Voting, Proposals, ProposalOf"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["AccountId","ProposalIndex","Hash","MemberCount"],"Documentation":[" A motion (given hash) has been proposed (by given account) with a threshold (given"," `MemberCount`)."," \\[account, proposal_index, proposal_hash, threshold\\]"]},{"Name":"Voted","Args":["AccountId","Hash","bool","MemberCount","MemberCount"],"Documentation":[" A motion (given hash) has been voted on by given account, leaving"," a tally (yes votes and no votes given respectively as `MemberCount`)."," \\[account, proposal_hash, voted, yes, no\\]"]},{"Name":"Approved","Args":["Hash"],"Documentation":[" A motion was approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Disapproved","Args":["Hash"],"Documentation":[" A motion was not approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Executed","Args":["Hash","DispatchResult"],"Documentation":[" A motion was executed; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"MemberExecuted","Args":["Hash","DispatchResult"],"Documentation":[" A single member did some action; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"Closed","Args":["Hash","MemberCount","MemberCount"],"Documentation":[" A proposal was closed because its threshold was reached or after its duration was up."," \\[proposal_hash, yes, no\\]"]}],"Constants":null,"Errors":[{"Name":"NotMember","Documentation":[" Account is not a member"]},{"Name":"DuplicateProposal","Documentation":[" Duplicate proposals not allowed"]},{"Name":"ProposalMissing","Documentation":[" Proposal must exist"]},{"Name":"WrongIndex","Documentation":[" Mismatched index"]},{"Name":"DuplicateVote","Documentation":[" Duplicate vote ignored"]},{"Name":"AlreadyInitialized","Documentation":[" Members are already initialized!"]},{"Name":"TooEarly","Documentation":[" The close call was made too early, before the end of the voting."]},{"Name":"TooManyProposals","Documentation":[" There can only be a maximum of `MaxProposals` active proposals."]},{"Name":"WrongProposalWeight","Documentation":[" The given weight bound for the proposal was too low."]},{"Name":"WrongProposalLength","Documentation":[" The given length bound for the proposal was too low."]}],"Index":15},{"Name":"TechComitteeCollective","HasStorage":true,"Storage":{"Prefix":"Instance2Collective","Items":[{"Name":"Proposals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cT::Hash, T::MaxProposals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The hashes of the active proposals."]},{"Name":"ProposalOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"\u003cT as Config\u003cI\u003e\u003e::Proposal","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Actual proposal for a given hash, if it's current."]},{"Name":"Voting","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"Votes\u003cT::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Votes on a given proposal, if it is ongoing."]},{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Proposals so far."]},{"Name":"Members","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current members of the collective. This is stored sorted (just by value)."]},{"Name":"Prime","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The prime member that helps determine the default vote behavior in case of absentations."]}]},"HasCalls":true,"Calls":[{"Name":"set_members","Args":[{"Name":"new_members","Type":"Vec\u003cT::AccountId\u003e"},{"Name":"prime","Type":"Option\u003cT::AccountId\u003e"},{"Name":"old_count","Type":"MemberCount"}],"Documentation":[" Set the collective's membership.",""," - `new_members`: The new member list. Be nice to the chain and provide it sorted."," - `prime`: The prime member whose vote sets the default."," - `old_count`: The upper bound for the previous number of members in storage."," Used for weight estimation.",""," Requires root origin.",""," NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but"," the weight estimations rely on it to estimate dispatchable weight.",""," # \u003cweight\u003e"," ## Weight"," - `O(MP + N)` where:"," - `M` old-members-count (code- and governance-bounded)"," - `N` new-members-count (code- and governance-bounded)"," - `P` proposals-count (code-bounded)"," - DB:"," - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members"," - 1 storage read (codec `O(P)`) for reading the proposals"," - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal"," - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one"," # \u003c/weight\u003e"]},{"Name":"execute","Args":[{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Dispatch a proposal from a member using the `Member` origin.",""," Origin must be a member of the collective.",""," # \u003cweight\u003e"," ## Weight"," - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`"," - DB: 1 read (codec `O(M)`) + DB access of `proposal`"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"propose","Args":[{"Name":"threshold","Type":"Compact\u003cMemberCount\u003e"},{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Add a new proposal to either be voted on or executed directly.",""," Requires the sender to be member.",""," `threshold` determines whether `proposal` is executed directly (`threshold \u003c 2`)"," or put up for voting.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1)` or `O(B + M + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - branching is influenced by `threshold` where:"," - `P1` is proposal execution complexity (`threshold \u003c 2`)"," - `P2` is proposals-count (code-bounded) (`threshold \u003e= 2`)"," - DB:"," - 1 storage read `is_member` (codec `O(M)`)"," - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)"," - DB accesses influenced by `threshold`:"," - EITHER storage accesses done by `proposal` (`threshold \u003c 2`)"," - OR proposal insertion (`threshold \u003c= 2`)"," - 1 storage mutation `Proposals` (codec `O(P2)`)"," - 1 storage mutation `ProposalCount` (codec `O(1)`)"," - 1 storage write `ProposalOf` (codec `O(B)`)"," - 1 storage write `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"vote","Args":[{"Name":"proposal","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"approve","Type":"bool"}],"Documentation":[" Add an aye or nay vote for the sender to the given proposal.",""," Requires the sender to be a member.",""," Transaction fees will be waived if the member is voting on any particular proposal"," for the first time and the call is successful. Subsequent vote changes will charge a fee."," # \u003cweight\u003e"," ## Weight"," - `O(M)` where `M` is members-count (code- and governance-bounded)"," - DB:"," - 1 storage read `Members` (codec `O(M)`)"," - 1 storage mutation `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"close","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"proposal_weight_bound","Type":"Compact\u003cWeight\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Close a vote that is either approved, disapproved or whose voting period has ended.",""," May be called by any signed account in order to finish voting and close the proposal.",""," If called before the end of the voting period it will only close the vote if it is"," has enough votes to be approved or disapproved.",""," If called after the end of the voting period abstentions are counted as rejections"," unless there is a prime member set and the prime member cast an approval.",""," If the close operation completes successfully with disapproval, the transaction fee will"," be waived. Otherwise execution of the approved operation will be charged to the caller.",""," + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal."," + `length_bound`: The upper bound for the length of the proposal in storage. Checked via"," `storage::read` so it is `size_of::\u003cu32\u003e() == 4` larger than the pure length.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1 + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - `P1` is the complexity of `proposal` preimage."," - `P2` is proposal-count (code-bounded)"," - DB:"," - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)"," - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)"," - any mutations done while executing `proposal` (`P1`)"," - up to 3 events"," # \u003c/weight\u003e"]},{"Name":"disapprove_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Disapprove a proposal, close, and remove it from the system, regardless of its current state.",""," Must be called by the Root origin.",""," Parameters:"," * `proposal_hash`: The hash of the proposal that should be disapproved.",""," # \u003cweight\u003e"," Complexity: O(P) where P is the number of max proposals"," DB Weight:"," * Reads: Proposals"," * Writes: Voting, Proposals, ProposalOf"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["AccountId","ProposalIndex","Hash","MemberCount"],"Documentation":[" A motion (given hash) has been proposed (by given account) with a threshold (given"," `MemberCount`)."," \\[account, proposal_index, proposal_hash, threshold\\]"]},{"Name":"Voted","Args":["AccountId","Hash","bool","MemberCount","MemberCount"],"Documentation":[" A motion (given hash) has been voted on by given account, leaving"," a tally (yes votes and no votes given respectively as `MemberCount`)."," \\[account, proposal_hash, voted, yes, no\\]"]},{"Name":"Approved","Args":["Hash"],"Documentation":[" A motion was approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Disapproved","Args":["Hash"],"Documentation":[" A motion was not approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Executed","Args":["Hash","DispatchResult"],"Documentation":[" A motion was executed; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"MemberExecuted","Args":["Hash","DispatchResult"],"Documentation":[" A single member did some action; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"Closed","Args":["Hash","MemberCount","MemberCount"],"Documentation":[" A proposal was closed because its threshold was reached or after its duration was up."," \\[proposal_hash, yes, no\\]"]}],"Constants":null,"Errors":[{"Name":"NotMember","Documentation":[" Account is not a member"]},{"Name":"DuplicateProposal","Documentation":[" Duplicate proposals not allowed"]},{"Name":"ProposalMissing","Documentation":[" Proposal must exist"]},{"Name":"WrongIndex","Documentation":[" Mismatched index"]},{"Name":"DuplicateVote","Documentation":[" Duplicate vote ignored"]},{"Name":"AlreadyInitialized","Documentation":[" Members are already initialized!"]},{"Name":"TooEarly","Documentation":[" The close call was made too early, before the end of the voting."]},{"Name":"TooManyProposals","Documentation":[" There can only be a maximum of `MaxProposals` active proposals."]},{"Name":"WrongProposalWeight","Documentation":[" The given weight bound for the proposal was too low."]},{"Name":"WrongProposalLength","Documentation":[" The given length bound for the proposal was too low."]}],"Index":16},{"Name":"Treasury","HasStorage":true,"Storage":{"Prefix":"Treasury","Items":[{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ProposalIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Number of proposals that have been made."]},{"Name":"Proposals","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"ProposalIndex","Value":"Proposal\u003cT::AccountId, BalanceOf\u003cT, I\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Proposals that have been made."]},{"Name":"Approvals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cProposalIndex, T::MaxApprovals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Proposal indices that have been approved but not yet awarded."]}]},"HasCalls":true,"Calls":[{"Name":"propose_spend","Args":[{"Name":"value","Type":"Compact\u003cBalanceOf\u003cT, I\u003e\u003e"},{"Name":"beneficiary","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"}],"Documentation":[" Put forward a suggestion for spending. A deposit proportional to the value"," is reserved and slashed if the proposal is rejected. It is returned once the"," proposal is awarded.",""," # \u003cweight\u003e"," - Complexity: O(1)"," - DbReads: `ProposalCount`, `origin account`"," - DbWrites: `ProposalCount`, `Proposals`, `origin account`"," # \u003c/weight\u003e"]},{"Name":"reject_proposal","Args":[{"Name":"proposal_id","Type":"Compact\u003cProposalIndex\u003e"}],"Documentation":[" Reject a proposed spend. The original deposit will be slashed.",""," May only be called from `T::RejectOrigin`.",""," # \u003cweight\u003e"," - Complexity: O(1)"," - DbReads: `Proposals`, `rejected proposer account`"," - DbWrites: `Proposals`, `rejected proposer account`"," # \u003c/weight\u003e"]},{"Name":"approve_proposal","Args":[{"Name":"proposal_id","Type":"Compact\u003cProposalIndex\u003e"}],"Documentation":[" Approve a proposal. At a later time, the proposal will be allocated to the beneficiary"," and the original deposit will be returned.",""," May only be called from `T::ApproveOrigin`.",""," # \u003cweight\u003e"," - Complexity: O(1)."," - DbReads: `Proposals`, `Approvals`"," - DbWrite: `Approvals`"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["ProposalIndex"],"Documentation":[" New proposal. \\[proposal_index\\]"]},{"Name":"Spending","Args":["Balance"],"Documentation":[" We have ended a spend period and will now allocate funds. \\[budget_remaining\\]"]},{"Name":"Awarded","Args":["ProposalIndex","Balance","AccountId"],"Documentation":[" Some funds have been allocated. \\[proposal_index, award, beneficiary\\]"]},{"Name":"Rejected","Args":["ProposalIndex","Balance"],"Documentation":[" A proposal was rejected; funds were slashed. \\[proposal_index, slashed\\]"]},{"Name":"Burnt","Args":["Balance"],"Documentation":[" Some of our funds have been burnt. \\[burn\\]"]},{"Name":"Rollover","Args":["Balance"],"Documentation":[" Spending has finished; this is the amount that rolls over until next spend."," \\[budget_remaining\\]"]},{"Name":"Deposit","Args":["Balance"],"Documentation":[" Some funds have been deposited. \\[deposit\\]"]}],"Constants":[{"Name":"ProposalBond","Type":"Permill","Value":"UMMAAA==","Documentation":[" Fraction of a proposal's value that should be bonded in order to place the proposal."," An accepted proposal gets these back. A rejected proposal does not."]},{"Name":"ProposalBondMinimum","Type":"BalanceOf\u003cT, I\u003e","Value":"AABkp7O24A0AAAAAAAAAAA==","Documentation":[" Minimum amount of funds that should be placed in a deposit for making a proposal."]},{"Name":"SpendPeriod","Type":"T::BlockNumber","Value":"wKgAAA==","Documentation":[" Period between successive spends."]},{"Name":"Burn","Type":"Permill","Value":"AAAAAA==","Documentation":[" Percentage of spare funds (if any) that are burnt per spend period."]},{"Name":"PalletId","Type":"PalletId","Value":"cGMvdHJzcnk=","Documentation":[" The treasury's module id, used for deriving its sovereign account ID."]}],"Errors":[{"Name":"InsufficientProposersBalance","Documentation":[" Proposer's balance is too low."]},{"Name":"InvalidIndex","Documentation":[" No proposal or bounty at that index."]},{"Name":"TooManyApprovals","Documentation":[" Too many approvals in the queue."]}],"Index":17},{"Name":"AuthorInherent","HasStorage":true,"Storage":{"Prefix":"AuthorInherent","Items":[{"Name":"Author","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Author of current block."]}]},"HasCalls":true,"Calls":[{"Name":"set_author","Args":[{"Name":"author","Type":"T::AuthorId"}],"Documentation":[" Inherent to set the author of a block"]}],"HasEvents":false,"Events":null,"Constants":null,"Errors":[{"Name":"AuthorAlreadySet","Documentation":[" Author already set in block."]},{"Name":"NoAccountId","Documentation":[" No AccountId was found to be associated with this author"]},{"Name":"CannotBeAuthor","Documentation":[" The author in the inherent is not an eligible author."]}],"Index":18},{"Name":"AuthorFilter","HasStorage":true,"Storage":{"Prefix":"AuthorFilter","Items":[{"Name":"EligibleRatio","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Percent","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"Mg==","Documentation":[" The percentage of active authors that will be eligible at each height."]}]},"HasCalls":true,"Calls":[{"Name":"set_eligible","Args":[{"Name":"new","Type":"Percent"}],"Documentation":[" Update the eligible ratio. Intended to be called by governance."]}],"HasEvents":true,"Events":[{"Name":"EligibleUpdated","Args":["Percent"],"Documentation":[" The amount of eligible authors for the filter to select has been changed."]}],"Constants":null,"Errors":null,"Index":19},{"Name":"CrowdloanRewards","HasStorage":true,"Storage":{"Prefix":"CrowdloanRewards","Items":[{"Name":"AccountsPayable","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"\u003cT as frame_system::Config\u003e::AccountId","Value":"RewardInfo\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"ClaimedRelayChainIds","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::RelayChainAccountId","Value":"()","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"UnassociatedContributions","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::RelayChainAccountId","Value":"RewardInfo\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"Initialized","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"InitRelayBlock","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Relay block height at the initialization of the pallet"]},{"Name":"InitializedRewardAmount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BalanceOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total initialized amount so far. We store this to make pallet funds == contributors reward"," check easier and more efficient"]},{"Name":"TotalContributors","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Total number of contributors to aid hinting benchmarking"]}]},"HasCalls":true,"Calls":[{"Name":"associate_native_identity","Args":[{"Name":"reward_account","Type":"\u003cT as frame_system::Config\u003e::AccountId"},{"Name":"relay_account","Type":"T::RelayChainAccountId"},{"Name":"proof","Type":"MultiSignature"}],"Documentation":[" Associate a native rewards_destination identity with a crowdloan contribution.",""," This is an unsigned call because the caller may not have any funds to pay fees with."," This is inspired by Polkadot's claims pallet:"," https://github.com/paritytech/polkadot/blob/master/runtime/common/src/claims.rs",""," The contributor needs to issue an additional addmemo transaction if it wants to receive"," the reward in a parachain native account. For the moment I will leave this function here"," just in case the contributor forgot to add such a memo field. Whenever we can read the"," state of the relay chain, we should first check whether that memo field exists in the"," contribution"]},{"Name":"claim","Args":null,"Documentation":[" Collect whatever portion of your reward are currently vested. The first time each"," contributor calls this function pays no fees"]},{"Name":"update_reward_address","Args":[{"Name":"new_reward_account","Type":"\u003cT as frame_system::Config\u003e::AccountId"}],"Documentation":[" Update reward address. To determine whether its something we want to keep"]},{"Name":"initialize_reward_vec","Args":[{"Name":"rewards","Type":"Vec\u003c\n(T::RelayChainAccountId, Option\u003c\u003cT as frame_system::Config\u003e::\n AccountId\u003e, BalanceOf\u003cT\u003e)\u003e"},{"Name":"index","Type":"u32"},{"Name":"limit","Type":"u32"}],"Documentation":[" Initialize the reward distribution storage. It shortcuts whenever an error is found"," We can change this behavior to check this beforehand if we prefer"," We only set this to \"initialized\" once we receive index==limit"," This is expected to be executed with batch_all, that atomically initializes contributions"," TODO Should we perform sanity checks here? (i.e., min contribution)"]}],"HasEvents":true,"Events":[{"Name":"InitialPaymentMade","Args":["\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" The initial payment of InitializationPayment % was paid"]},{"Name":"NativeIdentityAssociated","Args":["T::RelayChainAccountId","\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Someone has proven they made a contribution and associated a native identity with it."," Data is the relay account, native account and the total amount of _rewards_ that will be paid"]},{"Name":"RewardsPaid","Args":["\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" A contributor has claimed some rewards."," Data is the account getting paid and the amount of rewards paid."]},{"Name":"RewardAddressUpdated","Args":["\u003cT as frame_system::Config\u003e::AccountId","\u003cT as frame_system::Config\u003e::AccountId"],"Documentation":[" A contributor has updated the reward address."]},{"Name":"InitializedAlreadyInitializedAccount","Args":["T::RelayChainAccountId","Option\u003c\u003cT as frame_system::Config\u003e::AccountId\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" When initializing the reward vec an already initialized account was found"]},{"Name":"InitializedAccountWithNotEnoughContribution","Args":["T::RelayChainAccountId","Option\u003c\u003cT as frame_system::Config\u003e::AccountId\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" When initializing the reward vec an already initialized account was found"]}],"Constants":[{"Name":"InitializationPayment","Type":"Perbill","Value":"AKPhEQ==","Documentation":[" Percentage to be payed at initialization"]},{"Name":"VestingPeriod","Type":"relay_chain::BlockNumber","Value":"gBMDAA==","Documentation":[" The total vesting period. Ideally this should be less or equal"," than the lease period to ensure contributors vest the tokens during the lease"]}],"Errors":[{"Name":"AlreadyAssociated","Documentation":[" User trying to associate a native identity with a relay chain identity for posterior"," reward claiming provided an already associated relay chain identity"]},{"Name":"BatchBeyondFundPot","Documentation":[" Trying to introduce a batch that goes beyond the limits of the funds"]},{"Name":"FirstClaimAlreadyDone","Documentation":[" First claim already done"]},{"Name":"RewardNotHighEnough","Documentation":[" The contribution is not high enough to be eligible for rewards"]},{"Name":"InvalidClaimSignature","Documentation":[" User trying to associate a native identity with a relay chain identity for posterior"," reward claiming provided a wrong signature"]},{"Name":"InvalidFreeClaimSignature","Documentation":[" User trying to claim the first free reward provided the wrong signature"]},{"Name":"NoAssociatedClaim","Documentation":[" User trying to claim an award did not have an claim associated with it. This may mean"," they did not contribute to the crowdloan, or they have not yet associated a native id"," with their contribution"]},{"Name":"RewardsAlreadyClaimed","Documentation":[" User trying to claim rewards has already claimed all rewards associated with its"," identity and contribution"]},{"Name":"RewardVecAlreadyInitialized","Documentation":[" Reward vec has already been initialized"]},{"Name":"RewardVecNotFullyInitializedYet","Documentation":[" Reward vec has not yet been fully initialized"]},{"Name":"RewardsDoNotMatchFund","Documentation":[" Reward vec has already been initialized"]}],"Index":20},{"Name":"AuthorMapping","HasStorage":true,"Storage":{"Prefix":"AuthorMapping","Items":[{"Name":"MappingWithDeposit","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AuthorId","Value":"RegistrationInfo\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" We maintain a mapping from the AuthorIds used in the consensus layer"," to the AccountIds runtime (including this staking pallet)."]}]},"HasCalls":true,"Calls":[{"Name":"add_association","Args":[{"Name":"author_id","Type":"T::AuthorId"}],"Documentation":[" Register your AuthorId onchain so blocks you author are associated with your account.",""," Users who have been (or will soon be) elected active collators in staking,"," should submit this extrinsic to have their blocks accepted and earn rewards."]},{"Name":"update_association","Args":[{"Name":"old_author_id","Type":"T::AuthorId"},{"Name":"new_author_id","Type":"T::AuthorId"}],"Documentation":[" Change your AuthorId.",""," This is useful for normal key rotation or for when switching from one physical collator"," machine to another. No new security deposit is required."]},{"Name":"clear_association","Args":[{"Name":"author_id","Type":"T::AuthorId"}],"Documentation":[" Clear your AuthorId.",""," This is useful when you are no longer an author and would like to re-claim your security"," deposit."]}],"HasEvents":true,"Events":[{"Name":"AuthorRegistered","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been registered and mapped to an AccountId."]},{"Name":"AuthorDeRegistered","Args":["T::AuthorId"],"Documentation":[" An AuthorId has been de-registered, and its AccountId mapping removed."]},{"Name":"AuthorRotated","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been registered, replacing a previous registration and its mapping."]},{"Name":"DefunctAuthorBusted","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been forcibly deregistered after not being rotated or cleaned up."," The reporteing account has been rewarded accordingly."]}],"Constants":null,"Errors":[{"Name":"AssociationNotFound","Documentation":[" The association can't be cleared because it is not found."]},{"Name":"NotYourAssociation","Documentation":[" The association can't be cleared because it belongs to another account."]},{"Name":"CannotSetAuthor","Documentation":[" This account cannot set an author because it fails the preliminary check"]},{"Name":"CannotAffordSecurityDeposit","Documentation":[" This account cannot set an author because it cannon afford the security deposit"]},{"Name":"AlreadyAssociated","Documentation":[" The AuthorId in question is already associated and cannot be overwritten"]}],"Index":21},{"Name":"Proxy","HasStorage":true,"Storage":{"Prefix":"Proxy","Items":[{"Name":"Proxies","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"(BoundedVec\u003cProxyDefinition\u003cT::AccountId, T::ProxyType, T::\n BlockNumber\u003e, T::MaxProxies,\u003e, BalanceOf\u003cT\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The set of account proxies. Maps the account which has delegated to the accounts"," which are being delegated to, together with the amount held on deposit."]},{"Name":"Announcements","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"(BoundedVec\u003cAnnouncement\u003cT::AccountId, CallHashOf\u003cT\u003e, T::\n BlockNumber\u003e, T::MaxPending,\u003e, BalanceOf\u003cT\u003e,)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The announcements made by the proxy (key)."]}]},"HasCalls":true,"Calls":[{"Name":"proxy","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"force_proxy_type","Type":"Option\u003cT::ProxyType\u003e"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Dispatch the given `call` from an account that the sender is authorised for through"," `add_proxy`.",""," Removes any corresponding announcement(s).",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call."," - `call`: The call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"add_proxy","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Register a proxy account for the sender that is able to make calls on its behalf.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `proxy`: The account that the `caller` would like to make a proxy."," - `proxy_type`: The permissions allowed for this proxy account."," - `delay`: The announcement period required of the initial proxy. Will generally be"," zero.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"remove_proxy","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Unregister a proxy account for the sender.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `proxy`: The account that the `caller` would like to remove as a proxy."," - `proxy_type`: The permissions currently enabled for the removed proxy account.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"remove_proxies","Args":null,"Documentation":[" Unregister all proxy accounts for the sender.",""," The dispatch origin for this call must be _Signed_.",""," WARNING: This may be called on accounts created by `anonymous`, however if done, then"," the unreserved fees will be inaccessible. **All access to this account will be lost.**",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"anonymous","Args":[{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"},{"Name":"index","Type":"u16"}],"Documentation":[" Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and"," initialize it with a proxy of `proxy_type` for `origin` sender.",""," Requires a `Signed` origin.",""," - `proxy_type`: The type of the proxy that the sender will be registered as over the"," new account. This will almost always be the most permissive `ProxyType` possible to"," allow for maximum flexibility."," - `index`: A disambiguation index, in case this is called multiple times in the same"," transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just"," want to use `0`."," - `delay`: The announcement period required of the initial proxy. Will generally be"," zero.",""," Fails with `Duplicate` if this has already been called in this transaction, from the"," same sender, with the same parameters.",""," Fails if there are insufficient funds to pay for deposit.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"," TODO: Might be over counting 1 read"]},{"Name":"kill_anonymous","Args":[{"Name":"spawner","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"index","Type":"u16"},{"Name":"height","Type":"Compact\u003cT::BlockNumber\u003e"},{"Name":"ext_index","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Removes a previously spawned anonymous proxy.",""," WARNING: **All access to this account will be lost.** Any funds held in it will be"," inaccessible.",""," Requires a `Signed` origin, and the sender account must have been created by a call to"," `anonymous` with corresponding parameters.",""," - `spawner`: The account that originally called `anonymous` to create this account."," - `index`: The disambiguation index originally passed to `anonymous`. Probably `0`."," - `proxy_type`: The proxy type originally passed to `anonymous`."," - `height`: The height of the chain when the call to `anonymous` was processed."," - `ext_index`: The extrinsic index in which the call to `anonymous` was processed.",""," Fails with `NoPermission` in case the caller is not a previously created anonymous"," account whose `anonymous` call has corresponding parameters.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"announce","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Publish the hash of a proxy-call that will be made in the future.",""," This must be called some number of blocks before the corresponding `proxy` is attempted"," if the delay associated with the proxy relationship is greater than zero.",""," No more than `MaxPending` announcements may be made at any one time.",""," This will take a deposit of `AnnouncementDepositFactor` as well as"," `AnnouncementDepositBase` if there are no other pending announcements.",""," The dispatch origin for this call must be _Signed_ and a proxy of `real`.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `call_hash`: The hash of the call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"remove_announcement","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Remove a given announcement.",""," May be called by a proxy account to remove a call they previously announced and return"," the deposit.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `call_hash`: The hash of the call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"reject_announcement","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Remove the given announcement of a delegate.",""," May be called by a target (proxied) account to remove a call that one of their delegates"," (`delegate`) has announced they want to execute. The deposit is returned.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `delegate`: The account that previously announced the call."," - `call_hash`: The hash of the call to be made.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"proxy_announced","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"real","Type":"T::AccountId"},{"Name":"force_proxy_type","Type":"Option\u003cT::ProxyType\u003e"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Dispatch the given `call` from an account that the sender is authorized for through"," `add_proxy`.",""," Removes any corresponding announcement(s).",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call."," - `call`: The call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"ProxyExecuted","Args":["DispatchResult"],"Documentation":[" A proxy was executed correctly, with the given \\[result\\]."]},{"Name":"AnonymousCreated","Args":["AccountId","AccountId","ProxyType","u16"],"Documentation":[" Anonymous account has been created by new proxy with given"," disambiguation index and proxy type. \\[anonymous, who, proxy_type, disambiguation_index\\]"]},{"Name":"Announced","Args":["AccountId","AccountId","Hash"],"Documentation":[" An announcement was placed to make a call in the future. \\[real, proxy, call_hash\\]"]}],"Constants":[{"Name":"ProxyDepositBase","Type":"BalanceOf\u003cT\u003e","Value":"AAA2K0yO4w0AAAAAAAAAAA==","Documentation":[" The base amount of currency needed to reserve for creating a proxy.",""," This is held for an additional storage item whose value size is"," `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes."]},{"Name":"ProxyDepositFactor","Type":"BalanceOf\u003cT\u003e","Value":"AEAHWvB1BwAAAAAAAAAAAA==","Documentation":[" The amount of currency needed per proxy added.",""," This is held for adding 32 bytes plus an instance of `ProxyType` more into a pre-existing"," storage value. Thus, when configuring `ProxyDepositFactor` one should take into account"," `32 + proxy_type.encode().len()` bytes of data."]},{"Name":"MaxProxies","Type":"u32","Value":"IAAAAA==","Documentation":[" The maximum amount of proxies allowed for a single account."]},{"Name":"MaxPending","Type":"u32","Value":"IAAAAA==","Documentation":[" The maximum amount of time-delayed announcements that are allowed to be pending."]},{"Name":"AnnouncementDepositBase","Type":"BalanceOf\u003cT\u003e","Value":"AAA2K0yO4w0AAAAAAAAAAA==","Documentation":[" The base amount of currency needed to reserve for creating an announcement.",""," This is held when a new storage item holding a `Balance` is created (typically 16 bytes)."]},{"Name":"AnnouncementDepositFactor","Type":"BalanceOf\u003cT\u003e","Value":"AAC+mivlEwAAAAAAAAAAAA==","Documentation":[" The amount of currency needed per announcement made.",""," This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)"," into a pre-existing storage value."]}],"Errors":[{"Name":"TooMany","Documentation":[" There are too many proxies registered or too many announcements pending."]},{"Name":"NotFound","Documentation":[" Proxy registration not found."]},{"Name":"NotProxy","Documentation":[" Sender is not a proxy of the account to be proxied."]},{"Name":"Unproxyable","Documentation":[" A call which is incompatible with the proxy type's filter was attempted."]},{"Name":"Duplicate","Documentation":[" Account is already a proxy."]},{"Name":"NoPermission","Documentation":[" Call may not be made by proxy because it may escalate its privileges."]},{"Name":"Unannounced","Documentation":[" Announcement, if made at all, was made too recently."]},{"Name":"NoSelfProxy","Documentation":[" Cannot add self as proxy."]}],"Index":22}],"Extrinsic":{"Version":4,"SignedExtensions":["CheckSpecVersion","CheckTxVersion","CheckGenesis","CheckMortality","CheckNonce","CheckWeight","ChargeTransactionPayment"]}}},"StorageKey":"Jqo5TupWMOB8SK4MlVjO94DUHl4WBWdlvIRhhRByydc=","SystemEventsStorageRaw":"HAAAAAAAAABQlaIJAAAAAAIAAAABAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAAAAAAIAAAADAAAACwDwLYBLGbBmVpD2sxJpGy64+AzTuAAAAAAAAAAAAAAAAAAAAAAAAAAAR/7ChkfV8IBlSPOFJXFw12z26JD3Rn7zOzbcxbm+GxUAAAAAAwAAAAAAQN1KHwAAAAAAAAAABAAAAAsAX9zyIemHlmspZxSVP7aVKvwkrv4AAAAAAAAAAAAAAAAAAAAAAAAAAND9smeqL84FfMgaDiOX9aMv/YY3dT99DAwLcomwAtw/AAAAAAQAAAAAAEDdSh8AAAAAAAAA","SystemEventsReadProof":{"at":"0xed27ad8166e3d8a3ff54a4687547b77ef0d95600d94b2521af9f700665100bb3","proof":["0x80410080671cf7e4ebdc4ef51678ba1916e62aec5cb7f7ea43cf1ce42fbaff0e43895d0080b5370ded96a2da134631f276574b0487292b1f8ec8841ceef539de53a509c9c2","0x80bfbd80bdaa229522b8271abcfd400ddad1385681e5c9fff10e9999af9540677dc6f29c80bbe34285aac08969a117c2f83460ffc62b4ef922bcc768e2ea423ac1e722d4e8809aa632e7ea7da50ea2c8714c89ae8bfcc0e7b4c66d5e1b22960d16d7507de26480ea40967bd06f6cf553658c565bf76654bb015b700307ee23d4df57c40971b4ee802a3c7f6ba837dd11b6720fcdcdee0e053432f7e98645cfa843c4df5f8f48050c80aacbb55b9ac2144d822d44542e2b4382ada2b92267e1308628a2fa976c7ec6df80dab9d81e4575b2b25e238979422cee5f24f37b3f7896c3817439be26b16beb0480e869edf2aa20543218841086d2d88586ce2c40936eae0058c66427112e9ee2e68012ff4664a7ce8a36a8e2a07096750e5fade3f751b69a8980d175ee06ca2c188d8078fdbb77cf85207c8ab9c7604dcd190ebc250ebe491e84c5832330455b906dc8805c3f395517f2044272a6d8240c5f253ae7b56298ad73668631e21ca5f1aba07f804da156a08cdf592237fd29895b96a2a94b470df85dc91f38bcad60727c72bcca80551a2739d59c4ef1f8e83ceac67850c9bc3c5472fc056ec6a9a2f2de52f1ee85","0x8081048076536a1b3cf2a6f1fad2436e4b78dabfa45a2730f848ddf4b979632193786249545e8d434d6125b40443fe11fd292d13a41003000000802fe13dcbbae7fc3ff4e1b5c9296aa06a736bde302675a946216de39fc0c02f83","0x5ed41e5e16056765bc8461851072c9d7fd031c000000000000005095a2090000000002000000010000000000000000000000000002000000020000000000000000000000000002000000030000000b00f02d804b19b0665690f6b312691b2eb8f80cd3b8000000000000000000000000000000000000000047fec28647d5f0806548f385257170d76cf6e890f7467ef33b36dcc5b9be1b150000000003000000000040dd4a1f0000000000000000040000000b005fdcf221e987966b296714953fb6952afc24aefe0000000000000000000000000000000000000000d0fdb267aa2fce057cc81a0e2397f5a32ffd8637753f7d0c0c0b7289b002dc3f0000000004000000000040dd4a1f00000000000000","0x9eaa394eea5630e07c48ae0c9558cef7298f8026dc60189d4ef07023fe336913a32de5ff821498aa41ea2041b8a885fc0d264e80c356ed33a497f4279a73a68ad1cbd783659bb8a6aa94d9eb6dba561a623823524c5f0684a022a34dd8bfa2baaf44f172b710040180ef2c7c76d5e7b24737700afeeff7e5361b1b3ecf00f39a65d9e69481c2c3ca8980ee82770884fa117321d8d1bcb2d08239af66c3f421c67810c31acb9e361523408006e453c9f35cf7515fd0fe27c98b8f6c6ecc3804784a8daa648ab8aa609393b88003ca05f0c7c6ea8077bf0015043470b17185a638e93f01c1c68f99bb10b8b9b1705f09cce9c888469bb1a0dceaa129672ef828d0206d6f6f6e62617365"]}} \ No newline at end of file diff --git a/chain/pra/assets/moonbase_blockinfo_315553.json b/chain/pra/assets/moonbase_blockinfo_315553.json new file mode 100644 index 00000000..45d757fd --- /dev/null +++ b/chain/pra/assets/moonbase_blockinfo_315553.json @@ -0,0 +1 @@ +{"BlockNumber":315553,"Hash":"0xad8341389f66170206e2797ca56a837b3bdfc55387923e43f0d8ca399eba30fa","Header":{"parentHash":"0xf1e8f0653422859dea6705ec5d86015a3c9f4c7c03eccbcb4bf858682956fb28","number":"4d0a1","stateRoot":"0x932c9abc4e9966ecf08dd3a5aa7087b448a88e54d18b3caebdbb2559c3f8744b","extrinsicsRoot":"0x25385e8e912f3965b85334a4524a6e15b21fb3c1b355d9e64df395b856a63597","digest":{"logs":["0x046e6d6273802485ca9e9427894cb1864d725977e3c168171daf22bacf64c7ad5e0674c33173","0x0466726f6e890201e93015e1d2195ae2d73004a790db2e4bf394e40f14df3e0a2edd9dff0930e8a910ef0e6bfa9d8bb055f94e873f1df551b42df11d2cb053811279a1101e9c03fcf9cfa1b41ba3027ac3bbda9af6685440e8d89f12c7aca5987b334e91dbbaa4aa9356cb1e07577d7374463ec88709ce945f280b072b89f4bc8c0e70abf4b9b267c090d9ec032f7d7121f9bc2b2a8b9a6a06fd7f434cbebb963d6cfcb2e4206d2f43","0x056e6d627301019cb74fccc8c86d67b5766b1c035e16ed4de18ecd091d4dd8724185eb28dbd1612983e904aafb984f05bd27443b6f8a56fbea955e208718d02e52eb28d0e62e89"]}},"ScaleEncodedHeader":"8ejwZTQihZ3qZwXsXYYBWjyfTHwD7MvLS/hYaClW+yiGQhMAkyyavE6ZZuzwjdOlqnCHtEiojlTRizyuvbslWcP4dEslOF6OkS85ZbhTNKRSSm4Vsh+zwbNV2eZN85W4VqY1lwwEbm1ic4AkhcqelCeJTLGGTXJZd+PBaBcdryK6z2THrV4GdMMxcwRmcm9uiQIB6TAV4dIZWuLXMASnkNsuS/OU5A8U3z4KLt2d/wkw6KkQ7w5r+p2LsFX5Toc/HfVRtC3xHSywU4ESeaEQHpwD/PnPobQbowJ6w7vamvZoVEDo2J8Sx6ylmHszTpHbuqSqk1bLHgdXfXN0Rj7IhwnOlF8oCwcrifS8jA5wq/S5smfAkNnsAy99cSH5vCsqi5pqBv1/Q0y+u5Y9bPyy5CBtL0MFbm1icwEBnLdPzMjIbWe1dmscA14W7U3hjs0JHU3YckGF6yjb0WEpg+kEqvuYTwW9J0Q7b4pW++qVXiCHGNAuUuso0OYuiQ==","MetaData":{"MagicNumber":1635018093,"Version":13,"IsMetadataV4":false,"AsMetadataV4":{"Modules":null},"IsMetadataV7":false,"AsMetadataV7":{"Modules":null},"IsMetadataV8":false,"AsMetadataV8":{"Modules":null},"IsMetadataV9":false,"AsMetadataV9":{"Modules":null},"IsMetadataV10":false,"AsMetadataV10":{"Modules":null},"IsMetadataV11":false,"AsMetadataV11":{"Modules":null,"Extrinsic":{"Version":0,"SignedExtensions":null}},"IsMetadataV12":false,"AsMetadataV12":{"Modules":null,"Extrinsic":{"Version":0,"SignedExtensions":null}},"IsMetadataV13":true,"AsMetadataV13":{"Modules":[{"Name":"System","HasStorage":true,"Storage":{"Prefix":"System","Items":[{"Name":"Account","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"AccountInfo\u003cT::Index, T::AccountData\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The full account information for a particular account ID."]},{"Name":"ExtrinsicCount","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Total extrinsics count for the current block."]},{"Name":"BlockWeight","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ConsumedWeight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" The current weight for the block."]},{"Name":"AllExtrinsicsLen","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Total length (in bytes) for all extrinsics put together, for the current block."]},{"Name":"BlockHash","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::BlockNumber","Value":"T::Hash","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" Map of block numbers to block hashes."]},{"Name":"ExtrinsicData","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"u32","Value":"Vec\u003cu8\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Extrinsics data for the current block (maps an extrinsic's index to its data)."]},{"Name":"Number","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The current block number being processed. Set by `execute_block`."]},{"Name":"ParentHash","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Hash","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" Hash of the previous block."]},{"Name":"Digest","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"DigestOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Digest of the current block, also part of the block header."]},{"Name":"Events","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cEventRecord\u003cT::Event, T::Hash\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Events deposited for the current block."]},{"Name":"EventCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"EventIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of events in the `Events\u003cT\u003e` list."]},{"Name":"EventTopics","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::Hash","Value":"Vec\u003c(T::BlockNumber, EventIndex)\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Mapping between a topic (represented by T::Hash) and a vector of indexes"," of events in the `\u003cEvents\u003cT\u003e\u003e` list.",""," All topic vectors have deterministic storage locations depending on the topic. This"," allows light-clients to leverage the changes trie storage tracking mechanism and"," in case of changes fetch the list of events of interest.",""," The value has the type `(T::BlockNumber, EventIndex)` because if we used only just"," the `EventIndex` then in case if the topic has the same contents on the next block"," no notification will be triggered thus the event might be lost."]},{"Name":"LastRuntimeUpgrade","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"LastRuntimeUpgradeInfo","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened."]},{"Name":"UpgradedToU32RefCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if we have upgraded so that `type RefCount` is `u32`. False (default) if not."]},{"Name":"UpgradedToTripleRefCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if we have upgraded so that AccountInfo contains three types of `RefCount`. False"," (default) if not."]},{"Name":"ExecutionPhase","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Phase","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The execution phase of the block."]}]},"HasCalls":true,"Calls":[{"Name":"fill_block","Args":[{"Name":"_ratio","Type":"Perbill"}],"Documentation":[" A dispatch that will fill the block weight up to the given ratio."]},{"Name":"remark","Args":[{"Name":"_remark","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Make some on-chain remark.",""," # \u003cweight\u003e"," - `O(1)`"," # \u003c/weight\u003e"]},{"Name":"set_heap_pages","Args":[{"Name":"pages","Type":"u64"}],"Documentation":[" Set the number of pages in the WebAssembly environment's heap.",""," # \u003cweight\u003e"," - `O(1)`"," - 1 storage write."," - Base Weight: 1.405 µs"," - 1 write to HEAP_PAGES"," # \u003c/weight\u003e"]},{"Name":"set_code","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Set the new runtime code.",""," # \u003cweight\u003e"," - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`"," - 1 storage write (codec `O(C)`)."," - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is expensive)."," - 1 event."," The weight of this function is dependent on the runtime, but generally this is very expensive."," We will treat this as a full block."," # \u003c/weight\u003e"]},{"Name":"set_code_without_checks","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Set the new runtime code without doing any checks of the given `code`.",""," # \u003cweight\u003e"," - `O(C)` where `C` length of `code`"," - 1 storage write (codec `O(C)`)."," - 1 event."," The weight of this function is dependent on the runtime. We will treat this as a full block."," # \u003c/weight\u003e"]},{"Name":"set_changes_trie_config","Args":[{"Name":"changes_trie_config","Type":"Option\u003cChangesTrieConfiguration\u003e"}],"Documentation":[" Set the new changes trie configuration.",""," # \u003cweight\u003e"," - `O(1)`"," - 1 storage write or delete (codec `O(1)`)."," - 1 call to `deposit_log`: Uses `append` API, so O(1)"," - Base Weight: 7.218 µs"," - DB Weight:"," - Writes: Changes Trie, System Digest"," # \u003c/weight\u003e"]},{"Name":"set_storage","Args":[{"Name":"items","Type":"Vec\u003cKeyValue\u003e"}],"Documentation":[" Set some items of storage.",""," # \u003cweight\u003e"," - `O(I)` where `I` length of `items`"," - `I` storage writes (`O(1)`)."," - Base Weight: 0.568 * i µs"," - Writes: Number of items"," # \u003c/weight\u003e"]},{"Name":"kill_storage","Args":[{"Name":"keys","Type":"Vec\u003cKey\u003e"}],"Documentation":[" Kill some items from storage.",""," # \u003cweight\u003e"," - `O(IK)` where `I` length of `keys` and `K` length of one key"," - `I` storage deletions."," - Base Weight: .378 * i µs"," - Writes: Number of items"," # \u003c/weight\u003e"]},{"Name":"kill_prefix","Args":[{"Name":"prefix","Type":"Key"},{"Name":"_subkeys","Type":"u32"}],"Documentation":[" Kill all storage items with a key that starts with the given prefix.",""," **NOTE:** We rely on the Root origin to provide us the number of subkeys under"," the prefix we are removing to accurately calculate the weight of this function.",""," # \u003cweight\u003e"," - `O(P)` where `P` amount of keys with prefix `prefix`"," - `P` storage deletions."," - Base Weight: 0.834 * P µs"," - Writes: Number of subkeys + 1"," # \u003c/weight\u003e"]},{"Name":"remark_with_event","Args":[{"Name":"remark","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Make some on-chain remark and emit event.",""," # \u003cweight\u003e"," - `O(b)` where b is the length of the remark."," - 1 event."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"ExtrinsicSuccess","Args":["DispatchInfo"],"Documentation":[" An extrinsic completed successfully. \\[info\\]"]},{"Name":"ExtrinsicFailed","Args":["DispatchError","DispatchInfo"],"Documentation":[" An extrinsic failed. \\[error, info\\]"]},{"Name":"CodeUpdated","Args":null,"Documentation":[" `:code` was updated."]},{"Name":"NewAccount","Args":["AccountId"],"Documentation":[" A new \\[account\\] was created."]},{"Name":"KilledAccount","Args":["AccountId"],"Documentation":[" An \\[account\\] was reaped."]},{"Name":"Remarked","Args":["AccountId","Hash"],"Documentation":[" On on-chain remark happened. \\[origin, remark_hash\\]"]}],"Constants":[{"Name":"BlockWeights","Type":"limits::BlockWeights","Value":"APIFKgEAAAAAiFJqdAAAAEBZcwcAAAAAAcAYD6RLAAAAAQDmvU9XAAAAAQAAAAAAAAAAQFlzBwAAAAABwLqjvmgAAAABAIhSanQAAAABAKKUGh0AAABAWXMHAAAAAAAAAA==","Documentation":[" Block \u0026 extrinsics weights: base values and limits."]},{"Name":"BlockLength","Type":"limits::BlockLength","Value":"AAA8AAAAUAAAAFAA","Documentation":[" The maximum length of a block (in bytes)."]},{"Name":"BlockHashCount","Type":"T::BlockNumber","Value":"AAEAAA==","Documentation":[" Maximum number of block number to block hash mappings to keep (oldest pruned first)."]},{"Name":"DbWeight","Type":"RuntimeDbWeight","Value":"QHh9AQAAAAAA4fUFAAAAAA==","Documentation":[" The weight of runtime database operations the runtime can invoke."]},{"Name":"Version","Type":"RuntimeVersion","Value":"IG1vb25iYXNlIG1vb25iYXNlAwAAAJsAAAAAAAAANNK8mJfu0I8VAgAAAN9qy2iZB2CbAwAAADfjl/x8kfXkAQAAAED+OtQB+JWaBQAAAPeLJ4vlP0VMAgAAAKs8BXIpH+uLAQAAALydiZBPW5I/AQAAAL14JV1P7uofAQAAAKM9Q/WHMa2EAQAAAFgiEfZbsUuJAQAAADfIuxNQqaKoAQAAAB+6P/u34H6NAQAAAOqT4/FvPWliAQAAAAIAAAA=","Documentation":[" Get the chain's current version."]},{"Name":"SS58Prefix","Type":"u16","Value":"BwU=","Documentation":[" The designated SS85 prefix of this chain.",""," This replaces the \"ss58Format\" property declared in the chain spec. Reason is"," that the runtime should know about the prefix in order to make use of it as"," an identifier of the chain."]}],"Errors":[{"Name":"InvalidSpecName","Documentation":[" The name of specification does not match between the current runtime"," and the new runtime."]},{"Name":"SpecVersionNeedsToIncrease","Documentation":[" The specification version is not allowed to decrease between the current runtime"," and the new runtime."]},{"Name":"FailedToExtractRuntimeVersion","Documentation":[" Failed to extract the runtime version from the new runtime.",""," Either calling `Core_version` or decoding `RuntimeVersion` failed."]},{"Name":"NonDefaultComposite","Documentation":[" Suicide called when the account has non-default composite data."]},{"Name":"NonZeroRefCount","Documentation":[" There is a non-zero reference count preventing the account from being purged."]}],"Index":0},{"Name":"Utility","HasStorage":false,"Storage":{"Prefix":"","Items":null},"HasCalls":true,"Calls":[{"Name":"batch","Args":[{"Name":"calls","Type":"Vec\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a batch of dispatch calls.",""," May be called from any origin.",""," - `calls`: The calls to be dispatched from the same origin.",""," If origin is root then call are dispatch without checking origin filter. (This includes"," bypassing `frame_system::Config::BaseCallFilter`).",""," # \u003cweight\u003e"," - Complexity: O(C) where C is the number of calls to be batched."," # \u003c/weight\u003e",""," This will return `Ok` in all circumstances. To determine the success of the batch, an"," event is deposited. If a call failed and the batch was interrupted, then the"," `BatchInterrupted` event is deposited, along with the number of successful calls made"," and the error of the failed call. If all were successful, then the `BatchCompleted`"," event is deposited."]},{"Name":"as_derivative","Args":[{"Name":"index","Type":"u16"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a call through an indexed pseudonym of the sender.",""," Filter from origin are passed along. The call will be dispatched with an origin which"," use the same filter as the origin of this call.",""," NOTE: If you need to ensure that any account-based filtering is not honored (i.e."," because you expect `proxy` to have been used prior in the call stack and you do not want"," the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`"," in the Multisig pallet instead.",""," NOTE: Prior to version *12, this was called `as_limited_sub`.",""," The dispatch origin for this call must be _Signed_."]},{"Name":"batch_all","Args":[{"Name":"calls","Type":"Vec\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Send a batch of dispatch calls and atomically execute them."," The whole transaction will rollback and fail if any of the calls failed.",""," May be called from any origin.",""," - `calls`: The calls to be dispatched from the same origin.",""," If origin is root then call are dispatch without checking origin filter. (This includes"," bypassing `frame_system::Config::BaseCallFilter`).",""," # \u003cweight\u003e"," - Complexity: O(C) where C is the number of calls to be batched."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"BatchInterrupted","Args":["u32","DispatchError"],"Documentation":[" Batch of dispatches did not complete fully. Index of first failing dispatch given, as"," well as the error. \\[index, error\\]"]},{"Name":"BatchCompleted","Args":null,"Documentation":[" Batch of dispatches completed fully with no error."]}],"Constants":null,"Errors":null,"Index":1},{"Name":"Timestamp","HasStorage":true,"Storage":{"Prefix":"Timestamp","Items":[{"Name":"Now","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Moment","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAA=","Documentation":[" Current time for the current block."]},{"Name":"DidUpdate","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Did the timestamp get updated in this block?"]}]},"HasCalls":true,"Calls":[{"Name":"set","Args":[{"Name":"now","Type":"Compact\u003cT::Moment\u003e"}],"Documentation":[" Set the current time.",""," This call should be invoked exactly once per block. It will panic at the finalization"," phase, if this call hasn't been invoked by that time.",""," The timestamp should be greater than the previous one by the amount specified by"," `MinimumPeriod`.",""," The dispatch origin for this call must be `Inherent`.",""," # \u003cweight\u003e"," - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)"," - 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in `on_finalize`)"," - 1 event handler `on_timestamp_set`. Must be `O(1)`."," # \u003c/weight\u003e"]}],"HasEvents":false,"Events":null,"Constants":[{"Name":"MinimumPeriod","Type":"T::Moment","Value":"AQAAAAAAAAA=","Documentation":[" The minimum period between blocks. Beware that this is different to the *expected* period"," that the block production apparatus provides. Your chosen consensus system will generally"," work with this to determine a sensible block time. e.g. For Aura, it will be double this"," period on default settings."]}],"Errors":null,"Index":2},{"Name":"Balances","HasStorage":true,"Storage":{"Prefix":"Balances","Items":[{"Name":"TotalIssuance","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Balance","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The total units issued in the system."]},{"Name":"Account","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"AccountData\u003cT::Balance\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The balance of an account.",""," NOTE: This is only used in the case that this pallet is used to store balances."]},{"Name":"Locks","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"WeakBoundedVec\u003cBalanceLock\u003cT::Balance\u003e, T::MaxLocks\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Any liquidity locks on some account balances."," NOTE: Should only be accessed when setting, changing and freeing a lock."]},{"Name":"Reserves","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::AccountId","Value":"BoundedVec\u003cReserveData\u003cT::ReserveIdentifier, T::Balance\u003e, T::\nMaxReserves\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Named reserves on some account balances."]},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," This is set to v2.0.0 for new networks."]}]},"HasCalls":true,"Calls":[{"Name":"transfer","Args":[{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Transfer some liquid free balance to another account.",""," `transfer` will set the `FreeBalance` of the sender and receiver."," It will decrease the total issuance of the system by the `TransferFee`."," If the sender's account is below the existential deposit as a result"," of the transfer, the account will be reaped.",""," The dispatch origin for this call must be `Signed` by the transactor.",""," # \u003cweight\u003e"," - Dependent on arguments but not critical, given proper implementations for"," input config types. See related functions below."," - It contains a limited number of reads and writes internally and no complex computation.",""," Related functions:",""," - `ensure_can_withdraw` is always called internally but has a bounded complexity."," - Transferring balances to accounts that did not exist before will cause"," `T::OnNewAccount::on_new_account` to be called."," - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`."," - `transfer_keep_alive` works the same way as `transfer`, but has an additional"," check that the transfer will not kill the origin account."," ---------------------------------"," - Base Weight: 73.64 µs, worst case scenario (account created, account removed)"," - DB Weight: 1 Read and 1 Write to destination account"," - Origin account is already in memory, so no DB operations for them."," # \u003c/weight\u003e"]},{"Name":"set_balance","Args":[{"Name":"who","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"new_free","Type":"Compact\u003cT::Balance\u003e"},{"Name":"new_reserved","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Set the balances of a given account.",""," This will alter `FreeBalance` and `ReservedBalance` in storage. it will"," also decrease the total issuance of the system (`TotalIssuance`)."," If the new free or reserved balance is below the existential deposit,"," it will reset the account nonce (`frame_system::AccountNonce`).",""," The dispatch origin for this call is `root`.",""," # \u003cweight\u003e"," - Independent of the arguments."," - Contains a limited number of reads and writes."," ---------------------"," - Base Weight:"," - Creating: 27.56 µs"," - Killing: 35.11 µs"," - DB Weight: 1 Read, 1 Write to `who`"," # \u003c/weight\u003e"]},{"Name":"force_transfer","Args":[{"Name":"source","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Exactly as `transfer`, except the origin must be root and the source account may be"," specified."," # \u003cweight\u003e"," - Same as transfer, but additional read and write because the source account is"," not assumed to be in the overlay."," # \u003c/weight\u003e"]},{"Name":"transfer_keep_alive","Args":[{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"value","Type":"Compact\u003cT::Balance\u003e"}],"Documentation":[" Same as the [`transfer`] call, but with a check that the transfer will not kill the"," origin account.",""," 99% of the time you want [`transfer`] instead.",""," [`transfer`]: struct.Pallet.html#method.transfer"," # \u003cweight\u003e"," - Cheaper than transfer because account cannot be killed."," - Base Weight: 51.4 µs"," - DB Weight: 1 Read and 1 Write to dest (sender is in overlay already)"," #\u003c/weight\u003e"]},{"Name":"transfer_all","Args":[{"Name":"dest","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"keep_alive","Type":"bool"}],"Documentation":[" Transfer the entire transferable balance from the caller account.",""," NOTE: This function only attempts to transfer _transferable_ balances. This means that"," any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be"," transferred by this function. To ensure that this function results in a killed account,"," you might need to prepare the account by removing any reference counters, storage"," deposits, etc...",""," The dispatch origin of this call must be Signed.",""," - `dest`: The recipient of the transfer."," - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all"," of the funds the account has, causing the sender account to be killed (false), or"," transfer everything except at least the existential deposit, which will guarantee to"," keep the sender account alive (true)."," # \u003cweight\u003e"," - O(1). Just like transfer, but reading the user's transferable balance first."," #\u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Endowed","Args":["AccountId","Balance"],"Documentation":[" An account was created with some free balance. \\[account, free_balance\\]"]},{"Name":"DustLost","Args":["AccountId","Balance"],"Documentation":[" An account was removed whose balance was non-zero but below ExistentialDeposit,"," resulting in an outright loss. \\[account, balance\\]"]},{"Name":"Transfer","Args":["AccountId","AccountId","Balance"],"Documentation":[" Transfer succeeded. \\[from, to, value\\]"]},{"Name":"BalanceSet","Args":["AccountId","Balance","Balance"],"Documentation":[" A balance was set by root. \\[who, free, reserved\\]"]},{"Name":"Deposit","Args":["AccountId","Balance"],"Documentation":[" Some amount was deposited (e.g. for transaction fees). \\[who, deposit\\]"]},{"Name":"Reserved","Args":["AccountId","Balance"],"Documentation":[" Some balance was reserved (moved from free to reserved). \\[who, value\\]"]},{"Name":"Unreserved","Args":["AccountId","Balance"],"Documentation":[" Some balance was unreserved (moved from reserved to free). \\[who, value\\]"]},{"Name":"ReserveRepatriated","Args":["AccountId","AccountId","Balance","Status"],"Documentation":[" Some balance was moved from the reserve of the first account to the second account."," Final argument indicates the destination balance type."," \\[from, to, balance, destination_status\\]"]}],"Constants":[{"Name":"ExistentialDeposit","Type":"T::Balance","Value":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" The minimum amount required to keep an account open."]}],"Errors":[{"Name":"VestingBalance","Documentation":[" Vesting balance too high to send value"]},{"Name":"LiquidityRestrictions","Documentation":[" Account liquidity restrictions prevent withdrawal"]},{"Name":"InsufficientBalance","Documentation":[" Balance too low to send value"]},{"Name":"ExistentialDeposit","Documentation":[" Value too low to create account due to existential deposit"]},{"Name":"KeepAlive","Documentation":[" Transfer/payment would kill account"]},{"Name":"ExistingVestingSchedule","Documentation":[" A vesting schedule already exists for this account"]},{"Name":"DeadAccount","Documentation":[" Beneficiary account must pre-exist"]},{"Name":"TooManyReserves","Documentation":[" Number of named reserves exceed MaxReserves"]}],"Index":3},{"Name":"Sudo","HasStorage":true,"Storage":{"Prefix":"Sudo","Items":[{"Name":"Key","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The `AccountId` of the sudo key."]}]},"HasCalls":true,"Calls":[{"Name":"sudo","Args":[{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Root` origin.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB write (event)."," - Weight of derivative `call` execution + 10,000."," # \u003c/weight\u003e"]},{"Name":"sudo_unchecked_weight","Args":[{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"},{"Name":"_weight","Type":"Weight"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Root` origin."," This function does not check the weight of the call, and instead allows the"," Sudo user to specify the weight of the call.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - The weight of this call is defined by the caller."," # \u003c/weight\u003e"]},{"Name":"set_key","Args":[{"Name":"new","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"}],"Documentation":[" Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB change."," # \u003c/weight\u003e"]},{"Name":"sudo_as","Args":[{"Name":"who","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Authenticates the sudo key and dispatches a function call with `Signed` origin from"," a given account.",""," The dispatch origin for this call must be _Signed_.",""," # \u003cweight\u003e"," - O(1)."," - Limited storage reads."," - One DB write (event)."," - Weight of derivative `call` execution + 10,000."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Sudid","Args":["DispatchResult"],"Documentation":[" A sudo just took place. \\[result\\]"]},{"Name":"KeyChanged","Args":["AccountId"],"Documentation":[" The \\[sudoer\\] just switched identity; the old key is supplied."]},{"Name":"SudoAsDone","Args":["DispatchResult"],"Documentation":[" A sudo just took place. \\[result\\]"]}],"Constants":null,"Errors":[{"Name":"RequireSudo","Documentation":[" Sender must be the Sudo account"]}],"Index":4},{"Name":"RandomnessCollectiveFlip","HasStorage":true,"Storage":{"Prefix":"RandomnessCollectiveFlip","Items":[{"Name":"RandomMaterial","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::Hash\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Series of block headers from the last 81 blocks that acts as random seed material. This"," is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of"," the oldest hash."]}]},"HasCalls":true,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":5},{"Name":"ParachainSystem","HasStorage":true,"Storage":{"Prefix":"ParachainSystem","Items":[{"Name":"PendingRelayChainBlockNumber","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"RelayChainBlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" We need to store the new validation function for the span between"," setting it and applying it. If it has a"," value, then [`PendingValidationCode`] must have a real value, and"," together will coordinate the block number where the upgrade will happen."]},{"Name":"PendingValidationCode","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cu8\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The new validation function we will upgrade to when the relay chain"," reaches [`PendingRelayChainBlockNumber`]. A real validation function must"," exist here as long as [`PendingRelayChainBlockNumber`] is set."]},{"Name":"ValidationData","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"PersistedValidationData","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The [`PersistedValidationData`] set for this block."]},{"Name":"DidSetValidationCode","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Were the validation data set to notify the relay chain?"]},{"Name":"LastUpgrade","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The last relay parent block number at which we signalled the code upgrade."]},{"Name":"RelevantMessagingState","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"MessagingStateSnapshot","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The snapshot of some state related to messaging relevant to the current parachain as per"," the relay parent.",""," This field is meant to be updated each block with the validation data inherent. Therefore,"," before processing of the inherent, e.g. in `on_initialize` this data may be stale.",""," This data is also absent from the genesis."]},{"Name":"HostConfiguration","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"AbridgedHostConfiguration","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The parachain host configuration that was obtained from the relay parent.",""," This field is meant to be updated each block with the validation data inherent. Therefore,"," before processing of the inherent, e.g. in `on_initialize` this data may be stale.",""," This data is also absent from the genesis."]},{"Name":"LastDmqMqcHead","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"MessageQueueChain","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The last downward message queue chain head we have observed.",""," This value is loaded before and saved after processing inbound downward messages carried"," by the system inherent."]},{"Name":"LastHrmpMqcHeads","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BTreeMap\u003cParaId, MessageQueueChain\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The message queue chain heads we have observed per each channel incoming channel.",""," This value is loaded before and saved after processing inbound downward messages carried"," by the system inherent."]},{"Name":"ProcessedDownwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Number of downward messages processed in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"NewValidationCode","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cu8\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" New validation code that was set in a block.",""," This will be cleared in `on_initialize` of each new block if no other pallet already set"," the value."]},{"Name":"HrmpWatermark","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::v1::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" HRMP watermark that was set in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"HrmpOutboundMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cOutboundHrmpMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" HRMP messages that were sent in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"UpwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cUpwardMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Upward messages that were sent in a block.",""," This will be cleared in `on_initialize` of each new block."]},{"Name":"PendingUpwardMessages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cUpwardMessage\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Upward messages that are still pending and not yet send to the relay chain."]},{"Name":"AnnouncedHrmpMessagesPerCandidate","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of HRMP messages we observed in `on_initialize` and thus used that number for"," announcing the weight of `on_initialize` and `on_finalize`."]},{"Name":"ReservedXcmpWeightOverride","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Weight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The weight we reserve at the beginning of the block for processing XCMP messages. This"," overrides the amount set in the Config trait."]},{"Name":"ReservedDmpWeightOverride","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Weight","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The weight we reserve at the beginning of the block for processing DMP messages. This"," overrides the amount set in the Config trait."]},{"Name":"AuthorizedUpgrade","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::Hash","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The next authorized upgrade, if there is one."]}]},"HasCalls":true,"Calls":[{"Name":"set_upgrade_block","Args":[{"Name":"relay_chain_block","Type":"RelayChainBlockNumber"}],"Documentation":[" Force an already scheduled validation function upgrade to happen on a particular block.",""," Note that coordinating this block for the upgrade has to happen independently on the"," relay chain and this parachain. Synchronizing the block for the upgrade is sensitive,"," and this bypasses all checks and and normal protocols. Very easy to brick your chain"," if done wrong."]},{"Name":"set_validation_data","Args":[{"Name":"data","Type":"ParachainInherentData"}],"Documentation":[" Set the current validation data.",""," This should be invoked exactly once per block. It will panic at the finalization"," phase if the call was not invoked.",""," The dispatch origin for this call must be `Inherent`",""," As a side effect, this function upgrades the current validation function"," if the appropriate time has come."]},{"Name":"sudo_send_upward_message","Args":[{"Name":"message","Type":"UpwardMessage"}],"Documentation":null},{"Name":"authorize_upgrade","Args":[{"Name":"code_hash","Type":"T::Hash"}],"Documentation":null},{"Name":"enact_authorized_upgrade","Args":[{"Name":"code","Type":"Vec\u003cu8\u003e"}],"Documentation":null}],"HasEvents":true,"Events":[{"Name":"ValidationFunctionStored","Args":["RelayChainBlockNumber"],"Documentation":[" The validation function has been scheduled to apply as of the contained relay chain"," block number."]},{"Name":"ValidationFunctionApplied","Args":["RelayChainBlockNumber"],"Documentation":[" The validation function was applied as of the contained relay chain block number."]},{"Name":"UpgradeAuthorized","Args":["Hash"],"Documentation":[" An upgrade has been authorized."]},{"Name":"DownwardMessagesReceived","Args":["u32"],"Documentation":[" Some downward messages have been received and will be processed."," \\[ count \\]"]},{"Name":"DownwardMessagesProcessed","Args":["Weight","relay_chain::Hash"],"Documentation":[" Downward messages were processed using the given weight."," \\[ weight_used, result_mqc_head \\]"]}],"Constants":null,"Errors":[{"Name":"OverlappingUpgrades","Documentation":[" Attempt to upgrade validation function while existing upgrade pending"]},{"Name":"ProhibitedByPolkadot","Documentation":[" Polkadot currently prohibits this parachain from upgrading its validation function"]},{"Name":"TooBig","Documentation":[" The supplied validation function has compiled into a blob larger than Polkadot is"," willing to run"]},{"Name":"ValidationDataNotAvailable","Documentation":[" The inherent which supplies the validation data did not run this block"]},{"Name":"HostConfigurationNotAvailable","Documentation":[" The inherent which supplies the host configuration did not run this block"]},{"Name":"NotScheduled","Documentation":[" No validation function upgrade is currently scheduled."]},{"Name":"NothingAuthorized","Documentation":[" No code upgrade has been authorized."]},{"Name":"Unauthorized","Documentation":[" The given code upgrade has not been authorized."]}],"Index":6},{"Name":"TransactionPayment","HasStorage":true,"Storage":{"Prefix":"TransactionPayment","Items":[{"Name":"NextFeeMultiplier","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Multiplier","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AABkp7O24A0AAAAAAAAAAA==","Documentation":null},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":[{"Name":"TransactionByteFee","Type":"BalanceOf\u003cT\u003e","Value":"AEB6EPNaAAAAAAAAAAAAAA==","Documentation":[" The fee to be paid for making a transaction; the per-byte portion."]},{"Name":"WeightToFee","Type":"Vec\u003cWeightToFeeCoefficient\u003cBalanceOf\u003cT\u003e\u003e\u003e","Value":"BAEAAAAAAAAAAAAAAAAAAAAAAAAAAAE=","Documentation":[" The polynomial that is applied in order to derive fee from weight."]}],"Errors":null,"Index":7},{"Name":"ParachainInfo","HasStorage":true,"Storage":{"Prefix":"ParachainInfo","Items":[{"Name":"ParachainId","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ParaId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"ZAAAAA==","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":8},{"Name":"EthereumChainId","HasStorage":true,"Storage":{"Prefix":"EthereumChainId","Items":[{"Name":"ChainId","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u64","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAA=","Documentation":null}]},"HasCalls":false,"Calls":null,"HasEvents":false,"Events":null,"Constants":null,"Errors":null,"Index":9},{"Name":"EVM","HasStorage":true,"Storage":{"Prefix":"EVM","Items":[{"Name":"AccountCodes","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"H160","Value":"Vec\u003cu8\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"AccountStorages","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"H160","Key2":"H256","Value":"H256","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","Documentation":null}]},"HasCalls":true,"Calls":[{"Name":"withdraw","Args":[{"Name":"address","Type":"H160"},{"Name":"value","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Withdraw balance from EVM into currency/balances pallet."]},{"Name":"call","Args":[{"Name":"source","Type":"H160"},{"Name":"target","Type":"H160"},{"Name":"input","Type":"Vec\u003cu8\u003e"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM call operation. This is similar to a message call transaction in Ethereum."]},{"Name":"create","Args":[{"Name":"source","Type":"H160"},{"Name":"init","Type":"Vec\u003cu8\u003e"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM create operation. This is similar to a contract creation transaction in"," Ethereum."]},{"Name":"create2","Args":[{"Name":"source","Type":"H160"},{"Name":"init","Type":"Vec\u003cu8\u003e"},{"Name":"salt","Type":"H256"},{"Name":"value","Type":"U256"},{"Name":"gas_limit","Type":"u64"},{"Name":"gas_price","Type":"U256"},{"Name":"nonce","Type":"Option\u003cU256\u003e"}],"Documentation":[" Issue an EVM create2 operation."]}],"HasEvents":true,"Events":[{"Name":"Log","Args":["Log"],"Documentation":[" Ethereum events from contracts."]},{"Name":"Created","Args":["H160"],"Documentation":[" A contract has been created at given \\[address\\]."]},{"Name":"CreatedFailed","Args":["H160"],"Documentation":[" A \\[contract\\] was attempted to be created, but the execution failed."]},{"Name":"Executed","Args":["H160"],"Documentation":[" A \\[contract\\] has been executed successfully with states applied."]},{"Name":"ExecutedFailed","Args":["H160"],"Documentation":[" A \\[contract\\] has been executed with errors. States are reverted with only gas fees applied."]},{"Name":"BalanceDeposit","Args":["AccountId","H160","U256"],"Documentation":[" A deposit has been made at a given address. \\[sender, address, value\\]"]},{"Name":"BalanceWithdraw","Args":["AccountId","H160","U256"],"Documentation":[" A withdrawal has been made from a given address. \\[sender, address, value\\]"]}],"Constants":null,"Errors":[{"Name":"BalanceLow","Documentation":[" Not enough balance to perform action"]},{"Name":"FeeOverflow","Documentation":[" Calculating total fee overflowed"]},{"Name":"PaymentOverflow","Documentation":[" Calculating total payment overflowed"]},{"Name":"WithdrawFailed","Documentation":[" Withdraw fee failed"]},{"Name":"GasPriceTooLow","Documentation":[" Gas price is too low."]},{"Name":"InvalidNonce","Documentation":[" Nonce is invalid"]}],"Index":10},{"Name":"Ethereum","HasStorage":true,"Storage":{"Prefix":"Ethereum","Items":[{"Name":"Pending","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003c(ethereum::Transaction, TransactionStatus, ethereum::Receipt)\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Current building block's transactions and receipts."]},{"Name":"CurrentBlock","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"ethereum::Block","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current Ethereum block."]},{"Name":"CurrentReceipts","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cethereum::Receipt\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current Ethereum receipts."]},{"Name":"CurrentTransactionStatuses","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cTransactionStatus\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current transaction statuses."]}]},"HasCalls":true,"Calls":[{"Name":"transact","Args":[{"Name":"transaction","Type":"ethereum::Transaction"}],"Documentation":[" Transact an Ethereum transaction."]}],"HasEvents":true,"Events":[{"Name":"Executed","Args":["H160","H160","H256","ExitReason"],"Documentation":[" An ethereum transaction was successfully executed. [from, to/contract_address, transaction_hash, exit_reason]"]}],"Constants":null,"Errors":null,"Index":11},{"Name":"ParachainStaking","HasStorage":true,"Storage":{"Prefix":"ParachainStaking","Items":[{"Name":"CollatorCommission","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Perbill","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Commission percent taken off of rewards for all collators"]},{"Name":"TotalSelected","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The total candidates selected every round"]},{"Name":"ParachainBondInfo","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ParachainBondConfig\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Parachain bond config info { account, percent_of_inflation }"]},{"Name":"Round","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"RoundInfo\u003cT::BlockNumber\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AQAAAAEAAAAUAAAA","Documentation":[" Current round index and next round scheduled transition"]},{"Name":"NominatorState","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Nominator\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Get nominator state associated with an account if account is nominating else None"]},{"Name":"CollatorState2","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Collator2\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Get collator state associated with an account if account is collating else None"]},{"Name":"SelectedCandidates","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The collator candidates selected for the current round"]},{"Name":"Total","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BalanceOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total capital locked by this staking pallet"]},{"Name":"CandidatePool","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"OrderedSet\u003cBond\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The pool of collator candidates, each with their total backing stake"]},{"Name":"ExitQueue","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"OrderedSet\u003cBond\u003cT::AccountId, RoundIndex\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" A queue of collators awaiting exit `BondDuration` delay after request"]},{"Name":"AtStake","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key1":"RoundIndex","Key2":"T::AccountId","Value":"CollatorSnapshot\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Snapshot of collator nomination stake at the start of the round"]},{"Name":"Staked","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"RoundIndex","Value":"BalanceOf\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total backing stake for selected candidates in the round"]},{"Name":"InflationConfig","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"InflationInfo\u003cBalanceOf\u003cT\u003e\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" Inflation configuration"]},{"Name":"Points","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"RoundIndex","Value":"RewardPoint","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Total points awarded to collators for block production in the round"]},{"Name":"AwardedPts","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":true,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key1":"RoundIndex","Key2":"T::AccountId","Value":"RewardPoint","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Points for each collator per round"]}]},"HasCalls":true,"Calls":[{"Name":"set_staking_expectations","Args":[{"Name":"expectations","Type":"Range\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Set the expectations for total staked. These expectations determine the issuance for"," the round according to logic in `fn compute_issuance`"]},{"Name":"set_inflation","Args":[{"Name":"schedule","Type":"Range\u003cPerbill\u003e"}],"Documentation":[" Set the annual inflation rate to derive per-round inflation"]},{"Name":"set_parachain_bond_account","Args":[{"Name":"new","Type":"T::AccountId"}],"Documentation":[" Set the account that will hold funds set aside for parachain bond"]},{"Name":"set_parachain_bond_reserve_percent","Args":[{"Name":"new","Type":"Percent"}],"Documentation":[" Set the percent of inflation set aside for parachain bond"]},{"Name":"set_total_selected","Args":[{"Name":"new","Type":"u32"}],"Documentation":[" Set the total number of collator candidates selected per round"," - changes are not applied until the start of the next round"]},{"Name":"set_collator_commission","Args":[{"Name":"new","Type":"Perbill"}],"Documentation":[" Set the commission for all collators"]},{"Name":"set_blocks_per_round","Args":[{"Name":"new","Type":"u32"}],"Documentation":[" Set blocks per round"," - if called with `new` less than length of current round, will transition immediately"," in the next block"," - also updates per-round inflation config"]},{"Name":"join_candidates","Args":[{"Name":"bond","Type":"BalanceOf\u003cT\u003e"},{"Name":"candidate_count","Type":"u32"}],"Documentation":[" Join the set of collator candidates"]},{"Name":"leave_candidates","Args":[{"Name":"candidate_count","Type":"u32"}],"Documentation":[" Request to leave the set of candidates. If successful, the account is immediately"," removed from the candidate pool to prevent selection as a collator, but unbonding is"," executed with a delay of `BondDuration` rounds."]},{"Name":"go_offline","Args":null,"Documentation":[" Temporarily leave the set of collator candidates without unbonding"]},{"Name":"go_online","Args":null,"Documentation":[" Rejoin the set of collator candidates if previously had called `go_offline`"]},{"Name":"candidate_bond_more","Args":[{"Name":"more","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond more for collator candidates"]},{"Name":"candidate_bond_less","Args":[{"Name":"less","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond less for collator candidates"]},{"Name":"nominate","Args":[{"Name":"collator","Type":"T::AccountId"},{"Name":"amount","Type":"BalanceOf\u003cT\u003e"},{"Name":"collator_nominator_count","Type":"u32"},{"Name":"nomination_count","Type":"u32"}],"Documentation":[" If caller is not a nominator, then join the set of nominators"," If caller is a nominator, then makes nomination to change their nomination state"]},{"Name":"leave_nominators","Args":[{"Name":"nomination_count","Type":"u32"}],"Documentation":[" Leave the set of nominators and, by implication, revoke all ongoing nominations"]},{"Name":"revoke_nomination","Args":[{"Name":"collator","Type":"T::AccountId"}],"Documentation":[" Revoke an existing nomination"]},{"Name":"nominator_bond_more","Args":[{"Name":"candidate","Type":"T::AccountId"},{"Name":"more","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond more for nominators with respect to a specific collator candidate"]},{"Name":"nominator_bond_less","Args":[{"Name":"candidate","Type":"T::AccountId"},{"Name":"less","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Bond less for nominators with respect to a specific nominator candidate"]}],"HasEvents":true,"Events":[{"Name":"NewRound","Args":["T::BlockNumber","RoundIndex","u32","BalanceOf\u003cT\u003e"],"Documentation":[" Starting Block, Round, Number of Collators Selected, Total Balance"]},{"Name":"JoinedCollatorCandidates","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Account, Amount Locked, New Total Amt Locked"]},{"Name":"CollatorChosen","Args":["RoundIndex","T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Round, Collator Account, Total Exposed Amount (includes all nominations)"]},{"Name":"CollatorBondedMore","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Collator Account, Old Bond, New Bond"]},{"Name":"CollatorBondedLess","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Collator Account, Old Bond, New Bond"]},{"Name":"CollatorWentOffline","Args":["RoundIndex","T::AccountId"],"Documentation":null},{"Name":"CollatorBackOnline","Args":["RoundIndex","T::AccountId"],"Documentation":null},{"Name":"CollatorScheduledExit","Args":["RoundIndex","T::AccountId","RoundIndex"],"Documentation":[" Round, Collator Account, Scheduled Exit"]},{"Name":"CollatorLeft","Args":["T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Account, Amount Unlocked, New Total Amt Locked"]},{"Name":"NominationIncreased","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","bool","BalanceOf\u003cT\u003e"],"Documentation":null},{"Name":"NominationDecreased","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","bool","BalanceOf\u003cT\u003e"],"Documentation":null},{"Name":"NominatorLeft","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Nominator, Amount Unstaked"]},{"Name":"Nomination","Args":["T::AccountId","BalanceOf\u003cT\u003e","T::AccountId","NominatorAdded\u003cBalanceOf\u003cT\u003e\u003e"],"Documentation":[" Nominator, Amount Locked, Collator, Nominator Position with New Total Backing if in Top"]},{"Name":"NominatorLeftCollator","Args":["T::AccountId","T::AccountId","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Nominator, Collator, Amount Unstaked, New Total Amt Staked for Collator"]},{"Name":"Rewarded","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Paid the account (nominator or collator) the balance as liquid rewards"]},{"Name":"ReservedForParachainBond","Args":["T::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Transferred to account which holds funds reserved for parachain bond"]},{"Name":"ParachainBondAccountSet","Args":["T::AccountId","T::AccountId"],"Documentation":[" Account (re)set for parachain bond treasury [old, new]"]},{"Name":"ParachainBondReservePercentSet","Args":["Percent","Percent"],"Documentation":[" Percent of inflation reserved for parachain bond (re)set [old, new]"]},{"Name":"InflationSet","Args":["Perbill","Perbill","Perbill","Perbill","Perbill","Perbill"],"Documentation":[" Annual inflation input (first 3) was used to derive new per-round inflation (last 3)"]},{"Name":"StakeExpectationsSet","Args":["BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" Staking expectations set"]},{"Name":"TotalSelectedSet","Args":["u32","u32"],"Documentation":[" Set total selected candidates to this value [old, new]"]},{"Name":"CollatorCommissionSet","Args":["Perbill","Perbill"],"Documentation":[" Set collator commission to this value [old, new]"]},{"Name":"BlocksPerRoundSet","Args":["RoundIndex","T::BlockNumber","u32","u32","Perbill","Perbill","Perbill"],"Documentation":[" Set blocks per round [current_round, first_block, old, new, new_per_round_inflation]"]}],"Constants":null,"Errors":[{"Name":"NominatorDNE","Documentation":null},{"Name":"CandidateDNE","Documentation":null},{"Name":"NominatorExists","Documentation":null},{"Name":"CandidateExists","Documentation":null},{"Name":"ValBondBelowMin","Documentation":null},{"Name":"NomBondBelowMin","Documentation":null},{"Name":"NominationBelowMin","Documentation":null},{"Name":"AlreadyOffline","Documentation":null},{"Name":"AlreadyActive","Documentation":null},{"Name":"AlreadyLeaving","Documentation":null},{"Name":"CannotActivateIfLeaving","Documentation":null},{"Name":"ExceedMaxCollatorsPerNom","Documentation":null},{"Name":"AlreadyNominatedCollator","Documentation":null},{"Name":"NominationDNE","Documentation":null},{"Name":"CannotBondLessGEQTotalBond","Documentation":null},{"Name":"InvalidSchedule","Documentation":null},{"Name":"CannotSetBelowMin","Documentation":null},{"Name":"NoWritingSameValue","Documentation":null},{"Name":"TooLowCandidateCountWeightHintJoinCandidates","Documentation":null},{"Name":"TooLowCollatorCandidateCountToLeaveCandidates","Documentation":null},{"Name":"TooLowNominationCountToNominate","Documentation":null},{"Name":"TooLowCollatorNominationCountToNominate","Documentation":null},{"Name":"TooLowNominationCountToLeaveNominators","Documentation":null}],"Index":12},{"Name":"Scheduler","HasStorage":true,"Storage":{"Prefix":"Scheduler","Items":[{"Name":"Agenda","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::BlockNumber","Value":"Vec\u003cOption\u003cScheduled\u003c\u003cT as Config\u003e::Call, T::BlockNumber, T::\nPalletsOrigin, T::AccountId\u003e\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Items to be executed, indexed by the block number that they should be executed on."]},{"Name":"Lookup","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"Vec\u003cu8\u003e","Value":"TaskAddress\u003cT::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Lookup from identity to the block number and index of the task."]},{"Name":"StorageVersion","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," New networks start with last version."]}]},"HasCalls":true,"Calls":[{"Name":"schedule","Args":[{"Name":"when","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Anonymously schedule a task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 22.29 + .126 * S µs"," - DB Weight:"," - Read: Agenda"," - Write: Agenda"," - Will use base weight of 25 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"cancel","Args":[{"Name":"when","Type":"T::BlockNumber"},{"Name":"index","Type":"u32"}],"Documentation":[" Cancel an anonymously scheduled task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 22.15 + 2.869 * S µs"," - DB Weight:"," - Read: Agenda"," - Write: Agenda, Lookup"," - Will use base weight of 100 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"schedule_named","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"},{"Name":"when","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Schedule a named task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 29.6 + .159 * S µs"," - DB Weight:"," - Read: Agenda, Lookup"," - Write: Agenda, Lookup"," - Will use base weight of 35 which should be good for more than 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"cancel_named","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Cancel a named scheduled task.",""," # \u003cweight\u003e"," - S = Number of already scheduled calls"," - Base Weight: 24.91 + 2.907 * S µs"," - DB Weight:"," - Read: Agenda, Lookup"," - Write: Agenda, Lookup"," - Will use base weight of 100 which should be good for up to 30 scheduled calls"," # \u003c/weight\u003e"]},{"Name":"schedule_after","Args":[{"Name":"after","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Anonymously schedule a task after a delay.",""," # \u003cweight\u003e"," Same as [`schedule`]."," # \u003c/weight\u003e"]},{"Name":"schedule_named_after","Args":[{"Name":"id","Type":"Vec\u003cu8\u003e"},{"Name":"after","Type":"T::BlockNumber"},{"Name":"maybe_periodic","Type":"Option\u003cschedule::Period\u003cT::BlockNumber\u003e\u003e"},{"Name":"priority","Type":"schedule::Priority"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Schedule a named task after a delay.",""," # \u003cweight\u003e"," Same as [`schedule_named`]."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Scheduled","Args":["BlockNumber","u32"],"Documentation":[" Scheduled some task. \\[when, index\\]"]},{"Name":"Canceled","Args":["BlockNumber","u32"],"Documentation":[" Canceled some task. \\[when, index\\]"]},{"Name":"Dispatched","Args":["TaskAddress\u003cBlockNumber\u003e","Option\u003cVec\u003cu8\u003e\u003e","DispatchResult"],"Documentation":[" Dispatched some task. \\[task, id, result\\]"]}],"Constants":null,"Errors":[{"Name":"FailedToSchedule","Documentation":[" Failed to schedule a call"]},{"Name":"NotFound","Documentation":[" Cannot find the scheduled call."]},{"Name":"TargetBlockNumberInPast","Documentation":[" Given target block number is in the past."]},{"Name":"RescheduleNoChange","Documentation":[" Reschedule failed because it does not change scheduled time."]}],"Index":13},{"Name":"Democracy","HasStorage":true,"Storage":{"Prefix":"Democracy","Items":[{"Name":"PublicPropCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"PropIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The number of (public) proposals that have been made so far."]},{"Name":"PublicProps","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003c(PropIndex, T::Hash, T::AccountId)\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The public proposals. Unsorted. The second item is the proposal's hash."]},{"Name":"DepositOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"PropIndex","Value":"(Vec\u003cT::AccountId\u003e, BalanceOf\u003cT\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Those who have locked a deposit.",""," TWOX-NOTE: Safe, as increasing integer keys are safe."]},{"Name":"Preimages","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"PreimageStatus\u003cT::AccountId, BalanceOf\u003cT\u003e, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Map of hashes to the proposal preimage, along with who registered it and their deposit."," The block number is the block at which it was deposited."]},{"Name":"ReferendumCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ReferendumIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The next free referendum index, aka the number of referenda started so far."]},{"Name":"LowestUnbaked","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ReferendumIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" The lowest referendum index representing an unbaked referendum. Equal to"," `ReferendumCount` if there isn't a unbaked referendum."]},{"Name":"ReferendumInfoOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"ReferendumIndex","Value":"ReferendumInfo\u003cT::BlockNumber, T::Hash, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Information concerning any given referendum.",""," TWOX-NOTE: SAFE as indexes are not under an attacker’s control."]},{"Name":"VotingOf","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"Voting\u003cBalanceOf\u003cT\u003e, T::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA","Documentation":[" All votes for a particular voter. We store the balance for the number of votes that we"," have recorded. The second item is the total amount of delegations, that will be added.",""," TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway."]},{"Name":"Locks","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"T::BlockNumber","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Accounts for which there are locks in action which may be removed at some point in the"," future. The value is the block number at which the lock expires and may be removed.",""," TWOX-NOTE: OK ― `AccountId` is a secure hash."]},{"Name":"LastTabledWasExternal","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" True if the last referendum tabled was submitted externally. False if it was a public"," proposal."]},{"Name":"NextExternal","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"(T::Hash, VoteThreshold)","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The referendum to be tabled whenever it would be valid to table an external proposal."," This happens when a referendum needs to be tabled and one of two conditions are met:"," - `LastTabledWasExternal` is `false`; or"," - `PublicProps` is empty."]},{"Name":"Blacklist","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"(T::BlockNumber, Vec\u003cT::AccountId\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" A record of who vetoed what. Maps proposal hash to a possible existent block number"," (until when it may not be resubmitted) and who vetoed it."]},{"Name":"Cancellations","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"bool","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Record of all proposals that have been subject to emergency cancellation."]},{"Name":"StorageVersion","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"Releases","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Storage version of the pallet.",""," New networks start with last version."]}]},"HasCalls":true,"Calls":[{"Name":"propose","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"value","Type":"Compact\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Propose a sensitive action to be taken.",""," The dispatch origin of this call must be _Signed_ and the sender must"," have funds to cover the deposit.",""," - `proposal_hash`: The hash of the proposal preimage."," - `value`: The amount of deposit (must be at least `MinimumDeposit`).",""," Emits `Proposed`.",""," Weight: `O(p)`"]},{"Name":"second","Args":[{"Name":"proposal","Type":"Compact\u003cPropIndex\u003e"},{"Name":"seconds_upper_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Signals agreement with a particular proposal.",""," The dispatch origin of this call must be _Signed_ and the sender"," must have funds to cover the deposit, equal to the original deposit.",""," - `proposal`: The index of the proposal to second."," - `seconds_upper_bound`: an upper bound on the current number of seconds on this"," proposal. Extrinsic is weighted according to this value with no refund.",""," Weight: `O(S)` where S is the number of seconds a proposal already has."]},{"Name":"vote","Args":[{"Name":"ref_index","Type":"Compact\u003cReferendumIndex\u003e"},{"Name":"vote","Type":"AccountVote\u003cBalanceOf\u003cT\u003e\u003e"}],"Documentation":[" Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;"," otherwise it is a vote to keep the status quo.",""," The dispatch origin of this call must be _Signed_.",""," - `ref_index`: The index of the referendum to vote for."," - `vote`: The vote configuration.",""," Weight: `O(R)` where R is the number of referendums the voter has voted on."]},{"Name":"emergency_cancel","Args":[{"Name":"ref_index","Type":"ReferendumIndex"}],"Documentation":[" Schedule an emergency cancellation of a referendum. Cannot happen twice to the same"," referendum.",""," The dispatch origin of this call must be `CancellationOrigin`.",""," -`ref_index`: The index of the referendum to cancel.",""," Weight: `O(1)`."]},{"Name":"external_propose","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a referendum to be tabled once it is legal to schedule an external"," referendum.",""," The dispatch origin of this call must be `ExternalOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Weight: `O(V)` with V number of vetoers in the blacklist of proposal."," Decoding vec of length V. Charged as maximum"]},{"Name":"external_propose_majority","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a majority-carries referendum to be tabled next once it is legal to schedule"," an external referendum.",""," The dispatch of this call must be `ExternalMajorityOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Unlike `external_propose`, blacklisting has no effect on this and it may replace a"," pre-scheduled `external_propose` call.",""," Weight: `O(1)`"]},{"Name":"external_propose_default","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Schedule a negative-turnout-bias referendum to be tabled next once it is legal to"," schedule an external referendum.",""," The dispatch of this call must be `ExternalDefaultOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal.",""," Unlike `external_propose`, blacklisting has no effect on this and it may replace a"," pre-scheduled `external_propose` call.",""," Weight: `O(1)`"]},{"Name":"fast_track","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"voting_period","Type":"T::BlockNumber"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Schedule the currently externally-proposed majority-carries referendum to be tabled"," immediately. If there is no externally-proposed referendum currently, or if there is one"," but it is not a majority-carries referendum then it fails.",""," The dispatch of this call must be `FastTrackOrigin`.",""," - `proposal_hash`: The hash of the current external proposal."," - `voting_period`: The period that is allowed for voting on this proposal. Increased to"," `FastTrackVotingPeriod` if too low."," - `delay`: The number of block after voting has ended in approval and this should be"," enacted. This doesn't have a minimum amount.",""," Emits `Started`.",""," Weight: `O(1)`"]},{"Name":"veto_external","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Veto and blacklist the external proposal hash.",""," The dispatch origin of this call must be `VetoOrigin`.",""," - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.",""," Emits `Vetoed`.",""," Weight: `O(V + log(V))` where V is number of `existing vetoers`"]},{"Name":"cancel_referendum","Args":[{"Name":"ref_index","Type":"Compact\u003cReferendumIndex\u003e"}],"Documentation":[" Remove a referendum.",""," The dispatch origin of this call must be _Root_.",""," - `ref_index`: The index of the referendum to cancel.",""," # Weight: `O(1)`."]},{"Name":"cancel_queued","Args":[{"Name":"which","Type":"ReferendumIndex"}],"Documentation":[" Cancel a proposal queued for enactment.",""," The dispatch origin of this call must be _Root_.",""," - `which`: The index of the referendum to cancel.",""," Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`."]},{"Name":"delegate","Args":[{"Name":"to","Type":"T::AccountId"},{"Name":"conviction","Type":"Conviction"},{"Name":"balance","Type":"BalanceOf\u003cT\u003e"}],"Documentation":[" Delegate the voting power (with some given conviction) of the sending account.",""," The balance delegated is locked for as long as it's delegated, and thereafter for the"," time appropriate for the conviction's lock period.",""," The dispatch origin of this call must be _Signed_, and the signing account must either:"," - be delegating already; or"," - have no voting activity (if there is, then it will need to be removed/consolidated"," through `reap_vote` or `unvote`).",""," - `to`: The account whose voting the `target` account's voting power will follow."," - `conviction`: The conviction that will be attached to the delegated votes. When the"," account is undelegated, the funds will be locked for the corresponding period."," - `balance`: The amount of the account's balance to be used in delegating. This must"," not be more than the account's current balance.",""," Emits `Delegated`.",""," Weight: `O(R)` where R is the number of referendums the voter delegating to has"," voted on. Weight is charged as if maximum votes."]},{"Name":"undelegate","Args":null,"Documentation":[" Undelegate the voting power of the sending account.",""," Tokens may be unlocked following once an amount of time consistent with the lock period"," of the conviction with which the delegation was issued.",""," The dispatch origin of this call must be _Signed_ and the signing account must be"," currently delegating.",""," Emits `Undelegated`.",""," Weight: `O(R)` where R is the number of referendums the voter delegating to has"," voted on. Weight is charged as if maximum votes."]},{"Name":"clear_public_proposals","Args":null,"Documentation":[" Clears all public proposals.",""," The dispatch origin of this call must be _Root_.",""," Weight: `O(1)`."]},{"Name":"note_preimage","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Register the preimage for an upcoming proposal. This doesn't require the proposal to be"," in the dispatch queue but does require a deposit, returned once enacted.",""," The dispatch origin of this call must be _Signed_.",""," - `encoded_proposal`: The preimage of a proposal.",""," Emits `PreimageNoted`.",""," Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)."]},{"Name":"note_preimage_operational","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Same as `note_preimage` but origin is `OperationalPreimageOrigin`."]},{"Name":"note_imminent_preimage","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Register the preimage for an upcoming proposal. This requires the proposal to be"," in the dispatch queue. No deposit is needed. When this call is successful, i.e."," the preimage has not been uploaded before and matches some imminent proposal,"," no fee is paid.",""," The dispatch origin of this call must be _Signed_.",""," - `encoded_proposal`: The preimage of a proposal.",""," Emits `PreimageNoted`.",""," Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)."]},{"Name":"note_imminent_preimage_operational","Args":[{"Name":"encoded_proposal","Type":"Vec\u003cu8\u003e"}],"Documentation":[" Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`."]},{"Name":"reap_preimage","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"proposal_len_upper_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Remove an expired proposal preimage and collect the deposit.",""," The dispatch origin of this call must be _Signed_.",""," - `proposal_hash`: The preimage hash of a proposal."," - `proposal_length_upper_bound`: an upper bound on length of the proposal."," Extrinsic is weighted according to this value with no refund.",""," This will only work after `VotingPeriod` blocks from the time that the preimage was"," noted, if it's the same account doing it. If it's a different account, then it'll only"," work an additional `EnactmentPeriod` later.",""," Emits `PreimageReaped`.",""," Weight: `O(D)` where D is length of proposal."]},{"Name":"unlock","Args":[{"Name":"target","Type":"T::AccountId"}],"Documentation":[" Unlock tokens that have an expired lock.",""," The dispatch origin of this call must be _Signed_.",""," - `target`: The account to remove the lock on.",""," Weight: `O(R)` with R number of vote of target."]},{"Name":"remove_vote","Args":[{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Remove a vote for a referendum.",""," If:"," - the referendum was cancelled, or"," - the referendum is ongoing, or"," - the referendum has ended such that"," - the vote of the account was in opposition to the result; or"," - there was no conviction to the account's vote; or"," - the account made a split vote"," ...then the vote is removed cleanly and a following call to `unlock` may result in more"," funds being available.",""," If, however, the referendum has ended and:"," - it finished corresponding to the vote of the account, and"," - the account made a standard vote with conviction, and"," - the lock period of the conviction is not over"," ...then the lock will be aggregated into the overall account's lock, which may involve"," *overlocking* (where the two locks are combined into a single lock that is the maximum"," of both the amount locked and the time is it locked for).",""," The dispatch origin of this call must be _Signed_, and the signer must have a vote"," registered for referendum `index`.",""," - `index`: The index of referendum of the vote to be removed.",""," Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on."," Weight is calculated for the maximum number of vote."]},{"Name":"remove_other_vote","Args":[{"Name":"target","Type":"T::AccountId"},{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Remove a vote for a referendum.",""," If the `target` is equal to the signer, then this function is exactly equivalent to"," `remove_vote`. If not equal to the signer, then the vote must have expired,"," either because the referendum was cancelled, because the voter lost the referendum or"," because the conviction period is over.",""," The dispatch origin of this call must be _Signed_.",""," - `target`: The account of the vote to be removed; this account must have voted for"," referendum `index`."," - `index`: The index of referendum of the vote to be removed.",""," Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on."," Weight is calculated for the maximum number of vote."]},{"Name":"enact_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"ReferendumIndex"}],"Documentation":[" Enact a proposal from a referendum. For now we just make the weight be the maximum."]},{"Name":"blacklist","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"maybe_ref_index","Type":"Option\u003cReferendumIndex\u003e"}],"Documentation":[" Permanently place a proposal into the blacklist. This prevents it from ever being"," proposed again.",""," If called on a queued public or external proposal, then this will result in it being"," removed. If the `ref_index` supplied is an active referendum with the proposal hash,"," then it will be cancelled.",""," The dispatch origin of this call must be `BlacklistOrigin`.",""," - `proposal_hash`: The proposal hash to blacklist permanently."," - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be"," cancelled.",""," Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a"," reasonable value)."]},{"Name":"cancel_proposal","Args":[{"Name":"prop_index","Type":"Compact\u003cPropIndex\u003e"}],"Documentation":[" Remove a proposal.",""," The dispatch origin of this call must be `CancelProposalOrigin`.",""," - `prop_index`: The index of the proposal to cancel.",""," Weight: `O(p)` where `p = PublicProps::\u003cT\u003e::decode_len()`"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["PropIndex","Balance"],"Documentation":[" A motion has been proposed by a public account. \\[proposal_index, deposit\\]"]},{"Name":"Tabled","Args":["PropIndex","Balance","Vec\u003cAccountId\u003e"],"Documentation":[" A public proposal has been tabled for referendum vote. \\[proposal_index, deposit, depositors\\]"]},{"Name":"ExternalTabled","Args":null,"Documentation":[" An external proposal has been tabled."]},{"Name":"Started","Args":["ReferendumIndex","VoteThreshold"],"Documentation":[" A referendum has begun. \\[ref_index, threshold\\]"]},{"Name":"Passed","Args":["ReferendumIndex"],"Documentation":[" A proposal has been approved by referendum. \\[ref_index\\]"]},{"Name":"NotPassed","Args":["ReferendumIndex"],"Documentation":[" A proposal has been rejected by referendum. \\[ref_index\\]"]},{"Name":"Cancelled","Args":["ReferendumIndex"],"Documentation":[" A referendum has been cancelled. \\[ref_index\\]"]},{"Name":"Executed","Args":["ReferendumIndex","bool"],"Documentation":[" A proposal has been enacted. \\[ref_index, is_ok\\]"]},{"Name":"Delegated","Args":["AccountId","AccountId"],"Documentation":[" An account has delegated their vote to another account. \\[who, target\\]"]},{"Name":"Undelegated","Args":["AccountId"],"Documentation":[" An \\[account\\] has cancelled a previous delegation operation."]},{"Name":"Vetoed","Args":["AccountId","Hash","BlockNumber"],"Documentation":[" An external proposal has been vetoed. \\[who, proposal_hash, until\\]"]},{"Name":"PreimageNoted","Args":["Hash","AccountId","Balance"],"Documentation":[" A proposal's preimage was noted, and the deposit taken. \\[proposal_hash, who, deposit\\]"]},{"Name":"PreimageUsed","Args":["Hash","AccountId","Balance"],"Documentation":[" A proposal preimage was removed and used (the deposit was returned)."," \\[proposal_hash, provider, deposit\\]"]},{"Name":"PreimageInvalid","Args":["Hash","ReferendumIndex"],"Documentation":[" A proposal could not be executed because its preimage was invalid."," \\[proposal_hash, ref_index\\]"]},{"Name":"PreimageMissing","Args":["Hash","ReferendumIndex"],"Documentation":[" A proposal could not be executed because its preimage was missing."," \\[proposal_hash, ref_index\\]"]},{"Name":"PreimageReaped","Args":["Hash","AccountId","Balance","AccountId"],"Documentation":[" A registered preimage was removed and the deposit collected by the reaper."," \\[proposal_hash, provider, deposit, reaper\\]"]},{"Name":"Unlocked","Args":["AccountId"],"Documentation":[" An \\[account\\] has been unlocked successfully."]},{"Name":"Blacklisted","Args":["Hash"],"Documentation":[" A proposal \\[hash\\] has been blacklisted permanently."]}],"Constants":[{"Name":"EnactmentPeriod","Type":"T::BlockNumber","Value":"IBwAAA==","Documentation":[" The minimum period of locking and the period between a proposal being approved and enacted.",""," It should generally be a little more than the unstake period to ensure that"," voting stakers have an opportunity to remove themselves from the system in the case where"," they are on the losing side of a vote."]},{"Name":"LaunchPeriod","Type":"T::BlockNumber","Value":"IBwAAA==","Documentation":[" How often (in blocks) new public referenda are launched."]},{"Name":"VotingPeriod","Type":"T::BlockNumber","Value":"oIwAAA==","Documentation":[" How often (in blocks) to check for new votes."]},{"Name":"MinimumDeposit","Type":"BalanceOf\u003cT\u003e","Value":"AACQnc7agjcAAAAAAAAAAA==","Documentation":[" The minimum amount to be used as a deposit for a public referendum proposal."]},{"Name":"FastTrackVotingPeriod","Type":"T::BlockNumber","Value":"sAQAAA==","Documentation":[" Minimum voting period allowed for a fast-track referendum."]},{"Name":"CooloffPeriod","Type":"T::BlockNumber","Value":"4MQAAA==","Documentation":[" Period in blocks where an external proposal may not be re-submitted after being vetoed."]},{"Name":"PreimageByteDeposit","Type":"BalanceOf\u003cT\u003e","Value":"AEB6EPNaAAAAAAAAAAAAAA==","Documentation":[" The amount of balance that must be deposited per byte of preimage stored."]},{"Name":"MaxVotes","Type":"u32","Value":"ZAAAAA==","Documentation":[" The maximum number of votes for an account.",""," Also used to compute weight, an overly big value can"," lead to extrinsic with very big weight: see `delegate` for instance."]}],"Errors":[{"Name":"ValueLow","Documentation":[" Value too low"]},{"Name":"ProposalMissing","Documentation":[" Proposal does not exist"]},{"Name":"BadIndex","Documentation":[" Unknown index"]},{"Name":"AlreadyCanceled","Documentation":[" Cannot cancel the same proposal twice"]},{"Name":"DuplicateProposal","Documentation":[" Proposal already made"]},{"Name":"ProposalBlacklisted","Documentation":[" Proposal still blacklisted"]},{"Name":"NotSimpleMajority","Documentation":[" Next external proposal not simple majority"]},{"Name":"InvalidHash","Documentation":[" Invalid hash"]},{"Name":"NoProposal","Documentation":[" No external proposal"]},{"Name":"AlreadyVetoed","Documentation":[" Identity may not veto a proposal twice"]},{"Name":"NotDelegated","Documentation":[" Not delegated"]},{"Name":"DuplicatePreimage","Documentation":[" Preimage already noted"]},{"Name":"NotImminent","Documentation":[" Not imminent"]},{"Name":"TooEarly","Documentation":[" Too early"]},{"Name":"Imminent","Documentation":[" Imminent"]},{"Name":"PreimageMissing","Documentation":[" Preimage not found"]},{"Name":"ReferendumInvalid","Documentation":[" Vote given for invalid referendum"]},{"Name":"PreimageInvalid","Documentation":[" Invalid preimage"]},{"Name":"NoneWaiting","Documentation":[" No proposals waiting"]},{"Name":"NotLocked","Documentation":[" The target account does not have a lock."]},{"Name":"NotExpired","Documentation":[" The lock on the account to be unlocked has not yet expired."]},{"Name":"NotVoter","Documentation":[" The given account did not vote on the referendum."]},{"Name":"NoPermission","Documentation":[" The actor has no permission to conduct the action."]},{"Name":"AlreadyDelegating","Documentation":[" The account is already delegating."]},{"Name":"InsufficientFunds","Documentation":[" Too high a balance was provided that the account cannot afford."]},{"Name":"NotDelegating","Documentation":[" The account is not currently delegating."]},{"Name":"VotesExist","Documentation":[" The account currently has votes attached to it and the operation cannot succeed until"," these are removed, either through `unvote` or `reap_vote`."]},{"Name":"InstantNotAllowed","Documentation":[" The instant referendum origin is currently disallowed."]},{"Name":"Nonsense","Documentation":[" Delegation to oneself makes no sense."]},{"Name":"WrongUpperBound","Documentation":[" Invalid upper bound."]},{"Name":"MaxVotesReached","Documentation":[" Maximum number of votes reached."]},{"Name":"InvalidWitness","Documentation":[" The provided witness data is wrong."]},{"Name":"TooManyProposals","Documentation":[" Maximum number of proposals reached."]}],"Index":14},{"Name":"CouncilCollective","HasStorage":true,"Storage":{"Prefix":"Instance1Collective","Items":[{"Name":"Proposals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cT::Hash, T::MaxProposals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The hashes of the active proposals."]},{"Name":"ProposalOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"\u003cT as Config\u003cI\u003e\u003e::Proposal","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Actual proposal for a given hash, if it's current."]},{"Name":"Voting","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"Votes\u003cT::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Votes on a given proposal, if it is ongoing."]},{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Proposals so far."]},{"Name":"Members","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current members of the collective. This is stored sorted (just by value)."]},{"Name":"Prime","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The prime member that helps determine the default vote behavior in case of absentations."]}]},"HasCalls":true,"Calls":[{"Name":"set_members","Args":[{"Name":"new_members","Type":"Vec\u003cT::AccountId\u003e"},{"Name":"prime","Type":"Option\u003cT::AccountId\u003e"},{"Name":"old_count","Type":"MemberCount"}],"Documentation":[" Set the collective's membership.",""," - `new_members`: The new member list. Be nice to the chain and provide it sorted."," - `prime`: The prime member whose vote sets the default."," - `old_count`: The upper bound for the previous number of members in storage."," Used for weight estimation.",""," Requires root origin.",""," NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but"," the weight estimations rely on it to estimate dispatchable weight.",""," # \u003cweight\u003e"," ## Weight"," - `O(MP + N)` where:"," - `M` old-members-count (code- and governance-bounded)"," - `N` new-members-count (code- and governance-bounded)"," - `P` proposals-count (code-bounded)"," - DB:"," - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members"," - 1 storage read (codec `O(P)`) for reading the proposals"," - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal"," - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one"," # \u003c/weight\u003e"]},{"Name":"execute","Args":[{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Dispatch a proposal from a member using the `Member` origin.",""," Origin must be a member of the collective.",""," # \u003cweight\u003e"," ## Weight"," - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`"," - DB: 1 read (codec `O(M)`) + DB access of `proposal`"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"propose","Args":[{"Name":"threshold","Type":"Compact\u003cMemberCount\u003e"},{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Add a new proposal to either be voted on or executed directly.",""," Requires the sender to be member.",""," `threshold` determines whether `proposal` is executed directly (`threshold \u003c 2`)"," or put up for voting.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1)` or `O(B + M + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - branching is influenced by `threshold` where:"," - `P1` is proposal execution complexity (`threshold \u003c 2`)"," - `P2` is proposals-count (code-bounded) (`threshold \u003e= 2`)"," - DB:"," - 1 storage read `is_member` (codec `O(M)`)"," - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)"," - DB accesses influenced by `threshold`:"," - EITHER storage accesses done by `proposal` (`threshold \u003c 2`)"," - OR proposal insertion (`threshold \u003c= 2`)"," - 1 storage mutation `Proposals` (codec `O(P2)`)"," - 1 storage mutation `ProposalCount` (codec `O(1)`)"," - 1 storage write `ProposalOf` (codec `O(B)`)"," - 1 storage write `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"vote","Args":[{"Name":"proposal","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"approve","Type":"bool"}],"Documentation":[" Add an aye or nay vote for the sender to the given proposal.",""," Requires the sender to be a member.",""," Transaction fees will be waived if the member is voting on any particular proposal"," for the first time and the call is successful. Subsequent vote changes will charge a fee."," # \u003cweight\u003e"," ## Weight"," - `O(M)` where `M` is members-count (code- and governance-bounded)"," - DB:"," - 1 storage read `Members` (codec `O(M)`)"," - 1 storage mutation `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"close","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"proposal_weight_bound","Type":"Compact\u003cWeight\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Close a vote that is either approved, disapproved or whose voting period has ended.",""," May be called by any signed account in order to finish voting and close the proposal.",""," If called before the end of the voting period it will only close the vote if it is"," has enough votes to be approved or disapproved.",""," If called after the end of the voting period abstentions are counted as rejections"," unless there is a prime member set and the prime member cast an approval.",""," If the close operation completes successfully with disapproval, the transaction fee will"," be waived. Otherwise execution of the approved operation will be charged to the caller.",""," + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal."," + `length_bound`: The upper bound for the length of the proposal in storage. Checked via"," `storage::read` so it is `size_of::\u003cu32\u003e() == 4` larger than the pure length.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1 + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - `P1` is the complexity of `proposal` preimage."," - `P2` is proposal-count (code-bounded)"," - DB:"," - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)"," - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)"," - any mutations done while executing `proposal` (`P1`)"," - up to 3 events"," # \u003c/weight\u003e"]},{"Name":"disapprove_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Disapprove a proposal, close, and remove it from the system, regardless of its current state.",""," Must be called by the Root origin.",""," Parameters:"," * `proposal_hash`: The hash of the proposal that should be disapproved.",""," # \u003cweight\u003e"," Complexity: O(P) where P is the number of max proposals"," DB Weight:"," * Reads: Proposals"," * Writes: Voting, Proposals, ProposalOf"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["AccountId","ProposalIndex","Hash","MemberCount"],"Documentation":[" A motion (given hash) has been proposed (by given account) with a threshold (given"," `MemberCount`)."," \\[account, proposal_index, proposal_hash, threshold\\]"]},{"Name":"Voted","Args":["AccountId","Hash","bool","MemberCount","MemberCount"],"Documentation":[" A motion (given hash) has been voted on by given account, leaving"," a tally (yes votes and no votes given respectively as `MemberCount`)."," \\[account, proposal_hash, voted, yes, no\\]"]},{"Name":"Approved","Args":["Hash"],"Documentation":[" A motion was approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Disapproved","Args":["Hash"],"Documentation":[" A motion was not approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Executed","Args":["Hash","DispatchResult"],"Documentation":[" A motion was executed; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"MemberExecuted","Args":["Hash","DispatchResult"],"Documentation":[" A single member did some action; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"Closed","Args":["Hash","MemberCount","MemberCount"],"Documentation":[" A proposal was closed because its threshold was reached or after its duration was up."," \\[proposal_hash, yes, no\\]"]}],"Constants":null,"Errors":[{"Name":"NotMember","Documentation":[" Account is not a member"]},{"Name":"DuplicateProposal","Documentation":[" Duplicate proposals not allowed"]},{"Name":"ProposalMissing","Documentation":[" Proposal must exist"]},{"Name":"WrongIndex","Documentation":[" Mismatched index"]},{"Name":"DuplicateVote","Documentation":[" Duplicate vote ignored"]},{"Name":"AlreadyInitialized","Documentation":[" Members are already initialized!"]},{"Name":"TooEarly","Documentation":[" The close call was made too early, before the end of the voting."]},{"Name":"TooManyProposals","Documentation":[" There can only be a maximum of `MaxProposals` active proposals."]},{"Name":"WrongProposalWeight","Documentation":[" The given weight bound for the proposal was too low."]},{"Name":"WrongProposalLength","Documentation":[" The given length bound for the proposal was too low."]}],"Index":15},{"Name":"TechComitteeCollective","HasStorage":true,"Storage":{"Prefix":"Instance2Collective","Items":[{"Name":"Proposals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cT::Hash, T::MaxProposals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The hashes of the active proposals."]},{"Name":"ProposalOf","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"\u003cT as Config\u003cI\u003e\u003e::Proposal","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Actual proposal for a given hash, if it's current."]},{"Name":"Voting","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":true},"Key":"T::Hash","Value":"Votes\u003cT::AccountId, T::BlockNumber\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Votes on a given proposal, if it is ongoing."]},{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Proposals so far."]},{"Name":"Members","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Vec\u003cT::AccountId\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The current members of the collective. This is stored sorted (just by value)."]},{"Name":"Prime","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" The prime member that helps determine the default vote behavior in case of absentations."]}]},"HasCalls":true,"Calls":[{"Name":"set_members","Args":[{"Name":"new_members","Type":"Vec\u003cT::AccountId\u003e"},{"Name":"prime","Type":"Option\u003cT::AccountId\u003e"},{"Name":"old_count","Type":"MemberCount"}],"Documentation":[" Set the collective's membership.",""," - `new_members`: The new member list. Be nice to the chain and provide it sorted."," - `prime`: The prime member whose vote sets the default."," - `old_count`: The upper bound for the previous number of members in storage."," Used for weight estimation.",""," Requires root origin.",""," NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but"," the weight estimations rely on it to estimate dispatchable weight.",""," # \u003cweight\u003e"," ## Weight"," - `O(MP + N)` where:"," - `M` old-members-count (code- and governance-bounded)"," - `N` new-members-count (code- and governance-bounded)"," - `P` proposals-count (code-bounded)"," - DB:"," - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members"," - 1 storage read (codec `O(P)`) for reading the proposals"," - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal"," - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one"," # \u003c/weight\u003e"]},{"Name":"execute","Args":[{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Dispatch a proposal from a member using the `Member` origin.",""," Origin must be a member of the collective.",""," # \u003cweight\u003e"," ## Weight"," - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`"," - DB: 1 read (codec `O(M)`) + DB access of `proposal`"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"propose","Args":[{"Name":"threshold","Type":"Compact\u003cMemberCount\u003e"},{"Name":"proposal","Type":"Box\u003c\u003cT as Config\u003cI\u003e\u003e::Proposal\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Add a new proposal to either be voted on or executed directly.",""," Requires the sender to be member.",""," `threshold` determines whether `proposal` is executed directly (`threshold \u003c 2`)"," or put up for voting.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1)` or `O(B + M + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - branching is influenced by `threshold` where:"," - `P1` is proposal execution complexity (`threshold \u003c 2`)"," - `P2` is proposals-count (code-bounded) (`threshold \u003e= 2`)"," - DB:"," - 1 storage read `is_member` (codec `O(M)`)"," - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)"," - DB accesses influenced by `threshold`:"," - EITHER storage accesses done by `proposal` (`threshold \u003c 2`)"," - OR proposal insertion (`threshold \u003c= 2`)"," - 1 storage mutation `Proposals` (codec `O(P2)`)"," - 1 storage mutation `ProposalCount` (codec `O(1)`)"," - 1 storage write `ProposalOf` (codec `O(B)`)"," - 1 storage write `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"vote","Args":[{"Name":"proposal","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"approve","Type":"bool"}],"Documentation":[" Add an aye or nay vote for the sender to the given proposal.",""," Requires the sender to be a member.",""," Transaction fees will be waived if the member is voting on any particular proposal"," for the first time and the call is successful. Subsequent vote changes will charge a fee."," # \u003cweight\u003e"," ## Weight"," - `O(M)` where `M` is members-count (code- and governance-bounded)"," - DB:"," - 1 storage read `Members` (codec `O(M)`)"," - 1 storage mutation `Voting` (codec `O(M)`)"," - 1 event"," # \u003c/weight\u003e"]},{"Name":"close","Args":[{"Name":"proposal_hash","Type":"T::Hash"},{"Name":"index","Type":"Compact\u003cProposalIndex\u003e"},{"Name":"proposal_weight_bound","Type":"Compact\u003cWeight\u003e"},{"Name":"length_bound","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Close a vote that is either approved, disapproved or whose voting period has ended.",""," May be called by any signed account in order to finish voting and close the proposal.",""," If called before the end of the voting period it will only close the vote if it is"," has enough votes to be approved or disapproved.",""," If called after the end of the voting period abstentions are counted as rejections"," unless there is a prime member set and the prime member cast an approval.",""," If the close operation completes successfully with disapproval, the transaction fee will"," be waived. Otherwise execution of the approved operation will be charged to the caller.",""," + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal."," + `length_bound`: The upper bound for the length of the proposal in storage. Checked via"," `storage::read` so it is `size_of::\u003cu32\u003e() == 4` larger than the pure length.",""," # \u003cweight\u003e"," ## Weight"," - `O(B + M + P1 + P2)` where:"," - `B` is `proposal` size in bytes (length-fee-bounded)"," - `M` is members-count (code- and governance-bounded)"," - `P1` is the complexity of `proposal` preimage."," - `P2` is proposal-count (code-bounded)"," - DB:"," - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)"," - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)"," - any mutations done while executing `proposal` (`P1`)"," - up to 3 events"," # \u003c/weight\u003e"]},{"Name":"disapprove_proposal","Args":[{"Name":"proposal_hash","Type":"T::Hash"}],"Documentation":[" Disapprove a proposal, close, and remove it from the system, regardless of its current state.",""," Must be called by the Root origin.",""," Parameters:"," * `proposal_hash`: The hash of the proposal that should be disapproved.",""," # \u003cweight\u003e"," Complexity: O(P) where P is the number of max proposals"," DB Weight:"," * Reads: Proposals"," * Writes: Voting, Proposals, ProposalOf"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["AccountId","ProposalIndex","Hash","MemberCount"],"Documentation":[" A motion (given hash) has been proposed (by given account) with a threshold (given"," `MemberCount`)."," \\[account, proposal_index, proposal_hash, threshold\\]"]},{"Name":"Voted","Args":["AccountId","Hash","bool","MemberCount","MemberCount"],"Documentation":[" A motion (given hash) has been voted on by given account, leaving"," a tally (yes votes and no votes given respectively as `MemberCount`)."," \\[account, proposal_hash, voted, yes, no\\]"]},{"Name":"Approved","Args":["Hash"],"Documentation":[" A motion was approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Disapproved","Args":["Hash"],"Documentation":[" A motion was not approved by the required threshold."," \\[proposal_hash\\]"]},{"Name":"Executed","Args":["Hash","DispatchResult"],"Documentation":[" A motion was executed; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"MemberExecuted","Args":["Hash","DispatchResult"],"Documentation":[" A single member did some action; result will be `Ok` if it returned without error."," \\[proposal_hash, result\\]"]},{"Name":"Closed","Args":["Hash","MemberCount","MemberCount"],"Documentation":[" A proposal was closed because its threshold was reached or after its duration was up."," \\[proposal_hash, yes, no\\]"]}],"Constants":null,"Errors":[{"Name":"NotMember","Documentation":[" Account is not a member"]},{"Name":"DuplicateProposal","Documentation":[" Duplicate proposals not allowed"]},{"Name":"ProposalMissing","Documentation":[" Proposal must exist"]},{"Name":"WrongIndex","Documentation":[" Mismatched index"]},{"Name":"DuplicateVote","Documentation":[" Duplicate vote ignored"]},{"Name":"AlreadyInitialized","Documentation":[" Members are already initialized!"]},{"Name":"TooEarly","Documentation":[" The close call was made too early, before the end of the voting."]},{"Name":"TooManyProposals","Documentation":[" There can only be a maximum of `MaxProposals` active proposals."]},{"Name":"WrongProposalWeight","Documentation":[" The given weight bound for the proposal was too low."]},{"Name":"WrongProposalLength","Documentation":[" The given length bound for the proposal was too low."]}],"Index":16},{"Name":"Treasury","HasStorage":true,"Storage":{"Prefix":"Treasury","Items":[{"Name":"ProposalCount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"ProposalIndex","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Number of proposals that have been made."]},{"Name":"Proposals","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"ProposalIndex","Value":"Proposal\u003cT::AccountId, BalanceOf\u003cT, I\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Proposals that have been made."]},{"Name":"Approvals","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BoundedVec\u003cProposalIndex, T::MaxApprovals\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Proposal indices that have been approved but not yet awarded."]}]},"HasCalls":true,"Calls":[{"Name":"propose_spend","Args":[{"Name":"value","Type":"Compact\u003cBalanceOf\u003cT, I\u003e\u003e"},{"Name":"beneficiary","Type":"\u003cT::Lookup as StaticLookup\u003e::Source"}],"Documentation":[" Put forward a suggestion for spending. A deposit proportional to the value"," is reserved and slashed if the proposal is rejected. It is returned once the"," proposal is awarded.",""," # \u003cweight\u003e"," - Complexity: O(1)"," - DbReads: `ProposalCount`, `origin account`"," - DbWrites: `ProposalCount`, `Proposals`, `origin account`"," # \u003c/weight\u003e"]},{"Name":"reject_proposal","Args":[{"Name":"proposal_id","Type":"Compact\u003cProposalIndex\u003e"}],"Documentation":[" Reject a proposed spend. The original deposit will be slashed.",""," May only be called from `T::RejectOrigin`.",""," # \u003cweight\u003e"," - Complexity: O(1)"," - DbReads: `Proposals`, `rejected proposer account`"," - DbWrites: `Proposals`, `rejected proposer account`"," # \u003c/weight\u003e"]},{"Name":"approve_proposal","Args":[{"Name":"proposal_id","Type":"Compact\u003cProposalIndex\u003e"}],"Documentation":[" Approve a proposal. At a later time, the proposal will be allocated to the beneficiary"," and the original deposit will be returned.",""," May only be called from `T::ApproveOrigin`.",""," # \u003cweight\u003e"," - Complexity: O(1)."," - DbReads: `Proposals`, `Approvals`"," - DbWrite: `Approvals`"," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"Proposed","Args":["ProposalIndex"],"Documentation":[" New proposal. \\[proposal_index\\]"]},{"Name":"Spending","Args":["Balance"],"Documentation":[" We have ended a spend period and will now allocate funds. \\[budget_remaining\\]"]},{"Name":"Awarded","Args":["ProposalIndex","Balance","AccountId"],"Documentation":[" Some funds have been allocated. \\[proposal_index, award, beneficiary\\]"]},{"Name":"Rejected","Args":["ProposalIndex","Balance"],"Documentation":[" A proposal was rejected; funds were slashed. \\[proposal_index, slashed\\]"]},{"Name":"Burnt","Args":["Balance"],"Documentation":[" Some of our funds have been burnt. \\[burn\\]"]},{"Name":"Rollover","Args":["Balance"],"Documentation":[" Spending has finished; this is the amount that rolls over until next spend."," \\[budget_remaining\\]"]},{"Name":"Deposit","Args":["Balance"],"Documentation":[" Some funds have been deposited. \\[deposit\\]"]}],"Constants":[{"Name":"ProposalBond","Type":"Permill","Value":"UMMAAA==","Documentation":[" Fraction of a proposal's value that should be bonded in order to place the proposal."," An accepted proposal gets these back. A rejected proposal does not."]},{"Name":"ProposalBondMinimum","Type":"BalanceOf\u003cT, I\u003e","Value":"AABkp7O24A0AAAAAAAAAAA==","Documentation":[" Minimum amount of funds that should be placed in a deposit for making a proposal."]},{"Name":"SpendPeriod","Type":"T::BlockNumber","Value":"wKgAAA==","Documentation":[" Period between successive spends."]},{"Name":"Burn","Type":"Permill","Value":"AAAAAA==","Documentation":[" Percentage of spare funds (if any) that are burnt per spend period."]},{"Name":"PalletId","Type":"PalletId","Value":"cGMvdHJzcnk=","Documentation":[" The treasury's module id, used for deriving its sovereign account ID."]}],"Errors":[{"Name":"InsufficientProposersBalance","Documentation":[" Proposer's balance is too low."]},{"Name":"InvalidIndex","Documentation":[" No proposal or bounty at that index."]},{"Name":"TooManyApprovals","Documentation":[" Too many approvals in the queue."]}],"Index":17},{"Name":"AuthorInherent","HasStorage":true,"Storage":{"Prefix":"AuthorInherent","Items":[{"Name":"Author","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":true,"AsType":"T::AccountId","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" Author of current block."]}]},"HasCalls":true,"Calls":[{"Name":"set_author","Args":[{"Name":"author","Type":"T::AuthorId"}],"Documentation":[" Inherent to set the author of a block"]}],"HasEvents":false,"Events":null,"Constants":null,"Errors":[{"Name":"AuthorAlreadySet","Documentation":[" Author already set in block."]},{"Name":"NoAccountId","Documentation":[" No AccountId was found to be associated with this author"]},{"Name":"CannotBeAuthor","Documentation":[" The author in the inherent is not an eligible author."]}],"Index":18},{"Name":"AuthorFilter","HasStorage":true,"Storage":{"Prefix":"AuthorFilter","Items":[{"Name":"EligibleRatio","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"Percent","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"Mg==","Documentation":[" The percentage of active authors that will be eligible at each height."]}]},"HasCalls":true,"Calls":[{"Name":"set_eligible","Args":[{"Name":"new","Type":"Percent"}],"Documentation":[" Update the eligible ratio. Intended to be called by governance."]}],"HasEvents":true,"Events":[{"Name":"EligibleUpdated","Args":["Percent"],"Documentation":[" The amount of eligible authors for the filter to select has been changed."]}],"Constants":null,"Errors":null,"Index":19},{"Name":"CrowdloanRewards","HasStorage":true,"Storage":{"Prefix":"CrowdloanRewards","Items":[{"Name":"AccountsPayable","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"\u003cT as frame_system::Config\u003e::AccountId","Value":"RewardInfo\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"ClaimedRelayChainIds","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::RelayChainAccountId","Value":"()","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"UnassociatedContributions","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":true,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"T::RelayChainAccountId","Value":"RewardInfo\u003cT\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"Initialized","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"bool","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":null},{"Name":"InitRelayBlock","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"relay_chain::BlockNumber","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Relay block height at the initialization of the pallet"]},{"Name":"InitializedRewardAmount","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"BalanceOf\u003cT\u003e","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAA==","Documentation":[" Total initialized amount so far. We store this to make pallet funds == contributors reward"," check easier and more efficient"]},{"Name":"TotalContributors","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":true,"AsType":"u32","IsMap":false,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key":"","Value":"","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAA==","Documentation":[" Total number of contributors to aid hinting benchmarking"]}]},"HasCalls":true,"Calls":[{"Name":"associate_native_identity","Args":[{"Name":"reward_account","Type":"\u003cT as frame_system::Config\u003e::AccountId"},{"Name":"relay_account","Type":"T::RelayChainAccountId"},{"Name":"proof","Type":"MultiSignature"}],"Documentation":[" Associate a native rewards_destination identity with a crowdloan contribution.",""," This is an unsigned call because the caller may not have any funds to pay fees with."," This is inspired by Polkadot's claims pallet:"," https://github.com/paritytech/polkadot/blob/master/runtime/common/src/claims.rs",""," The contributor needs to issue an additional addmemo transaction if it wants to receive"," the reward in a parachain native account. For the moment I will leave this function here"," just in case the contributor forgot to add such a memo field. Whenever we can read the"," state of the relay chain, we should first check whether that memo field exists in the"," contribution"]},{"Name":"claim","Args":null,"Documentation":[" Collect whatever portion of your reward are currently vested. The first time each"," contributor calls this function pays no fees"]},{"Name":"update_reward_address","Args":[{"Name":"new_reward_account","Type":"\u003cT as frame_system::Config\u003e::AccountId"}],"Documentation":[" Update reward address. To determine whether its something we want to keep"]},{"Name":"initialize_reward_vec","Args":[{"Name":"rewards","Type":"Vec\u003c\n(T::RelayChainAccountId, Option\u003c\u003cT as frame_system::Config\u003e::\n AccountId\u003e, BalanceOf\u003cT\u003e)\u003e"},{"Name":"index","Type":"u32"},{"Name":"limit","Type":"u32"}],"Documentation":[" Initialize the reward distribution storage. It shortcuts whenever an error is found"," We can change this behavior to check this beforehand if we prefer"," We only set this to \"initialized\" once we receive index==limit"," This is expected to be executed with batch_all, that atomically initializes contributions"," TODO Should we perform sanity checks here? (i.e., min contribution)"]}],"HasEvents":true,"Events":[{"Name":"InitialPaymentMade","Args":["\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" The initial payment of InitializationPayment % was paid"]},{"Name":"NativeIdentityAssociated","Args":["T::RelayChainAccountId","\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" Someone has proven they made a contribution and associated a native identity with it."," Data is the relay account, native account and the total amount of _rewards_ that will be paid"]},{"Name":"RewardsPaid","Args":["\u003cT as frame_system::Config\u003e::AccountId","BalanceOf\u003cT\u003e"],"Documentation":[" A contributor has claimed some rewards."," Data is the account getting paid and the amount of rewards paid."]},{"Name":"RewardAddressUpdated","Args":["\u003cT as frame_system::Config\u003e::AccountId","\u003cT as frame_system::Config\u003e::AccountId"],"Documentation":[" A contributor has updated the reward address."]},{"Name":"InitializedAlreadyInitializedAccount","Args":["T::RelayChainAccountId","Option\u003c\u003cT as frame_system::Config\u003e::AccountId\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" When initializing the reward vec an already initialized account was found"]},{"Name":"InitializedAccountWithNotEnoughContribution","Args":["T::RelayChainAccountId","Option\u003c\u003cT as frame_system::Config\u003e::AccountId\u003e","BalanceOf\u003cT\u003e"],"Documentation":[" When initializing the reward vec an already initialized account was found"]}],"Constants":[{"Name":"InitializationPayment","Type":"Perbill","Value":"AKPhEQ==","Documentation":[" Percentage to be payed at initialization"]},{"Name":"VestingPeriod","Type":"relay_chain::BlockNumber","Value":"gBMDAA==","Documentation":[" The total vesting period. Ideally this should be less or equal"," than the lease period to ensure contributors vest the tokens during the lease"]}],"Errors":[{"Name":"AlreadyAssociated","Documentation":[" User trying to associate a native identity with a relay chain identity for posterior"," reward claiming provided an already associated relay chain identity"]},{"Name":"BatchBeyondFundPot","Documentation":[" Trying to introduce a batch that goes beyond the limits of the funds"]},{"Name":"FirstClaimAlreadyDone","Documentation":[" First claim already done"]},{"Name":"RewardNotHighEnough","Documentation":[" The contribution is not high enough to be eligible for rewards"]},{"Name":"InvalidClaimSignature","Documentation":[" User trying to associate a native identity with a relay chain identity for posterior"," reward claiming provided a wrong signature"]},{"Name":"InvalidFreeClaimSignature","Documentation":[" User trying to claim the first free reward provided the wrong signature"]},{"Name":"NoAssociatedClaim","Documentation":[" User trying to claim an award did not have an claim associated with it. This may mean"," they did not contribute to the crowdloan, or they have not yet associated a native id"," with their contribution"]},{"Name":"RewardsAlreadyClaimed","Documentation":[" User trying to claim rewards has already claimed all rewards associated with its"," identity and contribution"]},{"Name":"RewardVecAlreadyInitialized","Documentation":[" Reward vec has already been initialized"]},{"Name":"RewardVecNotFullyInitializedYet","Documentation":[" Reward vec has not yet been fully initialized"]},{"Name":"RewardsDoNotMatchFund","Documentation":[" Reward vec has already been initialized"]}],"Index":20},{"Name":"AuthorMapping","HasStorage":true,"Storage":{"Prefix":"AuthorMapping","Items":[{"Name":"MappingWithDeposit","Modifier":{"IsOptional":true,"IsDefault":false,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AuthorId","Value":"RegistrationInfo\u003cT::AccountId, BalanceOf\u003cT\u003e\u003e","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AA==","Documentation":[" We maintain a mapping from the AuthorIds used in the consensus layer"," to the AccountIds runtime (including this staking pallet)."]}]},"HasCalls":true,"Calls":[{"Name":"add_association","Args":[{"Name":"author_id","Type":"T::AuthorId"}],"Documentation":[" Register your AuthorId onchain so blocks you author are associated with your account.",""," Users who have been (or will soon be) elected active collators in staking,"," should submit this extrinsic to have their blocks accepted and earn rewards."]},{"Name":"update_association","Args":[{"Name":"old_author_id","Type":"T::AuthorId"},{"Name":"new_author_id","Type":"T::AuthorId"}],"Documentation":[" Change your AuthorId.",""," This is useful for normal key rotation or for when switching from one physical collator"," machine to another. No new security deposit is required."]},{"Name":"clear_association","Args":[{"Name":"author_id","Type":"T::AuthorId"}],"Documentation":[" Clear your AuthorId.",""," This is useful when you are no longer an author and would like to re-claim your security"," deposit."]}],"HasEvents":true,"Events":[{"Name":"AuthorRegistered","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been registered and mapped to an AccountId."]},{"Name":"AuthorDeRegistered","Args":["T::AuthorId"],"Documentation":[" An AuthorId has been de-registered, and its AccountId mapping removed."]},{"Name":"AuthorRotated","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been registered, replacing a previous registration and its mapping."]},{"Name":"DefunctAuthorBusted","Args":["T::AuthorId","T::AccountId"],"Documentation":[" An AuthorId has been forcibly deregistered after not being rotated or cleaned up."," The reporteing account has been rewarded accordingly."]}],"Constants":null,"Errors":[{"Name":"AssociationNotFound","Documentation":[" The association can't be cleared because it is not found."]},{"Name":"NotYourAssociation","Documentation":[" The association can't be cleared because it belongs to another account."]},{"Name":"CannotSetAuthor","Documentation":[" This account cannot set an author because it fails the preliminary check"]},{"Name":"CannotAffordSecurityDeposit","Documentation":[" This account cannot set an author because it cannon afford the security deposit"]},{"Name":"AlreadyAssociated","Documentation":[" The AuthorId in question is already associated and cannot be overwritten"]}],"Index":21},{"Name":"Proxy","HasStorage":true,"Storage":{"Prefix":"Proxy","Items":[{"Name":"Proxies","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"(BoundedVec\u003cProxyDefinition\u003cT::AccountId, T::ProxyType, T::\n BlockNumber\u003e, T::MaxProxies,\u003e, BalanceOf\u003cT\u003e)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The set of account proxies. Maps the account which has delegated to the accounts"," which are being delegated to, together with the amount held on deposit."]},{"Name":"Announcements","Modifier":{"IsOptional":false,"IsDefault":true,"IsRequired":false},"Type":{"IsType":false,"AsType":"","IsMap":true,"AsMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":true,"IsIdentity":false},"Key":"T::AccountId","Value":"(BoundedVec\u003cAnnouncement\u003cT::AccountId, CallHashOf\u003cT\u003e, T::\n BlockNumber\u003e, T::MaxPending,\u003e, BalanceOf\u003cT\u003e,)","Linked":false},"IsDoubleMap":false,"AsDoubleMap":{"Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false},"Key1":"","Key2":"","Value":"","Key2Hasher":{"IsBlake2_128":false,"IsBlake2_256":false,"IsBlake2_128Concat":false,"IsTwox128":false,"IsTwox256":false,"IsTwox64Concat":false,"IsIdentity":false}},"IsNMap":false,"AsNMap":{"Keys":null,"Hashers":null,"Value":""}},"Fallback":"AAAAAAAAAAAAAAAAAAAAAAA=","Documentation":[" The announcements made by the proxy (key)."]}]},"HasCalls":true,"Calls":[{"Name":"proxy","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"force_proxy_type","Type":"Option\u003cT::ProxyType\u003e"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Dispatch the given `call` from an account that the sender is authorised for through"," `add_proxy`.",""," Removes any corresponding announcement(s).",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call."," - `call`: The call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"add_proxy","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Register a proxy account for the sender that is able to make calls on its behalf.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `proxy`: The account that the `caller` would like to make a proxy."," - `proxy_type`: The permissions allowed for this proxy account."," - `delay`: The announcement period required of the initial proxy. Will generally be"," zero.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"remove_proxy","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"}],"Documentation":[" Unregister a proxy account for the sender.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `proxy`: The account that the `caller` would like to remove as a proxy."," - `proxy_type`: The permissions currently enabled for the removed proxy account.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"remove_proxies","Args":null,"Documentation":[" Unregister all proxy accounts for the sender.",""," The dispatch origin for this call must be _Signed_.",""," WARNING: This may be called on accounts created by `anonymous`, however if done, then"," the unreserved fees will be inaccessible. **All access to this account will be lost.**",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"anonymous","Args":[{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"delay","Type":"T::BlockNumber"},{"Name":"index","Type":"u16"}],"Documentation":[" Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and"," initialize it with a proxy of `proxy_type` for `origin` sender.",""," Requires a `Signed` origin.",""," - `proxy_type`: The type of the proxy that the sender will be registered as over the"," new account. This will almost always be the most permissive `ProxyType` possible to"," allow for maximum flexibility."," - `index`: A disambiguation index, in case this is called multiple times in the same"," transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just"," want to use `0`."," - `delay`: The announcement period required of the initial proxy. Will generally be"," zero.",""," Fails with `Duplicate` if this has already been called in this transaction, from the"," same sender, with the same parameters.",""," Fails if there are insufficient funds to pay for deposit.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"," TODO: Might be over counting 1 read"]},{"Name":"kill_anonymous","Args":[{"Name":"spawner","Type":"T::AccountId"},{"Name":"proxy_type","Type":"T::ProxyType"},{"Name":"index","Type":"u16"},{"Name":"height","Type":"Compact\u003cT::BlockNumber\u003e"},{"Name":"ext_index","Type":"Compact\u003cu32\u003e"}],"Documentation":[" Removes a previously spawned anonymous proxy.",""," WARNING: **All access to this account will be lost.** Any funds held in it will be"," inaccessible.",""," Requires a `Signed` origin, and the sender account must have been created by a call to"," `anonymous` with corresponding parameters.",""," - `spawner`: The account that originally called `anonymous` to create this account."," - `index`: The disambiguation index originally passed to `anonymous`. Probably `0`."," - `proxy_type`: The proxy type originally passed to `anonymous`."," - `height`: The height of the chain when the call to `anonymous` was processed."," - `ext_index`: The extrinsic index in which the call to `anonymous` was processed.",""," Fails with `NoPermission` in case the caller is not a previously created anonymous"," account whose `anonymous` call has corresponding parameters.",""," # \u003cweight\u003e"," Weight is a function of the number of proxies the user has (P)."," # \u003c/weight\u003e"]},{"Name":"announce","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Publish the hash of a proxy-call that will be made in the future.",""," This must be called some number of blocks before the corresponding `proxy` is attempted"," if the delay associated with the proxy relationship is greater than zero.",""," No more than `MaxPending` announcements may be made at any one time.",""," This will take a deposit of `AnnouncementDepositFactor` as well as"," `AnnouncementDepositBase` if there are no other pending announcements.",""," The dispatch origin for this call must be _Signed_ and a proxy of `real`.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `call_hash`: The hash of the call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"remove_announcement","Args":[{"Name":"real","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Remove a given announcement.",""," May be called by a proxy account to remove a call they previously announced and return"," the deposit.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `call_hash`: The hash of the call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"reject_announcement","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"call_hash","Type":"CallHashOf\u003cT\u003e"}],"Documentation":[" Remove the given announcement of a delegate.",""," May be called by a target (proxied) account to remove a call that one of their delegates"," (`delegate`) has announced they want to execute. The deposit is returned.",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `delegate`: The account that previously announced the call."," - `call_hash`: The hash of the call to be made.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]},{"Name":"proxy_announced","Args":[{"Name":"delegate","Type":"T::AccountId"},{"Name":"real","Type":"T::AccountId"},{"Name":"force_proxy_type","Type":"Option\u003cT::ProxyType\u003e"},{"Name":"call","Type":"Box\u003c\u003cT as Config\u003e::Call\u003e"}],"Documentation":[" Dispatch the given `call` from an account that the sender is authorized for through"," `add_proxy`.",""," Removes any corresponding announcement(s).",""," The dispatch origin for this call must be _Signed_.",""," Parameters:"," - `real`: The account that the proxy will make a call on behalf of."," - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call."," - `call`: The call to be made by the `real` account.",""," # \u003cweight\u003e"," Weight is a function of:"," - A: the number of announcements made."," - P: the number of proxies the user has."," # \u003c/weight\u003e"]}],"HasEvents":true,"Events":[{"Name":"ProxyExecuted","Args":["DispatchResult"],"Documentation":[" A proxy was executed correctly, with the given \\[result\\]."]},{"Name":"AnonymousCreated","Args":["AccountId","AccountId","ProxyType","u16"],"Documentation":[" Anonymous account has been created by new proxy with given"," disambiguation index and proxy type. \\[anonymous, who, proxy_type, disambiguation_index\\]"]},{"Name":"Announced","Args":["AccountId","AccountId","Hash"],"Documentation":[" An announcement was placed to make a call in the future. \\[real, proxy, call_hash\\]"]}],"Constants":[{"Name":"ProxyDepositBase","Type":"BalanceOf\u003cT\u003e","Value":"AAA2K0yO4w0AAAAAAAAAAA==","Documentation":[" The base amount of currency needed to reserve for creating a proxy.",""," This is held for an additional storage item whose value size is"," `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes."]},{"Name":"ProxyDepositFactor","Type":"BalanceOf\u003cT\u003e","Value":"AEAHWvB1BwAAAAAAAAAAAA==","Documentation":[" The amount of currency needed per proxy added.",""," This is held for adding 32 bytes plus an instance of `ProxyType` more into a pre-existing"," storage value. Thus, when configuring `ProxyDepositFactor` one should take into account"," `32 + proxy_type.encode().len()` bytes of data."]},{"Name":"MaxProxies","Type":"u32","Value":"IAAAAA==","Documentation":[" The maximum amount of proxies allowed for a single account."]},{"Name":"MaxPending","Type":"u32","Value":"IAAAAA==","Documentation":[" The maximum amount of time-delayed announcements that are allowed to be pending."]},{"Name":"AnnouncementDepositBase","Type":"BalanceOf\u003cT\u003e","Value":"AAA2K0yO4w0AAAAAAAAAAA==","Documentation":[" The base amount of currency needed to reserve for creating an announcement.",""," This is held when a new storage item holding a `Balance` is created (typically 16 bytes)."]},{"Name":"AnnouncementDepositFactor","Type":"BalanceOf\u003cT\u003e","Value":"AAC+mivlEwAAAAAAAAAAAA==","Documentation":[" The amount of currency needed per announcement made.",""," This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)"," into a pre-existing storage value."]}],"Errors":[{"Name":"TooMany","Documentation":[" There are too many proxies registered or too many announcements pending."]},{"Name":"NotFound","Documentation":[" Proxy registration not found."]},{"Name":"NotProxy","Documentation":[" Sender is not a proxy of the account to be proxied."]},{"Name":"Unproxyable","Documentation":[" A call which is incompatible with the proxy type's filter was attempted."]},{"Name":"Duplicate","Documentation":[" Account is already a proxy."]},{"Name":"NoPermission","Documentation":[" Call may not be made by proxy because it may escalate its privileges."]},{"Name":"Unannounced","Documentation":[" Announcement, if made at all, was made too recently."]},{"Name":"NoSelfProxy","Documentation":[" Cannot add self as proxy."]}],"Index":22}],"Extrinsic":{"Version":4,"SignedExtensions":["CheckSpecVersion","CheckTxVersion","CheckGenesis","CheckMortality","CheckNonce","CheckWeight","ChargeTransactionPayment"]}}},"StorageKey":"Jqo5TupWMOB8SK4MlVjO94DUHl4WBWdlvIRhhRByydc=","SystemEventsStorageRaw":"QAAAAAAAAABQlaIJAAAAAAIAAAABAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAAAAAAIAAAADAAAACgBksi0rjDyjEaDC3jS/eZ+BAciTYgwBCfxvVc9AaJ8C+6rXr3/nu6yKPSGGYAr8fT4QysYCcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXxAAAAAAAAAAAAAAAAb/onB+of6ByNr6dV509JYTM7dfqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGDsBhYAAAMAAAAKAGSyLSuMPKMRoMLeNL95n4EByJNiEJLphCP4raxuZNBgjlGf0c77hhSYOFxt7nDVj8km3caMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB1zQ1jYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8QAAAAAAAAAAAAAAAG/6JwfqH+gcja+nVedPSWEzO3X6AAAAAwAAAAoAZLItK4w8oxGgwt40v3mfgQHIk2IMBVmIT9OkYNswc7f8iWzHeYbxbjeCEN7UMYYXW/ZG/F8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHXNDWNgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXxgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg7AYWAAADAAAACgBksi0rjDyjEaDC3jS/eZ+BAciTYgj+Jcc+O5CJ+sN9VcTH78um8ErwTOvS/E1tfbsH4eUjTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArXjrxaodyNgAAAAAwAAAAsAb/onB+of6ByNr6dV509JYTM7dfoAAAAAAAAAAAAAAAAAAAAAAAAAAO8Oa/qdi7BV+U6HPx31UbQt8R0ssFOBEnmhEB6cA/z5AAAAAAMAAAAAAOiAY6sAAAAAAAAAAAQAAAADAk4AeEI6Oe+8H4tRBFQKwmUKdWV3QrFaEcwpW+b5eqRRjoULBktk+xEQRP1sN0nHigAAAAAAAAAAAAAEAAAACwBOAHhCOjnvvB+LUQRUCsJlCnVldwAAAAAAAAAAAAAAAAAAAAAAAAAAz6G0G6MCesO72pr2aFRA6NifEsespZh7M06R27qkqpMAAAAABAAAAAAAQN1KHwAAAAAAAAAABQAAAAsA8C2ASxmwZlaQ9rMSaRsuuPgM07gAAAAAAAAAAAAAAAAAAAAAAAAAAFbLHgdXfXN0Rj7IhwnOlF8oCwcrifS8jA5wq/S5smfAAAAAAAUAAAAAAEDdSh8AAAAAAAAAAAYAAAALAF/c8iHph5ZrKWcUlT+2lSr8JK7+AAAAAAAAAAAAAAAAAAAAAAAAAACQ2ewDL31xIfm8KyqLmmoG/X9DTL67lj1s/LLkIG0vQwAAAAAGAAAAAABA3UofAAAAAAAAAA==","SystemEventsReadProof":{"at":"0xad8341389f66170206e2797ca56a837b3bdfc55387923e43f0d8ca399eba30fa","proof":["0x804100807b21c854aa6cfd830a2e27133f6f4542940bad11499e2ac3d6f44c7128f94ed080f25c2ac60dfb521b77cb4c28e47dc03ef7aa68795ce6f135cb642ee025d15a7d","0x8081048018e52dc7e681a4678d7e435ad953b52200b751f1803edac5f521374bee1e28ac545e8d434d6125b40443fe11fd292d13a410030000008031515cdb3243f92a2b52bde342ed8f0973781de99623482ea8cdf5af519ff96f","0x5ed41e5e16056765bc8461851072c9d7fd1040000000000000005095a2090000000002000000010000000000000000000000000002000000020000000000000000000000000002000000030000000a0064b22d2b8c3ca311a0c2de34bf799f8101c893620c0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac6027100000000000000000000000000000000000000000000000000000000000005f10000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa800000000000000000000000000000000000000000000000000000000060ec06160000030000000a0064b22d2b8c3ca311a0c2de34bf799f8101c893621092e98423f8adac6e64d0608e519fd1cefb861498385c6dee70d58fc926ddc68c000000000000000000000000000000000000000000000000000000075cd0d63600000000000000000000000000000000000000000000000000000000000005f10000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa000000030000000a0064b22d2b8c3ca311a0c2de34bf799f8101c893620c0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f000000000000000000000000000000000000000000000000000000075cd0d63600000000000000000000000000000000000000000000000000000000000005f1800000000000000000000000000000000000000000000000000000000060ec06160000030000000a0064b22d2b8c3ca311a0c2de34bf799f8101c8936208fe25c73e3b9089fac37d55c4c7efcba6f04af04cebd2fc4d6d7dbb07e1e5234f000000000000000000000000000000000000000000000002b5e3af16a8772360000000030000000b006ffa2707ea1fe81c8dafa755e74f4961333b75fa0000000000000000000000000000000000000000ef0e6bfa9d8bb055f94e873f1df551b42df11d2cb053811279a1101e9c03fcf900000000030000000000e88063ab00000000000000000400000003024e0078423a39efbc1f8b5104540ac2650a75657742b15a11cc295be6f97aa4518e850b064b64fb111044fd6c3749c78a00000000000000000000040000000b004e0078423a39efbc1f8b5104540ac2650a7565770000000000000000000000000000000000000000cfa1b41ba3027ac3bbda9af6685440e8d89f12c7aca5987b334e91dbbaa4aa930000000004000000000040dd4a1f0000000000000000050000000b00f02d804b19b0665690f6b312691b2eb8f80cd3b8000000000000000000000000000000000000000056cb1e07577d7374463ec88709ce945f280b072b89f4bc8c0e70abf4b9b267c00000000005000000000040dd4a1f0000000000000000060000000b005fdcf221e987966b296714953fb6952afc24aefe000000000000000000000000000000000000000090d9ec032f7d7121f9bc2b2a8b9a6a06fd7f434cbebb963d6cfcb2e4206d2f430000000006000000000040dd4a1f00000000000000","0x80bfbd80bdaa229522b8271abcfd400ddad1385681e5c9fff10e9999af9540677dc6f29c80ba44bffdd933a2daf678363606618e65ea7fe13887f049cf7e76fdb75aa0d74e80223675677a6c9fab386d5d102a4c974f2b33e6eb28a1bc17294e2e31b7d13b1d80de76f099f9bc7ea189e6fde46251c09ae34f9488ba3849a1dca64b3b75627be780d2c86b9f8b6ead57db246652e90f22446e8f224151a39bb3ff9a4b2bbcb5833480f0b70be5ecce81654192fda59edbbee148648c654114f846bdba58def330d4af80e4b5f4d0bef93a4cc0fbccd472ab3744e2a08aad3996607cc96ea32a2b890fb280ae0f02ee6418ce22145fe81f14e30b0de0c7592ce21b366b148b3e97ec88c59880569dbe0d08a9a921a94c0e905f1981d2b91707a94a2b88e14e1eb49e8920556380dbf1e432ef68c5f18ba9a34458c10065631ba6dddd5469b274d8afe93c17d8fd80426e37689c78740c2e5eea04222dbd82665eeab050b6c37d9e76d4370cb65d43804da156a08cdf592237fd29895b96a2a94b470df85dc91f38bcad60727c72bcca808b9ee97e58ab3fd4dff8876e21ea9799a9cd9ccaa8185c1aca1f5aaf9456c8f0","0x9eaa394eea5630e07c48ae0c9558cef7298f80788584e18f92ee6df69f9bd821a2ad1670a118dd6ce81d2eb874ad7ff0bf14ae80c09e24467da9b0c153f37ca38fe6c3721bc5b6c4ba97fb6945f46d50754591d74c5f0684a022a34dd8bfa2baaf44f172b7100401806e69a01b6652d34059ef1b91410b20a0d8919142247521cfcc534554947fea948055b13f508adac5cce2889cdd8bf735434ac3a31fac37cd93d675c8239f83faf580c9e7ebe181b529745d30c6b76ecafc8284ab3a5ead545a94790a2c62a70cde1a80262b88c8176eeed0c3ff61a1dd64ab7012b91e94e0bfa3aea00b461c477a7d2a745f09cce9c888469bb1a0dceaa129672ef82c6d02206d6f6f6e62617365"]}} \ No newline at end of file diff --git a/chain/pra/assets/moonbase_blockinfo_814054.json b/chain/pra/assets/moonbase_blockinfo_814054.json new file mode 100644 index 00000000..d576d542 --- /dev/null +++ b/chain/pra/assets/moonbase_blockinfo_814054.json @@ -0,0 +1 @@ +{"BlockNumber":814054,"Hash":"0x462da730f8ccc68ff7e91ae17d26c194188dd0518dbf69d8d59c8711a1c9afba","Header":{"parentHash":"0x7c5eb3821324777aa80bcb46570cf52dde89b136c009b5e51c52f1efec2d7575","number":"c6be6","stateRoot":"0xa7b5be88098bbb994a595b60e4d33fa1167d9b19ba0ef242e3092d81059af071","extrinsicsRoot":"0xf715dc6d161ee4f970dcc197a0393d3f9692ec6f8b3ef21e71f92971d8514c73","digest":{"logs":["0x046e6d627380a0074c2df0eccdbfad8b29b3a284fa597e7e48ffffc86c842fe75fdef9d48071","0x0466726f6e8904018239fd93348272db330c1e3901b78be4112ab61d19519ad4a2e808d88df0ca4f207ab3cd8d71cee1aa5f90970b7c35cc1c523c02f0932572e4888ce7cc643509a1d48e3dc4382221d839edc8988309d2f51d6c2c24d61854602bfe5ee4aa945848eada3b8729301971b526b555143b78bdc79ab58a5f3469c9e6cb3fd7dc2ed0140a2c98df470b9c67e66b9d1c56a01134f4855c945ca6a5cb0db20088bad2dbb7b0cec4eee2a3b90afcd6bc9de569743251b8a620f65be7e3a8d3e35da5c456067239b6b7115de5d5aa73729cd621c95b7fab968f44128a7bf77d8569589c69e7fb98f13c088231b45bb8e5c0c36b1ededad1b64ea30407c669ac88f03cb263ab29bc13947146e7799e4f9f6f76e3f1dca42facf6b4eb28106085f8fb46bf1eea","0x056e6d62730101d8eb51a193bb725b141d0eee00adea6f9b1d9c83c340c4ce785bac5987059d0e96fc680c8b5af10182be5c589b2ca59dce3fc0f738b248ded6cca1b3ade50c83"]}},"ScaleEncodedHeader":"fF6zghMkd3qoC8tGVwz1Ld6JsTbACbXlHFLx7+wtdXWarzEAp7W+iAmLu5lKWVtg5NM/oRZ9mxm6DvJC4wktgQWa8HH3FdxtFh7k+XDcwZegOT0/lpLsb4s+8h5x+Slx2FFMcwwEbm1ic4CgB0wt8OzNv62LKbOihPpZfn5I///IbIQv51/e+dSAcQRmcm9uiQQBgjn9kzSCctszDB45AbeL5BEqth0ZUZrUougI2I3wyk8gerPNjXHO4apfkJcLfDXMHFI8AvCTJXLkiIznzGQ1CaHUjj3EOCIh2DntyJiDCdL1HWwsJNYYVGAr/l7kqpRYSOraO4cpMBlxtSa1VRQ7eL3HmrWKXzRpyebLP9fcLtAUCiyY30cLnGfma50cVqARNPSFXJRcpqXLDbIAiLrS27ewzsTu4qO5CvzWvJ3laXQyUbimIPZb5+Oo0+NdpcRWBnI5trcRXeXVqnNynNYhyVt/q5aPRBKKe/d9hWlYnGnn+5jxPAiCMbRbuOXAw2se3trRtk6jBAfGaayI8DyyY6spvBOUcUbneZ5Pn2924/HcpC+s9rTrKBBghfj7Rr8e6gVubWJzAQHY61Ghk7tyWxQdDu4Arepvmx2cg8NAxM54W6xZhwWdDpb8aAyLWvEBgr5cWJsspZ3OP8D3OLJI3tbMobOt5QyD","StorageKey":"Jqo5TupWMOB8SK4MlVjO94DUHl4WBWdlvIRhhRByydc=","SystemEventsStorageRaw":"sAAAAAAAAABYX48JAAAAAAIAAAABAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAAAAAAIAAAADAAAAAwKXVfHqem9gWN/AL5W8kyw8JhwfMU4AeEI6Oe+8H4tRBFQKwmUKdWV3AIDlkKGlVEUAAAAAAAAAAAAAAwAAABEGAICcvfn7AgAAAAAAAAAAAAAAAwAAAAsAl1Xx6npvYFjfwC+VvJMsPCYcHzFOAHhCOjnvvB+LUQRUCsJlCnVld3qzzY1xzuGqX5CXC3w1zBxSPALwkyVy5IiM58xkNQmhAAAAAAMAAAAAAEDdSh8AAAAAAAAAAAQAAAAAA/eYMxsdIADUg5d1k+5uRH5HYMY/AAAEAAAAAwD3mDMbHSAA1IOXdZPubkR+R2DGPwAAZKeztuANAAAAAAAAAAAAAAQAAAADAtB9B4NzvmDdEONfNSVZ7x8lAp2v95gzGx0gANSDl3WT7m5Efkdgxj8AAGSns7bgDQAAAAAAAAAAAAAEAAAAEQYA4J+QY4QHAAAAAAAAAAAAAAAEAAAACgANqqsHsHj9nMjrx/mArv0OKrouxgQ3vjU/IWz34zY5EB/WEMVC5qDAEJFz+hwdiwTTTtt8G4EHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADpidHA6Ly8weDQyLmljb24vY3g4NzFhZmEzYmQwYWRmYTEzOWY2OGFjODg4MTlkYTUzYWQ0NGVmMDc3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPT48rg6YnRwOi8vMHg1MDcucHJhLzB4MERhQUFiMDdiMDc4ZmQ5Y0M4ZWJjN2Y5ODBhZUZkMGUyYUJhMkVjNrg6YnRwOi8vMHg0Mi5pY29uL2N4ODcxYWZhM2JkMGFkZmExMzlmNjhhYzg4ODE5ZGE1M2FkNDRlZjA3N4puYXRpdmVjb2luAbhs+GoAuGf4ZaoweEQwN2QwNzgzNzNiRTYwZGQxMGUzNWYzNTI1NTllZjFmMjUwMjlEQWaqY3g4NzFhZmEzYmQwYWRmYTEzOWY2OGFjODg4MTlkYTUzYWQ0NGVmMDc3zs2DREVWiA29L8E3m17gAAAAAAAAAAAAAAAAAAAEAAAACgDAa+MyL3EYsUhDhhtCRphkaHXOZAhQ0iNzu4TtH57rWByRPm1F2RjAX4sdkPC+Fo8GpOaZSgAAAAAAAAAAAAAAANB9B4NzvmDdEONfNSVZ7x8lAp2vgQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOmJ0cDovLzB4NDIuaWNvbi9jeDg3MWFmYTNiZDBhZGZhMTM5ZjY4YWM4ODgxOWRhNTNhZDQ0ZWYwNzcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANvS/BN5te4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjhvJvyKEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANERVYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAsA0H0Hg3O+YN0Q4181JVnvHyUCna/3mDMbHSAA1IOXdZPubkR+R2DGP9SOPcQ4IiHYOe3ImIMJ0vUdbCwk1hhUYCv+XuSqlFhIAAEAAAQAAAAAAODNOhQDAAAAAAAAAAUAAAARBgBIvBp5ogEAAAAAAAAAAAAAAAUAAAAKAM+IqNf8Gmh4lfyP+q1WfzA5JrCUDAEJ/G9Vz0BonwL7qtevf+e7rIo9IYZgCvx9PhDKxgJxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz0AAAAAAAAAAAAAAABv+icH6h/oHI2vp1XnT0lhMzt1+oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYUwKtAAABQAAAAoAz4io1/waaHiV/I/6rVZ/MDkmsJQQkumEI/itrG5k0GCOUZ/RzvuGFJg4XG3ucNWPySbdxowAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQCfzLEWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAs9AAAAAAAAAAAAAAAAb/onB+of6ByNr6dV509JYTM7dfoAAAAFAAAACgDPiKjX/BpoeJX8j/qtVn8wOSawlAwFWYhP06Rg2zBzt/yJbMd5hvFuN4IQ3tQxhhdb9kb8XwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAJ/MsRaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACz2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGFMCrQAAAUAAAAKAM+IqNf8Gmh4lfyP+q1WfzA5JrCUCP4lxz47kIn6w31VxMfvy6bwSvBM69L8TW19uwfh5SNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACteOvFqBiC+AAAAAFAAAACwBv+icH6h/oHI2vp1XnT0lhMzt1+s+IqNf8Gmh4lfyP+q1WfzA5JrCU6to7hykwGXG1JrVVFDt4vceatYpfNGnJ5ss/19wu0BQAAAAABQAAAAAAyBRoqwAAAAAAAAAABgAAABEGAHqpo6oGAAAAAAAAAAAAAAAABgAAAAoAVCX11LorfcsnfDacy8tfDnGF+0EEjUaLX4I/jTgyLpxEM9GErfRT/T6qKM7ygAVqoGZJgfCBBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGFMCrEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA05VTwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANEQUkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAsA9Ra9vTKgtMsH0h8muRhbM2UCAn9UJfXUuit9yyd8NpzLy18OcYX7QQosmN9HC5xn5mudHFagETT0hVyUXKalyw2yAIi60tu3AAAAAAYAAAAAAPDOpTEAAAAAAAAAAAcAAAARBgAu7c2oSAIAAAAAAAAAAAAAAAcAAAALAB6esjBXrfRGd3x/qxpsVly4l3q1DaqrB7B4/ZzI68f5gK79Diq6LsawzsTu4qO5CvzWvJ3laXQyUbimIPZb5+Oo0+NdpcRWBgABAAAHAAAAAAAYnYe1EgAAAAAAAAAIAAAAEQYA1DHuOUgCAAAAAAAAAAAAAAAIAAAACwAenrIwV630Rnd8f6sabFZcuJd6tQ2qqweweP2cyOvH+YCu/Q4qui7Gcjm2txFd5dWqc3Kc1iHJW3+rlo9EEop7932FaVicaecAAQAACAAAAAAAkFX7sRIAAAAAAAAACQAAABEGAPz6DqUYAAAAAAAAAAAAAAAACQAAAAoAgeQrru4J3iiA1KiELtsZEXVaxI0ETewE51DKEVN8q82KnqsGSU3gjaNzW8iHHNQSUOGQvAQBAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHDwbEXAr7VPHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC/zhlknkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN8+2dXESmQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABaxfIyNrYxmzAAAJAAAACgDz/F+Hfp3ZAnfAcv89B9B2M3+inQwsrs0X0C9W+ol3BdzHQNotI3w3P3Bob04Nm9O/BADqegAAAAAAAAAAAAAAAIHkK67uCd4ogNSohC7bGRF1WsSNAAAAAAAAAAAAAAAARUUara0D7ZS5T87GA68R8ujxbBsBAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAKANUORji19YpmpfT/gfCS2yNX7Mb7DN3yUq0b4sibacKwaPw3jaqVK6fxY8ShFij1Wk31I7PvAAAAAAAAAAAAAAAARUUara0D7ZS5T87GA68R8ujxbBsAAAAAAAAAAAAAAACB5Cuu7gneKIDUqIQu2xkRdVrEjYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI4byb8EAAAAACQAAAAoAgeQrru4J3iiA1KiELtsZEXVaxI0ETCCbX8itUHWPE+LhCIulalYN/2kKHG/vJjlPTAOCHE+BAQAAAAAAAAAAAAAAAEVFGq2tA+2UuU/OxgOvEfLo8WwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACOG8m/BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI3wbUwZ3hQAACQAAAAoAgeQrru4J3iiA1KiELtsZEXVaxI0M3fJSrRviyJtpwrBo/DeNqpUrp/FjxKEWKPVaTfUjs+8AAAAAAAAAAAAAAACB5Cuu7gneKIDUqIQu2xkRdVrEjQAAAAAAAAAAAAAAAEVFGq2tA+2UuU/OxgOvEfLo8WwbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjfBtTBneFAAAJAAAACwBFRRqtrQPtlLlPzsYDrxHy6PFsG4HkK67uCd4ogNSohC7bGRF1WsSN+5jxPAiCMbRbuOXAw2se3trRtk6jBAfGaayI8DyyY6sAAQAACQAAAAAAsM7jyQAAAAAAAAAACgAAABEGAMiOGAEIAAAAAAAAAAAAAAAACgAAAAoA1Q5GOLX1imal9P+B8JLbI1fsxvsMjFvh5evsfVvRT3FCfR6E890DFMD3sikeWyAKyMfDuSUAAAAAAAAAAAAAAABL2ecZEKVSY3X0FyMiZLbc8cIeSQAAAAAAAAAAAAAAAIHkK67uCd4ogNSohC7bGRF1WsSNgAAAAAAAAAAAAAAAAAAAAAAAAATuLW1BW4Ws74EAAAAAAAAKAAAACwBL2ecZEKVSY3X0FyMiZLbc8cIeSdUORji19YpmpfT/gfCS2yNX7Mb7KbwTlHFG53meT59vduPx3KQvrPa06ygQYIX4+0a/HuoAAQAACgAAAAAAoDGSQQAAAAAAAAA=","SystemEventsReadProof":{"at":"0x462da730f8ccc68ff7e91ae17d26c194188dd0518dbf69d8d59c8711a1c9afba","proof":["0x80fffd800d67dcfa1e31cefafb35ca4c38cb7e1cb7d3a617fbf18ce007b7903a10368f5980cef19c9297e3db7ed185f928343333f9358485c7370d57a19dbaa80860db55e380d4c4d199d79c1190046c48bfc88c72fac9732919566d2566dcd010e0aa2315c58009b963e6b51d0fc746b4d0ab91672cef6ee4992d928f83dcf2a4025cb4def42c80e1be36528d6a79518f5c3c67863cb8afcb74539880abb508158f3b2302f32a0580ffca98047216097d347be172e1dcf8f68017b4a5d6b386a1ff9833befad5f2fe80ae60a8538a53ed8ce68a4128f7a3148befe5e748e926d4ec3afd82de03c7003c80a487bb6555298c63125b2128ed2e66d644b86c66d7873b5e2518be6890c594b78093572ac9ad43de2f80f8d6bf6444a19c776dcab734020bda35ddace02ed3612f801c3e100e1b48463ab52ba1380c6da19c99fd2b844ee7cc478d4d67b2beaae418805d3d1fcff2b49723b3da0dc655c5570c7d87b8bb24366aca972b5508b521572980e8f22e7a1cb280b29e75a1d59f62d08dae14cfd1e41c39753d2ad789892e5be480628d28e1ebaa968678f56970faa6b14f2daf3db427190d64e209c991cdcc0ff680d108afb00798775dfbbc6e95815fb87d29eec5b4822b9f803b34541fe0dbd4478015bcf68c353f320fa40d1f6cdcf90c9c3f5e57f3ced5426d43ea2244c8d4cbdd","0x800104805b127e9edc12e57e62b63cc287372aec129041e27cf536af12d9c19144b1144a8057f9b3c58731b9265e2805dd67413a71d455345e3868da268575e8edbf7302ce","0x5ed41e5e16056765bc8461851072c9d72d42b000000000000000585f8f0900000000020000000100000000000000000000000000020000000200000000000000000000000000020000000300000003029755f1ea7a6f6058dfc02f95bc932c3c261c1f314e0078423a39efbc1f8b5104540ac2650a7565770080e590a1a554450000000000000000000003000000110600809cbdf9fb020000000000000000000000030000000b009755f1ea7a6f6058dfc02f95bc932c3c261c1f314e0078423a39efbc1f8b5104540ac2650a7565777ab3cd8d71cee1aa5f90970b7c35cc1c523c02f0932572e4888ce7cc643509a10000000003000000000040dd4a1f0000000000000000040000000003f798331b1d2000d483977593ee6e447e4760c63f0000040000000300f798331b1d2000d483977593ee6e447e4760c63f000064a7b3b6e00d00000000000000000000040000000302d07d078373be60dd10e35f352559ef1f25029daff798331b1d2000d483977593ee6e447e4760c63f000064a7b3b6e00d0000000000000000000004000000110600e09f906384070000000000000000000000040000000a000daaab07b078fd9cc8ebc7f980aefd0e2aba2ec60437be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b81070000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f63783837316166613362643061646661313339663638616338383831396461353361643434656630373700000000000000000000000000000000000000000000000000000000000000000000000000f4f8f2b83a6274703a2f2f30783530372e7072612f307830446141416230376230373866643963433865626337663938306165466430653261426132456336b83a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037378a6e6174697665636f696e01b86cf86a00b867f865aa307844303764303738333733624536306464313065333566333532353539656631663235303239444166aa637838373161666133626430616466613133396636386163383838313964613533616434346566303737cecd83444556880dbd2fc1379b5ee00000000000000000000000000000040000000a00c06be3322f7118b14843861b424698646875ce640850d22373bb84ed1f9eeb581c913e6d45d918c05f8b1d90f0be168f06a4e6994a000000000000000000000000d07d078373be60dd10e35f352559ef1f25029daf81060000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037370000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000dbd2fc1379b5ee0000000000000000000000000000000000000000000000000002386f26fc8a120000000000000000000000000000000000000000000000000000000000000000344455600000000000000000000000000000000000000000000000000000000000000040000000b00d07d078373be60dd10e35f352559ef1f25029daff798331b1d2000d483977593ee6e447e4760c63fd48e3dc4382221d839edc8988309d2f51d6c2c24d61854602bfe5ee4aa94584800010000040000000000e0cd3a1403000000000000000500000011060048bc1a79a2010000000000000000000000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0940c0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac602710000000000000000000000000000000000000000000000000000000000000b3d0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa8000000000000000000000000000000000000000000000000000000000614c0ab40000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0941092e98423f8adac6e64d0608e519fd1cefb861498385c6dee70d58fc926ddc68c000000000000000000000000000000000000000000000000000004027f32c45a0000000000000000000000000000000000000000000000000000000000000b3d0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa000000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0940c0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f000000000000000000000000000000000000000000000000000004027f32c45a0000000000000000000000000000000000000000000000000000000000000b3d8000000000000000000000000000000000000000000000000000000000614c0ab40000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b09408fe25c73e3b9089fac37d55c4c7efcba6f04af04cebd2fc4d6d7dbb07e1e5234f000000000000000000000000000000000000000000000002b5e3af16a0620be0000000050000000b006ffa2707ea1fe81c8dafa755e74f4961333b75facf88a8d7fc1a687895fc8ffaad567f303926b094eada3b8729301971b526b555143b78bdc79ab58a5f3469c9e6cb3fd7dc2ed01400000000050000000000c81468ab0000000000000000060000001106007aa9a3aa06000000000000000000000000060000000a005425f5d4ba2b7dcb277c369ccbcb5f0e7185fb41048d468b5f823f8d38322e9c4433d184adf453fd3eaa28cef280056aa0664981f0810400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000614c0ab100000000000000000000000000000000000000000000000000000000000000034e554f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000344414900000000000000000000000000000000000000000000000000000000000000060000000b00f516bdbd32a0b4cb07d21f26b9185b336502027f5425f5d4ba2b7dcb277c369ccbcb5f0e7185fb410a2c98df470b9c67e66b9d1c56a01134f4855c945ca6a5cb0db20088bad2dbb700000000060000000000f0cea5310000000000000000070000001106002eedcda848020000000000000000000000070000000b001e9eb23057adf446777c7fab1a6c565cb8977ab50daaab07b078fd9cc8ebc7f980aefd0e2aba2ec6b0cec4eee2a3b90afcd6bc9de569743251b8a620f65be7e3a8d3e35da5c4560600010000070000000000189d87b5120000000000000008000000110600d431ee3948020000000000000000000000080000000b001e9eb23057adf446777c7fab1a6c565cb8977ab50daaab07b078fd9cc8ebc7f980aefd0e2aba2ec67239b6b7115de5d5aa73729cd621c95b7fab968f44128a7bf77d8569589c69e7000100000800000000009055fbb1120000000000000009000000110600fcfa0ea518000000000000000000000000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d044dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04010200000000000000000000000000000000000000000000001c3c1b11702bed53c7000000000000000000000000000000000000000000000000000002ff386592790000000000000000000000000000000000000000000000000df3ed9d5c44a643000000000000000000000000000000000000000000000005ac5f23236b6319b30000090000000a00f3fc5f877e9dd90277c072ff3d07d076337fa29d0c2caecd17d02f56fa897705dcc740da2d237c373f70686f4e0d9bd3bf0400ea7a00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b0101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000a00d50e4638b5f58a66a5f4ff81f092db2357ecc6fb0cddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d80000000000000000000000000000000000000000000000000002386f26fc100000000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d044c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f810100000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000237c1b530677850000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d0cddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b8000000000000000000000000000000000000000000000000000237c1b530677850000090000000b0045451aadad03ed94b94fcec603af11f2e8f16c1b81e42baeee09de2880d4a8842edb1911755ac48dfb98f13c088231b45bb8e5c0c36b1ededad1b64ea30407c669ac88f03cb263ab00010000090000000000b0cee3c900000000000000000a000000110600c88e1801080000000000000000000000000a0000000a00d50e4638b5f58a66a5f4ff81f092db2357ecc6fb0c8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9250000000000000000000000004bd9e71910a5526375f417232264b6dcf1c21e4900000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d8000000000000000000000000000000000000004ee2d6d415b85acef810000000000000a0000000b004bd9e71910a5526375f417232264b6dcf1c21e49d50e4638b5f58a66a5f4ff81f092db2357ecc6fb29bc13947146e7799e4f9f6f76e3f1dca42facf6b4eb28106085f8fb46bf1eea000100000a0000000000a031924100000000000000","0x80410480a5d1d10f719b25189d69cee1500b2bdf5d747a5d834af2fb7bf3c8009352047d80c6a0dbe93fb0d6eabc13a415545fc917583db2ceb3132a37fd2b577ca984ff248079a049df09bffcad30d32c4f69ebc11bf1732ef47defd27ac368292273dbf460","0x9eaa394eea5630e07c48ae0c9558cef7398f80d7b3e23a3e88acc21d0a65e825397d6315df2f04ea6425697229be4b759d2808809ad724b847153c8f72bc55c93456c5a925bdbb37aa4de1d201f99bd9b4afb1ba505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b71004018045ebc4af9c052c8a75e2df8f8324ceaf4d855606c819a00213e3d26e199da2fe8084184d6de6a4e5d74b4c37f18d7c1d4b4102d8a51abffa7766f6e45902c1a79280de82b080f4ff4307862cd2058e48806ce66aa3a7373e754c39369fd85042a04780777f917207078879034726deebc7268366f961e9c6ba053aa7c9fd0b5983449f745f09cce9c888469bb1a0dceaa129672ef82cf50a206d6f6f6e62617365"]}} \ No newline at end of file diff --git a/chain/pra/binding/bmc.go b/chain/pra/binding/bmc.go new file mode 100644 index 00000000..dd15c28b --- /dev/null +++ b/chain/pra/binding/bmc.go @@ -0,0 +1,777 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package binding + +import ( + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription +) + +// TypesBMCMessage is an auto generated low-level Go binding around an user-defined struct. +type TypesBMCMessage struct { + Src string + Dst string + Svc string + Sn *big.Int + Message []byte +} + +// TypesBMCService is an auto generated low-level Go binding around an user-defined struct. +type TypesBMCService struct { + ServiceType string + Payload []byte +} + +// TypesGatherFeeMessage is an auto generated low-level Go binding around an user-defined struct. +type TypesGatherFeeMessage struct { + Fa string + Svcs []string +} + +// TypesLinkStats is an auto generated low-level Go binding around an user-defined struct. +type TypesLinkStats struct { + RxSeq *big.Int + TxSeq *big.Int + Verifier TypesVerifierStats + Relays []TypesRelayStats + RelayIdx *big.Int + RotateHeight *big.Int + RotateTerm *big.Int + DelayLimit *big.Int + MaxAggregation *big.Int + RxHeightSrc *big.Int + RxHeight *big.Int + BlockIntervalSrc *big.Int + BlockIntervalDst *big.Int + CurrentHeight *big.Int +} + +// TypesRelayStats is an auto generated low-level Go binding around an user-defined struct. +type TypesRelayStats struct { + Addr common.Address + BlockCount *big.Int + MsgCount *big.Int +} + +// TypesRequest is an auto generated low-level Go binding around an user-defined struct. +type TypesRequest struct { + ServiceName string + Bsh common.Address +} + +// TypesVerifierStats is an auto generated low-level Go binding around an user-defined struct. +type TypesVerifierStats struct { + HeightMTA *big.Int + OffsetMTA *big.Int + LastHeight *big.Int + Extra []byte +} + +// BMCABI is the input ABI used to generate the binding from. +const BMCABI = "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_svc\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"int256\",\"name\":\"_sn\",\"type\":\"int256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_code\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_errMsg\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_svcErrCode\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_svcErrMsg\",\"type\":\"string\"}],\"name\":\"ErrorOnBTPError\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"_next\",\"type\":\"string\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"_seq\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"_msg\",\"type\":\"bytes\"}],\"name\":\"Message\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_network\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_bmcManagementAddr\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBmcBtpAddress\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_serviceName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"_addr\",\"type\":\"address\"}],\"name\":\"requestAddService\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getPendingRequest\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"serviceName\",\"type\":\"string\"},{\"internalType\":\"address\",\"name\":\"bsh\",\"type\":\"address\"}],\"internalType\":\"structTypes.Request[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_prev\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_msg\",\"type\":\"string\"}],\"name\":\"handleRelayMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_rlp\",\"type\":\"bytes\"}],\"name\":\"decodeBTPMessage\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"src\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"dst\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"svc\",\"type\":\"string\"},{\"internalType\":\"int256\",\"name\":\"sn\",\"type\":\"int256\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.BMCMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_msg\",\"type\":\"bytes\"}],\"name\":\"tryDecodeBMCService\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"serviceType\",\"type\":\"string\"},{\"internalType\":\"bytes\",\"name\":\"payload\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.BMCService\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"_msg\",\"type\":\"bytes\"}],\"name\":\"tryDecodeGatherFeeMessage\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"fa\",\"type\":\"string\"},{\"internalType\":\"string[]\",\"name\":\"svcs\",\"type\":\"string[]\"}],\"internalType\":\"structTypes.GatherFeeMessage\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_to\",\"type\":\"string\"},{\"internalType\":\"string\",\"name\":\"_svc\",\"type\":\"string\"},{\"internalType\":\"uint256\",\"name\":\"_sn\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"_msg\",\"type\":\"bytes\"}],\"name\":\"sendMessage\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"_link\",\"type\":\"string\"}],\"name\":\"getStatus\",\"outputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"rxSeq\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"txSeq\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"heightMTA\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"offsetMTA\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"lastHeight\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"extra\",\"type\":\"bytes\"}],\"internalType\":\"structTypes.VerifierStats\",\"name\":\"verifier\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"blockCount\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"msgCount\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.RelayStats[]\",\"name\":\"relays\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"relayIdx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rotateHeight\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rotateTerm\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"delayLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxAggregation\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rxHeightSrc\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"rxHeight\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockIntervalSrc\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"blockIntervalDst\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"currentHeight\",\"type\":\"uint256\"}],\"internalType\":\"structTypes.LinkStats\",\"name\":\"_linkStats\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]" + +// BMC is an auto generated Go binding around an Ethereum contract. +type BMC struct { + BMCCaller // Read-only binding to the contract + BMCTransactor // Write-only binding to the contract + BMCFilterer // Log filterer for contract events +} + +// BMCCaller is an auto generated read-only Go binding around an Ethereum contract. +type BMCCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BMCTransactor is an auto generated write-only Go binding around an Ethereum contract. +type BMCTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BMCFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type BMCFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// BMCSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type BMCSession struct { + Contract *BMC // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BMCCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type BMCCallerSession struct { + Contract *BMCCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// BMCTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type BMCTransactorSession struct { + Contract *BMCTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// BMCRaw is an auto generated low-level Go binding around an Ethereum contract. +type BMCRaw struct { + Contract *BMC // Generic contract binding to access the raw methods on +} + +// BMCCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type BMCCallerRaw struct { + Contract *BMCCaller // Generic read-only contract binding to access the raw methods on +} + +// BMCTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type BMCTransactorRaw struct { + Contract *BMCTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewBMC creates a new instance of BMC, bound to a specific deployed contract. +func NewBMC(address common.Address, backend bind.ContractBackend) (*BMC, error) { + contract, err := bindBMC(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &BMC{BMCCaller: BMCCaller{contract: contract}, BMCTransactor: BMCTransactor{contract: contract}, BMCFilterer: BMCFilterer{contract: contract}}, nil +} + +// NewBMCCaller creates a new read-only instance of BMC, bound to a specific deployed contract. +func NewBMCCaller(address common.Address, caller bind.ContractCaller) (*BMCCaller, error) { + contract, err := bindBMC(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &BMCCaller{contract: contract}, nil +} + +// NewBMCTransactor creates a new write-only instance of BMC, bound to a specific deployed contract. +func NewBMCTransactor(address common.Address, transactor bind.ContractTransactor) (*BMCTransactor, error) { + contract, err := bindBMC(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &BMCTransactor{contract: contract}, nil +} + +// NewBMCFilterer creates a new log filterer instance of BMC, bound to a specific deployed contract. +func NewBMCFilterer(address common.Address, filterer bind.ContractFilterer) (*BMCFilterer, error) { + contract, err := bindBMC(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &BMCFilterer{contract: contract}, nil +} + +// bindBMC binds a generic wrapper to an already deployed contract. +func bindBMC(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := abi.JSON(strings.NewReader(BMCABI)) + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BMC *BMCRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BMC.Contract.BMCCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BMC *BMCRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BMC.Contract.BMCTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BMC *BMCRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BMC.Contract.BMCTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_BMC *BMCCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _BMC.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_BMC *BMCTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _BMC.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_BMC *BMCTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _BMC.Contract.contract.Transact(opts, method, params...) +} + +// DecodeBTPMessage is a free data retrieval call binding the contract method 0x2a15e32e. +// +// Solidity: function decodeBTPMessage(bytes _rlp) pure returns((string,string,string,int256,bytes)) +func (_BMC *BMCCaller) DecodeBTPMessage(opts *bind.CallOpts, _rlp []byte) (TypesBMCMessage, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "decodeBTPMessage", _rlp) + + if err != nil { + return *new(TypesBMCMessage), err + } + + out0 := *abi.ConvertType(out[0], new(TypesBMCMessage)).(*TypesBMCMessage) + + return out0, err + +} + +// DecodeBTPMessage is a free data retrieval call binding the contract method 0x2a15e32e. +// +// Solidity: function decodeBTPMessage(bytes _rlp) pure returns((string,string,string,int256,bytes)) +func (_BMC *BMCSession) DecodeBTPMessage(_rlp []byte) (TypesBMCMessage, error) { + return _BMC.Contract.DecodeBTPMessage(&_BMC.CallOpts, _rlp) +} + +// DecodeBTPMessage is a free data retrieval call binding the contract method 0x2a15e32e. +// +// Solidity: function decodeBTPMessage(bytes _rlp) pure returns((string,string,string,int256,bytes)) +func (_BMC *BMCCallerSession) DecodeBTPMessage(_rlp []byte) (TypesBMCMessage, error) { + return _BMC.Contract.DecodeBTPMessage(&_BMC.CallOpts, _rlp) +} + +// GetBmcBtpAddress is a free data retrieval call binding the contract method 0x2a4011e9. +// +// Solidity: function getBmcBtpAddress() view returns(string) +func (_BMC *BMCCaller) GetBmcBtpAddress(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "getBmcBtpAddress") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +// GetBmcBtpAddress is a free data retrieval call binding the contract method 0x2a4011e9. +// +// Solidity: function getBmcBtpAddress() view returns(string) +func (_BMC *BMCSession) GetBmcBtpAddress() (string, error) { + return _BMC.Contract.GetBmcBtpAddress(&_BMC.CallOpts) +} + +// GetBmcBtpAddress is a free data retrieval call binding the contract method 0x2a4011e9. +// +// Solidity: function getBmcBtpAddress() view returns(string) +func (_BMC *BMCCallerSession) GetBmcBtpAddress() (string, error) { + return _BMC.Contract.GetBmcBtpAddress(&_BMC.CallOpts) +} + +// GetPendingRequest is a free data retrieval call binding the contract method 0x90f4fe72. +// +// Solidity: function getPendingRequest() view returns((string,address)[]) +func (_BMC *BMCCaller) GetPendingRequest(opts *bind.CallOpts) ([]TypesRequest, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "getPendingRequest") + + if err != nil { + return *new([]TypesRequest), err + } + + out0 := *abi.ConvertType(out[0], new([]TypesRequest)).(*[]TypesRequest) + + return out0, err + +} + +// GetPendingRequest is a free data retrieval call binding the contract method 0x90f4fe72. +// +// Solidity: function getPendingRequest() view returns((string,address)[]) +func (_BMC *BMCSession) GetPendingRequest() ([]TypesRequest, error) { + return _BMC.Contract.GetPendingRequest(&_BMC.CallOpts) +} + +// GetPendingRequest is a free data retrieval call binding the contract method 0x90f4fe72. +// +// Solidity: function getPendingRequest() view returns((string,address)[]) +func (_BMC *BMCCallerSession) GetPendingRequest() ([]TypesRequest, error) { + return _BMC.Contract.GetPendingRequest(&_BMC.CallOpts) +} + +// GetStatus is a free data retrieval call binding the contract method 0x22b05ed2. +// +// Solidity: function getStatus(string _link) view returns((uint256,uint256,(uint256,uint256,uint256,bytes),(address,uint256,uint256)[],uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256) _linkStats) +func (_BMC *BMCCaller) GetStatus(opts *bind.CallOpts, _link string) (TypesLinkStats, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "getStatus", _link) + + if err != nil { + return *new(TypesLinkStats), err + } + + out0 := *abi.ConvertType(out[0], new(TypesLinkStats)).(*TypesLinkStats) + + return out0, err + +} + +// GetStatus is a free data retrieval call binding the contract method 0x22b05ed2. +// +// Solidity: function getStatus(string _link) view returns((uint256,uint256,(uint256,uint256,uint256,bytes),(address,uint256,uint256)[],uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256) _linkStats) +func (_BMC *BMCSession) GetStatus(_link string) (TypesLinkStats, error) { + return _BMC.Contract.GetStatus(&_BMC.CallOpts, _link) +} + +// GetStatus is a free data retrieval call binding the contract method 0x22b05ed2. +// +// Solidity: function getStatus(string _link) view returns((uint256,uint256,(uint256,uint256,uint256,bytes),(address,uint256,uint256)[],uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256) _linkStats) +func (_BMC *BMCCallerSession) GetStatus(_link string) (TypesLinkStats, error) { + return _BMC.Contract.GetStatus(&_BMC.CallOpts, _link) +} + +// TryDecodeBMCService is a free data retrieval call binding the contract method 0x2294c488. +// +// Solidity: function tryDecodeBMCService(bytes _msg) pure returns((string,bytes)) +func (_BMC *BMCCaller) TryDecodeBMCService(opts *bind.CallOpts, _msg []byte) (TypesBMCService, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "tryDecodeBMCService", _msg) + + if err != nil { + return *new(TypesBMCService), err + } + + out0 := *abi.ConvertType(out[0], new(TypesBMCService)).(*TypesBMCService) + + return out0, err + +} + +// TryDecodeBMCService is a free data retrieval call binding the contract method 0x2294c488. +// +// Solidity: function tryDecodeBMCService(bytes _msg) pure returns((string,bytes)) +func (_BMC *BMCSession) TryDecodeBMCService(_msg []byte) (TypesBMCService, error) { + return _BMC.Contract.TryDecodeBMCService(&_BMC.CallOpts, _msg) +} + +// TryDecodeBMCService is a free data retrieval call binding the contract method 0x2294c488. +// +// Solidity: function tryDecodeBMCService(bytes _msg) pure returns((string,bytes)) +func (_BMC *BMCCallerSession) TryDecodeBMCService(_msg []byte) (TypesBMCService, error) { + return _BMC.Contract.TryDecodeBMCService(&_BMC.CallOpts, _msg) +} + +// TryDecodeGatherFeeMessage is a free data retrieval call binding the contract method 0x9624379f. +// +// Solidity: function tryDecodeGatherFeeMessage(bytes _msg) pure returns((string,string[])) +func (_BMC *BMCCaller) TryDecodeGatherFeeMessage(opts *bind.CallOpts, _msg []byte) (TypesGatherFeeMessage, error) { + var out []interface{} + err := _BMC.contract.Call(opts, &out, "tryDecodeGatherFeeMessage", _msg) + + if err != nil { + return *new(TypesGatherFeeMessage), err + } + + out0 := *abi.ConvertType(out[0], new(TypesGatherFeeMessage)).(*TypesGatherFeeMessage) + + return out0, err + +} + +// TryDecodeGatherFeeMessage is a free data retrieval call binding the contract method 0x9624379f. +// +// Solidity: function tryDecodeGatherFeeMessage(bytes _msg) pure returns((string,string[])) +func (_BMC *BMCSession) TryDecodeGatherFeeMessage(_msg []byte) (TypesGatherFeeMessage, error) { + return _BMC.Contract.TryDecodeGatherFeeMessage(&_BMC.CallOpts, _msg) +} + +// TryDecodeGatherFeeMessage is a free data retrieval call binding the contract method 0x9624379f. +// +// Solidity: function tryDecodeGatherFeeMessage(bytes _msg) pure returns((string,string[])) +func (_BMC *BMCCallerSession) TryDecodeGatherFeeMessage(_msg []byte) (TypesGatherFeeMessage, error) { + return _BMC.Contract.TryDecodeGatherFeeMessage(&_BMC.CallOpts, _msg) +} + +// HandleRelayMessage is a paid mutator transaction binding the contract method 0x6f4779cc. +// +// Solidity: function handleRelayMessage(string _prev, string _msg) returns() +func (_BMC *BMCTransactor) HandleRelayMessage(opts *bind.TransactOpts, _prev string, _msg string) (*types.Transaction, error) { + return _BMC.contract.Transact(opts, "handleRelayMessage", _prev, _msg) +} + +// HandleRelayMessage is a paid mutator transaction binding the contract method 0x6f4779cc. +// +// Solidity: function handleRelayMessage(string _prev, string _msg) returns() +func (_BMC *BMCSession) HandleRelayMessage(_prev string, _msg string) (*types.Transaction, error) { + return _BMC.Contract.HandleRelayMessage(&_BMC.TransactOpts, _prev, _msg) +} + +// HandleRelayMessage is a paid mutator transaction binding the contract method 0x6f4779cc. +// +// Solidity: function handleRelayMessage(string _prev, string _msg) returns() +func (_BMC *BMCTransactorSession) HandleRelayMessage(_prev string, _msg string) (*types.Transaction, error) { + return _BMC.Contract.HandleRelayMessage(&_BMC.TransactOpts, _prev, _msg) +} + +// Initialize is a paid mutator transaction binding the contract method 0x7ab4339d. +// +// Solidity: function initialize(string _network, address _bmcManagementAddr) returns() +func (_BMC *BMCTransactor) Initialize(opts *bind.TransactOpts, _network string, _bmcManagementAddr common.Address) (*types.Transaction, error) { + return _BMC.contract.Transact(opts, "initialize", _network, _bmcManagementAddr) +} + +// Initialize is a paid mutator transaction binding the contract method 0x7ab4339d. +// +// Solidity: function initialize(string _network, address _bmcManagementAddr) returns() +func (_BMC *BMCSession) Initialize(_network string, _bmcManagementAddr common.Address) (*types.Transaction, error) { + return _BMC.Contract.Initialize(&_BMC.TransactOpts, _network, _bmcManagementAddr) +} + +// Initialize is a paid mutator transaction binding the contract method 0x7ab4339d. +// +// Solidity: function initialize(string _network, address _bmcManagementAddr) returns() +func (_BMC *BMCTransactorSession) Initialize(_network string, _bmcManagementAddr common.Address) (*types.Transaction, error) { + return _BMC.Contract.Initialize(&_BMC.TransactOpts, _network, _bmcManagementAddr) +} + +// RequestAddService is a paid mutator transaction binding the contract method 0x7e09bd86. +// +// Solidity: function requestAddService(string _serviceName, address _addr) returns() +func (_BMC *BMCTransactor) RequestAddService(opts *bind.TransactOpts, _serviceName string, _addr common.Address) (*types.Transaction, error) { + return _BMC.contract.Transact(opts, "requestAddService", _serviceName, _addr) +} + +// RequestAddService is a paid mutator transaction binding the contract method 0x7e09bd86. +// +// Solidity: function requestAddService(string _serviceName, address _addr) returns() +func (_BMC *BMCSession) RequestAddService(_serviceName string, _addr common.Address) (*types.Transaction, error) { + return _BMC.Contract.RequestAddService(&_BMC.TransactOpts, _serviceName, _addr) +} + +// RequestAddService is a paid mutator transaction binding the contract method 0x7e09bd86. +// +// Solidity: function requestAddService(string _serviceName, address _addr) returns() +func (_BMC *BMCTransactorSession) RequestAddService(_serviceName string, _addr common.Address) (*types.Transaction, error) { + return _BMC.Contract.RequestAddService(&_BMC.TransactOpts, _serviceName, _addr) +} + +// SendMessage is a paid mutator transaction binding the contract method 0xbf6c1d9a. +// +// Solidity: function sendMessage(string _to, string _svc, uint256 _sn, bytes _msg) returns() +func (_BMC *BMCTransactor) SendMessage(opts *bind.TransactOpts, _to string, _svc string, _sn *big.Int, _msg []byte) (*types.Transaction, error) { + return _BMC.contract.Transact(opts, "sendMessage", _to, _svc, _sn, _msg) +} + +// SendMessage is a paid mutator transaction binding the contract method 0xbf6c1d9a. +// +// Solidity: function sendMessage(string _to, string _svc, uint256 _sn, bytes _msg) returns() +func (_BMC *BMCSession) SendMessage(_to string, _svc string, _sn *big.Int, _msg []byte) (*types.Transaction, error) { + return _BMC.Contract.SendMessage(&_BMC.TransactOpts, _to, _svc, _sn, _msg) +} + +// SendMessage is a paid mutator transaction binding the contract method 0xbf6c1d9a. +// +// Solidity: function sendMessage(string _to, string _svc, uint256 _sn, bytes _msg) returns() +func (_BMC *BMCTransactorSession) SendMessage(_to string, _svc string, _sn *big.Int, _msg []byte) (*types.Transaction, error) { + return _BMC.Contract.SendMessage(&_BMC.TransactOpts, _to, _svc, _sn, _msg) +} + +// BMCErrorOnBTPErrorIterator is returned from FilterErrorOnBTPError and is used to iterate over the raw logs and unpacked data for ErrorOnBTPError events raised by the BMC contract. +type BMCErrorOnBTPErrorIterator struct { + Event *BMCErrorOnBTPError // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BMCErrorOnBTPErrorIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BMCErrorOnBTPError) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BMCErrorOnBTPError) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BMCErrorOnBTPErrorIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BMCErrorOnBTPErrorIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BMCErrorOnBTPError represents a ErrorOnBTPError event raised by the BMC contract. +type BMCErrorOnBTPError struct { + Svc string + Sn *big.Int + Code *big.Int + ErrMsg string + SvcErrCode *big.Int + SvcErrMsg string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterErrorOnBTPError is a free log retrieval operation binding the contract event 0x45eab163faa71c8b113fcbc0dcc77bd39e7e3365be446895b5169bd97fc5522a. +// +// Solidity: event ErrorOnBTPError(string _svc, int256 _sn, uint256 _code, string _errMsg, uint256 _svcErrCode, string _svcErrMsg) +func (_BMC *BMCFilterer) FilterErrorOnBTPError(opts *bind.FilterOpts) (*BMCErrorOnBTPErrorIterator, error) { + + logs, sub, err := _BMC.contract.FilterLogs(opts, "ErrorOnBTPError") + if err != nil { + return nil, err + } + return &BMCErrorOnBTPErrorIterator{contract: _BMC.contract, event: "ErrorOnBTPError", logs: logs, sub: sub}, nil +} + +// WatchErrorOnBTPError is a free log subscription operation binding the contract event 0x45eab163faa71c8b113fcbc0dcc77bd39e7e3365be446895b5169bd97fc5522a. +// +// Solidity: event ErrorOnBTPError(string _svc, int256 _sn, uint256 _code, string _errMsg, uint256 _svcErrCode, string _svcErrMsg) +func (_BMC *BMCFilterer) WatchErrorOnBTPError(opts *bind.WatchOpts, sink chan<- *BMCErrorOnBTPError) (event.Subscription, error) { + + logs, sub, err := _BMC.contract.WatchLogs(opts, "ErrorOnBTPError") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BMCErrorOnBTPError) + if err := _BMC.contract.UnpackLog(event, "ErrorOnBTPError", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseErrorOnBTPError is a log parse operation binding the contract event 0x45eab163faa71c8b113fcbc0dcc77bd39e7e3365be446895b5169bd97fc5522a. +// +// Solidity: event ErrorOnBTPError(string _svc, int256 _sn, uint256 _code, string _errMsg, uint256 _svcErrCode, string _svcErrMsg) +func (_BMC *BMCFilterer) ParseErrorOnBTPError(log types.Log) (*BMCErrorOnBTPError, error) { + event := new(BMCErrorOnBTPError) + if err := _BMC.contract.UnpackLog(event, "ErrorOnBTPError", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// BMCMessageIterator is returned from FilterMessage and is used to iterate over the raw logs and unpacked data for Message events raised by the BMC contract. +type BMCMessageIterator struct { + Event *BMCMessage // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *BMCMessageIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(BMCMessage) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(BMCMessage) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *BMCMessageIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *BMCMessageIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// BMCMessage represents a Message event raised by the BMC contract. +type BMCMessage struct { + Next string + Seq *big.Int + Msg []byte + Raw types.Log // Blockchain specific contextual infos +} + +// FilterMessage is a free log retrieval operation binding the contract event 0x37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b. +// +// Solidity: event Message(string _next, uint256 _seq, bytes _msg) +func (_BMC *BMCFilterer) FilterMessage(opts *bind.FilterOpts) (*BMCMessageIterator, error) { + + logs, sub, err := _BMC.contract.FilterLogs(opts, "Message") + if err != nil { + return nil, err + } + return &BMCMessageIterator{contract: _BMC.contract, event: "Message", logs: logs, sub: sub}, nil +} + +// WatchMessage is a free log subscription operation binding the contract event 0x37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b. +// +// Solidity: event Message(string _next, uint256 _seq, bytes _msg) +func (_BMC *BMCFilterer) WatchMessage(opts *bind.WatchOpts, sink chan<- *BMCMessage) (event.Subscription, error) { + + logs, sub, err := _BMC.contract.WatchLogs(opts, "Message") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(BMCMessage) + if err := _BMC.contract.UnpackLog(event, "Message", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseMessage is a log parse operation binding the contract event 0x37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b. +// +// Solidity: event Message(string _next, uint256 _seq, bytes _msg) +func (_BMC *BMCFilterer) ParseMessage(log types.Log) (*BMCMessage, error) { + event := new(BMCMessage) + if err := _BMC.contract.UnpackLog(event, "Message", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/chain/pra/client.go b/chain/pra/client.go new file mode 100644 index 00000000..3d680160 --- /dev/null +++ b/chain/pra/client.go @@ -0,0 +1,205 @@ +package pra + +import ( + "context" + "math/big" + "regexp" + "time" + + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/wallet" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/icon-project/btp/chain/pra/binding" + "github.com/icon-project/btp/chain/pra/substrate" +) + +const ( + BlockRetryInterval = time.Second * 1 + DefaultReadTimeout = 10 * time.Second +) + +var RetryHTTPError = regexp.MustCompile(`connection reset by peer|EOF`) + +type EthClient interface { + TransactionReceipt(ctx context.Context, hash EvmHash) (*EvmReceipt, error) + TransactionByHash(ctx context.Context, hash EvmHash) (*EvmTransaction, bool, error) + CallContract(ctx context.Context, callMsg EvmCallMsg, block *big.Int) ([]byte, error) + SuggestGasPrice(ctx context.Context) (*big.Int, error) + ChainID(ctx context.Context) (*big.Int, error) +} + +type BMCContract interface { + HandleRelayMessage(opts *bind.TransactOpts, _prev string, _msg string) (*EvmTransaction, error) + ParseMessage(log EvmLog) (*binding.BMCMessage, error) + GetStatus(opts *bind.CallOpts, _link string) (binding.TypesLinkStats, error) +} + +type Client struct { + ethClient EthClient + subClient substrate.SubstrateClient + bmc BMCContract + log log.Logger + stopMonitorSignal chan bool +} + +func NewClient(url string, bmcContractAddress string, l log.Logger) *Client { + subClient, err := substrate.NewSubstrateClient(url) + if err != nil { + l.Fatalf("failed to create Parachain Client err:%v", err.Error()) + } + + subClient.Init() + + c := &Client{ + subClient: subClient, + log: l, + stopMonitorSignal: make(chan bool), + } + + if len(bmcContractAddress) > 0 { + + ethClient, err := ethclient.Dial(url) + if err != nil { + l.Fatal("failed to connect to Parachain EVM", err.Error()) + } + + bmc, err := binding.NewBMC(EvmHexToAddress(bmcContractAddress), ethClient) + if err != nil { + l.Fatal("failed to connect to Parachain BMC contract", err.Error()) + } + c.bmc = bmc + c.ethClient = ethClient + } + + return c +} + +func (c *Client) SubstrateClient() substrate.SubstrateClient { + return c.subClient +} + +func (c *Client) newTransactOpts(w Wallet) *bind.TransactOpts { + ew := w.(*wallet.EvmWallet) + context := context.Background() + chainID, _ := c.ethClient.ChainID(context) + txopts, _ := bind.NewKeyedTransactorWithChainID(ew.Skey, chainID) + txopts.GasPrice, _ = c.ethClient.SuggestGasPrice(context) + txopts.Context = context + + return txopts +} + +func (c *Client) GetTransactionReceipt(txhash string) (*EvmReceipt, error) { + receipt, err := c.ethClient.TransactionReceipt(context.Background(), EvmHexToHash(txhash)) + if err != nil { + return nil, err + } + + return receipt, nil +} + +func (c *Client) GetTransactionByHash(txhash string) (*EvmTransaction, bool, error) { + ctx, cancel := context.WithTimeout(context.Background(), DefaultReadTimeout) + defer cancel() + tx, pending, err := c.ethClient.TransactionByHash(ctx, EvmHexToHash(txhash)) + if err != nil { + return nil, pending, err + } + return tx, pending, err +} + +func (c *Client) CloseAllMonitor() error { + close(c.stopMonitorSignal) + return nil +} + +func (c *Client) lastFinalizedHeader() (*substrate.SubstrateHeader, error) { + finalizedHash, err := c.subClient.GetFinalizedHead() + if err != nil { + return nil, err + } + + finalizedHeader, err := c.subClient.GetHeader(finalizedHash) + if err != nil { + return nil, err + } + + return finalizedHeader, nil +} + +// bestLatestBlockHeader returns the best latest header +// in testnet if the chain do not support finalizing headers, it returns latest header +func (c *Client) bestLatestBlockHeader() (*substrate.SubstrateHeader, error) { + finalizedHeader, err := c.lastFinalizedHeader() + if err != nil { + return nil, err + } + + if finalizedHeader.Number > 0 { + return finalizedHeader, nil + } + + return c.subClient.GetHeaderLatest() +} + +// MonitorBlock pulls block from the given height +func (c *Client) MonitorBlock(height uint64, fetchHeader bool, cb func(v *BlockNotification) error) error { + current := height + + for { + select { + case <-c.stopMonitorSignal: + return nil + default: + latestHeader, err := c.bestLatestBlockHeader() + if err != nil && RetryHTTPError.MatchString(err.Error()) { + <-time.After(BlockRetryInterval) + continue + } else if err != nil { + return err + } + + if current > uint64(latestHeader.Number) { + c.log.Tracef("block not yet finalized target:%v latest:%v", current, latestHeader.Number) + <-time.After(BlockRetryInterval) + continue + } + + hash, err := c.subClient.GetBlockHash(current) + if err != nil && (err.Error() == ErrBlockNotReady.Error() || RetryHTTPError.MatchString(err.Error())) { + <-time.After(BlockRetryInterval) + continue + } else if err != nil { + c.log.Error("failed to query latest block:%v error:%v", current, err) + return err + } + + v := &BlockNotification{ + Height: current, + Hash: hash, + } + + if fetchHeader { + header, err := c.subClient.GetHeader(hash) + if err != nil { + return err + } + + v.Header = header + } + + if err := cb(v); err != nil { + return err + } + current++ + } + } +} + +// CallContract executes a message call transaction, which is directly executed in the VM +// of the node, but never mined into the blockchain. +func (c *Client) CallContract(callMsg EvmCallMsg, blockNumber *big.Int) ([]byte, error) { + return c.ethClient.CallContract(context.Background(), callMsg, blockNumber) +} diff --git a/chain/pra/client_test.go b/chain/pra/client_test.go new file mode 100644 index 00000000..e601fbe1 --- /dev/null +++ b/chain/pra/client_test.go @@ -0,0 +1,101 @@ +package pra + +import ( + "encoding/hex" + "fmt" + "testing" + + "github.com/icon-project/btp/chain/pra/substrate" + iconcrypto "github.com/icon-project/btp/common/crypto" + "github.com/icon-project/btp/common/log" + "github.com/stretchr/testify/assert" +) + +func hashSubstrateInt(number uint64) substrate.SubstrateHash { + return substrate.NewSubstrateHashFromHexString(hex.EncodeToString(iconcrypto.SHA3Sum256([]byte(fmt.Sprintf("%d", number))))) +} + +func TestClientMonitorBlock(t *testing.T) { + + t.Run("monitor from lower finallized header", func(t *testing.T) { + startBlock := uint64(1) + finalizedBlock := uint64(5) + endBlock := uint64(finalizedBlock + 1) + + hashMap := map[uint64]substrate.SubstrateHash{} + hashInt := map[substrate.SubstrateHash]uint64{} + subClient := &substrate.MockSubstrateClient{} + + for i := startBlock; i <= finalizedBlock+1; i++ { + hash := hashSubstrateInt(i) + hashMap[i] = hash + hashInt[hash] = i + + subClient.On("GetBlockHash", i).Return(hash, nil).Once() + + header := &substrate.SubstrateHeader{Number: substrate.SubstrateBlockNumber(i)} + subClient.On("GetHeader", hash).Return(header, nil) + } + subClient.On("GetFinalizedHead").Return(hashMap[finalizedBlock], nil).Times(int(finalizedBlock-startBlock) + 1) + subClient.On("GetFinalizedHead").Return(hashMap[endBlock], nil).Once() + + monitoredBlocks := []int64{} + + client := &Client{log: log.New(), subClient: subClient, stopMonitorSignal: make(chan bool)} + client.MonitorBlock(startBlock, true, func(v *BlockNotification) error { + assert.Equal(t, v.Hash, hashMap[v.Height]) + assert.NotNil(t, v.Header) + assert.EqualValues(t, v.Height, v.Header.Number) + + monitoredBlocks = append(monitoredBlocks, int64(v.Height)) + if v.Height == endBlock { + client.CloseAllMonitor() + } + return nil + }) + + assert.Len(t, monitoredBlocks, int(endBlock-startBlock)+1) + }) + + t.Run("monitor from heigher finallized header", func(t *testing.T) { + startBlock := uint64(3) + endBlock := uint64(10) + finalizedBlock := uint64(1) + + hashMap := map[uint64]substrate.SubstrateHash{} + hashInt := map[substrate.SubstrateHash]uint64{} + subClient := &substrate.MockSubstrateClient{} + + for i := finalizedBlock; i <= endBlock; i++ { + hash := hashSubstrateInt(i) + hashMap[i] = hash + hashInt[hash] = i + + subClient.On("GetFinalizedHead").Return(hashMap[i], nil).Once() + + header := &substrate.SubstrateHeader{Number: substrate.SubstrateBlockNumber(i)} + subClient.On("GetHeader", hashMap[i]).Return(header, nil) + + if i >= startBlock { + subClient.On("GetBlockHash", i).Return(hash, nil).Once() + } + } + + monitoredBlocks := []int64{} + + client := &Client{log: log.New(), subClient: subClient, stopMonitorSignal: make(chan bool)} + client.MonitorBlock(startBlock, true, func(v *BlockNotification) error { + assert.Equal(t, v.Hash, hashMap[v.Height]) + assert.NotNil(t, v.Header) + assert.EqualValues(t, v.Height, v.Header.Number) + + monitoredBlocks = append(monitoredBlocks, int64(v.Height)) + if v.Height == endBlock { + client.CloseAllMonitor() + } + return nil + }) + + assert.Len(t, monitoredBlocks, int(endBlock-startBlock)+1) + }) +} diff --git a/chain/pra/error.go b/chain/pra/error.go new file mode 100644 index 00000000..c099216a --- /dev/null +++ b/chain/pra/error.go @@ -0,0 +1,10 @@ +package pra + +import "errors" + +var ( + ErrBlockNotReady = errors.New("required result to be 32 bytes, but got 0") + ErrInvalidBlockUpdateProofSize = errors.New("invalid BlockUpdate.Proof size") + ErrInvalidReceiptProofSize = errors.New("invalid ReceiptProof.Proof size") + ErrInvalidEventProofProofSize = errors.New("invalid EventProof.Proof size") +) diff --git a/chain/pra/mocks/BMCContract.go b/chain/pra/mocks/BMCContract.go new file mode 100644 index 00000000..1f529ac9 --- /dev/null +++ b/chain/pra/mocks/BMCContract.go @@ -0,0 +1,84 @@ +// Code generated by mockery v2.0.3. DO NOT EDIT. + +package mocks + +import ( + bind "github.com/ethereum/go-ethereum/accounts/abi/bind" + binding "github.com/icon-project/btp/chain/pra/binding" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// BMCContract is an autogenerated mock type for the BMCContract type +type BMCContract struct { + mock.Mock +} + +// GetStatus provides a mock function with given fields: opts, _link +func (_m *BMCContract) GetStatus(opts *bind.CallOpts, _link string) (binding.TypesLinkStats, error) { + ret := _m.Called(opts, _link) + + var r0 binding.TypesLinkStats + if rf, ok := ret.Get(0).(func(*bind.CallOpts, string) binding.TypesLinkStats); ok { + r0 = rf(opts, _link) + } else { + r0 = ret.Get(0).(binding.TypesLinkStats) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*bind.CallOpts, string) error); ok { + r1 = rf(opts, _link) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// HandleRelayMessage provides a mock function with given fields: opts, _prev, _msg +func (_m *BMCContract) HandleRelayMessage(opts *bind.TransactOpts, _prev string, _msg string) (*types.Transaction, error) { + ret := _m.Called(opts, _prev, _msg) + + var r0 *types.Transaction + if rf, ok := ret.Get(0).(func(*bind.TransactOpts, string, string) *types.Transaction); ok { + r0 = rf(opts, _prev, _msg) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*bind.TransactOpts, string, string) error); ok { + r1 = rf(opts, _prev, _msg) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ParseMessage provides a mock function with given fields: log +func (_m *BMCContract) ParseMessage(log types.Log) (*binding.BMCMessage, error) { + ret := _m.Called(log) + + var r0 *binding.BMCMessage + if rf, ok := ret.Get(0).(func(types.Log) *binding.BMCMessage); ok { + r0 = rf(log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*binding.BMCMessage) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Log) error); ok { + r1 = rf(log) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/chain/pra/mocks/DataError.go b/chain/pra/mocks/DataError.go new file mode 100644 index 00000000..a9ac8919 --- /dev/null +++ b/chain/pra/mocks/DataError.go @@ -0,0 +1,40 @@ +// Code generated by mockery v2.0.3. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// DataError is an autogenerated mock type for the DataError type +type DataError struct { + mock.Mock +} + +// Error provides a mock function with given fields: +func (_m *DataError) Error() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// ErrorData provides a mock function with given fields: +func (_m *DataError) ErrorData() interface{} { + ret := _m.Called() + + var r0 interface{} + if rf, ok := ret.Get(0).(func() interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} diff --git a/chain/pra/mocks/EthClient.go b/chain/pra/mocks/EthClient.go new file mode 100644 index 00000000..3cfe03ba --- /dev/null +++ b/chain/pra/mocks/EthClient.go @@ -0,0 +1,143 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package mocks + +import ( + context "context" + big "math/big" + + common "github.com/ethereum/go-ethereum/common" + + ethereum "github.com/ethereum/go-ethereum" + + mock "github.com/stretchr/testify/mock" + + types "github.com/ethereum/go-ethereum/core/types" +) + +// EthClient is an autogenerated mock type for the EthClient type +type EthClient struct { + mock.Mock +} + +// CallContract provides a mock function with given fields: ctx, callMsg, block +func (_m *EthClient) CallContract(ctx context.Context, callMsg ethereum.CallMsg, block *big.Int) ([]byte, error) { + ret := _m.Called(ctx, callMsg, block) + + var r0 []byte + if rf, ok := ret.Get(0).(func(context.Context, ethereum.CallMsg, *big.Int) []byte); ok { + r0 = rf(ctx, callMsg, block) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, ethereum.CallMsg, *big.Int) error); ok { + r1 = rf(ctx, callMsg, block) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ChainID provides a mock function with given fields: ctx +func (_m *EthClient) ChainID(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + var r0 *big.Int + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SuggestGasPrice provides a mock function with given fields: ctx +func (_m *EthClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + ret := _m.Called(ctx) + + var r0 *big.Int + if rf, ok := ret.Get(0).(func(context.Context) *big.Int); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*big.Int) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(ctx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransactionByHash provides a mock function with given fields: ctx, hash +func (_m *EthClient) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) { + ret := _m.Called(ctx, hash) + + var r0 *types.Transaction + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Transaction); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Transaction) + } + } + + var r1 bool + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) bool); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Get(1).(bool) + } + + var r2 error + if rf, ok := ret.Get(2).(func(context.Context, common.Hash) error); ok { + r2 = rf(ctx, hash) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// TransactionReceipt provides a mock function with given fields: ctx, hash +func (_m *EthClient) TransactionReceipt(ctx context.Context, hash common.Hash) (*types.Receipt, error) { + ret := _m.Called(ctx, hash) + + var r0 *types.Receipt + if rf, ok := ret.Get(0).(func(context.Context, common.Hash) *types.Receipt); ok { + r0 = rf(ctx, hash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Receipt) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, common.Hash) error); ok { + r1 = rf(ctx, hash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/chain/pra/receiver.go b/chain/pra/receiver.go new file mode 100644 index 00000000..f32b6cfe --- /dev/null +++ b/chain/pra/receiver.go @@ -0,0 +1,200 @@ +package pra + +import ( + "encoding/json" + + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/config" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/log" +) + +type receiverOptions struct { + config.FileConfig + RelayBtpAddress chain.BtpAddress `json:"relayBtpAddress"` + RelayEndpoint string `json:"relayEndpoint"` + RelayOffSet int64 `json:"relayOffset"` + PraBmvAddress chain.BtpAddress `json:"iconBmvAddress"` + DstEndpoint string +} + +type Receiver struct { + c *Client + relayReceiver relayReceiver + src chain.BtpAddress + dst chain.BtpAddress + l log.Logger + opt receiverOptions + parachainId substrate.SubstrateParachainId + rxSeq uint64 + foundNextEventFromRxSeq bool +} + +func NewReceiver(src, dst chain.BtpAddress, endpoint string, opt map[string]interface{}, l log.Logger, cfgAbsBaseDir string, dstEndpoint string) chain.Receiver { + r := &Receiver{ + src: src, + dst: dst, + l: l, + } + b, err := json.Marshal(opt) + if err != nil { + l.Panicf("fail to marshal opt:%#v err:%+v", opt, err) + } + if err = json.Unmarshal(b, &r.opt); err != nil { + l.Panicf("fail to unmarshal opt:%#v err:%+v", opt, err) + } + r.c = NewClient(endpoint, src.ContractAddress(), l) + paraId, err := r.c.subClient.GetParachainId() + if err != nil { + l.Panicf("fail to get parachainId %v", err) + } + r.parachainId = *paraId + + if len(r.opt.RelayEndpoint) > 0 { + r.opt.BaseDir = cfgAbsBaseDir + r.opt.DstEndpoint = dstEndpoint + r.relayReceiver = NewRelayReceiver(r.opt, l) + if err != nil { + l.Panicf("fail to marshal opt:%#v err:%+v", opt, err) + } + } + return r +} + +func (r *Receiver) newParaBlockUpdate(v *BlockNotification) (*chain.BlockUpdate, error) { + var err error + bu := &chain.BlockUpdate{ + Height: int64(v.Height), + BlockHash: v.Hash[:], + } + + r.l.Debugf("newParaBlockUpdate: %d", v.Height) + var update chain.ParaChainBlockUpdateExtra + if update.ScaleEncodedBlockHeader, err = substrate.NewEncodedSubstrateHeader(*v.Header); err != nil { + return nil, err + } + + if len(r.opt.RelayEndpoint) > 0 { + var err error + vd, err := r.c.subClient.GetValidationData(v.Hash) + if err != nil { + return nil, err + } + + update.FinalityProofs, err = r.relayReceiver.newParaFinalityProof(vd, r.parachainId, v.Hash, v.Height) + if err != nil { + return nil, err + } + } else { + // For local testing without relay chain + update.FinalityProofs = [][]byte{nil} + } + + bu.Proof, err = codec.RLP.MarshalToBytes(&update) + if err != nil { + return nil, err + } + + bu.Header = update.ScaleEncodedBlockHeader + return bu, nil +} + +func (r *Receiver) getEvmLogEvents(hash substrate.SubstrateHash) ([]substrate.EventEVMLog, error) { + events, err := r.c.subClient.GetSystemEvents(hash, "EVM", "Log") + evmLogEvents := make([]substrate.EventEVMLog, 0) + for _, event := range events { + evmLogEvents = append(evmLogEvents, substrate.NewEventEVMLog(event)) + } + + return evmLogEvents, err +} + +func (r *Receiver) newReceiptProofs(v *BlockNotification) ([]*chain.ReceiptProof, error) { + rps := make([]*chain.ReceiptProof, 0) + els, err := r.getEvmLogEvents(v.Hash) + if err != nil { + return nil, err + } + + if len(els) > 0 { + rp := &chain.ReceiptProof{ + Height: int64(v.Height), + } + + for _, e := range els { + if !e.CompareAddressCaseInsensitive(r.src.ContractAddress()) { + continue + } + + if bmcMsg, err := r.c.bmc.ParseMessage(NewEvmLog(e)); err == nil { + rp.Events = append(rp.Events, &chain.Event{ + Message: bmcMsg.Msg, + Next: chain.BtpAddress(bmcMsg.Next), + Sequence: bmcMsg.Seq.Int64(), + }) + + r.l.Debugf("newReceiptProofs: newEvent Seq %d", rp.Events[len(rp.Events)-1].Sequence) + if bmcMsg.Seq.Int64() == int64(r.rxSeq+1) { + r.foundNextEventFromRxSeq = true + } + } + } + + // only get ReceiptProof that has right Events + if len(rp.Events) > 0 { + r.l.Debugf("newReceiptProofs: build StateProof %d", v.Height) + key, err := r.c.subClient.GetSystemEventStorageKey(v.Hash) + if err != nil { + return nil, err + } + + readProof, err := r.c.SubstrateClient().GetReadProof(key, v.Hash) + if err != nil { + return nil, err + } + + if rp.Proof, err = codec.RLP.MarshalToBytes(NewStateProof(key, &readProof)); err != nil { + return nil, err + } + + rps = append(rps, rp) + } + } + return rps, nil +} + +func (r *Receiver) ReceiveLoop(height int64, seq int64, cb chain.ReceiveCallback, scb func()) error { + if seq < 1 { + r.foundNextEventFromRxSeq = true + } + + r.rxSeq = uint64(seq) + + if err := r.c.MonitorBlock(uint64(height), true, func(v *BlockNotification) error { + var err error + var bu *chain.BlockUpdate + var sp []*chain.ReceiptProof + if bu, err = r.newParaBlockUpdate(v); err != nil { + return errors.Wrap(err, "ReceiveLoop: newParaBlockUpdate") + } + + if sp, err = r.newReceiptProofs(v); err != nil { + return errors.Wrap(err, "ReceiveLoop: newReceiptProofs") + } else if r.foundNextEventFromRxSeq { + cb(bu, sp) + } else { + cb(bu, nil) + } + return nil + }); err != nil { + return errors.Wrap(err, "ReceiveLoop: parachain, got err") + } + + return nil +} + +func (r *Receiver) StopReceiveLoop() { + r.c.CloseAllMonitor() +} diff --git a/chain/pra/receiver_relay.go b/chain/pra/receiver_relay.go new file mode 100644 index 00000000..bf4f2195 --- /dev/null +++ b/chain/pra/receiver_relay.go @@ -0,0 +1,281 @@ +package pra + +import ( + "github.com/icon-project/btp/chain/icon" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/mta" +) + +type relayReceiver struct { + c substrate.SubstrateClient + bmvC icon.PraBmvClient + bmvStatus icon.PraBmvStatus + expectMtaHeight uint64 // keep track of localRelayMtaHeight per paraFinalityProof + expectSetId uint64 + store *mta.ExtAccumulator + log log.Logger +} + +func NewRelayReceiver(opt receiverOptions, log log.Logger) relayReceiver { + rC := relayReceiver{log: log} + var err error + rC.c, err = substrate.NewSubstrateClient(opt.RelayEndpoint) + if err != nil { + log.Panic("fail to connect to relay endpoint %v", err) + } + + rC.c.Init() + rC.bmvC = icon.NewPraBmvClient(opt.DstEndpoint, opt.PraBmvAddress.ContractAddress(), log) + rC.bmvStatus = rC.bmvC.GetPraBmvStatus() + rC.prepareDatabase(int64(opt.RelayOffSet), opt.AbsBaseDir(), opt.RelayBtpAddress.NetworkAddress()) + return rC +} + +func (r *relayReceiver) findParasInclusionCandidateIncludedHead(from uint64, paraHead substrate.SubstrateHash, paraChainId substrate.SubstrateParachainId) (*substrate.SubstrateHeader, *substrate.SubstrateHash) { + r.log.Debugf("findParasInclusionCandidateIncludedHead: from %d paraHead: %s", from, paraHead.Hex()) + blockNumber := from + for { + blockHash, err := r.c.GetBlockHash(blockNumber) + if err != nil { + r.log.Panic(errors.Wrapf(err, "findParasInclusionCandidateIncludedHead: can't get blockhash %d", blockNumber)) + } + + eventParasInclusionCandidateIncluded, err := r.getParasInclusionCandidateIncluded(blockHash) + if err != nil { + r.log.Panic(errors.Wrapf(err, "findParasInclusionCandidateIncludedHead: can't get events %s", blockHash.Hex())) + } + + if len(eventParasInclusionCandidateIncluded) > 0 { + for _, event := range eventParasInclusionCandidateIncluded { + // parachain include must match id and parahead + if event.CandidateReceipt.Descriptor.ParaId == paraChainId && + event.CandidateReceipt.Descriptor.ParaHead == paraHead { + + header, err := r.c.GetHeader(blockHash) + if err != nil { + r.log.Panicf("findParasInclusionCandidateIncludedHead: can't get header %s", blockHash.Hex()) + } + + r.log.Debugf("findParasInclusionCandidateIncludedHead: found at relayblock %d", header.Number) + return header, &blockHash + } + } + } + + blockNumber++ + } +} + +func (r *relayReceiver) buildBlockUpdates(nexMtaHeight uint64, gj *substrate.GrandpaJustification, fetchtedBlockHeaders []substrate.SubstrateHeader) ([][]byte, error) { + bus := make([][]byte, 0) + blockHeaders := make([]substrate.SubstrateHeader, 0) + + // Fetch headers with fetched blockheaders. + from := nexMtaHeight + to := uint64(gj.Commit.TargetNumber) + + if len(fetchtedBlockHeaders) > 0 { + from = uint64(fetchtedBlockHeaders[len(fetchtedBlockHeaders)-1].Number) + + // not need to fetch again + if from == to { + to -= 1 + } + + nextMtaHash, err := r.c.GetBlockHash(nexMtaHeight) + if err != nil { + return nil, err + } + + nextMtaBlockHeader, err := r.c.GetHeader(nextMtaHash) + if err != nil { + return nil, err + } + + blockHeaders = append(blockHeaders, *nextMtaBlockHeader) + } + + missingBlockNumbers := make([]substrate.SubstrateBlockNumber, 0) + + for i := from; i <= to; i++ { + missingBlockNumbers = append(missingBlockNumbers, substrate.SubstrateBlockNumber(i)) + } + + missingBlockHeaders, err := r.c.GetBlockHeaderByBlockNumbers(missingBlockNumbers) + if err != nil { + return nil, err + } + + blockHeaders = append(blockHeaders, fetchtedBlockHeaders...) + blockHeaders = append(blockHeaders, missingBlockHeaders...) + + for i, blockHeader := range blockHeaders { + var bu []byte + var votes []byte = nil + if i == len(blockHeaders)-1 { + votes, err = r.newVotes(gj, substrate.SetId(r.expectSetId)) + if err != nil { + return nil, err + } + } + + bu, err = r.newBlockUpdate(blockHeader, votes) + if err != nil { + return nil, err + } + + r.log.Tracef("buildBlockUpdates: %d", blockHeader.Number) + + // Sync MTA + r.updateMta(uint64(blockHeader.Number-1), blockHeader.ParentHash) + r.expectMtaHeight = uint64(blockHeader.Number - 1) + + bus = append(bus, bu) + } + + // Sync MTA + relayBlockhash, err := r.c.GetBlockHash(uint64(blockHeaders[len(blockHeaders)-1].Number)) + if err != nil { + return nil, err + } + + r.updateMta(uint64(blockHeaders[len(blockHeaders)-1].Number), relayBlockhash) + r.expectMtaHeight = uint64(blockHeaders[len(blockHeaders)-1].Number) + + r.log.Debugf("buildBlockUpdates: %d ~ %d", blockHeaders[0].Number, blockHeaders[len(blockHeaders)-1].Number) + return bus, nil +} + +func (r *relayReceiver) buildFinalityProof(includeHeader *substrate.SubstrateHeader, includeHash *substrate.SubstrateHash) ([][]byte, error) { + finalityProofs := make([][]byte, 0) + + r.log.Debugf("buildFinalityProof: remoteRelayMtaHeight: %d", r.bmvStatus.RelayMtaHeight) + if r.expectMtaHeight < uint64(r.bmvStatus.RelayMtaHeight) { + r.expectMtaHeight = uint64(r.bmvStatus.RelayMtaHeight) + r.expectSetId = uint64(r.bmvStatus.RelaySetId) + } + + r.log.Debugf("buildFinalityProof: localExpectRelayMtaHeight: %d", r.expectMtaHeight) + // For blockproof to work + for r.expectMtaHeight < uint64(includeHeader.Number) { + stateProofs := make([][]byte, 0) + + // Justifications includes Validator Signatures, we just need to build into VoteMessages, of the last block + // type GrandpaSignedPrecommit struct { + // Precommit GrandpaPrecommit + // Signature Signature + // Id types.AccountID + // } + // + // We can build Votes Message as + // + // VoteMessage{ + // Message: SignedMessageEnum{ + // IsPrecommit: true, + // AsPrecommit: SignedMessage{ + // TargetHash: justification.Commit.TargetHash, + // TargetNumber: justification.Commit.TargetNumber, + // }, + // }, + // Round: justification.Round, + // SetId: setId, + // } + // } + gj, bhs, err := r.c.GetJustificationsAndUnknownHeaders(substrate.SubstrateBlockNumber(r.expectMtaHeight + 1)) + if err != nil { + return nil, err + } + + r.log.Debugf("buildFinalityProof: found justification at %d by %d", gj.Commit.TargetNumber, r.expectMtaHeight+1) + blockUpdates, err := r.buildBlockUpdates(uint64(r.expectMtaHeight+1), gj, bhs) + if err != nil { + return nil, err + } + + // Update expectMta for next message + r.expectMtaHeight = uint64(gj.Commit.TargetNumber) + + eventGrandpaNewAuthorities, err := r.c.GetSystemEvents(gj.Commit.TargetHash, "Grandpa", "NewAuthorities") + if err != nil { + return nil, err + } + + // New validator list so, we have to send them on BMV with new setId + if len(eventGrandpaNewAuthorities) > 0 { + r.log.Debugf("buildFinalityProof: found GrandpaNewAuthorities %d", gj.Commit.TargetNumber) + newAuthoritiesStateProof, err := r.newStateProof(gj.Commit.TargetHash) + if err != nil { + return nil, err + } + + stateProofs = append(stateProofs, newAuthoritiesStateProof) + r.expectSetId++ + } + + r.log.Tracef("newFinalityProofs: lastBlock at %d", r.expectMtaHeight) + finalityProof, err := r.newFinalityProof(blockUpdates, stateProofs, []byte{}) + if err != nil { + return nil, err + } + + finalityProofs = append(finalityProofs, finalityProof) + + // Early exit and only need one StateProof + if (r.expectMtaHeight) == uint64(includeHeader.Number) && len(eventGrandpaNewAuthorities) > 0 { + r.log.Debugf("buildFinalityProof: GrandpaNewAuthorities and LastBlock is equal at %d", gj.Commit.TargetNumber) + return finalityProofs, nil + } + } + + bp, err := r.newBlockProof(int64(includeHeader.Number), int64(r.expectMtaHeight)) + if err != nil { + return nil, err + } + + includedStateProof, err := r.newStateProof(*includeHash) + if err != nil { + return nil, err + } + + stateProofs := [][]byte{includedStateProof} + + finalityProof, err := r.newFinalityProof(nil, stateProofs, bp) + if err != nil { + return nil, err + } + + r.log.Tracef("newFinalityProofs: blockProof and stateProof at %d", includeHeader.Number) + finalityProofs = append(finalityProofs, finalityProof) + return finalityProofs, nil +} + +func (r *relayReceiver) newParaFinalityProof(vd *substrate.PersistedValidationData, paraChainId substrate.SubstrateParachainId, paraHead substrate.SubstrateHash, paraHeight uint64) ([][]byte, error) { + r.bmvStatus = r.bmvC.GetPraBmvStatus() + if paraHeight <= uint64(r.bmvStatus.ParaMtaHeight) { + r.log.Tracef("newParaFinalityProof: paraHeight smaller than paraMtaHeight %d", r.bmvStatus.ParaMtaHeight) + return nil, nil + } + + includeHeader, includeHash := r.findParasInclusionCandidateIncludedHead(uint64(vd.RelayParentNumber), paraHead, paraChainId) + + if uint64(includeHeader.Number) < r.bmvC.GetRelayMtaOffset() { + r.log.Panicf("newParaFinalityProof: includeHeader %d <= relayMtaOffset %d", uint64(includeHeader.Number), r.bmvC.GetRelayMtaOffset()) + } + + localRelayMtaHeight := r.store.Height() + if localRelayMtaHeight < r.bmvStatus.RelayMtaHeight { + r.log.Tracef("newParaFinalityProof: sync localRelayMtaHeight %d with remoteRelayMtaHeight %d", localRelayMtaHeight, r.bmvStatus.RelayMtaHeight) + missingBlockHashes, err := r.c.GetBlockHashesByRange(substrate.SubstrateBlockNumber(localRelayMtaHeight+1), substrate.SubstrateBlockNumber(r.bmvStatus.RelayMtaHeight)) + if err != nil { + return nil, errors.Wrap(err, "newParaFinalityProof: sync MTA fails") + } + + for i, blockHashes := range missingBlockHashes { + r.updateMta(uint64(int(localRelayMtaHeight)+1+i), blockHashes) + } + } + + return r.buildFinalityProof(includeHeader, includeHash) +} diff --git a/chain/pra/receiver_relay_event_fetch.go b/chain/pra/receiver_relay_event_fetch.go new file mode 100644 index 00000000..22392767 --- /dev/null +++ b/chain/pra/receiver_relay_event_fetch.go @@ -0,0 +1,14 @@ +package pra + +import ( + "github.com/icon-project/btp/chain/pra/substrate" +) + +func (r *relayReceiver) getParasInclusionCandidateIncluded(blockHash substrate.SubstrateHash) ([]substrate.EventParasInclusionCandidateIncluded, error) { + events, err := r.c.GetSystemEvents(blockHash, "ParaInclusion", "CandidateIncluded") + paraIncEvents := make([]substrate.EventParasInclusionCandidateIncluded, 0) + for _, event := range events { + paraIncEvents = append(paraIncEvents, substrate.NewEventParaInclusionCandidateIncluded(event)) + } + return paraIncEvents, err +} diff --git a/chain/pra/receiver_relay_seralized_parts.go b/chain/pra/receiver_relay_seralized_parts.go new file mode 100644 index 00000000..a3495be4 --- /dev/null +++ b/chain/pra/receiver_relay_seralized_parts.go @@ -0,0 +1,143 @@ +package pra + +import ( + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/mta" +) + +func (r *relayReceiver) newVotes(justifications *substrate.GrandpaJustification, setId substrate.SetId) ([]byte, error) { + v := Votes{ + VoteMessage: make([]byte, 0), + Signatures: make([][]byte, 0), + } + + vm := substrate.NewVoteMessage(justifications, setId) + var err error + v.VoteMessage, err = substrate.NewEncodedVoteMessage(vm) + if err != nil { + return nil, err + } + + // r.log.Tracef("newVotes: ScaleEncodedVoteMessage %x", v.VoteMessage) + for _, precommit := range justifications.Commit.Precommits { + vs := ValidatorSignature{ + Signature: precommit.Signature[:], + Id: precommit.Id[:], + } + + bvs, err := codec.RLP.MarshalToBytes(&vs) + if err != nil { + return nil, err + } + + v.Signatures = append(v.Signatures, bvs) + } + + b, err := codec.RLP.MarshalToBytes(&v) + if err != nil { + return nil, err + } + + r.log.Debugf("newVotes: at %s", justifications.Commit.TargetHash.Hex()) + return b, nil +} + +func (r *relayReceiver) newBlockUpdate(header substrate.SubstrateHeader, votes []byte) ([]byte, error) { + var err error + var update RelayBlockUpdate + if update.ScaleEncodedBlockHeader, err = substrate.NewEncodedSubstrateHeader(header); err != nil { + return nil, err + } + + if votes != nil { + r.log.Debugf("newBlockUpdate: BlockUpdate with votes %d", header.Number) + update.Votes = votes + } + + bu, err := codec.RLP.MarshalToBytes(&update) + if err != nil { + return nil, err + } + + // r.log.Tracef("newBlockUpdate: at %d RLPEncodedBlockUpdate %x", header.Number, bu) + return bu, nil +} + +// newBlockProof creates a new BlockProof +func (r *relayReceiver) newBlockProof(height int64, expectMtaHeight int64) ([]byte, error) { + hash, err := r.c.GetBlockHash(uint64(height)) + if err != nil { + return nil, err + } + + header, err := r.c.GetHeader(hash) + if err != nil { + return nil, err + } + + encodedHeader, err := substrate.NewEncodedSubstrateHeader(*header) + if err != nil { + return nil, err + } + + at, w, err := r.store.WitnessForAt(height, expectMtaHeight, int64(r.bmvStatus.RelayMtaOffset)) + if err != nil { + r.log.Errorf("newBlockProof: height %d mtaHeight %d %v", height, r.bmvStatus.RelayMtaHeight, err) + return nil, err + } + + bp := &chain.BlockProof{ + Header: encodedHeader, + BlockWitness: &chain.BlockWitness{ + Height: at, + Witness: mta.WitnessesToHashes(w), + }, + } + + r.log.Debugf("newBlockProof height:%d, at:%d", height, at) + r.log.Tracef("newBlockProof %x", bp.BlockWitness.Witness) + + b, err := codec.RLP.MarshalToBytes(bp) + if err != nil { + return nil, err + } + + return b, nil +} + +func (r *relayReceiver) newStateProof(blockHash substrate.SubstrateHash) ([]byte, error) { + r.log.Debugf("newStateProof: at %s", blockHash.Hex()) + sp := make([]byte, 0) + key, err := r.c.GetSystemEventStorageKey(blockHash) + if err != nil { + return nil, err + } + + readProof, err := r.c.GetReadProof(key, blockHash) + if err != nil { + return nil, err + } + + if sp, err = codec.RLP.MarshalToBytes(NewStateProof(key, &readProof)); err != nil { + return nil, err + } + + return sp, nil +} + +func (r *relayReceiver) newFinalityProof(bus, sps [][]byte, bp []byte) ([]byte, error) { + msg := &ParachainFinalityProof{ + RelayBlockUpdates: bus, + RelayBlockProof: bp, + RelayStateProofs: sps, + } + + rmb, err := codec.RLP.MarshalToBytes(msg) + if err != nil { + return nil, err + } + + return rmb, err +} diff --git a/chain/pra/receiver_relay_store.go b/chain/pra/receiver_relay_store.go new file mode 100644 index 00000000..84007862 --- /dev/null +++ b/chain/pra/receiver_relay_store.go @@ -0,0 +1,60 @@ +package pra + +import ( + "path/filepath" + + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/db" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/mta" +) + +func (r *relayReceiver) prepareDatabase(offset int64, absDir string, relayChainNetworkAddres string) error { + r.log.Debugln("open database", filepath.Join(absDir, relayChainNetworkAddres)) + database, err := db.Open(absDir, string(db.GoLevelDBBackend), relayChainNetworkAddres) + if err != nil { + return errors.Wrap(err, "fail to open database") + } + defer func() { + if err != nil { + database.Close() + } + }() + + var bk db.Bucket + if bk, err = database.GetBucket("Accumulator"); err != nil { + return err + } + k := []byte("Accumulator") + + // A lot of int64 variable should be uint64 + if offset < 0 { + offset = 0 + } + + r.store = mta.NewExtAccumulator(k, bk, offset) + if bk.Has(k) { + if err = r.store.Recover(); err != nil { + return errors.Wrapf(err, "fail to acc.Recover cause:%v", err) + } + // c.log.Debugf("recover Accumulator offset:%d, height:%d", c.store.Offset(), c.store.Height()) + // b.log.Fatal("Stop") + //TODO sync offset + } + return nil +} + +func (r *relayReceiver) updateMta(bn uint64, blockHash substrate.SubstrateHash) { + next := r.store.Height() + 1 + if next < int64(bn) { + r.log.Fatalf("updateMta: found missing block next:%d bu:%d", next, bn) + return + } + if next == int64(bn) { + r.log.Debugf("updateMta: syncing Merkle Tree Accumulator at %d", bn) + r.store.AddHash(blockHash[:]) + if err := r.store.Flush(); err != nil { + r.log.Fatalf("fail to MTA Flush err:%+v", err) + } + } +} diff --git a/chain/pra/receiver_relay_test.go b/chain/pra/receiver_relay_test.go new file mode 100644 index 00000000..748b5a29 --- /dev/null +++ b/chain/pra/receiver_relay_test.go @@ -0,0 +1,290 @@ +package pra + +import ( + "fmt" + "os" + "testing" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRelayReceiver(t *testing.T) { + subClient, err := substrate.NewSubstrateClient("wss://kusama-rpc.polkadot.io") + subClient.Init() + + t.Run("should build newVotes", func(t *testing.T) { + t.Skip("Manual testing only") + require.NoError(t, err) + + r := relayReceiver{ + c: subClient, + log: log.New(), + } + + gj, _, err := r.c.GetJustificationsAndUnknownHeaders(8007753) + require.NoError(t, err) + require.NotNil(t, gj) + + v, err := r.newVotes(gj, substrate.SetId(3000)) + assert.NoError(t, err) + assert.NotNil(t, v) + assert.Equal(t, "f9f4d9b5017412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002014000000000000b80b000000000000f9f4a0b865f863b840f8a3b4e6712ddd01a33e9e4516e1824f7439d4e399b97b1585da6568550ad630905246a4e608536dab8c7915c192520a884f3426ea55c34a87c93211c08f6007a0000e441df11d2886c8134a93a68dbf26e82d5eb862e0b652dac36452d1de7ef5b865f863b840f0a0dba9261c4fdec2638d88fe7b270747632632224ce645131a62ea9401409be7a4aaaeca6ca1e2f49b9c5bcee37d90a2aaf09280c4b9dc48783fac92fa6008a0010934937c0ecb5ca84b298a6be8a05aa2739db425fe03ce195744b9c751bebdb865f863b840919e55f926139f6c662bfafb2c00a58e289d26ab871df16466e0c8e6c5241c7d5194b95c5bfc8887ccf77a43414dc0d2037d49833516a372cd4dbe6dbe3c300aa0011295d0cec16375dd645e533e11778d79760b1328239d9b8845a48de8e3daf8b865f863b840b6f2de2bc2d6f3e163d0d6d4c9aceb303e7eda7f98dd569f3cfdbb70f34cd305751757a0d831ede690d12e6f366dbc294510e19281bd3ac01becd783ae9db905a00244364beceba2a73ddff1eca2f19e6c212b92e33796afa29ece15c309d0dc01b865f863b840bc9f4b4f0139bf95077a45d133adfab518dcdc3ffafec235869bc760ce5642249e787e8cb666d799633cf4990b568358c2f364c52504348dc705503fdf0ec203a002c79e9966db0947a30acd331e753aa883e758ca3645dbdb19301c4eba95a6d4b865f863b840cdae639ab3b86008e959226413fe1c70b84822d7049f61503c1eac6a5235eff3f0b78347b91e668c1b869df27aa63f3577549c6fe386e2d54a3279e3a425290fa0032251cc887b1db49b021e033ab4438bf26f69dc78202cd9e035fbdc62b9782ab865f863b840ca2cecece16a9000a96595d93a093636c20feb5fb65fe70eb4eeb4bb05cde8b3b7b2a4bf60aeda0cba0c0f730a286a4899b59c3a5732dd8a8317487b0039b50ea0035eb6fae7043b47cdd23d2faa0c65a67c4e5b6c5590f546da78ed240ece1c70b865f863b8403e4ab01d3dc9762591fe6a96b163b445720da13f01683c6d1bde3c82aa77447cfb7fd240399283a6b228335dea534f3763536eb22ee169d8e8763aff54ee8c0ba004cbadf3d7638796f99321fc02006649e6faf7143d1c74bdca8c63ccb688bb34b865f863b840caa02fcb2fea7981dd0816b9cd6e8e8c180a6f555a75cc4d702f2b6eb668caa3bc2e2b7886298892356a66bc6f55de1f6af53fb76e8bc9b8233c82b9b4804f04a005073dda3c462b94815e11fbbda3a021d792b7ef58e49be9c51413cacaae2292b865f863b840a62347b5d9ef98152074505750709ed855413e3ae518a4f01f040541e2568ec8c7e2bb36bda88d423ab9e7b826d4037341a402f67f153ee5be22861f4b478603a005c401b4934e3f81c181da546eeac19c0bea5aa3e4f3c0322ab4c05513a789bab865f863b840af745a5ad9b81d2d642be8d5637e792e5adc8d0726a19bbe0796ebef444c3beacdc937964f3dc3bf1d68313b7251f2055f8c41d755f2138d8f3dbfb09fe6bd0fa005d9fa10ff7ad73bc75f401cf2ba4c69271d0a6cfaf5ea98f2b81f1817c3e5d4b865f863b840d2683bd9119ad06b6a8b6eb2493dba77ea7e3aee8813a701b61dea5595c082cca65e6e109ee8195db18dfa04207d55d16b279915d03ef27d96b6c1d8a194630da006918fbeea920656f3fb0f71f22fd896a8e9d6549d3e60bd30413a457c4f9a68b865f863b8401f3ee7a98af8fa6f3e3861b37eda0ceed19c9ff5f08cdb5deb610295102c5227973340694898e097db5478f564ff00c6a5140389ebedfc587ec09b5d8373470ea007059c40884d31b801dd5733495a3c9a0102728ccfc4db5fa8eb9bcfbdf24c89b865f863b840fb430f10e61795591bc0e30902f7620cf0056b34ab9cb1d36e0c3e5aa850c789025f6b8f047cc8c17fc51f00cc88ff1ca6d5af3c2b6e657f739508e1346d3f01a007197f87de414de14666153a601dbea8070f80964fc99ed58f0fcfda471e8560b865f863b84083c46be6195466b143ade4a8c4d6e11b404e4d85e34567fc63e57968928b570cd555234f4b62647958979797a18725f523f33a95ee8a0cdd2ac8762a354cf70fa007256e10a14dea5216ac1594ca108a5da57c224f12aaff57ac139c95984adeb9b865f863b840fedc542dc845055a09cae2456bd4de86a57d02ce3fd90968b874051f1f40761d4108e5e624e93a22ba0a7217e4e03a9a8391c2fd3a95e2649fc85d93300d800ba007a6e225b63d830bcbe5d0fe9f2478cced8db0f1c01d40d0864ff4163d61db1eb865f863b840a063164ae7b982530dee2170bb83d06d3fe592430d2bda74e3d88cc0ef9af3be733e03b36a8db1e7efb811fed082c736dc818996482cac8e2e0570eac33db408a007f00c1582571966d460bfb41c2b5f2e5144e01f0856c15e6bd3b43beab7dbc0b865f863b84090e108e461d1ed272ffe9baebb35933883dcaad390179c8b70a5de2151f986cd5c944605c7166b72cd4585d052e65854760690343e023233fefefd4e1666cf07a00842cf210a31a977d49e0cca95f903a410009663c40f3a57abfd32e3636191d2b865f863b8402f0c1b564413adc228240539aec27215b78c19e7c685e78dd64999d32f82651550143bb940afab6c18838e5489a0a02f885beb2388ce22205d1aaa53acf72f08a008575524a0cfb5d74dc0b9a8e16ae0d8a51a076732cdf87c248cb7b9f55633fcb865f863b840730d4af0c25bc4cfb502a9edae757c3f731991ea19f1bf7c54c752161447ff30c78bb1086cb1849cf1b7bed0c49d4ac59ec8c27d06560a6db046499cf9033c06a00880eca6a462d2d8daf7bb35af246df0da742d5d532562575ac203d965a67893b865f863b840fc27fdf89994b3f603ede68a1153bc7203114cbb7a9bccfa579fa34dfe357fc0cc3add37b54a818287ae9ea8ac81e3688679d0c01fc63c9f8955cbc35ff9240ca008836ddb7c53a60022f25256719c70c16a42924c14db61787ac87e5d39037037b865f863b8403e31b4736ab4f4d8d04a75e199a4a54ff8f6d50fb3127c0a5f4a48b2475201f7fc7ad871f00307a7023f1010a4ce80366af3e46aaba2b94090ec69d329643805a008e3d6d26c79f4711f4881590a4fe13cef4ee3dbdffe9358324e8d3139923a16b865f863b840812dbb65b0c418562d97224a4fee3fd63992ca149d0591e513530887b666883eec7ee75968316595029c24752994a1c7f1463f630b1b89bcdc6b08c560f23f01a00934f5f12034f9df3e95a2b4aaa009b81b9597a7b12d11503d38a21745d838dfb865f863b8409fde1a96ef268304401559a8bce26e487c23197844f487cff41598ac5eaa6a4620b79e94b165f8c2da6992472ef8016514c0e4c46db1ae63a925f899dcebfc0ea00a293f665105de4dc0f73a4c05e23e85e99495cd626604c001848fb6ad28c23cb865f863b8408ffbdc7b995450ecfa59c2770913ee3dd63ec1392775b234d29d8093fb40746cade26963ed8d96159547cb772ac1d7d5e6c33d404d8c114c0ebca5cd555e8e00a00a8002450dca58abfab495af10fb5c63539c281321413956a50447565da2601fb865f863b840e1d437a711aa9bd4358aa741adb603accdfd9b932926e304b41ef23cd0202a1873297fd901d21556c8e18d58e8e19a78424c4c7728197b6248104d2ca3d28504a00c585052f87854d6ae4308d3c24807d3ba34ff7c9bd348244a7606ff913fb2c4b865f863b8406aa7982776add277697547948061f29a906b8affe30b315bce4c06ae84506920b21783099995aa52b2269cd6ff33b7209c009786efa5bdb8d286035c3cd9eb0ca00da51f531108ba182be4fce0f270913688935fa4dbd9ef99a01d12dd4ec4351db865f863b840979da9c61ac726eaa8019376a3cc88f8026847d259c8feb4f5d34d45c1d892646da4ced60368be53d8f854573714947844f0948bb5e4e8fc2de5f10454b1ec06a00dcd8f189b05b1f230e78bb22b4f76236a731ea2e64857bfa802f1bb511e58cab865f863b840328f5c0e918d9e9a703c8de6ed966c19509c338f107ba319a9d033026045682cd249c7356e26f04c9b17727ea52c66683a1897707ea8919511bbf14a851fa10ea00edae107f4fb04b110afb1ecf53d4243a66f15c4c69876d6b751f5548f926b8fb865f863b8401aeb58fcc48b72d7123cf2908d9eff13fef9443cdcd6e0ce7c4f1e2fec136618d805648646d1c106b6ec1ae4dd53a003e466a1e4ea52393e3a32db9c05fe3504a00f4ae91d0c81c7bb1027f8b741c3162c33a14e67d258a02aea3615d41335285eb865f863b8401ca11ee4fdf474c9cebb6cd202e423af643f66d989e51d46efc27800f27f0ac4e53098a83809637a69511eab6521a8879ce03fb52c05add64311d151f4dcbb03a00fae5e80cfed04be08a8566ef8ca22a40bd429f23f44954a3df9c1c1603c339eb865f863b840c9a3962333a36d0325c09617d86747c7f751ce178fc42071fad3592ae583c2f526d009539f1a403c9de6059d82316deb91d5fc23faa4f0206ad72a2f3eddde0ea00ff5797efead90c9a065afefc72709e0a5ea2ea98cffb9059f995638b45ff77fb865f863b8405e980e1460ad8a886cce66d87b224779c08bdd6d1687e44a60a6fc9c16509b5187c13cd2d91228d98212d3d86ba1d05c89e9a19de1c36d92531d23c8c5af0606a010180e4023674a455295547a417d515d167c101fcd03110f5607e076dd6f90ebb865f863b8406435725038cd86984989f6547105becb20a72a192d5ba55501a2f2f803ea434a785ebcecd8c5c3b4c4f59aaaf43c07cd1ae7886fde417ead9095ea37d4500d06a0103e63d2855685e2c7082a5f2ad28ea8c72923a9e73ea7cb8e014213258a760db865f863b8403ffb3db5879dc3d7df723e9db264f34b647538b14a8cf4ec785b7282fc4750d048fd6559bc7eb2fd9a0ccdc7525e1e5e0d562dd832a7b23beba90baffde2c001a010f6d511ddd2ce18be483709e72a906dda0bca20ba5609a564eb3cd8555215ccb865f863b84067a76d644049b10deedeadbc2ee6f0a666034e27a5a1fdb2dd3222a4cf2255085dddaa85fc3c73f389742a30ba06a1937dada9703772eacf8ca309a8a315e00da011186b5717e21360a51e6ba054de53ca7078693fb9e1c30cbbb166023d7b1e85b865f863b84083cba7e17e043b0384f8b475731f248983626a7cee0730cfbacf695acebeaab311548d582dfdbab4f76718ff6c6e2fa6f160e8aadac7c283230e9b16a7b8d108a0113743e1317d516f7938153594e3ec05a58a30db8214e6665c2272432fb10259b865f863b840c82dafa7848caa298feecdde2a78106eccacfc58c50651b125ef7173a383e665e3a0e6171c315ceeebf43a8d25f74d6e69449c19b4e7ffd015099fa53abcdb0ba011786c3c28485db63801ba00096fd41385239a7d98abd3c3a559149775bf364eb865f863b840ac4ca0cea46ee402cc0e4ed95f07e1a56cf2f57553126d20404f0e8411f4581e336fd6909404eefe0eb51c4fc8de6f6d736256af3d0ef8751040672e624e650fa011e96804dd8b6b436e12a1c9b7a4e9395a373cfd80280f99433ed42eb583e983b865f863b8403116a8c3a2095e85f974bed14c614ddf5fae1300f2dbfae9fe0d02f8e25df8a74baa4aabda3e24f5211e5f581d87409b25f11b1282542621f1852479bfd62908a01276bbbe6034c52980b03b3afe1bb1b50abb6fd0b1f1b8636c4d8ac82e42c937b865f863b8406c8f8f1458aa70dccb7f9a3bd52f7e190eb083a8519839fbe5e277e03c71fa9824c4b0340ecfb4459ec2bcb70282f39a7023dabf43ab77e9c6bda08e3b37130ba01332b62bfb86742317bb10f17fa6b4225fcb31cb4f3429dc709c8120ac841ae8b865f863b840efaf13f22dd842189864fe57fcfca8477b8ba2af2e5a4d47af23f868e8991b41f86564123f2e05d5417f2e31cebf022229cc3ecb01df46a999e50bc7a35a0302a013936b39107386249f83bc407a047c78623d4b83b72fccdcd7b753d7a091d770b865f863b840965a9c03be09560f9cc52457ae56d5ded1edd91c3a4dd33ff1af584b96ec16193003f81ad6bf77a883eec24511d25fd0ed9181f58ba693c7cb33664e8a929e0ca014205545ee3a0a8577ac313e8b1430cd79daf877ad9aafc59f43f6718bbb648eb865f863b8409869fc3d02a37c1702159f5f6e2b79c8e3b902f60b56a8241db54babeaf6ebdc4aa4a5d35529f8d27108d02c77c637c5ae10fa5f40c7ca1d8788fac5ddec5c04a01431e7c61d0cfead281702e149818c670308d85099bfd6526a764806cdc82b52b865f863b840a36f3c46562162d633c8c340db072a5fe1e4920109ce2de26e1fdbb4278a78a6fedfcdd9506a1da19a8817d633326ffa17859c1b187b83a598db712f3bc13308a014cf21c5d206353d0a3ec887c2a90dffd0f267e9ac78ef45fa99b153116ab0b5b865f863b8401e232cfe09241840a3f1ac2380b612a92c1f6e7fa4d37ef31c272155688112d2810bf99c3529ae21f9424a4291241d72109c31caf4aab8721a6f0a4e4348c607a01575952139d99bfc450335ca368d690de04d951e75473fa94a9d9e7a6e39808bb865f863b840463f8d0716ad1683eb9bc3c3fa7f201e4c17b5feb1c432a14e26faa8115d2c4886997ebafe44617cbcdc3928694dc98d24b52d00fad2b25fe86ec87e84602d0da015db890d6ab6f3b3d7359364c6a93c19d122dbe593007619d72da3fac72542feb865f863b8406ebcc117c7917b42a5b85d1cef039ebcd2c6fc6d108963d041824a4a19fb2ff42d8b29bdd63f669031aec1fa0dfb7c84e4ba14c3d97b01ecb853fd191388860fa015fe3df6ecabb57bb90cb9b10b79569f1cdb2f7dbd6bc3e95cec8d9c62dd5efbb865f863b8406dc0542a1b61394d773ab2139d01213600d11b3923937210a3f5696457344a8f9f96cb236a0cd9c5af3658db03d8c3c7cedd962ef0a4095b2e92b1bfcd59a70aa01629a134ad8e3c2b2d107d170ff8783a58af27f4387432a294ac7f16ef5c8b81b865f863b840e2bf4c751323759042fceddaef24c3ddafd82cfc83fe76cc59c13105f6f85d615b472d5b9b1db3816203319c0075758688cc4a96a1bafdb5b6a0b628317c5100a016ab5563ff24c62a172247d0022fb6ec0fcfcc013996368c1ead68b29effc868b865f863b8403297df652b706897b9384cf4ae7d064e24bc71e2b62f17bed375a7c4cad5d2542092af19631e5d5ff9c4da7cad77b8972bd7e7118c171e5c1220fb33c2a3870fa016de42b0da1b44f8921af23c1adf65169e827e6ba63cb81930b77ac3c090f494b865f863b84028e51e24244336a8d7b0e1b4c26da3f271d170784018738406fa35fa277e98e466465c697529745188bc62509fadb6ea725e8987f4d662c49a6fd9add8d20009a016e0aa37d33f049c260f21fc0dd3c5adcde5a2adb039e2d4fe04dc40ea38f484b865f863b8408351f39010ae4b3b474447396610ce855cfe3468d1ed8601d04d2c2868c8b27592f09dd94b658d9b68948de0e297f76338070be92e0b531eac5b88aaff670800a01721b85adc12f3f21cac0d4e26ce580af00bc0482126538ee92905aedae6ce5fb865f863b840a0ac4bd8039fdfeeb7489cd470116b937f0b50adf9de0042cab66f9141d5bec9c0611c422a0c713d232e7431923299fc9e6e3a72bcd98b24d39326157b331c08a01737a839e095aafab0ff8412e0bde45070b16c38bab99a8f14581625c87184e7b865f863b8401cb3f2aadc26f9b75f1481c629e2cc1773f0cb0ca52a04a26990f770ed64d0a7b0367f5e59f78395e0f6cdd32e04d093b6c4c1849bf6bf8fc67642fda76c130ea0186da0f65b865b067ef9c0617558bc5f4cb76f1bf9115093d4fc688c799b26b2b865f863b8409b7ba53710cc2c3015307ac5991dba94eaf66fe352fd00ea838c3538bfa1da01dced5d528aceed475c1c33b42d12ede9d2897b622d5a65a8419ef3bedefa8f00a01964933057fa15f03b807dd35816b81d06be5407eeba37e2dbeea0d2758cb7bfb865f863b84058e2e7239cc307a9dd333deed2c465c96151e2bac40a5b23beebcf1818cb76bd60f35575b84f74d794b5cea031dc8dda9e3f30a0e3640eea3b5d5a675ec5ae07a019f3033cbdac4b7b650dcd0d22e9c6c8349e2561c48c904bafab1a9d630eda4ab865f863b840df5ccc3aaa9b0ec72c4b9da1c959cfe2d7267d520f61b14e28f394f2dbfeb52a77396753bdfae0e217c03eba12606e5a1c851ed3f228e3f96264a16a5951dd00a019f674198627ee586c93f538f4013ea3624d9ee78176a65ddb9b81bb2240769fb865f863b840e8554736a86ed3afd84b76ba6e1f368658d523b28bef5e202541b78c9fc824e0112858aae6ae0d26ec0d2ca56c4060f55d9465a1d611d16befea5a7cd31fc101a01a1453da679611351b7abda6a29d2305fa68392f48b2a9ce27117202e63fb0b2b865f863b840ee40544faa9a651cfda05b440771d65f330f794c86df9c108c2b338297a0abb350402f8219406d6b71c2d411e96871deb2b83f1389609ce4d51c71313e51f60ba01a621619f82c1498d100c25a49f9e5a41ae1d549abafc8ad919082a5e3922773b865f863b84017160b64d2ba20b29ca0b9a1a74ec91d2869198c7979b8c5e304d0ec1090dc6290774d1d817c946c9360600d5be27ef91772b9d7b8de04f87636f7e122878208a01b00bc4259bd38dba94ce98f0c7b8564645a929232ac09b0988e21549c070ad2b865f863b840c373a87ee129a7869e6d4b6d4ec36c4aecbcd552e4c8f7ad30a0d513959ec77140d3cd38d545975488db26cfe97b18f3c6cf854273af4b3a0c5af3e0211feb0ca01b5274a1614c68cdec40868ae34336e6c8e2738b1a4974bcc2830645f24af79fb865f863b84065f3a18d8af532f59193fec1334728841e8b4434da5866d1f2fb4c45da92358a9ca92f330d8a983caa3f6646fb3a8264810c73567382e1d3d39700340153f60fa01bca3939a0da1e8fea6f673c96bf55a27ac431fef7b2e14376cd5c3e54a18567b865f863b840e492ff8c153242b172bf0a0156e5dd05b1777c0dfae441b2091a77ab2c8481e8c27c45a263767ada7151a1512eddd88030b923af6266d4d763065ecaface1900a01c84ffe679c52b9be6fa56992429da5b57852ce168bdce7b69d8ef2be027f77bb865f863b8406c34e655964de9df8fec3031973ff81646de6f6c4856526caa2f4e11e5edff17eaae2cab67675c5b2e898391cad25f537549eed0de463e6cecbab0c3417b5301a01d0866f96bad61ad548632a2a38c62fbaf449fcf601edd37f66c8eae052f8263b865f863b8402a3241e1430dc3ff43ac77451e99336ac4e3baec8c8fa62eadfbaf2b08b2042fd4fbd3856ec8947eb633343368038f5297a786d5bdc71fac30b63e1fe14a8d00a01e59efb1ea0992ef40c7903d74e338db59a7e5cfbb46ced1971ffab848fc8c6eb865f863b8409f970f387088957f31812f30427ca67c2e5e197105ae7ad05af3e88f253ac4a60fd2cbd2838e4000d75c5dbee90a8cd12dc17a74d23e145e265ca9771316fb0fa01ec3fe0ea67c80f6fa1c94f6401a67517beea40fe497c98fc594d8ae68a0023ab865f863b84080db68258970174fcd847f78e5be4d658e660633bab9c67827a706d6ca60e67357c18348a0daebc79f553ade04fd16c1196a7c5282c362096deb45bc2e3d4708a01ef94e359e2b8bc0df722b4d3b83321ef968f22d848233611470e514dca6e23fb865f863b8400fe40587a170cdc05bb43f7cb34302814203e255d53fc5dff75237d20de5684fde4ac138d2bf8ab9aa33f4c70f435fcd7aa656a28f668f9c20090ce44308e40ca01f0bf1d32ffa3d521aa29e2c847864415ab738cbbd92faedba6044610502a064b865f863b840b479e9bbb6343e1bff3f63f5de3910798c04fcad584fe25662461eedb8d865cea824ef0a8e1888470bfce896feddc4762acc8972f728f6198385009639f53a01a01f0d090aae06e1216ff251f990289742fa042537db56176029296236873b2e2db865f863b8405013c0316a5e6baee73672bb9ba38a29023569f7659c377e8e9e1026eee543ab34c8aeec0aa367b4a81b5c4f4773d269de50d29600d4a2ec8653bc3884c8dd0da01f834777e70a38b7d68db63f5b3b876b6d059cd2871a2e060534f4c49686a3d0b865f863b84049d620562306f52eebcb09289b81844982c7b4ddfde379a70dbae61070609096c2569eb064f1ec3f4822cb8bd24941c08e50c01664f6a2f3bcd6896be19f8907a01fab7985c3e86ce8b0e7edc19b0118e97529929ffcd514258d29c3e445408c96b865f863b840ffcdead63343420658822f2c24011c1ed5d2342219ff6d39386cfd988179d43dd1a0202e0fc19f2d67febfbf39264b49fb6b05b118f6c209b58486e34c5ab40ba0202342f70f6f3667268a6e997526ac8a12688e4f7d487e044f7a651dd6af5fb3b865f863b8409d02cd63ebf49efedb35ea7b80032428b9f42a0e84e80e8d54f5c3e5acec97063d5f4a304017803dea9f4f6b5e033e2c3f38e938250083b65c6884b23a480602a0204ecb11e2cbe2fdc760b77c2708163d99a7fa6b25c1032332fc2f792d50d9a8b865f863b8400e87b815bfa3205da3ae74c577515c7297d2e19fe074008b7f3fbb2e40007391128f3d1d691dbd2ab35d3deda39a289e4f8b8d4b38424c00e09b2892875c9108a020fb5eaba86692e24c64e3c2f7feeec80a76ccef9a122e20c59ed0c459b4a7a3b865f863b840f9b3b7e49ae1ebff47de49b415db368270f287deb013b2a57ff74964592d7df7ebce5df5fa84b53a6263a4a82008f7e819cd8d790870f792bc990ebc8a11540ea0218f73f89206b94b66229d663de9327f7c3a326d74cc609e1146aa18eb64c19eb865f863b840598239993d7eb2536064c27bec35a49c3a46f0119dc2920077a9aa9c98e130c16896977cb7b5abda512d19911b8994cdc5f382e84eb7519077af89e7950b8a0ca021a2c7244635d9a632b872b7f88bd407efed5932de44dc6bd8a88b6178ba7f4bb865f863b840df05edd9ccd2abaa552a8038e07477ea54e78dca4eba1bff944246467723e1d20ae4bb37f8536f34e6eb8f7bc2e4c5c9b875fda8ae017bd417fa694c75fb4d06a021a6331abad55acaee26f5f1843fa6b34356c3952cf8441e3ae430e5594fe03cb865f863b840f51034575960c92b106c1360906236a57349430557e2796f601acab58dc3c48ec4167a159b9d306e7513f449c3cfe58f4d4f0e2b2a51bc9166b42073084efd04a021b1e00b9f893c9d6c0897811284b2b312f5bb85f566df2aacd736f62e66259eb865f863b84016e7fe9e8ee27ce0f23ca492f4673ee3c5f23190ad293a629a34e8a56ecad7f2abb3f635a1f1c0ecad01f22aa5a3415843f366475c3e68df5708aa8ce54ebc01a021c67cf8f405be15a1204690dc99b75867efd0e4965a76d0c5055e2cf93636f7b865f863b84059f19aa2032c84dc0f32f6102fe2687df23f75703b6f6d7f92749112e9e391b4ac75da5213a72c5e8420c57a282cf6eff42447f3124ca4f982d85abc6b2dc909a021c8c75426837b740b7fe6f5e7e74af33d58852adbc595d73557757927e6e2c4b865f863b84029b98427c07fede7df4241a8ec43e116898e71e99a0930fafec9ecb0a42aa7dd1e88ea3f0242cfe15597c6aa793e34bd1ec359f13d790930ff5d5fbe9b75af01a022041166308736f6a8327f85be60fbfa2625aeb3cf3908d2133ad51759b8d53fb865f863b840b3087ee7824fd785258ae943b474e1c8969b06e60de56d5b01fa87c103420bea2ae99e3ad0b2fdbd58ee79f5dc249a934146c1b0989a7ae2a51cabefc9937202a0225280ca91339a751f61a5b8546aa0ee10e75201445f267b0e2c2566796d72a7b865f863b840ba4ca82af26d02f7fbb13f7c8921ecf4d413a40560a50dc1939f13b66c9b6e9c24b3288b12418ff70bd27f7fc471143952e3b2cc1b7af6e4b9abb464daf0800aa022debd07671469757bb62c902564bcc850f10cda1da8e78eb7caf79c9882fab5b865f863b8403634488711f39fd1394443165e9d4ab906760724e01ff68b5ad65fbc9d8616c9e16d492a7f662c3df50f6792fe5a0d0e70a0019da8ee9ca1057cbd8aed3ccd09a02309e8fa28e5378178652ff984995ddbcb301b73e416106ec69eceb3b448d736b865f863b840b5af961b0a17b1381264096c26a64d34934b6302b992754b250d8a44d0f51a161f492fd8b39115567b6d2e8431a761a9765779cf6577fc29cfa76c0870ec030ea023270c415db05fd6d5c184e152b3d75a8d53fa9a43b025b58925caf748c9a23eb865f863b8400541049f31708b373b625f5717e28b850e64b69ca0b18959db123f5473484d78f2cc8a828178b03526c201adfa579c532e0115455ee69d409caa4491b4a76706a023d2fd575dda24f66dfb33da885b714848d8bdedbee3702e7af08e4ed97dc0bab865f863b840513c6cc5fa6dbd8abf521d63e6f43e72ce77887453ed225011cacfcd049c3082fe9ec73090726a18122a8382ede0d0dee28cff7b193d072bf7580d01723b0e08a024453bbc105fea95468b3cfa025ed820cf33192515d7c062b543da1d452d4916b865f863b840c47818f8bb4e73e20ef084258cfc566c17164144d42aa26c4e1e3c6ff5e1365f8636bdfaeb59100498756fe7b7c4955e0878e438ffe697502823123b0ed92306a024d36064edf815632b7d07e10886ad1dcabe78e923d1f14f7c73b21a0f3a95b1b865f863b84036015a94e4a345ca5ec9ea59bc9c8f52c72ecd25e0f7096b91f8e9eee4c1b7a3fa075645ac5d58d492ad3d84f77f6a9fc73fca3030f0aa60e68fd926063e8c03a0253622dd0e934fde3d1f8ab9308f027a6155152d0410210fd257a1e521e8f844b865f863b840b7ead6a4f88398525c39949525a2737a203c9bb90a42b547dba0a6878d7d678fd1bb8405cd340444f933d2a1a814da7ace644eb291a1111d851c4690d3528004a026be8d259a81badb0b0e53bec4cacb34d1d6853bc99394c02629766200946f84b865f863b840adf657744553f6a829d2d25706362c044fec4c675d4d9355f03ba1796e63bc3af9d6d387266c71b7c5c67bb20f2c1fa6f3eca47423bb3b2b01f489e5ba35df05a026c264a070c0799212702ea08d9c30efff41d199302b7da89e976aed33892928b865f863b8409e88c1cebd9511afd5caf1b7496ff3fa40548f8c3a3184fbf5526132aa3a755755e38c3cd9b5e9897aab13dfca51914977284d71db66bbc54c394e7936c29606a026f54492e909b90a4d83679717793dd90da6b690ea67f0bb50cbac8b9845de71b865f863b8404ea9ca7b257121bb3b5ef120e941407a7b638d86f9366d36cfc3e319aff06a6103199b30e564f886f61e63fb61ba6c0b6e73cecdfa621e1d18fcf658fcec490da0283122aa6a6aeeb235b7a2385f75f015b9af4ece967695e419f63accd6703737b865f863b840f80068156a721cf129021fff2d94374107754e39c1d506f81634c8d995ba152f20f01e50b0c8c812aad1e161942ff90f19af0d294106d6100e916f9358f03704a028a6ad0258633548f3111512f2bec89d9af385a28002a0dacaa95e40d681d4deb865f863b840e17ff7352687a9e1c3c331c117c9642a4f8d00375fba16f92666b74c2462b0ea3ddeafa89a8fc53ef384379b40790d1646af4fcf293f6ece25e19eeef42c5b02a029139bc1e3cea954ca9ea3c4c19aae309ccfe8f6536ad6e6a7c81031906929ccb865f863b8405fc23d552143a3e056874e95b3ce4908544825c4b4653795367dd09de3a13723bce04f417ada21f43412fabcd5b022a8a636ddb4b48e1c570afaa7b653f13003a02970c966cfa1d786631f4b50c4ca8bce2d61c3719f0cbe663f2c12d2aee49800b865f863b840cda6d8a155faf239f61cc33732e36134c5bd8cc91002386e237ac9051dcfd2ce65c706c3229fd1b41c9ab0cca2a7f30c3196c067b4adf53bbf63d72c3821d002a02a53a045f66449948ceda879ddb1a3c7eb74d438963905407bc1068d5adad630b865f863b840245411b400600b24a3f71bbb63a311b49e1f4cadf2ec670979be70d1cee0be98dca71c9419be2198d381f12b246743fcb20c37841b2719077613d586f83ffc04a02ab31b2ae41579b19899dc1a5cf88cbc064a13b9d4e5b8609504bb9100736638b865f863b840393efa37ca1f8517fd308a9d3aef55e027dfbd1d6f579939a295ec2673c25e049d151af6bafc9b546739a68383b0144b6f9a8d28f90c336b9d4ece806378660ea02b1dc16ba4794f65e3b9a80acc4c621cf3771f137ae66a10777c24e64dd93915b865f863b840a043336d576e050c3ba364a4fcfdeb30b79a4aaa7e559101fc712ac2242708598313f2fe1d09040e6c7ec9c35e4bfb1ba442300e6799f60d0636ad8e2e809a01a02c3e4f6da2743839a2d5bb56bd69325aa63b7bf075729180615f16cd13c57355b865f863b84087d34661c1c232ddcc467771d5a60182674b779000960313b56c978ed8488343e17a6345b9c5c734d23c6eec55521a37158fb461e5680750ed86390859d67402a02c3eac29c084cc7f9b3385186b09fcaf3199412d01a254e4e4cc092f41e6d894b865f863b840cbe8250e7c85abda600c1e083ffa2ae6e60072f2b7097607632833fb0097d585e3c2891a4b3d294f19d1e395b4d2be459253b31c12723de83761017c7f934b02a02cd4047d533ee477213ceeed6a1e0d92ffdb576b46fb19f88641116afe993ef7b865f863b8403be173ff931120f19f72667110c1e4336484cb3dd5af75914224b09f3d4f5af02738a9b31d3a81ddc37208301cb025d9c818ca9b414f40fd533f5d291dc6e901a02d7046462826854856153545ea81e9b22b133c4c3ad3c17828cf755a2b8769cbb865f863b8404444c90358ff72e817ca2a9d98ab85f3b1c7121b1e7617c4d248fcde439e9eb1e7135b4fb8b11a2d1d2a36746fd2c98c7cf8c8b0d113bc6f4db1c0c689b8a70ea02db2358096567b7bdf89f7b55e92dd1137648685efaa69f9d207bb9ec829bec0b865f863b840f682a524629af33ff8e0aa9465918fe90f0b87b58afa863311437f0b91287d65ce1eaf47feff65552dc21971ebafc3515617ef885843098d9c32ed0803de8b00a02dfb3abb18baec3f897802f3f74ce2251197c70e36b4d1f0d4eab81a5416725eb865f863b8403d01c02d02846ae8968a321bc778bba3177927dd36827e5a81c028176a034cf6325447a7a000d5790b0c38d394db32984de2f1f28bdbe9694bb18880432a3906a02f3c7a9a8f310a2d0425ba6a32270bd26f1cdffd989dbb730cd27ad3960d9a81b865f863b840085e6d8fe3660dbd340115ebe285dd5171c6a822b1296509496affb6d815c64ca788582adef8f06d9cd605a67039f020678d4202e985e72bcdd8afde9fa5b909a02f759c1e5068bd51cae5928b7b35ec904c8e3648ece7531503ad08e69af8977eb865f863b84097b0239c3c11dd0ba3749c15a8a381496801646c15ce373afce47e0c2b1849b0ac35ac3950763448a666bc3c74fa7bb2033d67e55bf72e7e2c9651c51d6bba05a02fb55309a83acc38a41da27befd22353f8088e123b5fc1507cfe3f646dcc188cb865f863b840985519a9201b877375d7d98f25a15b8ba4dc73bb29181145d7d4e478af7ad877784ed58a21fa503ed2b10e3ebd30297975cee6a4dd1967918b3c4951a6c8d60ca02fddf0ac69664b427a82afc4314acde474cb17f9f03ea675a2382db413892df7b865f863b8402d3f3865204d6c85de87024806b0b238d383530a925a7e639250fe0126585145d94262358ebc48d4241f5fd4ce8751fbf255159b6f3b519f7a02a3877e865601a0304ae76636739d9981396037b5cf68e06d203245dfd2e2621fcbcc6064bc3778b865f863b840ff0958c8dd5a52dabcbc6778e10fbccc2e8abd7dedae109dad764c1c9b3df52d21a83a8e181fcc5fb866e1a7d4b93b83225c846dd0b18023ae860eada338d703a0305b275d503f1ec6fe226c84620f7f95e8669e4db5e9ef051cb2415b19a1d291b865f863b8403944fdf6c4a5e26bab95ccff2cc740f16d3f1d996c456ad85098b7190a9659ee9ca06f61a3c9d3f66d34662ca089525a713d51cce33f7c0c071fa21c8a09200fa030bdcfce24e42e80af515891400731958366af9fd180a4d578d1c15b9de17497b865f863b84086254b432983cf2df151364d6b43defca2f5a89faf48101972e29387e969fb68db893278b7a299321f1f9fdb5373cb2b9a5a537bd4849acd1b8d5d469fcb0f02a0326a7dbdda2921799f6636335772a1fb9a4897c22b16721b0700e1bb9bce7213b865f863b840360f16a2f39931579f1a4cbf9b429bab33b481932520280fadbae9a69af639bdf97b12d5196ddc8e5478257d4e6e1010129de704312e9a2c8a67f10c232ca80ba032940116b194fa6d290105599fd178fa7a28c293fb27640abd23c53b53b26801b865f863b840b14004bc23135328786ff59fcbb3dee547700f85cff63a415c9607182cf5ab07ae488f834cf7d950f973b891c26501ab22df96f88e9eab98ab86ca060749c208a033a4f301590b0a75ea41a24f65ae64eedf9e9934677d5151d6282974623270c8b865f863b8402107dbf63e46f9fb282df91ee0f107f967c761ac8d12adb4a2f18dfbfaa76242d665c1dbf0ca4880715c0fe71a5a4e104e8844f2a2293061dc4518782747eb06a033bc56e557f9d68a0e80999c3bcbb0afca2eee221c611b88bd474daad0a15995b865f863b840614379db9e5c1e9dd8b990f216211ce73218bcad2d2a60e1198179447b99f84eddda6491ef7cb668b5bcf18ab98df131da63305a2b2482974b3828181aec0103a033f6e05d1d79b8534474c6bec3b3934c8235674b37131afbd7157d6c1b49787eb865f863b8408b47e3536750516de90a5947d95b9096a2cab43986da71ed186b75f538eb2bf200951ac7a51c0eeceed8948c4c4311557d4cc9d7793f8cbdd100520bbeffb400a03412008d64e339eb4a3a763d7ba24af096616dde67dabbddc741f0b07833a87cb865f863b840022c414a730f76a4dbffdf7fc2cf319f25bb205db34fdf5f1b6ee6c2c67dd7f95814e38723c9662293758b3b8d27897602204a9195b25e51fe5a5c43c7821308a034396d0ef461de6a067837b71e0aae27bd3d5fa63a1ce2b9169fdcf4b71c3fb4b865f863b8402979f06d33dd65cb3aca70e2fa42846f0242325effc607372d96dd72b303c05e847faf98caa399e7483cbf5fc9503b783c1968baace090ff67c396269232c90ba03496dff90280cd98397e43ad0f42bb94a31bd613f0df1b33f708a99081d70155b865f863b840e82df5295a50d80c5b25ada65727ccb66e15fdbf3202bf9c9a1e2b4d0be4abddcd1da00735dc79d059eacebe1dff598812c12d6b5c455792ff6a8aa09a92d702a0353c39bc2d6ffc87b4587ab34ca983b28c23076d5f9e22a33809be375d86739fb865f863b84037a878cea88276fefe42b4a08d1a99b6e8f28ce794ce2cdce124587b9ac721bcbfab8184f417e1f8fde96116cfaa6c7f6aa89725eb4fdf4e40e6a6064bafd109a0359b54946f5dc8f4c6fcc54cd9b68bbd4b67e00d5f2687d7e1b0136a60caf728b865f863b840f0eea57d7e03852e20457f7a8094f5c43a6249c00daa4303033c807b216cc2a309e0e054b6932812b761591a74d101065e3134936a03a3d9c62f24a161eb7a03a035c2f0a6843fb1add0a7dcafc4d0c37da14a4a9f869de803ae7419fb09cad0e8b865f863b840ee351528744a3dcd6977d6a2f5e01545c6af547fb9c4d6fa22fcddc2b750943900e6e8e1edfb7d64e0a6350c034c5b62495cbe47f832a1fb35ecf27ca6aba90da0361c5b336ba1098582674e32dbe2f0abe02a0c09ab40ae5e28d66f828b718ae7b865f863b8402da4accddc33ec03cec5d3c4ff88f80d0c06fdb762418f5bc23064a9622c2825ba4a7e1a3f52227fb5907d829fef33c5c6bd526b7aa51fa17993d05b0f54ab0fa0375d6fcf1c75bc35eeb4bcf41356e21d797ca7097af0f5293454817e6daca7b5b865f863b8404b336f13b069d9f2932cb0f9b5a626703d83739d12853659096e7778f231595db6a012e535541433c80c04b5ff8bf86301fad23c48a1277de7a0195cfa66c90fa0379cb36d9bc0ed5523df498018874190e839ee50092b1578e22d5afc891ad24cb865f863b84021974ac629db0bfa521d14063b4a00fb0881d128a27fda37d4554ee3fe6304e49f2354ea8792bbb0708c86fa59a9794b5c98985768ff0b44696c2b5e47a22b04a037d604831863e4ff3f264e39b38a2a3ee32fe81b50cf02ae61ca1022df58f2b2b865f863b840b7b619263219081e5fd2fa9240decfc95599202db70dd6f60b6ab14dd5040ca9da61887e508277a551f7fce658e83c7c3f048f39dc34ab3d0642660c0981d803a03803107577b82e8f20cd20c228eb814193524ee92c9f260954f7f46fd54c874db865f863b8408138ff7a22bc04065c10f00726e4990cba8eebde8dffef38cc2ff2712e5f8171663fd9c708dc376c58df8650fdc06122dc86a1c51989b14f27ac684ce8405507a0380f0c705a821343bcb98cb060bd7faf9b1d21b84eb7d9d29b48b9bde01f9973b865f863b84058ee72d00dc874e4bfba77950657c079efa459fffbef16f57cfb60ecf335fdff2cee0fabd572016f546203a7bbf1a3d5cbdcd5fc99ba66a16033f93aefa41d08a038aa6a77dc828d66462074eec66882b37efb0691c879d1cd1364f7626f1ff1f0b865f863b840076e521cbfbf35e66001fba4d6342bef0c89a5831d0dc630ef4ecef2751fab7bb9c6551cd41f237fb3f052acf9dda1cd1630df25a1d503db6e00bc1f59b6f207a038f4100d5495dfc9f0f1a5efc786446db941934631128fcc040fafb679e8138ab865f863b8404806806244d79cc5f7695dc5992b273606135811495927f28d85186c13c3ca76d60e7d1604b6ffdd47de951fd595497ed50b0449974e79c0bb1c8034a306ce04a039d2d8cf3d9f79fbc7d7fd3440e987c2914c462777105a1ca512e3d74ad54ff4b865f863b8408c637340e16151e44f73127d24d0392278f8479088aeb756a7c348c55e45abec3fb1f99b3e2343dd2e5a173c0b9e3b26dad2b80937c83f156e7bc80293a6f002a03a0c5425d01569b13d8fe13fda74859ac4fb6d72f607fd461f25c4ebf7e98cd4b865f863b8402527739548a81231f568671cb8cfbe83e5b83c08f74adab262c9f89b5063e6b7fdef42f256a159cb9ff1f7bf3262ca0f772f931ce901129287a76c1f0981c40ba03a1e7190ab0c48a10028ac7c9cdb0025126fe68dc6a06088ed6a03121d305735b865f863b8408fa520905398feca2ff9a8726b7c71bae4128fccd7ff1dd239b5a9c910dd3ef9981058343b2847c85921d3e6a8259c5f5527383e0813ff5db8f2e7d301075205a03af509aa20017d20f865999ff56aa2a4c38f643014cb0f8b1038359d210f1106b865f863b8405fc8fa727b6d87a83bc6681e9cb915ac711531404acf10b29d4ee975489c26f1c3f564675bc03e7657b238e37dc27264f9f17b7bf9224d45d76b58c26d599709a03b62a9d2df9885f9af0c15057e3d03969dca0690b91abe26ec70f400ee5f21ebb865f863b840886cb3d53181a2221b11efa4e89da4df9d625f732f9a5d1751c18e47ab0b16a8012d25c99b5d5216e82f849dad83e5d629f9bf4b2050aeb82e4dec3c5fe14b00a03bfa274a31d381881971d92b6aec6b2b6d7475c8caecf66d9d0d3daab8223ff7b865f863b840d155719a121e62ea6997eb3a333d06fe7f7d1f5f3f1556ee71a457282a4e9a8bdb341cca12ab6b91e921ccbeec9917a2b3da4822cab9646aa6506f6436340504a03c0c399e9977eadabcee0f5038b1746d0aeca846c8ec3a2c4489c38a5885eecfb865f863b840fbf050268474f064b37870b2b49298ad90f6c84d267b754ba6d4cbc609e8d6c3f27e14b8577fdc0cb9454a79d63db42867946f216222e907739558464087de0ca03c52628a9f4d7aad5eab967a259e609dd9d32b149c614e9d954cd9a17ca72fb2b865f863b8406c53b40496219d6f71986f176f0f9cb9e08efd61dfab307eb4969a5c54e8cd791074b813d1ec80aac8560cbb6acfc6184555c1422197cc7d0ec9233bc3b8ac00a03cbb3b5e54e92a209b7107a0dbb4eaf65fc7b529e56ad68d3e21c4014caf3c87b865f863b840fafc14d0b29414fdb913d3962bd3eb3cda1607011c841be51da9f33c56f57026d7eaa2d695edc893e1831c626de9c304e7837aeb2528e33004e9ba8ef50c7a06a03daf19d13486111c2f7ff397c97db75d7a0780f5ea3a29ff9e30bdb58ba91ef5b865f863b84078ad38035faab7ba25d72920173012712110c4493f450cdf53161e392ecd8f12edfcc5ab55b30f9ee1440fce77fad18ed40830c04eea22ba5252b2546ee12804a03db091de7d340fa29193d647a5086084434764ba257ab1a1d52de901acb444d6b865f863b84059f226ccc90a18485d970bf5b655e4263e4324647faaf1cb129b5e307f2f4fc3fd97f148bf6e7421b909f71d39589cb463c71f30b0801725e76b7313f3994c02a03e286fd02bf1cfb10bdb52397245dc6446b48f9dae0a0919bd1e4e793d49bda0b865f863b840d01088fac39bf6d97420a88c158a658708c60fb471f7ea28ac98504b91c0fe3c28372ae35f47557d3dfc6da54f0307eabd2acb207dfaf4e7775323a24fb59609a03e3e7abf910f06e75aa5e8c3e3f04935ea0648fc64a241a85044e461c24afefbb865f863b8401311cf233cf31745d9c54894083f0d9eaccdc0639b4adcaa2a602fc734d23a5aff5bee9f81b2d3cb2b9cdcc613bf106ae7145f20901ac24bfe29515d9f862308a03e5e47776b2f943726df1a547403d4fa114d5c04ab3557e758bfcd4774020a8cb865f863b840417ac01baaa1de7676f7e1bf841d37902a25184e6c80cb7ad76c2863d807a1197a02a1741745dcb29b1de6291c84f675e01101af1f814ecbfaee944346350b00a03e713e430285f05f5f5b5aa810113969cc88dfad7e6a7198c4bca55c322e9554b865f863b840a4f5c3db37141cecc49eb3333fb5ee74a9953969ab661294300a3de770e781803a89f792af6f9c76ab3c4dac20c1576d1c1b7c46bba2a474a6919faacc205d07a03e89ba8a647dfef3ef8e54bb106a40db4b56d49818f673c5f71391b2ce822f69b865f863b840bf8dd042b9bb69f2e88afa907d1c86e5667945915913f3239e147dc354bcf153c171ec995cd84df67056b57227601769ec68114a9425b45e418263c419c04c05a03eb46abb27a83dd13ec79f9f06736f0992f39b3fd2cd14f12a635c09e507c15bb865f863b840737e3392c6fbc6a7cdb723a10936fe03730c0483d36e16faecbc835db995d0f224c69d699ec95fc0dbe4ccbc3b96187a0e0dbdcd7711aa78139db2316d2f9201a03f4064978bf74c7b93d3890f97f4062c39837a83e0475465f2381b0ec34df3c2b865f863b8403db8f5f9e93efd6efa044df9d639e3c5b4685f8810207c16c3f82690bc47897631a7159c35c051d4a36156940511904b28afc193a2c1af5597a0c571bcf46b03a03f4667ae05eec7353bf6623aae8faf05047db1aed2807d1d336c04c21d79b9c3b865f863b840f154f462c2a8d761c4040c64590e15f52237432d1bb2ecd62a1d6f4738ee31994405eb0026800e98e7fef238161c296fe9acce1300829116f3a2814a402d3f0ba03f70e4f56e2aba2477ead6d3eccb6a5c9be147381d12ee53e31f2c0e13dc6aa9b865f863b840fc4a153a1877b18231d6e621a39cd6958a0967f90cec5a624e935dcaebb2cbea4a743200fb9fad97e3e2604c395758cafee49b4a8abc5a7e9b1875c565ddaa0ea040365e14834690d417b9f0cbefaedb88bb9fe7ab1d0332ba3a3851e5c30d9a18b865f863b84086feb98e4e38cd6ba012ed8a66a0e3de553e67b9d7db99dac49ee73a872dcb6094b0c8a27714c47d578a92dc9bf4a17a6628b9eb8c21db732b0904bb2ff4b509a040960707fe94477ad1f764aff50e467b9fafab66cf713566268df50a5c5ac6c3b865f863b8407d7d7eaedff7a15e5b86866247d0083846592797001b6f9cdb6264decc1e9a5fcc43dfa5142c8dff0b68313f4d011e659ae217edb9624d37f786bb4880b8a201a040fd1ef66fb78b57869f701781b06209662152e15196b39e96f64c14f1ea6404b865f863b840954e5c81a6b7e48926bc61781799dc9050ef3011d02a7fab6db5ea5d5fbe07af5e165d5134134ce1bd5d0d1af7f4a6b75fe09bb1f294b78e8a2a2d170aa19b01a041b5120a0018e3d9b6b51dcc0a06e438fb9f97750ad49430cc1c6f865ec7097cb865f863b8405f54d3764412c172a4803725fb35fb97c23df7a7556a00831a75cb64bfbdef8b335fd77c9f3367a1d994475414b8eb26f5b0a13ca11678d7c1096c1721746f0da041fc0d03aec399311e556614afee67f1e87ca997b072e61cfc60b22962be08d3b865f863b840660e1b588b01296f0ffff2df1c84d9d84bf1173c568af715fdacb718608615b5024c3bf1b5be1eb85b58142937b70014184181803b26ccca1b647907ad027b07a042561211c18eba2f5c8a4beedbca78fdc72151e5f3e80ee249bf92af960076fdb865f863b84096e888aba86e147a9ca2a238c4b175f4ded8f72701ecca609f2c5dcac84d751db04e3e6261d233651b1c00151f5cf6330b707a25694491a2c1e44152be423e0aa0429b0b9e4a5696e259c0a8857f1f8be9583f005b7bfea1373429c53a919dce04b865f863b8409f15f6dfe415bde851d73bb0186e41659f62a540da2b6e3c649613abc7dba1e9b3fab6ecab9b9ac9293e1162bbd3ce34da701a3a222476bc6ae03fd53e5e470ba043184792bfdb9d45804ccb5fb0e4552cdc3b13afcccf0efa222a4fa7f28f3575b865f863b8404ceb2dfba2ff44192b60a565f8d9ec8d6ace4554a539263d8b249434f78f52b52c9abd0be6eb0eb6fbaf02f92b3df2fe3013b936f8ab6c8ceaef9b21545f050fa043446bf453ba593fd178be36b2e995e69528bab0bdc44ab589c820f22c2f0539b865f863b8400834980caf48de5b9818956a7b070c752653e785e7f54fa859d4f0aea27474aa5c6f039dc062ded8f2b9606e5d0e289b287f0b4efa07721f298af308f3c8c00fa043891eafd4434c9addebbc20ea4191b8828fb70562c01541bd33066f474a19f9b865f863b840a0553c0d0a41fb054614a5b0dbb24019fb96ff165e063ab18e98c7498d73c60245c88ff8216c6a9b1c689f503e3ca0f5485c6f343f813158beed3347aa95b802a043f686a3895023c6fa1ceee1ec0a9a982e644611516c4ad6d617b99a859a9ba8b865f863b8405cec73b79ee785e95877db84bfb0346c67fe9b96d593d06b412a9b3bef40c9ff95413f63ea7ddb2ac89ded9b2250199998be0960dd363bb1db8b444698bcbd0ba04400c7b8264677b90a0bedd06b785db94aab6f49c044200146b2e6fa7897c001b865f863b8407f55ef2a1c40858e1046dc5bb0fd1f60dd44a039a4c158627f7495eb09e0a4ce8789201491d627f2374a76e4d68ba4607d6398839fbb650d318f19616e1d620fa0440f7fd2202aff96fec10e0dccebd183995e4619136fe2b07d3b6796b67e3623b865f863b8403bd9975b4b1b26402a479563ffb38743abb462118839c48578819b6b1653e414708d5ece0dbe844eecef651d6ecf8899002faf7a46a487f16cd3fa8b21db7902a044608cad21550591119de80028ce45dcf43d4e644b3a5f4522bbc9c8f1d689dbb865f863b840679f1efb82fca1550d2707242b8f44af043e1aec4da7fb84432ca5d8479c6ba23355df9b307806f19391a21b0bc2358687a9cad21c590cd09b172cfc2944f00fa0448f1982ef4fe90e4e5605050d4f5489a9cf11f214afa13a907b62526c828558b865f863b8403087c3c38052186fd1b66dfaced98e763336160a7b274ec7e5f7808dd656edae494cfee33ad6556dd0d7a39750baa7ed8f559602a509b4b515735ccdd7f10e04a04564e0bfbeff8c865ef5d48db991f6503303be12ecb56382128451a1d5bb5dfab865f863b8406348a3a14f3f0cac67c4d47fb92e3ed1c5e31201274923faa2056d1cdacb03186376f1d01799f4baf7b106bfd1f4234bcc61bd19ee5317272d58c80120fd7200a046074de41299025a6d2d07d73c39089142843ede2fb749f37b7387b9d00b0cf1b865f863b840e3a99bb87c2565ae253474fc5d99409749f533fdd529929bc8e3ae86dcd74d5fdbc9eb353306b1fe37485bfb703a1c2a932fd90e918f03c1ff20dd7c0ce87500a046865091b6e11557f254c05b26de7f15eb851e387ea66b38f0ab148fb30aaa50b865f863b84032126f428a20217434c09eed46c5fbfdda2cac0569486485456bdb8d2cd0371a49c240ae2419462d8b4cf543dabe0de350578edf0e8d09a76c05a2ac7be1c004a047276a7e970f22028e7d0107b7e4d20165b2c7ffc536f38d55ab170e4bc4d6e7b865f863b840117294c9c0eb4909d1c374d6c0746fb534e61fb2f5f507d7673a4f8726956bab7e3d567935b7367e675a8c75ab56d8125ff32adcae89eafc89f5a6960567be0fa047b05c0a92b2977fc8af13303be9356102d5e9202ee6bffbc6883f1ddd92e5dcb865f863b8404a62ab45db2444a3a7d4cc1829061ed737ab1ccdc445ab7d8b6110f40b42dabeb793a4b23b1ea66a4ac918fc660debb6db8785ccd5ca29cb43ef07b4d87ed90fa04856dab87d66f24db52d94e06d8d543d635f4aa6ad939ff5233939493b338735b865f863b8409f18e3048503f73bc08b62d6bd133f7d1ebebe3ec4bd1b4808880420b6583f35889e1482382dce8142de1b91cadf1cbfe7230e9b5e30a7bed50eebb4eb89b109a0490bcf479a778226dfc7b823230ddc52873a69d077d52974250b778d382efbf1b865f863b840aa136f06548754a88f1e9137b62e1540c6ae7c86e80a05ecea3a2cc458b3ddefe41afc6d9f61988b64780fea6307a3a02bb3988c0d24e1c40806ac13728e550ca049255d41d1f619c20b94c516d89791cb41475eebf5684abda7c8d4b07d7745b6b865f863b840fc43e83e6a6897ce2fa95f1a67a4b0e224475627fcf7143f47b4d46491be31059d4b713fd655470a1456bf5703081ffbabebeacac8a9363f6c2322863a9e4709a049bc3aa9209291467d377d4b0ffd9de0fce6ef58ee5bdeaf03ce85d01e97b063b865f863b84093980a4faba11d7af2472284358da9ecfd33354f029dde1600b4cdeb961eba89a517bac10a8889513db50148e6cfebfc177cc66f8d95b667dc2c2e79035e6f0aa04a2a5bd3982f1e6c55659c399353769c6cb1663bccb1e3c109e1338fc5d0e6d6b865f863b840d87e17328d157b0b903d1897a98200a5b9a3153854f3142e9b2ad0184758b27f1a4f81a31890479701659014371c031c78f2f7912297bd50a67c7df9e9980e00a04a565811312aab87dd8ffbfd01902f9596aff03dcee6659ffcc00296383e01f0b865f863b840b143b57af4f7d91693f91caf93825e603fed97913effb6ffc5e92af6b1ffa9fdf31e6de6ac9031d52e75152a0813528a6daf7a6212285333d2a58275894e4a07a04a8b3f021bd9f94473beb64359c0434f40b7a47f8e146e87067d01bdba51efd5b865f863b84072987f5c2ee7f0f04d178c42358a402badbfe4c14f1d7de787606e6e75206ef4504069b98f8014b8e9ff0fbb95b095e35bf10241517b286cb0099ba1b3a8330da04a901eb6da8345dff7e59a4d0b84cb36218835ca2997de099870519026b53286b865f863b840212b000e68dfea8d63208ac00fa8d598876becf801ed029f909cc0eab3ca232ca0136061003774e3d79b5d812844692af43d1191e30735a6399684cd9566a80ea04a90775307346c824a4b8d125cc825fc94214c1e4b226d84f28e794c3cb4caaab865f863b84090dce85fd2d29ec59b28b2664fd474a2c56d78496888ce9907d257f2a2df9ae5810fe69976997c5417df17458be7e53d5d964c6984a6b8bf9730073f8fb46a04a04a9a44bdfb838d1f9715e5430b6b9d36a184355cf43ce417bbffa67597652a99b865f863b840135f0f26298180cb9cb44ee1ce16b1af1bcb3738309a7493842efbe241ef3521ecc6bde9d8965310bb731c43e5f532d0eb2fc17a341f0bd6ab6f9bd045ebe809a04af586cf1105ad073193a842f1d1b23143d050c54158df67bc272c0b83d2d0f1b865f863b840b9d75e2e39dab43a3d22480568d21c4b8c523ba8916817d9fab2e731f615b05ef3b8694e13950c979873d90e07cf2bf676a52d44edc2766636ab3f94a94de80da04b24b54151cb10627a6533a9740790e3000104be718da94c1a83665327630076b865f863b84098b5453040138d29971c1eb96c8edb3fd6dd70915e1e5bee7258c1fbcd257f4a173077f2e031497ec2ab10db1d0a5537ad7208046be6bb17ed64c2fe026b2c08a04b8bc8228499fa756a2d072e275c8546264f17f88a3df07ea786bcaa62c10345b865f863b840f322a86e6f96ca680c32dbf5ccdce7403d021dcb2170d06153b2758666ee8ee30e90b4deb7a5a4a8bc7347fbba7cee77c1211868f20725fcdfddaad6c7612c06a04bf80cb235d2be39045e6d83d95d073f9f22c806e39e81a4890923be707eefb0b865f863b84068b159e65b83f350aa14b107e65029d3c28fb10661bc78f7ff809d5240951d80538bf891cb83692c33a256637751f123522d32ee274a8fc0f81ef4c9f2321f0da04c22782ee1bd4165a7a48f578ceabdf94af60bda2e1a5f5354edd480b7896fbfb865f863b840c776268e48ccf465bf63b55caf531c0aaec9bdf27b14e1a28ea376af462e16333f5b824a70632d74ac6bf315f7bec42b5d5a3f8003baae7821604f06d57fb301a04c912ce07ea5eb582045b0ab2528e6a1b624f6035db2ee8e8b013edd59d9d8a3b865f863b840ca534baf9f31eb81c1cd22109cd847dec8af08b43e68623fe81840759d7b682cba8af65fdcd64b34876bd6d4e8a43a7777ee295a88341f79399c51330372c700a04d98ce8f37fd944570179d42c8e1e7cfc38a40ce02875347d6c914281ed9fd3cb865f863b84056bd4d5208065541fe188bee915d2b03a81741386e0c6a035e816397c518d424edd39f5462f6cb8295d03b0529670412cc9eb79cd76935100988033d4bb28b0ca04dc35a94f5f4454ff25518b4f6de1c86581a997634c240b5672b2b49104e55c4b865f863b840e2d4edbf6ef92e7636123a0b317d4608d1b71130b4ed870c00e4df54259bd77ed9676bdfab75fa9573ca91f9a18ddf3298120886f7903c5c3875a5ee2bebf400a04e2be3b94e292f4a0d2428fddc8829fe71fb0e31a83ab1560bf12bb249a3e5d0b865f863b840ac3aad08519a9d400ec58a71b784c57dc66179c070cc3b35f2fd94352ef7c50f86b4bec7d0af5c69a354d7aa1c8c0eeca028b53f4a788f6e1a2b4d2b2c184b0ca04e6fd64100eca46bba08a38a453089ea7622a4f7f67d55c5df1280e2faea3530b865f863b8406e9b8077c7093b5cf599975a5cd57f1f27144e1fb794c57c48a655e750593a26ce7507d6a15af18be72cdd7a53adf238aa58738bdc4db1af724c7bb0616ce80ca04e8341dbbd838cd2ffb287707b5257f7753e9ffc77bb157c235703fef6c885e4b865f863b84009be33af4f12011ed4dc67d12cdf6c0d5bc99e15a306d879c700443f3b66fd793b3d5018efcabf38c4081059e7fe50544ddb0276f46de084c602ea6618251005a04eee1b610420fcb8180d66f548a01f4bb3899fd9a41cf3c075fd17fc9c9c64abb865f863b840fcd03dc3c284be4fc8f6ac09ea44f079a4506abb06fc0d5d156698a4eb84e234434c09aacc6252d50f23491a8b2b589dbb7dcacd5b62a141cade25af96700d08a04f3f4e2f62f2246baa2cf1e8928ea617b2c4e2e2172b80bd633a1d336d3590a0b865f863b840e31ce0eeb03927fe2ded81e52e15610b147b08addda54198cab1556f9f787dc7c8f93a8b4f897652f1916b64f038a47f6422b206f191f25ab4f7c0f616705903a04f55c96297d0826f2c06a387cca6d20e5107d53674d5d0e70035f79cdbc3e412b865f863b8407636b8284266b68ff0684bb5d28a453bb1aa998d4b4c13d5a67f7c4450bed9d964fea42b38c76e994f0ba7243f6614e0a42262ea14d5adca51e5f4be6b2f7f05a04ff2be6fc896ad5c5360330efdf46d26f881fb1cc3c589ea407db7c06b27fe9cb865f863b840fc8b2df0fd2530f800ae7b23f4906ff9c9b3339e085f4c4225bc28887ffd55ae50811679564eddcc02e61900edfe0f283a8eecbe9e33115112652f30507e5e0da05087dd41a18af353a6f2c48d07a7491a1bf81c03a8e5a4bc9d81f0135c4699e1b865f863b84089dae716b520d55abfce9d3475761f04b25c7c70d94d6e1b4a259ede093d8a613825837888bfa09490de157e99ff3af96ae9f84f60c74cdf681b897e02afc003a0511eae6e782c850632e0a6b138e5bb4235e83e9cef23a76929815790bf7a0644b865f863b84041d5948f3f56144f56e641f9b14945a42fd34147ee50f5f6c94433c8214159dd36189529e189e844db51ae35aa0123c7b85b3ea1d283f067d34e64ca59147301a05187dbf2c8f595ac73922fb5253513e0475e80c25b161ddfcd56895f4f7a4845b865f863b8409c1a6dfc129047b76a23914d972cdc08b73169dc10599b70c5f6dc0ff6ac82d8bc56470147df943ed2bb35963fcc558b03fbd39b2c4667f424cbb89795621706a0530acbf1b93c99d7a6970be2179809938dc8df68b1fac4109fa850b8ce9e26b3b865f863b840ce8cff8e18f3c1ff255dcea75ac12157419e53b8afdc4a3e29920086c5b6fdf3eb96ee4149d81d7bbb74665f27d49e72905fe958daab9f7438a51bc00aee6e04a0534826e9e480f6bc4f6625f450bc052be80fbac272a195241ae5c70c58de610fb865f863b84096101953e6daa794c99a2dde215dd627070b14f9563035bb6055c72527a20d2b25a36030a54eef51ad561c953ba783925a3cc303d1a3042b3d896b82435fce06a053e767bb54032aca54a343182ae3269578bce30ac9205a8d1d18dee301fd9d77b865f863b840fe82a3ce4d20b5cd3c43417e1e73a7e6c506c3cf9d9dfcfdd06571da4f696011967c299c29be647d5fe356f745d74c83b5d172936635db35ad3758ed0b4daf01a054c3eac557155cd833ea69ba4a6ba752cd5168b351aae397d234b340a5ad0395b865f863b84056358fc399c2c68b74d77f6ee84cec4146e87e0ce848172ac54669720b174ba03db59e4524b45b1d40fd2df3260f5cfe29dfa3a3d2771b6676f819d5052e8d0aa055439c22ef4133af0dc7efbc594a05851925f608f6dda5b91ba3f2ac30e086ffb865f863b840adcd425239a6dc92fb7f0b029e949e958bd3895e241eb29d1506fc13f0b4c5083702fabd44649514d50a715206406bce7ebfe46715233e369fdaa15c7a37950ca055643edc287c8be779b2f36eee9921c5b205d4fb48d7e9de026802d742d09df3b865f863b840ee77cfda4b4562b467f0c2960be1a5f9ee80279c9bf36d2306943cdf665256db2dbfbd2ce5348c77a305fc877ec4cb9477cb8b1388068dad10df172266677a02a055863d94ef9c234b891606ae1c06840314ec1b6612d4f5b334447f53d2587d19b865f863b840539868b8f5c21fd4619f76fa3fb1607587e2111728ef407ffea73c9fcc2982eff7f22dce34ac61374a344148704746554de254f17b21a5467ad280265afd1c0ba0568280011edba62374ce5a2fdad01c1bff1b09f9c1961e42d7848505f1c0b434b865f863b840adae31102e1392af6c22be74b7268387eed455c32c721f5a03bbbd621b8fda67ee50e44a98efd70b217b9b8901f09a5de96f4de38bf214153790515b8e502e00a056d2787fa13875b10d1144dc0bb5bf596bc01cf68ef539ab28053581a6a9617db865f863b8403a4c931f8d106415e9676e425c425f18ebbf8588d3f8651f611cb41a76503d72b4658283213794b7fa0290f67cc69a9987fd5929c0571788888bd4d57e707e0ca0574d311ee9b8f6412e94f33b280f8d1c9db75a6dae79bd98176155aa101cb99fb865f863b840c7d5e94db32de92b844d90482db37db635dd4f789cb0802016dd37b0e6ea0e13b80204bdfe1a315ef4760ca49bd089123428135c1a0fb6557318a862d0d7cc06a0576cfd896ca75daadd83777a43741eca3ed5da2ff5b9ef11418cd5956f48b1dfb865f863b8405e304b2bb4d398675d7fd5a300cc2680f8ac969cf7afcd169d7c5481c65562d0867d9b01922c0c430479c3cd9dd2d9a3d9fcd88e3c8adc46e61022f064c18509a057988cd8d0ef29afce154a6e0fb28137e971ed9cab06784b99c50e97e443be0bb865f863b840cfcf858bdfd1472e2c763255b437c54d7582d5546d547daef0106c2803338eb46b45e68688efe2cc0868d07b106b1819f0b382f6d9b3418b0025eb1dc1b19107a057aaface7bb681c831496495d5b6c08504381343fcbf8ec0f0a542c7b363480fb865f863b8405d06bb08068fd99e621dfdcf3d9c5a019b1e0871a27bd31b60dcea1793c10fded943cab693a1f251aa454fdf08344d4e7e79e334c0bb6024b440577fdedba005a0582c42a4813d5a1e71d1cfab67a81315a6703800ce0c8d4a63dc67a47b75e83fb865f863b84005c19c5c0748e34c213bc006b32ac929b591eacdcb552c8b90d2279e3b6201aec245710afc43005cb2ef3eef7c86f9bb27bf7e07c580b28d37607f32695b9300a05833f9c4c9cfea2583f1e554f5ad92a24463d37cc10135c96bc170ee98e1b7f2b865f863b840ff91d52606c6a94073f90550dbf949af75531de0af75769a8be75fedb4965ba68f8f05e7679fce070210102cf4cd1bf4813d590844825215e88d3bd27ede140ea0596ec665b0fa30467a2430e227b7a8c9638a8a9fc94eaa82fd39323c40255f26b865f863b8403e2cbc6bee16744eb82622e870095abfad1a3c61c93d94c5487f31a349b71b1de0fc7acf0e3a0b5d87a53f5bfd52d008e200c3ea86088deec4ad78dc78c6ab0ea059cf0b3e53a440bcc2079b3cf1224972215ac7f243a2c9032e7569a025988cf2b865f863b840d0414eedae9bca997b61f3fd353a36f82e7cb03736006d4954e6bf3d107a15810e8ee694f25d263efe2a94ade8cddea937cdee7af19bd696eee710ca66da5d03a059f9d3756cc564bc353571a07d364e3589bb15fefd6ff8a0f8e5fa319a937d71b865f863b8402f09a1d4bf6d63ae1763a2f99aa1c3f9309d95b4ace2646e7bff3e580f5f0488108dc2f9cdc36edfa6629a6d79971c3e5049afc375ed35e5a5dd3a65627bf00ba05ad83188b1047fbb4284a4c93d3ae2b4583cd486ac02440b6f641a8adec340afb865f863b840c15addb432eaf2f95223167a7eb764e5523c771eddad727905c04555edb3d7b675661e9ef57ef2102ce24d41b9027dcf2f26a94c6fc773e3a70ad3bef4792009a05c435ae0e7c0649d19979b3d8daab7e5e355f895e9ba5f60b4bac3b141964253b865f863b840f8ef123f8f343d1fee289e3ea5d44e7edb13facd3953f125690934e2380caa823d29d2e8e2cd0f0abb7698b74dd9d972597ef4a889a49b74436b0f05bc58110ba05c5cc6edd22658d7f53fda685417f4a013a2af9ac83772bc7ae8c996f0aac3efb865f863b8401f3bc9d8336713b03dd41f529e6e8565e02d2f393d6b045e8bcb12fcc5e04d0cae6deed298bd6926ddb07fac34fe44217392bf38f272b23ff98ba7c42ac1440ca05c7830737b8750fe722d0005a35220d5c127b07060f8d0155d13f8795871f66eb865f863b8404f2ce0778b221846e634b688045e86bf09c240f032cbeafcd84c6e7bbdf5ea523184c89d58bcdea8525b725dffdba222e3f50588595c75b1960d6d0adfc2e600a05db809d5f7bc2511156ef12602a10a56e20c942f2042d0a2e33d77f4b395f6ddb865f863b840f674a43f9e280c1854d39514fe75ba626e3953f572c63850fadbaef8bc425a500a99f8053e7bca24944d48a1ee22f6d03a874bd81f94bef346cd1d2f4a63d702a05dff3ba50c1a660bf944a055f4b26406b262c2bb03978f7cb00d073ab0a31827b865f863b840e42076d2234f62833a37a0a737d818600d589717bbdc12d9bc2671dc81d0d67a0c6ddae048a164b99f1d1fff92ceb48295c065c3b5dfbe6c6f8e0230a2b3150aa05e1c7747ff7e6b67cabb457df0ea82ca827e514033b5ed18c97bde71d00349a6b865f863b8407586f9d4138a60d57df526efdf06ea20ac7a8499fb223cbe6c9db697c79a0c86dc7f13377227018630268b4259ca646eb05cd3de8c5af5bffe90ea4064267803a05ec09f6ef4baf679ef603f4a6aa61d40f176953e6b03fc7c42bb85958b51350db865f863b84047106b095cad86c6eb240aec0f913c33479c211f28aa33ad75c27848c4c38c5857c9e0afb7119d2d998cea2310c98fc8acfccb72d4c180cd3e269ccf0e47fe00a05ec705b77e999b8edae1caed1ceaeeefcad946dd79d73ecedaa064c24d4292eeb865f863b8407e1950ccbac1e5bcae36ea006a43d8ba5485924a056ebfd215c3a7db4be50a0bf4332a9f643da096cd82fb0ce21f42c00759bcca4db0382e1f7a20cf015f110ba05eeabc5e404cfd4edef8cc92a75815b86778a129c50c13923aecb177a13a993ab865f863b840dbdb493f0d9a2dc5d25c548fcf98d0b8bbb2067dbdcf62fddf483090a382b3dfc038ff2772da4869f4e4f60c7e59252b30019effb044a10f07fd656acf4cf90ea05fd8b483f7c9e962ef658f54ccc310d8a83e0760fca816b0052ad938e4120439b865f863b8402158300d1e2289312a94caec3a6e7539684485fbfb9bf67152735515de46ae73b9ba88eab8294e35324210c2cd689bc24ba5937928acdb15af9de1f8af7d5306a060148b39a7a4d383a2a3072f84ac526e608f89210f50fa61f48d38c4bb037097b865f863b8407ddf7c870d9d3ee8a6e726e2fb870fe5e3ba4f6422240b9b4281a9c2bc5c67eaa246128078be34cd2f8c23977def2f2cb42fabfcfe4321cde1fa9ed6ff1aea06a060e5146c0ca238b6f39d96c6012c1999189f871d7b31dfa09a3c4507d872704cb865f863b840f125b22255f75a5d27467f75b5a46af0af14e2e4000f05a528422b31f2953ec289dbc1ef5e3991aa492746d2c47717f8ffae4df69b3ee8e8ce409826a0ce1306a06119b8207b905c55ae6107b924a47939049fea78ce967da1f3ef3ef9ed1533e3b865f863b840fe6dddf20b727ecda123f2e115a7832db28dec75ed680e16c46450fbdc10ee7677a5188f76d93ad0cd482074376d640a453ed8f882bc5cdf09a3ab33db928c07a0613344adc4bed6c29a8bffa0b797324e90ef7e297af8ffc6a7524c313d11d405b865f863b8409f9d97593fea2bd2b0ce1b29abadc642b6f880cb073f1a54fb47653237f6bdeb12c2ea9cabf53b8096e5a04c6a0382dd150c5f12fcb9d7612ce425f53333630aa0613aad308f149d696255e29bf2ee326089fd3ac59212cb11f4b2473bc19801ccb865f863b840237a42bca4c57124f2444d34840ae5921f1d5ac6ca416362c91cff24bddb587946fe6e8f8fc9f94faccedf454aa213cb4570a4ccbbfd47d97a2bf7523bfa6d06a06175208a6061d7fd1c9f0728d8a4a44445703045e6a2f97be321c84946af2035b865f863b840f6e12753a86334544cf10ae4ed757e9aee2afbe22fb4d1a87565008f1f81dba6349b5ab02c1aed3d46d7238d17003e348f72e7467d3725caa661573b46676a0aa0621dc2f9add2ac0165af497f1d0f873a7b61bd26de61bab0db6049cfc79ce4eab865f863b840be24ee7c8395f7856216d873b944ef7517d9bcd0778ec1eb89560139d4b660cf1195cd780c2df8247ca1e8d868e669ddd5b0c0cb1004b85bdcb7c211fb32a208a062f015934a6bcd3fec141264a7e8497131cd2ffa2cf6522450d058c798c9eccbb865f863b840e0f7a64e653c54aeadede909675af1465fbbb92039a46b95baa9627166c95ca1a77a980ea8382c75dc1d40303979cbbce1d2a9793f4db2927c46407b75406800a0633ff03d0221001ee36f12a30092f977b6926cb50b3ca2b36204beddb8fe2e62b865f863b840991bb89bbb0b2fc717ea29c8d838149ba30efbf5736036152b30e91e1fd5767eb79027cf4b913c0e799a36be15c47e4ba9e05931ceef5c9b91729524cdf4c40ba065a52bfb05ff5b8bdcd3baf401901497443439c0807abb056c2e242b114d13beb865f863b8403ad447d258a73dff160f135a4d070e73fd1f7f96b38a14f164111c897ec476ed8d81ab361ca7db5de0da7f6fb5d3d165e37c8de4b00be3cd97d301f2f5f48005a06660785068475785ff105179854565924173c18214fd57ca8edc07390717289ab865f863b840d62ba3f361c71d1d34f8c4ce8d94da53e711d0f30eb0171c8237f79aaa3ff41bd2c8176fe7262cc594a8d6cecff54e3b6a689d6f876b1ea268d4b3ea5bc42101a06685deca424d2ac70550117ce316aea82fab1a1bcb9f88b984f56ada58c9e626b865f863b8406eecd8e22c40fc7572176caf17307d14ca277e5de946152bea200ad5b70d7bcf6f111d4a276f129dda6a611aa7f1381e6508c19829296d9a65c7450417549804a06739149e1d0aff1af497ab1c9d8e78e2ca6d87ab761fa777c14ddd285d62168fb865f863b84011ede09c4f4df9dff92bbe8c5b19087733932fa19113acc4c8df4f73d266154a9c3cf8ac1a347bdab7d5ac2c30e7020696dfaea4be646c69e7ff9d12459b1d08a0673e90633b273b9f6766a2e21f1c665151d3646cf3a2cd3a4b5b036eac26adacb865f863b84023f518318970fd33e082818159fc6757abfc7609f8685dd07897a8a085be4f1cead0e00a0c8ba2eeffbaf4342d50bae33826dc9de5e2fad67b172512d0a51b0da0673fa06be490959aea5df6bf2526b7dccacb4647470e12ece01b4f3f3b667516b865f863b840b1be9c0c897f28f07dc2ef30134ccfd263767265ad43130e37804c8c674eac94b69c2686870c5369a296ab86b9b5ef8c323ecc7c45dbcf31993dd4c99f97cb01a06783db38a069fcec0260c6282648e68c76fd22e918322897c782ce87f425ffe3b865f863b8401b6b624fb27f8bfbfadd708696fbf5a99dbb98a0a6545c47679adb569f7fa350695943a59854acb7a73e7b83c46079d1f1d7d57855d0e22b2e55ea9171e1f107a0681d0cbdc65fb57da7ce62ab6c3184c1f32bb4f828895a36a249b91b7e720bc1b865f863b84013a7def3a80545db8d26f586ff8146282ed1093efec7efd42629e8721b9b4b7601a647173369359318e40398620e4fbead4a73ffd78729809790ad6c8152570fa0684ad0bf43ba729e3547bcab78afa53488ed288aabcc3b4d1cc4525e008fc080b865f863b840d4584c8ec0997ff5ddb522b2df757c724dc3a28a73f93bc7c6a024ea356846d90e7e6c74877f77e5183688a14dced9bc9da987f2cb20b977c52bff38c9a0bf04a0685e13a5a3067b84cc9f2c11b88d46c300a17fcdefd2a817cdf67ff26211f044b865f863b840819e4cc22cf1ec68e7137d3c16fcc2e4cd7ea14903b28c1dab5a2d4bc0ae50a2b2ed2e8182b188f67a1c8ce532400f1028cb6f87e7178eb6c37e5654f0209b02a068f18f66c890885232950ec2cbdb963c711575c9eaa7f2b62386d9ea8d31b7a6b865f863b8407ec78437f56d1b0d54c8d69bf5a79d9ed3885884366839dde5f0fb9cb7cc9f1e99247de88d81239745c9f99338c92551c0b19ed43661ae672373e1b7f7a52a0ba06907563e0bf7299f501d1e13f128fa1b71714695e522abff5d47eb11bb61fce2b865f863b8402a064242d0883fa9dd025c91c43a6df54d2999f88b663a4b137c0ce2bb06d587ed01089482c99377aab9e6ab91206d26871194eb8f081ede1dd3224f10284a0fa06a194c14f9909bccbc63892708f4ba815f2d50cf0a90fb61260c1ed358d59e1bb865f863b840dcfda005c48a801bbdacfb9c5efa999ac6c80a4a1046d44da884887e85559076d03ccaff35a23c1a8c9f846d1bbae26b733a7c14cb2e8bf531e841902f716707a06a3a54d4ce2550488de3867a7c7b453d2816bf6441cc237c60433c26aaa2e81db865f863b8403f441f66ce93a6a1a0eaf73aefd8fe5255717e8d753023c8717cd8c042b0c997cbcb5d94470dbc82daa1354d2c62a30cbddff2b2c6382a38076b518412e46f02a06aafd971e86cc44a92aa26ccc667e6db300a2f8af4463b8d96d29deba9078a94b865f863b840081ad2415481e3f448ec20aca51f0dd897015e9678dc11b3eb264173e22b8407128967c0b28c5d7985ae7fd3716d422e1f1656e510ea54c4ff32915302515602a06b4b0ac0ac5e69c7b1ed46470a658bb5da61335e8be7f38fb6ec97da66128c4ab865f863b840c6b57448c006c1c67d4caff8ee20e20fde8b65789d68f58e58219384ddd893ceceaadf9314c541baa9181140ca6e8c77ea11fd570b6444ab9c2f376d3b86dc07a06b6a19f15c174611066de8720f768b6081a5d6886f152ce34fc77e21cdb59a73b865f863b840a363d31cb386a9f6c68e1c43f4d22ae690670e7cad717f3008ae8944da25a8e5104ae1f6010b1b71ba4f5b711d3e7657a8cd2ff548f5404554385612d4abad0ea06c390d6645c1cdb92be1bc732520fe9c9eca06d614aaf11cfbabe41e13305ddcb865f863b840a107a392c3f211ac83a3cd5d8049d49664fc82a92d1a2c4a84ccfdfb10dd8c4a7f5364692d1a4d68eeb4202cfbdf242d72b6c01378e1a6169479656d8054dd06a06c62907831c967bb4db42ae20d648db8fd0146264aad16e508ddf888e3a98535b865f863b840410e8062ab443f1ac984580c913c47c771e49f13b5e50e1c9af78393529ddeb28717149fbf151f08709b13fb4b2852a13c1733260f33c88fc730f2e02a78e107a06c99bbbc4ffb0804ad8df786eb90a9d49535465f3715d649c5d278f1fa8def29b865f863b840e1bef7c722ddb69632a28fac02efbe21a596be6041ba91015fbf6de50851e0a367c718c25691235a2fc60c62564159f297b427be4c4552c0890455a2cb5a7e04a06cec43a2e692f493c2a395ee052b0544f33ffa09ced6abb1cd2210f332db525fb865f863b84052ec31fd77491d8719b85488eb3344e0bf44b01c6aed7ebcd984e137e823d4297c68a71d1d39d81e2fcd02381ae07e86ef7d6f582ffaf6c830409c0b29d93e0aa06e0cb7806a2eb0beb624c136d223f3c21f0a58faf5f15e3eda1dc74759294247b865f863b8409a0b40f01838e3dfb27b0016349db92fe5248ef315aacefa3fac67b34fcb3840cd3875e47f8b61042d512622eb822d9a98dacf9c574854fe8e20acdf013d1d04a06e34b9840761c7c6e6b1deb0ac67e204f6b59449c52c538ec018fcd3b1e1e72db865f863b84023899844238a545a2d67f2da4648a64ebc5abe191585e6397775b6e4e86f88f76fbd827d03d478a2e55e2e7478821c19cdf1ccba8332f89de8e766453a4c8807a06e49c862bdea504631c48eef8a6a9c2a3d1b8e5e8a410bb4e033509840663f9eb865f863b84035c7aff88e7acce445a6c8ebe8c7100a4207d2cc8a8f7cd64e12f93443643c5953f47d6dc0c6a196ab837691199914a7fd34dce57a34d1f80c1029ffb267720ba06e613b022cea1b970987ba57e63b8f0e4d5d005258370cece607661418b228c1b865f863b8405f60863e006b33a521eb15c8c25914d701f57dfcfa11e84a019d53af9055f20f30c217ee526b6767e5c7f8135df23d0eae0a58148769467cc36394a6fb1a7303a06ed9b3aea78843793d02b0c86baf16fa9a3690461f4f4e36dc54436832a2484db865f863b840b2ab5adca0a0d1ddd157898626ccde2b15e3ad12b93fa3a9873e861b13bc66cf7080ead76d45e75c585b1b3e11e84b4f46f3ccf2a4111db67adbec67f837f806a06f48967c6e23a57d5a49050022d6934a9d8a8a0f2f3c349368c52791763911e0b865f863b840693beb11969a8d9c9e69f9788a5fc52e33e1a6495b65b38038013105229c17db3e5ef57dd2307077997407318a53fd218f4cbcfd40ef3bf40eb51df260ec5208a06fdfba86a5dfb0a34a14a78181f8f5d035e670aaa804a9e05fca15a1d870f847b865f863b840f342ba1de7520b872edc60e03acd4de4f0a5636b1595f2018c1c2d1390c166a830ad2f3a740971c32f67c02fbdd8ff34ee11c91d0155545bb56245f896b6990ca0700223b64491477f429ddc8ca14942b29aa4be210bbe40c1c56a3c833a540d23b865f863b840773d8c15efd536a24767d7b96b6253b7fca695c3cb768242ef3f732df6690898fc10c64d6ceb65a6be3d94d1c7c27a0b41821de0c6662f98865327ee92444509a0718ca158e1424e8e30f606dcaf65aecb409fdab5246a9c8ff91323ed4d4667fcb865f863b840111d119d4598a34440a20f1866a13890ec7ab09e74706fbf3711e103328477a45295dc94bc15e0dc0b97f1f08f29c5329169a67d562fd6acc529c6ab88effa0da071bbcf98df63123620e1f99d2a6ab0a30e9a928d788698ba7d30b13756dc79fcb865f863b840b79babdcbac331bbef7310d4b5bf92e8882f9c69be39a0eefd9e4e517716ad818e94361f4a9908ed399ab53e85fde1ffe354ce4781f0a472b753c4ab37a1df02a072b06fef1fd8952b4f929b2f550cd3234d313fe4e1d6393aa01c092f34b11a84b865f863b84011f55db40bd72a69220e58025a0ebc727ef110080e72712914126768fe844f9408b06647166efd3dcf3c04008bc1d6181d7de19551044ed2964fd0de5bfb4901a0737978a4671cbde483f2f603463bc1218b7c7fe5089172bf1fa83fc850f83e7cb865f863b840718d8ed78023044ca8a4505d7b0c65b33502c82655f58e9d5044f4ea272fff9b266f0d2471307f3a87d773006580718da1930f8becf7d441f834bf1887f54905a07441764ae201450364d2c89533ef302c3c4ec040888265d3bf0ef2b101de1251b865f863b84074b01cf2d7be9d1868aacf77d072c6297177cb1db4fdcdfea969f9dc2a613b50f17f345cfbb8ff7876725819b5be298814d66fd120d6529ea0d353b95670fe08a074d61698b1059634844c0bee9cf4d058dc0e21f30160fb627e90f462093c0618b865f863b84078b7a224aaea26fa4c61c7235ff3faac82e35e3d947d9d9d7abb69160b2bf2af179efb8d7ed450f846c4c522195ea5039c30c62849a40edf0cadeb26d646b50da0756edbabc1c36767b191589ae4ce322bc3a5b851a67141cf9603ae5c1d41c01bb865f863b8402c0c7afd7cf965fbfd54b89db767327607bdcad95e491fc1797b598cc9edde5dd5577c99a6f5e7abb3d5cbb86a8b6324a7998c2a373718f1136c3004d619b300a075ae5467a8b261be5fd72b0fa5b9b2553a39c5ccd8392c819c14e7596cc12fbeb865f863b8406a6e1ce24e7fa3b76ad2517870122aba442edfccd31efab4a462baa62fe740e3f87f8063684dea0f49da742fd0d16e4cb43b7c1bc619a327b2d48c314ffe9302a076802d0c4d5db17e9a82d75695770f084be40023cdf4b172e75f4bc0bbdc35d4b865f863b840f761e25a3e843776d2dcadec65500bf36617ba5e6427d405207652c9387fb2a77463aab40661ac43457a044e85685e5e4a7af54b15675f0fab26d4d6bcb7b70da076da76100d843e94ae878d288b5692567d2185e93b817929f030cb696e3d95fab865f863b840c6bb997441ea02c33281e9949fa1d0afacf311c3d4272097987162b95d183df966b055f7bb55c9ff40d337b6737b999ede39bbaa91610b3489a6e2beb9156801a077baf3d84563069557df8f4b85203873658a90b9ebf83e5b61900e911c4005c9b865f863b840f756d180ffa50307a9f623de505fd2704c141f66a1afd8c0e8169d0fc56f71fc9a78059f99f0f2f346ed64333eec88fbdd25ea91e29d78c60d0d0a9f0fc2c00ea0785692c450d49390007b93bab9e629d4785cbed20583769ea1d470c0870ba622b865f863b840f78d6ea651e8e24df2154205b12d0e73048732735197d2a64560056735c669bebc22b62f8fdf21cb206bde30c0fe06ffcc8466929c2afc04af2526b55e23cb07a0787c542fbda4c4fddc72cf3506e0d6479e193a828a1bc2b2384861cb74a4010cb865f863b84008d6b165fcbf8dca3db19967c0961361a883cfd2535436c1d0ce8f05cc690af86067ccdf19b8d5026bf07e6caeb418a657cfd35243d7697f24448c390e755106a0788aa019130c26218bef62d3717d66d28ded0a312c86e42c51162ef4feb92865b865f863b840b2ccb13c5702f38c66cd3248eef56e3dff68f5a2b66df8b4df3d870a96a58afa387ac8eaccd7d79e0e75fd850b4c5e79636b7c9b29b3fcb742b29980393bda02a0789181c18300a85f40aa7b8c2a1638ba99d71fb4b5c75b26a199111032403cdbb865f863b840986b138a39b8b0f475a2968f60e16801c5d7d918642c903d0f728eb9345bf492b301af54cbbffc3770f8a9d87810299d958a9351319ad2a6897606f002caa10fa078d57fbaaeec744e3409809d1225faa90275f603714ab8e2e732833fb33514c7b865f863b840e0889858e3c68d3a7e5207e86a0f9ef5e1cf119efe879dfdb2461ad48a7c4b0a5a9868750a07adbc23f6afb830a97ad149e8359707ea468aea1d099522eda80aa079438c21483e3383e22984f877bf50567f1a6504f255f1813050a05c8f7f3e7eb865f863b8402bcdf68b99a1928ef4b7151caf7db6d2ef690c6f06b11d5c3597e94359443acbd4636fafb4d48980389e3b36f1017a20f7df26dce3999546868f9453d93f2a08a07af05fd9d5a3f01efb73a814c5e0c1eb3864a1a992c292200a3ce2c5b8253377b865f863b840d39f9ccf3a3f4b33763ea3658f342619b43753b1fa0d67ba23cc716e981dc0140c338a064fc5ee5e6926b95407488c66a5b274776425cf823c719ca3887d8203a07b6600bd3f549724000813621a808b1d6224a871fe0e1dd460c3fe9ca946f030b865f863b8407b867b82bb1baa0be8c60c4ee54e0d46ddd932ef578deda7c6807b1c3457b5a73e1d0ca326180e970b9ed2925a116e82fe8a4a761b747d65919dd48b13b4b509a07bf341e1c4d0deec119316a8849c8d4e912a8b5dadb38575db14e937f45b7e27b865f863b840099c2ead7720234c7a195488d3887fce50004c408ae3e79657c2fa5fd31991d9c1d97bd0420c951cebf86341cc45273534d648d0c1b9823ff4317f5c86b22f02a07c601959f37a0f0bd567f8ba246a0af5a6e4e2514b3a7dc438c3dbbd09891266b865f863b84056f6c63d8f4672dd9d1c5f1fd91d0ff4d431b51974419549018702aa6b9b03d9893a3081e528112e1c4c43802dd47d469ef4ecd901d22de117f3dc6040057000a07c7f87fdca644e1e6ba1a3fc5cf27776ee3bae1c0280754a0b0d2bbe0e395e60b865f863b840f3aad9f611a667b8625b34c8675d01e1eae87c19c9fe46d2400663a064d9cb1acf7ec16c7c4d205d418f0a69b467d0ae4f206e71b6d295920859952f85a0b60ea07c9415f4fd22e5aced5385d9ba56d21dbcdf10df8382dbbfd2cf5dc444803202b865f863b840ef2ab528aaccbc619f459e2197f62b85ee2df28c9776d2fd2bf390a823780562535120429b91d48949c5555793b65e3e4839503a34985f518fb1c9a2bd40210ca07c9df999a1c4a9c1b5477489dc0c223a32a7f21eacffb6e937e7e1d7d1eee704b865f863b840f6384109dd446c28b11abf8609f51494e3a85b907644de19adfa32b3cadd5e368e68b21171340a7754221f49f317e6e3ffe88e5fad75572a563b7f0353337500a07cb9c4415a2544ab08649dac164edb5b274204f337dc44e5827af6ef3d8a50a6b865f863b8408189b351b7eda92e0d9fc9f1c2de84f564e2b05ea5b3bb28c8dd6e08713dee045f6654771feda5fc68229e40fbed48d051c3d2981741dd1cff6cd24b83ced704a07d09c7a996e2b21bf33cedc6394bcf47fc94ae83e5f027f121839de3903b39b3b865f863b840fab57a074275a41e5c519f1b9fb477d526e0897558fd88985515cf464dd0b4826497a45fabaf73cd7233a67c5799b906631f03a062960867da8d31e0755ef60da07d3e5d4bb8356453c3430bc745863fd1ce138b9a9e586e647f34c4bea232e57bb865f863b840369668b08a6e26928ed687e34b2a9165eada1a9783bcfb57056cb7ef810c4af4dde9add0289a57dd9912fa8285fd5f8bebcbd1e13711c4e96b8441a79f0fa20ba07d694cae8e241c14d2a2d4adbc5337732a014acb9861702eefe7d94e861a650eb865f863b8401d78e20d6f0d2891d8c074357697c70fe36b8c20e64e27cf9897d4160d35b7ce063fa34f6817b4bf3b85df09e0677a0f2adfceb188a5174874480d565918130ca07de5eca494ed60b231920dc3ac991265ec72e4922e75c50b8a1dd8737e87a9c3b865f863b840df3232000f60264c9f2fc74ccc180495cdb7aeb49a0c4610dbc218a017f6b91960bedd36230b0204cc8504597702dbd2cdff6935d59516ebf2314d85cca83a05a07e09f2e050c4f868e62eb530f3e27bbe96c2aae037f979af89af5162b5f3a056b865f863b8402a43241c329a50b1ef7979469e6c327e5fe46c2d0cf67190abb67e5910e4127ff653769c55f8816ea4828cc1b5bbb3249886cf786bdeba27b917ca89d190ef00a07e5c6dfe75783f2dd0a78473662a4158f99a7883970171cfa97cb55afe8a0e20b865f863b8400f591f511a8c850c063f5c810f5db873de7d8800241d174cdb60fb3ecfe1eb76c7c4ba84bf32171fbcf69cd23f200000129ff11d26684913d312c5bd5ed71200a07ec79d621c3ef96464329b27439e633f43798b8da05729310ccfe5fd602aca24b865f863b840e2198fdd85f864d3ad41a6e4aa4abbf414d59a9ce2f56ba4960600837aecef7df5a7a36fafdd86f35222a8243c69c1eb51f0e26533788692e235b15916cbd504a0806676371ed3e5b4772a390f8c60c73c84ef84e7ce1d9596768883d2185af3b1b865f863b840ba531959bad2d7f54288c1776eb3409e80c487f2dec30fcf0af3ae9f813d10eeea525ded252563d01ae75a10c454eb76bd298e000ed9a07bb039296f5f6f7f07a08079949be9bf709a0f8f80f2ede6970faa000172a89d7800284acc90c50180e3b865f863b840b25b6509249c84c21e8bcfc990bcfa5f4b17bb56c19f80783889acaba14c1201ca444e76eff0e9bc257427b160bbd062e22601243795411c9b00aa7943920f03a08147eed34b4f35e6f68f10e5d452736be46b6f94ad328e37a7734250e143536fb865f863b8408949bded5983a1da83614a6b006ce8f977f167e57a7289df8736d1dcc9900970d3abc1e41c1eba252e200664b3ed2edf0c1a051f024b69f8aa792bf8f9557408a081a1b892765b2eded1751ca78ec80c1b1288e4e42c918bd7043e9a81ad9085bbb865f863b84034e4e833ad96472fcb545998d9d15060d17db9d091369f80ad8d8cf2c8c836b33a1c7d2c42556921d6fb5d47c626c970d22d952beec7a4ee340147b67fb19400a082355a7ac8ff56b100f146a370566d6b6e3f82af39402e5d7df61393264fbb98b865f863b840fdd2916950d5d568573aafbbc1ecaa9c581c44b49987f96c3113498764024df48a55504acf320dc54d98736cc83eb7354671b54a839f65e80dcdbb81a5169d0ca0823e9ce884c8acf839d5aa6fa8a0f61bbf085595c545c34bb71767c2bb2e0b57b865f863b840ad73d6046a2e09926557a4da701a5310627551e34772a33d58ee77dfb6da69e473c2efaf29213d9672d1377d1f1621976956bf4cc74b4af26853e64a15df9c06a08315fddc0cac4b20982abcea40ee93b5b524f1dae736a9fe29383bd0bdaecba7b865f863b840f4b4e68e530d264daf59cbed2d8691a2c05f29318d3006311620c3d8376d7d5f081881da3571c2fc3a028a87685d348b01b058b73f2be658ddb46100bd55ee0fa0834a62ff8daa95f63d81a7315907db602560ee717818c27497ba6b497af86481b865f863b84098386993241d461c7ffe07f6bf6567c00c95cd5677f01ebc1ca022cd16cf316ed67756ffb9ad122a41fd89a7c029c7866c5861a4c5f7a42e148bae94407f030ba0838b4fc81fbce9e8c4f1733c8e42bd676f1c6e9fecf2d6d6e9d9e6d56fc3b682b865f863b840cc72a05beec1a7bb582911c97d1c8c8601e6f01acad94fca13a720badc966e3a0793a59864ee799a25870c526d07e57910fd302faf647cfb9e56e560795a770ba08400bea0359f3e8a1285aeb1268e524985e7046ad330d486efed20753ca51f07b865f863b840ad7bcbe77a5895992d6c9ab586e7102048d116550de0ffa35472e483ca0bc1539c495fd95205ca670e10cafac2f22b8615ad0a8b5932dad3b471f6ead500cd03a08425aaa4a3a0eb35643fa678a90851f3b6daec24a5e8a63df2119ae53082d637b865f863b84052cd5c0826f0b597b4e9b56f7f85f5f948b546298aa0139f75eb5c270c2dad24eac6bd279431e7ce4a1b4b0bcfa75a4bbf0b805a1711c0ecd6ca7e74bda20b01a0849cff700b439b6ab6fa13e3036bc1ea023e214a8b7cccef8cbddb08203df4c3b865f863b840c698529f3a1950fe6667baf5d57a61d3899dc0ab8d468d98aa2c76fe17eb9359d4960505fdf8177e742591de448d607cca21e5facf79fdb3261c1efaca87230ba084d2d37e08262ade7a57eaef4d6691454bfb33678be52dabdb34f75f0a48e78bb865f863b840bba793bcb8c2c677325817faa55d0ad6f960b9549555981f7406b024e83d1b7a7c03c560f1bf39ab96de0d1b0f80c3ade3e7b62729d773671729b9ef20eaae00a0859cee8097a433d9d36faa96a3896d81dfc1efcdcd931123b1c5cf0f60d94a2fb865f863b840f10698c612e77ab63da9629957a9405cb1dd9431d42fa4bdc610df20340993667534b7308833bb40472e6983860300a492d57d47bfe85e71702e0242fc326c04a0863948b755f8a20c20cd94643a1c24adaa748e3d14d17e1ad9e5045ee89d5b9eb865f863b8400fddc9549ddf4fe0ceb5b9c83f14557d6eb5ab9fcc69602f139be66ad3c13181bee8fa08830aca23a5cfa225785cedfd58c74355876367f018a91a85b6052f0ca086476968265e08889345ac22c9ce3434e34a005186996273726a6ad0d3be4e17b865f863b8400c1cf771be1382cb8d413b7f8d9d32cb866ffa11e60876672c0c736c93cf44e545fd511c706d8e5961a9aa59e742322a0523c0b580e71db9c6c218ad24a5ff0aa0865b352972db47341ecf0679c3e556f21f1acf96130bdc5fa5b8667651b0799db865f863b8403de13c89aec25c90e5c61e97582390fb68dfff0073c2922f42436fa5a17ec3d37bdaf8f6ff4ab15547d2fae9ed4346cbea20a41ba1dbb2f7774c42f2e7234504a0866e80ad56a1f0b962210282480c097731ecaf0587efad21193181095fbf6b11b865f863b8409800181403283d4f573081557f5b41d0dd4c80e7bf867811648d6c6ffdae5c9c464dd977d219282990e17d1b2eac90dfe1007abe481d4fef9cf3792ae8137b00a0867dd8888743609b4894b0afbf40ce62a2415b54b4e7206458b3019435ea3c46b865f863b8401156e940eaf740a94706c63bf94b975fe6efb09d837e851e63e2f92a322556b4c4dab1bc78e706931d3941a3805079953f64a0e682fcbcebbe4f54b80b716905a086c1ec938634b944f9bccb3326cb1f09621577230812e2771cfd95bbbc4d2a27b865f863b8406ce8d0349412d518a01ba60c5d220fd999b62018dd172080b3fa0e25fe305cda554bac327579060d37dfb895cc7d7f98eedba75b00741aa2f94ddda8f62d3d0da086d07882373aa6a5efc34515b70ae649ada234850f7886d7617e392dae037165b865f863b840c31b10c8308794bb7a613df22fb124ac4f38d50736232f81380311817e2403bb97caf065f9e3f4223e2367d19807bcc5ddf3a56a3ce981042eb93993a9bb740aa0872f1f668e60d55dbec0088fb9afa0d1e4dd1383feba48cead619f2e4db2f3a5b865f863b84062e6fa28985b44322555b7e1457df4fd997ac840eeba97add5cd847e37f410ccec8e4e2b8e936f8d1eb72283882efa9573442448d80703284fd3640c2a48fe0da08746e1d07a12a67441f01cb8613ad5f517a716450482a60866cd9ce58d800786b865f863b8400078a879e35dbd49187cfa1635fcbd643f21dfc4253f62753a46f783b52b34d0f1d82fc8e78c838e5d9a83155972427c320775623f9af4bb87e996900eb14b09a087a8cf45061d3b0092eeda15be8c40d0c04fd24cc57552c5f42e35b19300480fb865f863b8405d78528fe8e26c1bf955702ba386d37857d0b9a985f4696a930c313c7316598e10b51756ffbc9c809c4f549c87fb383de2bb93af04444b780b83b15c1121d902a088b6c18aed539a125811322da1278f3695ef4484c59d8c33a9f5934bead2a83eb865f863b840819fd5f286c9dc2d08529f85af6f03b6ab100daea6ca2647fd92f589be010007f4db3918a92e3139b9af9ba680d1e6dfa454abafb7c7acd3da7e219b33f3570ba088c21cc835d5bcd7d7e864d6cdbdafad58b92090b56cbeeab40469879bd1fcfbb865f863b8401dd3b67d84a4bfd03a005f777b65615c8bac9ec09f855a121a59c7a6c6a4f6a007ef0f2c8b00d8222f781bf39db1fc13e90bf64cf6530ce06510aeb5f471d205a088fba241518b05e47dd6d063d6922c91354fd619c18f5f62efdb3b749eee6fe6b865f863b8408fd4f0595447b87c6da2b4586058dc68de3dbd7d742a1da817fbc22d6f47f27bfaed4b6fab27238ad6d8cd6e64476a97007c1ce37f415ac52ac26cc0b373300da089d38a1e70eef588f846dc9a0bf6d665a9f1fc690addf61499929aa97dd38798b865f863b84047855be7a8d64a02817d1fd82c01b6ca7e6a31d22862e618aad21cc6b49bb5844f850482ddb420fb4aa67b45fc1ea0c3376a0138b105004d487004e81896f90da08ab765bda6e24809df0d447f7657c96ea5562bc7cfbc848dc14c44b0c7f5d73cb865f863b8407b21328b38f823fc612311113184623eba5270246056a53f6225cc3967f6d2d54863488bc2866a18b0f881e98dda5c004dfb3aa61e47c63511e51f6766d0580aa08aeae69850306b97024454ef4ebb7600adc211a479ea04c57d60dfde36a7a2aeb865f863b840baa5970c4746915d50da8f388dc5a059217796d2188fe84484a40e72e5d87c49177a89236b5a5c10196bf8ab2a1ac43f4fb16ad3f4bb3646ca9785840acf6d04a08b436b8f4ae43ffb64ae83136b91a22cd32029f06c21d70beb12437c1bd75101b865f863b840a8bce4f8dfe11c76ad6a70f565b088d5697c8258b137da89afcc8c467849699a40778b80006bd3e5f7179fbc3e27013702e42db6c6c6854969912bd005f2bf0ea08b46bbd10e610544f112d385659948acaf7098bca31210b409c36a2f04797d77b865f863b8402c27a85258227d501c93e9ff7b1f214f5d77bf93e57953cace9036e6e49ec672360c49dd13eec877307b38005a79bfe9af1b8842288d4bf4b1c5a3cf1ad74a05a08b98f3d09929b8d8c1963b67000056e6b44c70fb0c04986c9e0dde0850e986c1b865f863b840cd0cbc20dfc42509e7e1735d9e02a187a815d266d23f1e69bd22b50709eb0bd79d124fd10e3f5deb23baf9cff349418ddb2029095db1e3bd6eb589ef9cad5b0ea08bc0ad9f851aa355db0565889d6e2b8f8ff1451374937ae2e8d6dd973b067103b865f863b8407459ad3910df48f642f028e77892cd0623338623d112cde29ebd882c69a7b6d1a6c0569552a6b12689faa104dc55fa8c35e466a399f397d20e36942c757dc40aa08c994ac4a4f13bebbbbf3c178c52201eb0fc1c1eaea148a7168df8242d4b1a0bb865f863b840ed1ba5d923a569909ab647f8f80b4ca50915986ec9bb107c5d1a5d8074b90c02fe419e4d939965e49c3cf9e835d682f258e0700540ac819bcbdd4193f68d9408a08cef064802f8f0b91f05c7955e0d7b8e55857a5a203e3a72bcc37b1f15c0582cb865f863b840a3368d2b3db7e2e5d356699f7de8db93def73397f2911b4dcf722f56f2ec34aa49e9d76e67a3daad0442d3049a139d7f1d04e5b9d205ec57f6db30e8b7fe2c09a08cf07ddf0f6499ca7d8ba13a3d4802c144b57f1959198881cb1e79195c343071b865f863b84055259d369a8159e326c809224ad54a35a7f29bbbfad4c0100e17121d25163bbda4040fbfa89865c74220c2559324afdcc1db584ec17d10f00f5c6df38874bb06a08d62524b3d56c18c6e75dfef038edfcf7ccd08eaf4aa95b9262de28e58fece10b865f863b84092f06c4607e3d115b92a7a44e6e0b77897821b1963d88a9f1ae41a0d5aab322be8ceeda2abb8e7dae4c42c6fa069ee9f62a9b373b08e33c4c15135949cd55b05a08d8022cb348ed8ed40a06fa45ca18ef5e91433464ff8a19c29b0d9f42b8b4481b865f863b8402324f989ea101dd9cfb47124c8b2fd75344e41a864764b51f9e81bb73a8e96be2d9e8620fa5e19f110b0d8dec76af4dd3cb81115058ec71c56866db5ceebb70fa08dd7e1228f9cbcb02e6cc13744dc5b219d5353c74080f3332073d72bcaa100a6b865f863b840d92cf4363f582b2fe24dd56e5c7fee666e4b6e2efdc067dd0ff4a85df5c6cc2182f80f6aae8748b52a78cb7120a3e14e84e4e9f7e2d0a503dae91562cb090b0ba08de6d0a0345e7556c0f97a7a7c30f26d6ce0ded318afd2f808e7fcbfbd9082e5b865f863b84037a4a4a93ab68f0c533841f821d425d523374479dbad56ae95adecb5d2e6dd30f76fc5ac0a391da8c07a6c4638df314007b0a748ae2bfdcc75fff5bd4244850ba08e166084f313ef5fe71dc8aa0a082cff2a135983b2b9dc07bd14e451da3729a5b865f863b84005062b727bb3fbcb2a80395622eff6611929c603980d56bf685dfd05673b104ca84ee64c02bd1ffef960fa2a41c4ac95e93167882fa29f83f723ea7af40d9703a08e24aef8ad77dc29a92fa0618c83ebe16aa3d1c8b8ac1e499059908aa384a8c6b865f863b8403260e4c9892c047e5a9652cfacd44b4a5e17d3d630fef0a7e9bad94c21cc957b5978f28a8ad90d02f0be46ee0c5a0fe64b0ef80ce049a3a9ae8743f976ddf508a08f2e7b543a7d3a2231cf7d2dd51606452431eae3413c9cde087b2f32baa34508b865f863b8409b243bffe34a2fcf4674d491928d89c51d4fde9885371809ce53308350fad502477e6b9aa3da3fa2c351fecbbb0435a3831c6da72b3d152e7a8c77e223b27203a08f7f55c6fcf428a34ab2c01f74401ef0306b8528f9c6fe4ecb44539599cf170cb865f863b8406c1c2cea0502db7b8df39910857d851c947d6dfc71bb4706540d33813dae80da28c5ee68f812d7af884b8fc24148c4afcc371ee94b0df0b8f6ad2936b1d36607a08f94514e131d81e94358970430dbc2cfdca7605fe70848b6011611bf1709c6aeb865f863b840f71363b104c3e64a5dc25e18cd93b58246de5c637fabcb2cbc45bcb1ca26784ee70ed0be9d0133d31b36eae4363bf79a0c894a57280b128f9afb4661bae19d09a090d05b5a62309d67e40a31cdb7fef61194b23809e105b481eb62096f6afc146fb865f863b840e0bbb7413be15718481ae4202cc7108c8b26c722734980303001b5d742f4687da60fa633ef872c281cc1ad62c9efea8d0dc23ba702c831793e9dc42a22c30e07a090ef50a1c931d0f1e40f7812d390a5639028298c4376cc323552e2714a779d99b865f863b84034a308309d694e783ffadd45eb373de56828a36bc1dbb66ed4fe044c245a4daaed32c369ecd6393f58833054238d7786ba186665f569b700049321fcfd14600ea09121777694a833eab58ff374b5ac7679bc8d5443d74377e7f465c630c334d9c3b865f863b84055b97600d5b4921db62ba4e1db4174cde4834a30f53c3b140a217e4c5f3f81c78195f8232bec7ca28545cdc6f66b22f9f2477d84e1c14a460ee1185616893209a09129dc0162c2596f9c5f90df9f11d63bf9ddb6d714f32fd7d045e1a8a69a169eb865f863b840f7fbc18d9c23690056f5982613359c241348874c3657352d7a79a1e638cbd1b626c824bd011222138094672f530bf9f55327e72501777db5d0ce0d86ebb27d03a09136b5b2c6343ec492d3081dd7c6e81d26e32b7daaa478c1b9b0e7a16be6511bb865f863b840854c61740ddf2d0b12cf2d34869f9643241f7be549df1e7b6eed797d887031c1ac32e9b0c33c3bd57a5cd71523025c4a9f37d597b70fc6f6f4c9d9522b7cc10aa091a5421d7b9317a93e0f04b9014f07b71ea4139b7cef7c73d8363e2fdbda0befb865f863b840d12bb5cf40cba39ba796b6440c52a45443dca12369fd8bbcdfa70a6ed3bc4df88226405f751c60ec7482c32f79d7619c16f2e93a9769e159f0d4d9e8fd6c2803a09222bbf46c21869db9bbd87f1d6fad66f2e6c74c146ea2f8b33779b175f9d0ecb865f863b8407201bd53b5d64ceafe8ad2794ebc3c279616ca934d1878d3eaf5deafa857d53dcf30748c05632abc9681bcd2b521833fd0c350b7f83229bc6491b45da4ee460aa092e0b7b5e98974739f250d8dd641559b01e6979f23829289b1b109d5ffae85b3b865f863b840ed2a30a507d28fdd704df7a150b60b166948704b4b8526e975cd1f708bd752b3d7ede81ae71e89fc24e435508a2df76ed7e207af1394abc1532daf8bd1e3480ea092f21f9e78b08c7238f842364fac39b3ec2c47b9534fe1ce7003ab4c7594cb85b865f863b840266c381295fe7974e8eddfde8c9518f05942c709526787c5c6649d80cbea6b30f44720653b7c88e32a954fc523506c319967b220bb0e32ae6133f3d2134bc30ba092f4bb5443e977b9b5c4ca9a40323c134edd90c3b6e9f52d6731f1c83e8cc6b9b865f863b8400c52ab5e3d62a00b68abe995040a0c601afebff8af1ee2580770d261a3009eec615420fc3798ebf41dfa7584b19cee45cb73596324282c1d52f576b924778508a0931a85a2f74b6bd670fd7e4407388ab76fdd72991e45fa0f426c5f697540e2c1b865f863b840477097e31b72e5f2018c33ece290a48e01105580edf06b66eb52b6ca93e4a39025bdbb4a895701178e7766bff0b3714c733e6d488e494c71c96872c993709f0da0935092b3e5066a82cf818a62536d88d842ee67102c1e1a6db0a57dc56f5b5641b865f863b8404266b132f8378995b6560f7d98a63f63357fd6690b82854b4b842491afb8eee05e82cf150e4b6aaa383783a0061b5582fdbca3cdbc3bb57f507803e5bbc0ce04a09395033dd901a0d13e0e6f71a6cb3b3c95acda28d89bb008f5acd7d123942948b865f863b84036b391579de804cad405ddba3d436f68af5dbf1dbdb74259bbdabee0319b9b0ec83df03cc69f64e41bf6ff319d34569d7c41374d221b2461febda9cb5cddb50ea0940bd9d799f05c51b2dbc5cd4d9585f85c74658216ed405647e76db23aa79267b865f863b840bc0cccb30ebc7e8611dedd216bdc2779ae0153df50fb36f2681f3b5bbd4a58c5319d8a89764226a364566eb46eeb1312ee767d74e706331abbae6426428a880da0943a813369d3d3d464312c58e93c4bd5bc3d50c9694954c126e0269bf2a0a8dbb865f863b84069ee8f2f36d44a88c3f40db976ffa31e8d25ea4fc76e3c11247719365cdda6efee8258b675089571189c1546f79c7eac96cfbe9226282a0a07887c33474db00fa0961ce00c5b6f9edcb760e2a841debfc3883c624b9153fd2d12d7180107765c82b865f863b840d2c06b998678244f9ceae2ad9aeff189765cb63cd4747930e6ff0022956602b984c50bcddffc4640639242cd9b3ac6f0833a5227d1017dd3646991babb237802a0966b56f0ae20c78c0b332728c842dd5a14cffaf181d538c30e20cc4cc41c4e95b865f863b84045dd203779cc819ac23990cb9f5965153ef6a3467bcabd9f16804161e240a9a6c02738bee53fa0de2404042ee34756791dbcb07472600d6c6349c77fbfcc2701a09681dfefe4599c287026535a56f591620d3e42159639eaeb8112c2eca760e41bb865f863b840cdbb3402eeed84853f74b7fd15f02517c429be80236a5041eaf3b8e9823c3ec521185d17ce7e8290da3d4ea978a552d2fcd1f6a4f0c99c74615bb899082b7c05a09774f75ddda9f906fbf2d6f4c8479010a47d42e0362c62e0b41e0c20af7759cab865f863b8405010b78df0ff6511a2b5a6d52285ff4a11a9807f904b87d4b6050312c6a62bbbb4b8527ed92f5b3696bd56994863031ec9860be89ff8fb1a99294e1344c91d0da0985f05f92ec9034bb1936bcbdab54ed51b6d98ea0213862ec8678fcdf5265df7b865f863b84011ab98e5f5fadbf2c2550449868f55c13bf97e996fb0b341c0d19554ad23f2742848e8408eafed21552940d9711d62fcca12a72b3586fe4ca6acbb47058cbd0ba098dec4d87976105e47917142f1ea793a9ddf937de57051b13a38b019097a3254b865f863b840a1da275c354be1f051f524f3b25187f064836c02db519a72384e9313d7b6332925aff289a72dd18e2c8a68dac8a4f0d0fc659c9d91ffad436211b0f2d398230fa09a0900e8790faf85d4a4b85270d9814ae4f4fe365ade06d11da2c0e0493711cab865f863b84030aef012c552d49c53a845fea457aa15e19a04e7435403170416d514a3ba7cda4e2ab4cde6b8dcc66b736350c0450263ed951cca387c65041981df6b54cae406a09b5c8dd1f08b3106c1761cd0d1e1c7a914a77c290df2ecb132f1eaf601e46f46b865f863b8407c8a0e7ba6ee0c852605b729bfdb90b5cfcebb240d7fbb474d9d60ac40c3c1310cf778f10efe9ea050d2b41d1bd2dd57c28d3f65c6f944d1f3c2170dbe537c0fa09bddcfe82d3d0feff63ad7d8cd06f33197f56b2a4db91cc15248758ae2df1e8eb865f863b8403de630288ae858b093a6ed35f5108d3b9667c2f842cc56a70a0a63e452599a22568ed5585c0e52ee19d483dae8bd6b4e2dd1482f3c4bebd4ca8afcca0ddd780da09ce56193ec04e02678964990b508bed9f483cd1775b30261994e01b033237c29b865f863b8402e4d7488bd2ca38a9748d35780e48457eedb8db1a098abf4c8d3fa538e8946e30b6d59628e6070533e26fee39d66c710499b996ac42c0278a4b59449b595a502a09d06e18d1e5f6cb00840c4d85bd8ae1ed5482f580bfd50b0726d725882cf96a3b865f863b8405fc1282162d96fd4f8f9f44cb78014e7d2dd63e99439ae09ce9f98edc95d7ec0b07fb14d874abf94570dd23f04a464738893040b0f7ae5fd8d436071ac48180da09d26d4c59d4c0b6fd1e1ab3814c8d17c79ea0af849f2c605817011169af14d58b865f863b840a05228078f75375c4b41de2b1dd884a45a8b52c3d2b7eb256c11a70dd87204ea6e4ee4318ce074c2852c9a44e4cb31c8f8db9f8b8eed533b171b45ce512d820aa09d38599d913dcf75c0cf0ed05ea8391b154d850c176172d05e19fa5d458f87b6b865f863b8408e892f98ebb6716d2a8cd169cb5fae49b8bf6a4617a826f227d3f81c7b79bc7ca6e8125f5488b9fc43cdfca7e24c28f5b0f40109493933cf3e944313c53cb90fa09d5996b4acd2540ed963922a54f58ed8e6f4ba7e54fd99cf98e0d5e616cff9cdb865f863b840225787332f8b9e4f514ae90dd20217301c63bc30a7d91817448ea12e1f91cfc2f50e6d48961548af6d39f751abf47c7144c81e127727f350496e00c7bc461c06a09e3e51f98f85152018170765153e7c73051c224846f8f97ffb4784fe6cec9716b865f863b840cb9817398792cc22a9c1fdc94ea8ecb13599bb9922e5aa3f5c6e07ad425e253adc854186761e7a506e7aa8c389fe52b9c93693c1ad93c79bc2d4881d6087c405a09f1e709208ea44927151589eeb60d681bf40411c58345dfcc53945f319c2cd44b865f863b84055a09b5f4256df4c4b43cabd1a5d1591ee25a1d47d8858ec6dbc206a79750bea0e288f43ff8194dcb38145e579217eab83c0b70d689837c39c84641938533d02a09f5ab655f25e2fc1202af6fa6fd8aaa23d90d58270344dbc4029d164e7114a4eb865f863b840894f6cb3c578de57151a96bf832d12f3aa1cd4e0cfe4e92ad0420c7507043e555ca6761f6f8e0cdd1aba7dd282455b1d0ef7ca654fcff638d8c5cb73a02df100a09f6f97bbce670bd39e73f6c7302206b073c18f4b11c8ed28898019264e17b29bb865f863b840b3121f3a306405447846ff6338f1f60edbcbf956e966856afbdcf3ca9003e1c3fe4acecaefdb41bcb86071eb4875580171e1a4ba97bd4d518fc24f5da58f8101a09f79e5cf8ae4b08e71b51481a361ac7e5cee5a64c42252f997d9aa102e3c3733b865f863b840b69a3db2df4bede8372fa1897267dbbc9d9b792bed057c627f2bcd557f30cb2408673a8d49cd852e0c7f77cc9da82963c1d3a736a4f0bf1bd991b7be09b6cc02a09f8d3b64d16c8f30defa53135d8a1e7a66af672256006f202dec3434a2d33f94b865f863b840265a83330298138c066345df584ab951d9271a29fe1ccc126cd860c40b59b9539e9cb9e464a592b1b86e9abe415bcf8dbabff7dca9fbd99b77064f83bb5b660da0a0204a4ec85cb752262952ee60e410ffd17f08cf1232d96e940ab4b33d0db112b865f863b840bc1985495f28a02ad119645f7b5660a90b40da637e8698d177bb4e08794f232c11ccab3679119ced71401363d1c32475f88e515469b0d5288a75546bf90e6e0da0a0396d88aa19bd81ea72d0193fd22fb6d0478b9bd3756b3dd2a7977f0dbb978ab865f863b8403e8dd8a76f7ba051b79e77b34243400f80f3e799d14c1b7eb332416f27107b338de6ab30205bb4c92d1db950925a70c9fe9704d0d551f2107d1ab2d4fa8ae906a0a0c2fe8b44725caea506a24c4c453c4ef82de882acf0d1393285aee2767d51f2b865f863b8400903c0190279fef53b34f4667c50407a500d3a46139078c1772e5e275ed679a8e2408ca50e3b7a5cda5d757eff087bf780a6166d9b458bc102e176bc56a32c05a0a12af7f6211bfb76f1056d2b2399ee06b7fc78db2ddf6fe01be5a3ef02451040b865f863b840cb8f86b9b55579e3d4147908d8d37b086a51165f8944c2d722ea8c77e432562f5c2755c1cd71c37701cea4dceb1ae9a9cabc2f91d38c67586c5854ef0a2b5d07a0a131370b8ac30d7c9d1ae404f06d100bb3399108e254abb60c7f9b996af2a7c5b865f863b840f9d8984fcafe20f636f7a64e3cd204decb7e6e9055ec31743dbc6e695594af3f5b96c7f89d28b1b8add0aa6efeddbee32116ba42ac4e40dfc4c993b01af59a0da0a21976f58656ae4e2e40809dabf9888a7f9503564ed0854d9d73c2690ebd68f2b865f863b84070b56f34262b5c4a05ab64716ea9cdf9b6ff3d97691b641437ae485ce5dd4bd4890fb014fe5e5be246c19ba2e306c0f9369bb2bd9cb23ceb031545b66d064208a0a27550e2a03f99f1308f5abee215451d283b357d5576acd283e44487e7e6aed7b865f863b840ec8b79d2444742844fdf0bbc00fbec2fe7f13eee53497b394cdad71bbef857ebd184c5338436190e4b2a73f505fc3ee6e97d30e8c311037ed20c71800a2ceb02a0a2876c0d1376c2f71e52d61b21cb7753562dd57f5d5a3385da6b13ffbb1ae0afb865f863b8405db5ca2524f4202718fd1b80689acf4823060d1b0229a9d38b5714cd94e5af13f6fd506d3ded6d934ba8830a3195534afcea2c84d059033f3428cfcdd8b4060aa0a299ac12c664db7152ab0251c1276bf98ec7247e3275a6564f5459d950499c82b865f863b840d50f7849e654a2b4cb9e63ee1e7730c9123857d0ba94f2d52d1f37ab27b60268947a3eb13e4677857f7562af1209e873514045de63c89b5f84f5aa4f0b79ec06a0a2d6f27b74b8fbaaf666d67ddfc3c3617cbb7d9cc77a358329a2a5735646ad92b865f863b840628a426386126d93865f8b804f89600ae952c7e3f54733a3ee7a51b06060ca3843b67430013c44d821a927e65ca3f5c411a9e7f775322ccfe82028811d587b0ca0a2d7344bd60e18fabf7c81a3f24e5ca3fe7a281bd5ce42b11c2d7857b96d6e97b865f863b840a058b81fbe41c774062a369b285e11a526f3ee76ce09d0c053a7d1fabb6a8713442e5fe215a1c69e242d26884447579968174c7f59b704be6eb87dff8b246300a0a319f5954c8fb46a9a9d3f30b72b8b2413f75c8385393421fc55b8a83fc16e17b865f863b840cce69d9cba7b2129afb18785082a0a4f94dde5a5443250538f452b691cbc38e00c23f129e89d5113131267051d17a8593023e8f21597e10d23fabb24d899730ca0a3e0d754d858a9ee8ec6a09741806d416f94816ef2a7ec1648d377707b64d83bb865f863b840f3a7a17274b7940ec94247d4a5f367009154151c6faf300470816abedcd655d1d9e541b1fa6ff09e812d867484e342cbb2529994b2ff472b3e80f5d7d02b7707a0a4ab152e8217389a7762161e2b3aa9c17297533bc6b63157f2b3038778d6183cb865f863b84056cbe5e92c69113b2ff5dcbafa103ee8c538aef48c9e4b1ae71e425409ef378d5e8f325a3c9d4b0c1542611f46d54096ae53fff118d98f09997fd23fc7542f00a0a57f4ef5d8b2faa65d10005966600a5642308665a49e7dd3739a7d847577836eb865f863b840174f38f80b67c3cfba056d92b0dcde76f5be1168c95db27a914c52f3ef36ed1d53230aa828387c8cb242b623009d1d21b1e6e52a84f4d65c3ad3c247b344fc0ea0a5c1c12800b85f715bddc4185a30df728500854c339b7dde2dcd83384e88f7aab865f863b8401c357a4d104813e941d9246932563d4bf482b115dd036d2d269875832fbbab0286b5d3d90524744a848fdfec8d337645b60c98067bdfb5fd27b836128ee68d0aa0a6200c8d1eba0c02b69a4cf77db7cf425cef22b73d1ff2c421150017a0032eb4b865f863b84084b2984c7178a070207d36b71b2ddc2f535bdf2b9d45aa4606fd762960d001af4564ee8b6eacef054dfc0c9ba9640c48a9a100118f31209d837bfcd64de2d808a0a66e70a9149e82a31c6fde0a8c884215e4a79fb7335c00ea97f383195fb5c0fab865f863b8402cee4847899ef94cd404bb06d34e5efcd6d0157eef7591921a768d38dbcf81321f26f0f05b5c43e87ab8735c616be118b9c14a1611e628115550b48037eef703a0a66ed3182a2075e89ce53ab00189e041a7984debca51385efe6c3447e2f3c11bb865f863b840d80e633cfdc2e14bdf6c4fac91c13dc8a652031ec6cbec2d29319ec8f1735553d3f29d5934193281fddc998c0bab8e9d723e0ff908f4ddda5c74e1daa21b940aa0a678f8c4844e42b51f8cf7a5eedb46741e1c27df766e40ebeab9847283ef5812b865f863b840eb937e8d19fe1e93cccb00cada6b7572782f3b9e60a75f02df7e5e3f22940255fe2755d8b697c14ae37e20f08102338fd66fa9d3aa571359de6d951c76fb750da0a72875685b414acae14e60d9e80a6995019b8795e628311c4315f22a8ab7d097b865f863b840617e7572da827b51792493ce256611d8cb9385827ee80203ea2f015025ea7124d151ef3a76e611a9b0f31a3017270cb571b881c6309e6b0aeb46e1ef07073a0aa0a74539130fd91001439cde4016398c36dd19afc23e53672971ad47f633086c91b865f863b840b29da906bd5781488e82abc95f5b9956db9d0aff1e50655981d5b50635dd85bb3bd55863296849e666d2d650e2cb3e7741dd268a7ac2a5bc8c4bfd86390a3904a0a74665ddba3dfd94ea6f01974c73bf680c222c87441ddacea5e99699933aec39b865f863b840aad0ae89082254c31537cc539b1b50824ef02c2ad4ccebf6249a3fff41c090c3b528ea21cc2fde49e752c60dd3e5dd6c7109bdad3724a30d2f27c00e4369fd04a0a7524a19e867aa1174ebf5cf819b028c4082bd6681f6366e25f0028d46bf54f1b865f863b840b1663c46fd1a474ec26f75f2fb34ea44cc9bd074698ce8f02c48c1c0479144e00736d5e45c0fe7aff70245284bb8476a2923f0218da475179415b61d7e687101a0a7b6c6d65df9aee65c7a9324847a4217cfcdc24b57725d7c1d50a70548274986b865f863b8406c2df9b3ce1f8345efa1e7f4e92de6cf4b76aff0d965925d03bd9a114b8b60ceffd36435692e12491a162539e4475f02f8407fc30bf18cbbcb4b8a7dd9f89407a0a8926e96f23a2bed399246d8ddb5cfedfa56c03a0df12ccfec08034c52406387b865f863b840a409831fb01e8a7aaa48bbee2f314046093f5f7bbf1cc510323a8cbbfa2e5c72dfdd8fa8c7d54b624ca95eebabf5623703be2b4ff0eae2def3d697e5d8232600a0a8dde508bcc0842474451ed9139f53995833988e9af34a38f8a8a85191a4f377b865f863b8404026b8e6f61fe3b73c2a4359d4fcb6a5ce82e3f648420bb44e7b28f9f84be30cf656c5efe67576cee7fced9d3c1b33a31745240429e3afa8f84574f57e3f8f00a0a9c90bcc71d596ac5329a84807fefa1e91cba5130aa4c33546dd47520418adefb865f863b840c8bb69ed9cf16c7d63c1133de20400b95611ace231f156aec556b839c7f2054247ec799ecc9ee9832aab669e971d599968d14d2a4d5a3fc46f8d98cf5eb18b0ca0aaf08be2eae68fa730030691eeba70ca0f8962bdf030677624edff892cdc7191b865f863b8409c395e0314afa892062b4f1880508463b174c7137bc1fc7b0320fa02f038fa413cd1161de69dc8fc70477df285f6cc2fe89d43b056e1fd81b5b9a611adeb9e04a0ab215a352a0297936881d90af86cb6b6c4d4d37d3ceeca39af64ff5385a2a2d6b865f863b840bc8fbb785617af1604b07b5766f89ca153bd9c8741ce6e53185bd6fc6eb66c7c770c35a2542257a8fd5063099bb87f9b86e5c2835e690f590e427a3a3493760fa0abe520e51cc414cfa7c66b0ecb74f8307014fe9bf73c88edde443eafbe4f5e94b865f863b8404288422b51e29bcf53b1171f1ba95cfaf6cb6b49ebd24794a6cc54e7765c6e36f2f8ed0bf4175eb74076ad69042b2e7cef7f6e54980c42f225b1ee0bd91d2403a0ac25cbd2ac6262f60f1a32bbd658b3f5974bf0b6eaf4cbd2f9e7e90c3d8cce7fb865f863b840af6240b04aabc291ba04d4d71a70402cda8147e10e79c660e143e071eaed1b7ceb9c82ed11f0376b197fd2ecc1d0bf4f9756bbbffe212994736b2a48845fe00ea0ac894a56d59cfc3f34ccd338efd2ca9566dc3b959f37234aa390801ccccbfa62b865f863b840663e0405e1e361027d9a2e4d4be371635f3561308d93a14a31416fe892ad8ffc3fedc99aeef2902f2b86a3051d2f7ed6f7c3dc707f5d23e7c2820b5170a0780da0ace4d13893db4511557374af1882f8b8d859105e7ef8dd0121d4d5da9dd1587eb865f863b84000fae888c174ba5b883165b79f93fa563b159bff059ee0dbb321c27f2011a9ce98fe4a61e64ea128e8681520c3de32b6690a1bcadf122be31616cc4f076ed009a0ad02d319dd46887a6b101b85899941d6c53b50cfe860d6057d8e1135f9f49f5cb865f863b8405e1b7914ec830b24f3f9c2418c0beee6b68fbffec74b4c79169636e27c8bb12917554b8ab9e948bb1dddaf047d523df375e976f1db0fa10e467c2a97e738ba07a0ad055482f247e14bb474ccb7d99d8010c7b3c8d2b26a64a213f5621b8818e6a2b865f863b8402e16aea8323d7b8cd3a09ee9891b9196bddf8f6672196775c31a7cc4557dfe9122fd72f8a4a31e5c237e42e02822f9f0753acf4889003f677e7822bd73328b08a0ad10229fde173b0120795502716fbab5bc44eea02707798ba1f4b95c1a2bf6b2b865f863b8407cf07b00f721a1c9de164ead55a0346f06369295d9c6a7deaf60344916ce746d3451653fb6b85de86e8b4e61bdbd7e764d08e8bb8e3e35dbec3b9b3883cc070ea0adb55f11cc68686f2d6b83a002d68f1d820ce67445494b8aa7ec19ef89ca1b5fb865f863b84048eb8e348daeb9032b21841ab86d8b7fed6f97383131b8369f59b206185f7f4060af4d929a6f0c4ab045c552ca98880c209e4e5f273a2f97340be8b8f8f3150aa0ae37da60940e7919c60c3d3719aa0bad05f0116fe8f2e7ffdb24bf6ee22a76ccb865f863b840cf893dbc065a2a7ba14dd2a1370a1c0d8a2e8a1c72720b9cb7760cdacc442bcc25fc1cdb339e7cbb789f7cf61fa1f804d9541bb04670e166aacad5981c9e090ba0aee90dc5562fb2d5a966aa0037f4e0676df1cb4a055389c4b7f025bf58f5c695b865f863b840221dc00d11f2468b01077c04d436de9553624cb52b4b6498065acd17c21850aa9939074e2cfb9db16f42c142ba7a1788f17b79bbbefed8124f2a015f7358a40fa0af572a74dfbf8a41cd025961ba0b3544cae23c7e1c3ef4f27f06706e14ac115fb865f863b840cc0e8aacb5406f7eb2f3c71f484d71d5c0c19f1fd2f62b686b659a4630a81a1c355b4652652e92273a1277eb119c089550e28c1a00ed552599e8e2c3d6cc8701a0af65bf47b48d0fc3e920b3d99db62c93698759c5aa467a612cd1fd30e2a91bb6b865f863b840d081118606904b76382b13d39941cc68e2ae7aa4cb5e6f44a422218543565cfa278d373b37b3c6de953a759531b18aa835ba2e4f13e6a280783bc1ee125ce300a0afae4682dc8021cfd88cd9a6ad21e94d59a341fba9ab9a576ee26c37450a0c21b865f863b840772688a769082c77c3001d9f4d67986141c683b4e7b204d674a4358420f6fbb552141d40d3b43f4a535b89c3f0c98e4cec315e26d3bc7ee42da7c7fda3bc890ca0b00eb77a5716be0feaa6fcd4c2ca1ee2f67d1f49284b6a6871746707b7269d1db865f863b84012b41fea5358b318894f8a911a494270712389c94027a37955674d9c8ff142057c92b8775f96be98bf51610c0c5e4c3a1bb66a3dbb1f642890a1b63c89d69605a0b07108bc0c406bce6b6e3a6c511badbb94846970ea03c5a9571e6bba22105f22b865f863b840fa775bf7056774ad0ef12017e01bf7c67f2b40b3b9d57e5d83cffaeb5ebc76a16681a94324775c6125bf660ea28ae04fb7c92fbf81a74bc4dee0b6ceb5a1e407a0b0d09c25c58aba24de6fa8cc28468832b9c57ca423d37c214414590b9826c2afb865f863b840d5bace21bccbc9610dff2af1ea46ac2eff53cacfda10a0e5b8c26cc182128d912bb7507eafa559432a7e67eefb51b6a3df6ea15d855dda0ec23ed050d87e5102a0b0dcabff3c67fc93fcee2c59aa2c5543217fbb821bac73f0c48153914e49d049b865f863b840e91049ee4debfd2adc9a28f4537b9d85a1a4bd254b8ce23a6483b37a2318854173921227073c3ec6bb43036e979491109eb29718389055cd64411a8349b12007a0b0e0061804de004dff8b16421345738af191e4e5fd82ade83509cc0ad97ff5dfb865f863b840838b58c36fbc601a374cabad9c64c921c34af1d72bbc334f0a2eea8b0f162fdfd69a0f8fe506f628d30946e2f834cfed94e7627849b64d0d17557bc0fb76d306a0b0f81e9c4c670b51db5b990c7cf1ab617f4325f0e49ca87a3d3d0276aace80fdb865f863b8405ffe489ca340b0dd9386c7162e6e6037692dd33682dd51f0b874b6478ee17ac50a0f1a589517dd841ec1c17cb5c10eae2941e7e6c3298fcc42367f909e69dd04a0b115837b3eae48d82466dc106412a70b6f893c7799849fb6c136bd5d831cdeeab865f863b840aec2ef664ee05b910a073340b85ad09f186f6bab65caf8dac9be8ee2e78a95dad763995bb7082bab77e7ae36a58235ed3822f721d58aadc5ac189b4d9dc8ed06a0b11fc43bd5e2b238c0d0337be8285193b001101c9a8a2110bbe35eeafc30ab8db865f863b84095ac6674cec0ac1555fe0be6cd4caa3d046c5010d7da6057cb82aabc265468e834a4fa38b68d96131f7f5256966dce035e76b5d04462eb545d7e2f2920275e01a0b14fcef1f5bba5a60792c8ea3d3ebde60942b79c1d34eca9f789da14cea27c27b865f863b8402f34311285a871ce428d9d6531477edc53803cc51077bbf712128522c08903410acbf546d4ece34125458c607d925775028048a3631ea0a078710fd55b467105a0b2c0e57cfd006bc4a31e9d1ced78da3590d43ebcbd9179d6ae4ca50c2b84b4d6b865f863b8406b5e8cf9ef75896f2843f1635dfb7053643df3c806e96d7db0f2dc56a0ca4df26350f7882c34ccc000ea681fb51704d43088f4f3119734fd490a64544aa80f0fa0b2d7b70cf3c888c7d97296dd813fe50083b95cb81d95260c94a2d629edb4e7a8b865f863b8408c9a3bd1f3cfe8f2ac36316c065bb9ce6a9bbb412c1d58558383fcd7b0f16a887afa3633bf2c57a2e6ed2a54f5b5d479d9e29a11923a969e2f85643ae494f807a0b353b30799978fbcf275ab0af188be27355b9cedc94a7ec7bc76c0c32e07ac04b865f863b840a13f696b3bab6a70ed441ed461b93b5c3ef1e53a97b6cb7e159167755750fe2be763e3e4e82f1de40d6e88f426eee96da657b751c6b94f8291da62fd17309f0ea0b369e06fa9a8518d817273abb92adfa85cd0c3592f3bb68c53d32c51bc9571a9b865f863b8403326a42afc72107140ca743fb74da4f323ea45043ea0c6ab76c9da3bc2feaa18e1882a6d5985bd5d0806ebe063a910bebd3197284c33ad983f1d4523b6c62508a0b3a3ad0b9fc2bacde1f590f1e1079f81204d7983a5d0eac972c480b3f203b9feb865f863b840a774f69181881f06431d45e105e55421dd8fcf65cb2154283796feb76bc89091f364f84d22800e0cd64af0af41c0eb26ee5003e673ebe40d6a68687ea98f4c07a0b3d131d4391544a459854b4cc2fafc54184d49dfd1f48332edf97aa4b6d3e6e6b865f863b840600be8428c0f27a15759289cf625777322f89e08282f6129bf29cba6f1f1d54a9510ea9d692b7f6948e09b2e10ce0524f4677e59a03761129acd4943473d9105a0b448549ae4c0f9b3ac8a7f2a04a646d867dccbda656b80ec62c84768a0f3390eb865f863b8408f76a50926418dd67109dfc4594074dff34ed36f42c0ec79584ea3d62f23bed78a2ab586e3a771eb2ace99047af46054f847b303c6c238d73ab6c36640aec307a0b4f1f98fb7fb551724f107664aa75fb9bc9ea5c94e6e0671843a3bc6a8086997b865f863b8407199084190ff55500e33ae520e4e0dc0100e0ec365ae4c6cb4b7a11ece916acc2c034dde78b9c2348e5936bf9af21a286119b92f688cbd2f74124d103ff12b07a0b5305b4dc8972fec6a4eafe2977f2b80b6601d49672ce48a6f6c5bafa53f070eb865f863b840233ed20f964779360bdbab945838223422af2d7543ecb810d90935d1c53f4b20c3151103363c885209efc845f7090229c4c534924fb5ec449a5ba68a2ef04b03a0b74fb00fd2a352f99e02671c07f22de71298b3b5101f08a78ba2f438d77ffab9b865f863b8400bb8c43b003a480281a026370a9cf5484106791c08616eb0918c21633af1fb6459999800c98d163efcc1e11245e45880808ad074c1cf065db253556767072601a0b78db05da773a4ec530bba4eaa9e40376547225f65ec87bd6dc793d9f6728fd8b865f863b8400beb15b83b968a7d2137e71e38d8b5ff78e6fad7bd3c9c7da4d7399d18000ffbca6495947d33ee414e22909ff55f99fbc344cccf5eddf1820baf263945c9b80ea0b814f1a2c043f377d6221e57a551d0333d54c8f0b983a03611fb92c42c31bd54b865f863b8404aacd9642333248c38ec215d0a9a78ee94e952dd65f0c2c0b0d0b1f20eeccdc092efc81643a387202795018d17c115cc4f0f555b80b4541b5920727ec1ccea09a0b8479cde71991c00078532a159000e52525926e98520f98dd19f8e1bee5a211ab865f863b840f4c64d713ec744b31437a5e3af31d9b7ae409661f46da65e023aec8bb03784fc2d6f789c931c522e0167230e231667fca2f5277797d0a781162580547f080b02a0b8d4589f88fad4eade69539a5edb61b5666fc9fbde60e91660ee8b5fa2f828ccb865f863b840f38174122b3a171e524fa2fb641996d7a8100673da07c6e67c02d3d148b5c2b91f4b6adb823ac76ab72cd9735c408dda05a6775072bf02dced2bf9f09442ff0ba0b995396e80067ec48dac0597a14549f56c4ff0bafaee702a3254aa262c34ee8bb865f863b840eaa47febed1b0caedde6633ca3a7cd01b8d048b791b07c1788676e94df0d8136b3dfcd978d1db13aac20de4d2586f66d11a88d434e346017e806f885d524090ca0ba0e04fc1845507fec83d480ba04d488b536cacb3ec02505d25a579db0754337b865f863b840fc70b9c98323d44558a5f5e8b1b0713ea17f801b526af04893ffa41105431adb44811aedcfdd6ec468afd28a49f46be28f1738b36ef85cec3b1cdd1a77a68b02a0ba731644e4df752af2b1fcd86dee7f5925f69c450f1f829f7d745741e782fac9b865f863b8409d483beb072778fb47e118fa1851fc845539e1fce4b4e05cd25ffab335b36faeabb810409854f92b8b24caa91e117d434b04d54aa7c5cbdec3fb818c9fc1f60ca0bab9bcb14cb509e46e3bfff35ebfcbe4411e0d99625a41fd43aa4ea4cf09b5c1b865f863b840e3ac214e0ceb710418d5db4b2d1f13ec97aa82bc7774d2f4c4169325db2f9921ae06b03f87e684f18b8c0f995450e6fafd77d7ed0925f28e267e5a3844afd606a0bb08b1df805d94ce82d4db0e2554afc6dc5769f0d742222a981f440e70ba21f4b865f863b840121d1b6864fae962b1d5cc0c786336cd0f689323e6e4fe0cb77933285d7ccc4d12386c5e9d2d43e6db7d02373645b0620ba31cda396973d6893491e76666aa07a0bb29100974b8ca522740fffabbffa21d2b9ff3a02187e2cb21a08aab2ac14b2eb865f863b84063c461511efcc8c6df1dde9d9043061a509b7197b036d58093ac4e599ec90b1640bb7a6c103095be85afe349a20790268f701db55e5b7944228dd0895dcb7b0ea0bb83f16032279139614340761befaf857f2ea2a198b1561790b5de31664ece51b865f863b84000fc837ebc9484e2cf68c61aebcc12b973b51603b5042910459732f921baae061b5727a5ef3406fb13c982c3db415c5db4c40b71511d9d39940c06ca30888205a0bc1cd6a00cbaddaf3dcb9b86db199416b1b58fa5ac5f372e4b3d4347f4fb2f26b865f863b84051183fb600ff04e06a607916157cb4ac3bce10b1642368a63ec72b4e79797b0d1028d2f57ef1dd933c122ac842e8f54b0afacd8d31c74192645858a9d3883409a0bc253d7765dc282f082b0c969a58facc0313c9fccbe37b60aea969fbc8418c0eb865f863b840e34b2cc4176f4b077bbd0aa84218fc439e6638ac93d6eb00ac6532122896367ea8f8d2cd2312e55b38706b11a0973510e13a5324115465ab7c643acd6713370ba0bc565a620c9fa13a975f6b8b37a25c6eb1749776ed5b1c3308a935a83b4719f8b865f863b840c0a54543086304184e848b23da1bbcca377eaf86de247ad8eda38886127ea82fa90b8ff6acac8af69ad549dae62c45d891fe755ccef9f5fa4cc8a8f52d0ebb0fa0bc9b4c9af1d5130035e8c3b3c7043d0a837af74472f9adb2afdbf602612bb595b865f863b84058f1e14614880fc263110b7c121ac228dc726c1f174be1d5928d7d9ae3ba8149c4e75e9d7fc7bdbe697128a700897ce20ba36b2b29453bef954295b70e761b0ea0bcbdc30eda2988dbc3fcf6aca2f92fe640864df1eb3fe89c1dfe7fc35aa417b5b865f863b84040abc1ab6555b314e9bc8723ed03fd69fef3fe82a8bb5f0c97865c2fb3c8911d229a8f0497f5338c18faa723457f5722c5f39aaeaaebadc21c96fa78c30e6b07a0be0a17bbe3dba128d996f6b057cf161fe47a899201f85ea14585406ff7f63a5eb865f863b84050e3ed05580cc539ee09477fe8cc928c35ad317d3a89e6ce58f810d2068be29b61750637c9c0905e1ee9b63cee879db02219c701c4453cf2bca69ec7c1b87204a0be6c659a264fa4283e80deef8725369d2191fc0c44b0ee199caba1c6223b7db2b865f863b840f08ed60569fb5c0c97a6be3dcecf528a388b9d4425b8f66daafb77b900dd2453a9ec721b2c07a0c653598ecd3c0cbba4c89990391d7ed959910b7b6fe53b7600a0bf05ad6855fa0c2caeb606384e08af4b6885de5cbc5973937bf0ec9baabfe3c2b865f863b840b077a7272ad9b483cbc21f01f6c9b46ba972a091f30d7b392c65e320ad266c214a0ff047c6be793dcaaec1ebda5076fa086beb314f9d8ba23a2ed312ad723b08a0bf2c5678423f2197fadcf2e7e1167e713aaa651f6d07edcd842d4c137328d297b865f863b840c5c86cf1c80a288494603b3abd7ca75cadecca2dd99b5ea1c3b8a8297f1fc3bffcba731c1794f0b624d3031f8189f369409f33c6dd3d8200bb218b9dd9636e02a0bfada9adeec1268accfa432231a6bb49699c038960fc5e75b2fef94759d6f0c5b865f863b8401f785c932e76e9dfa1c8675af5a4276cec2b3367621cfc78afe17ace4ba3872a5cd5bce130c008de62a886b2b9f6f33419e6f0f78994b3a7d023e00febd19200a0bfb6804ca72cb4e32dc8bf8ad30a5defebb90fb79871bcaddd266028694413b6b865f863b840677a0d95fe459dd7f8876180293f7f76bb57f484eb340639c1999a0150a4efdb08e8eaea6e624c89174f99c9d85f1e983bce419ddcb13f3d4977600655f5a60ba0c00699fc0c1712b9b73f06e32f7adc6020a92bebb50268c14547704477319fb1b865f863b8406f751233f892821af2ddbcd738a01255c927addc0ad8f5f8b10219227ef827cc0e252f6ec91a85bdf5a661cb7d91be5358a81709317a03d7393033769a23470ea0c09ee215905d9b3796467164ba1c1d8d7a8df228f003d528b2c5b6da6940c732b865f863b840bdfcdf9b72841f84ea99f664681869b81c4630d8caccb042980d436373cf79578aeeeb241f0618bdc99bb69189528190432b736912e996b7a312c057bfff5d06a0c0a1faac2c3fce942577d07dd7113b58e146149f627f25c86a526ce64d9d002cb865f863b8407b9d8e6356e982130854216cc0f19245703e4109275e30b1c41abee119a802b94893d138fc1abafe676c32a1105ec6f6dc1e7a3caeea427c9c6f5c0d361ddb02a0c0c8b899b2fb3daf9a7f6b71e887141a92da1f473a3d63790d28a25c30f9799cb865f863b840d84513f8a07af8653d68897526edfedd38ea8f1429f3d6604bb752aaad0d26ee7dcc1fc8395ed7c3e3be062ce92f6848c1bc80f0e356ee5599785ed41e390c04a0c13ec46e6e4c05622b2103c215c7814e48afbb789362765c709b9afa366e39aab865f863b840fe65f80474ce152e8066b687be7a24e8ead0150b110917e859c79dd39a4056aefca87b22a58b96192be770b8665b8d7360408482e7d0341077be2f972ed4080aa0c2408e30d9a4867b4f45eedc81c5550db900086af0fb6501d6786e9082c93643b865f863b840ccfe58ed09fc30045d0ab8511e554d3c87a6190c82c01af820511666a0524586e90b30ec2dd587782b3f509ca3a75b137e197c99180a8e30c893612f414aea02a0c25069b7d51e3077be57bc11db23800b79c972056e5589c54a0b1b0509713a5cb865f863b840f9d39d7f45f0acb6d596224dbc5268dc92c674344eea6b0965b66f8aeda0416856408736c7c5223c8e418a2f20ccac6f98b3207677db7cf7255956abd85a430da0c277f631f7215a61fee7dc682c6962b40226369a9fa68d04fd1b5d0ffca668a4b865f863b840544c246eb0341e91ff0d12cf7758bdbda797ee64e79b351a42635383ad07c24b71938fe161d88d6c4f39470581afd240d004deac2b8daa8980213ed9f9d14900a0c45b5870f33be25386a110b8178ddba4c073bbe1ae9ceb277515bb3837916a82b865f863b84073ab21ca37f38b248df433df4e9146c9d73f2cca6a690a1d001898a3c8159e890c8d085fdf4d194c43c7c4154eb5fda133a50422fd87fa6c40f53fe71740440ea0c4c6cf5e15dbcaf804ebb370b3f34d6c88317c199870d5793f84ff14198f6f64b865f863b8406d680f67f1f88bb407308cce55da3ad66ff224f480df9dd57710ee9f8479fac1d904230f93f8d7c781b6e551d39fd04f0f5f89761b851d297d2bf97953c66201a0c4ea85b552adc87d10d1dd976a6aeec6e07a1f22127a846e8d448b2cc93d10e8b865f863b840919f32dffeb050919a11f05b358375778975535f159518c9b23b32694c39893c6c215b078eec546eb903d9de693e6a9b81d1548c0725bbcad7bb6112b87df000a0c511ee786680f69643a8219504d27d4b1db66d53a67403cf4591e2aab3b09982b865f863b8400dcca38df3df830efe34e80cd33d7c0562ba25e31c5e3054fd82b45cbc1df9554a43b58d4486e41865a53011fa3b60bb973e074c6be9a9a1d7b1cd6041702505a0c5bbfd947059a580002d769bd6c0b8c4095a11b9451710674753b25a88f3b5f5b865f863b840c9b0928cfc8852ccf7f8e8f916698d71fbce90ec948230bb4a1282e6787c7bc2835280a22b3121425deefd1ccc0fb9ea19530cf15fc09622939dfcd28317200ca0c6691c3cf5eebd250d01013b93d5d4dd6e406c38ea6a6966fc4d2a34956c4c4ab865f863b8406a160b012ccd548cd52050adfa2deb91a4f9fca2dc0ca08ef2b1df506b2055913ec43ff256acb5c24b4b6c60c696d148f734e4d4bb1f97b2668f5f9e427bea0aa0c69c025f13a202d8e5fff9b0b209eb185b6ded25593530eae893c83036341aaeb865f863b8409992acc32a3d31569479776320ec83b4b6453d3b7263fde6c8858db70e54644ce6dc5590b25372a1248bd51ce5dddaf288cc9bb8f06d87aad47157c7be352205a0c6c7d5c50023bc108ec8af8ec260715d19bc72448527ef4bbe7f11e3d877df54b865f863b840e4bc9fd4bdad6bce02867fe462232b64ed7dde386d33003a0cbf86817612ecc90288117d8029fc6ea74c92ae403f6c2a658d7ba4fb320d0fd25cd88a6f35f504a0c6f0c0135aa15a67f14bd670ca877085c7f263f811cff7db248bb130d34a4870b865f863b84068566da842abed5e04b1f09df38354932b2b2651808384bcc060cf9fd228226c9a66999b4d6de7d7adf0f954205d5339f7c786577d9afa49450517a77fa79503a0c80b0c7d6df417f1fba2e532590cdd2a98a7bc0e418ccd5f326dfb0b89c35464b865f863b840f74d0bf72b4f2a95503e93a9403f1b20e76e16593e483507698488d839a72729d65863ee3ba67bf1307e78addaa5cb3e2830cb17960b782aa7806044be04410aa0c84a5643fe7a66dfa5133d9fac89d58dfd37b4d3856cdce379bbb1a4f3cee5a3b865f863b840a424810db5c3f1e7b5d09284a9fd29e5a368a921991930fb46952908b140d9241cc911124f5d8df70273046ebf1412f79b6346448db0271673851e34280d0700a0c8d5cfbca144fc4aac29dde930f9b372c61d3d1efbc1496dcd563e20c45a280db865f863b8404cb200fb20700b3a9578b49a988ed38c5e563f58e1a741d2ae29e92a381abfc2bbebd9f32e4d6e4fa8522ff48a74bfc51dc775835e166457c0bbcf5741cd4501a0c98dc6754b2cb297986bec40c51907a9a7fda0d2ac808a03e4cf36d2e7986e56b865f863b840d73c08c17bcacfdfd0e1c37172770c2aa03fc0b7fc8fe4e08eb16d7aa0db990d162c3d577f9645601dd996029bdae829cb353c98219430fb392bd2f640daa303a0ca01f68ec2720c5d4aca9b9120ef2b3158b6435596a11adff45f4348aae1f765b865f863b840a23f31716b06726b91e4c76f440d2787360a9ea5cb551a9f1a4badfd1a2d7690b60822a6aeea74b6d89264b0de0e92fa81007af3bd17a1728926de5ea188150ea0ca347a9db580cbb32128af8a26fb5724a21ec7547cd004d23123322065843350b865f863b8409f100e9a5e32af65be88e4d3bc64a72ccac82dd00eb2a53f5bcc6bfdaa5d77b8d3a05d6343dea97b406a84c0f21c88a156b28e05efb9480f91f6604ae780e20ea0ca77e7a167aa17939810ff2e6e5e4c66b997b214b8ad41ccbba8be7c202481e3b865f863b840dce74ce7486c6b0c0a7a27a505a97cd12bd4fc2cce662c0968b62df82d72a32f3afd5dfb82f9b8933c8f355e518700b8f51646d5175f7b03260c86179d9f800fa0cb023376af50df3b6d8ad5b61ba232c815a91105e3f73e945678f248226a8c1eb865f863b8404389cbb47aefea9df3e57e16128644808e81bbf8a3121827280c768effabe95958b7bc81c6d5a07b7101d1fb49bc2cf9d5a3e9fc5aee569d1bf979969a96570ba0cb092febdd11ff3b9077cf19177f9c8494898bd36f4ce900d21ecd2d73c9330eb865f863b840ebc7b1994bf686714b826cdee65e3b7c88ec37a35f604ae9e1fd3eb57b9a8d422aa1a28d832436794983818b1df0ccfebe9aa4d5b6b1b1325e19b15088b01b02a0cb29686d490f5cdffecd907fd6b189317ebaa1cf01b916e4eb86e340ee3da01cb865f863b8401562923f6bac6917ba6ac99a737d83c5ccc0f643ba58e0eebe974d9ec86c1be50bc19ec012a63494e97d8be706f9f31315099121fde295e0b512beb0e235d400a0cc3b077b2c20246f3b5a658ca418502818cc833c7e07c8c0677caea580dd4596b865f863b840855df22e5713845b812e3ba971564e7901b27406490355a446e6f40daacbcc89301d7475fdd01f23e6729afa653e85458cbd0ada60a6d267de9b73775b9e4203a0cc45569e5ec31f0d258c9a323de87bafd9100d0c3deb05a203ad74ddf2690d90b865f863b8402ac033e7a83a447f05374887fcae947675ba0d5a3cfaade158305b0d9b87493017857cdb190b9eaa2f3dacba38a743d6f8af740bb7697d69ba0b70bda0a61c08a0cd9b00dadbfdb24477e9a9bac83a783861e262651ec28c3d027b8c399b2017bdb865f863b84049b9cd134b46303aa6f946133bbbf5c3e01bf8ceddee77cb9cd9ccfb769d57388d2e32f65c5cb78ab9a8465591f041eeab8912fdba9558147201b09a060c810fa0cde31382e67335289f8f62f08e3ce2acc022abda0ebef80fdf7351028354d7edb865f863b840ce7efcb07aa252288aeb007d9590ee4eea766d752113119ab038b7c804d1d0dc90a96d033ad21c6ec58c2595f23219f34e8ae338d1345acf8e995331077cde06a0cdef6778461a36475a54b64d3c68035a18dde6c13ad5c68b71a45dbccffc00f9b865f863b8406a99fa36ad201662836ac55c7708a3b87d4f74a0eca438e0d4bc185a238151c61c920eb3997f235a2dafc01faa2b612e16b652acef2ca367dc4795ac72cb8f0ea0d032999ac80ad859c086354dea8cf45b1ffb569225d5724cb08cd7fba990f521b865f863b8401496d1093018767fba010425b31e4a1b996963f8590c43135d6e26285b6c17bf29f771a0cc8c9f235620b9314f6c1fb933ba3b4b9110665923deda915e012a09a0d04747076d8c7140d383e50d6d0eb20628e0ede74562189a934fe8fccb7a88d0b865f863b840a3d0bbfd263bb94805ced55635dfcce6a05354cc66a1473fe02ac25f1636b2d7a0afa2e279249b7fa6f81071e610959ba456b0aa8f2a27a9a60eb2e72c57e706a0d047c90d446415dd16e5cc315b4c87c306f1db47ff592a5ac35ca4bea7dc9d47b865f863b84034387a05e9c9f20232381caa10ad5df297010e44d921cfefe6bbd8281889c809f88b4f5e59797e45f4acf7ac9f998e2a2f2df00076522e1b5c4bba258bf50702a0d08d7c4133b81aa7151ed03d636786af200d38ea0e8de42e5eb1196065a6b28bb865f863b840687119a5bbc05c7b796bf27b3ed1ec303f8f01f4ff49536b6a0ede3f6dedc4a039d848c2031e35fac2d5cf1162a17a56de29248a903a3e85b6b2a43e62618d00a0d0ee6f6c9eaf4d7d07bbc73f5174b9e1be17bb7acffd7e4e163b9bef36f89c98b865f863b840b6979302b05087f1129394a76d5e0e51cdd04352e3219497caf7e1f45e0a04cfd15239e4be234a17a38f54b8f896a40acdff597c9e8a451853e52a6ba2ee6a0ca0d0eea5e4c798a264c2eb4d88a52f4ef92cd8ea328892f6f2ed4696759cbb4415b865f863b840ddcf9e9219c4543e8ca35039b2cbeb45cd41d2547d9d76a42d9502d5528b111448a244eab4495ddfcaea081da67dcf44b5b297b59c0ad3838fe662dc7ed3190fa0d129795011ae7ec4f692d23281f4dc5a9b77224404a2d7129ec17026b23825cfb865f863b84046540f2ce40b24855c4ee7c1dd12929feec2c00aca012fb789b6ee2befeefd9c5d0ea34655429ac0c81e567c92a9b5179b741784fa12f7ec3d68ce747cc28309a0d1388eec35655b3b51f0154bebef738858199df35486142fda47b2dd3b4c1cb3b865f863b84071b51bb3ab57f3c6d4132d8a9633c8185c9813e89088b37095b9375410e0cc7996893fa86582abd112efffdbbb64d8d5ac7057aa2d6601580811c3719ee4ce02a0d1d5ea16074e9141839bb433b2559732caf4d3e93da77f9b99114be98611325eb865f863b840918ee9ec49c8724f1cde48fe8d4c2a05a57e48e4a91fe02bc9734bf23e2541c6a3ba1bfdefdd0eda648a9fcf6fba5c2ede796827d3fecd1668ae149a811b4501a0d27d8804ded5971b644f63672db1cfd92402425f8c1c986b64fe19a9a3ac1964b865f863b840f4320e3b33a0972723317cce6e18e60d92347dffcb2ded1358a5fc8c5bba00f1d326d82aa242c307541e3ec3a2c283a3180dec51dc47dbb83b54148fbea0180ba0d377934593854d84cdced03f3115f754bf52bbc1729545258003dc9401dbf836b865f863b840b1afd887b2e782925e0dae9022c3d90363161780c7467f169b5025b62fba61cb3029920ecb1757769a084e3fd4e3daaa42075cc04242eeb58515d2f8df1b630da0d3e23f291216fb4e48d17ba0c7ac0acdf1867e0a20196fb27edfdc8b6e030462b865f863b840e04c1f137f4fef2e15e4f15cf589856572aad795ab3c6ab4295345369d0e6908ecfba82a52e4f631d466bb195dc8cdbd9ac962e8b26573d1ac36ec4d804b5f0ca0d4152846e2064ec57aaa20576c0944f49caa910e9cbd3760285a13d1629291e1b865f863b84071b468775fae15f5fa4f6bd01eba82d0f51a54a70fb17c4f0a57d3042147ed31b73949a892b7f621b041a92a51eba47ec918b91dea8f995b6b1530bd704b5900a0d434cfc66ad9e8fe9f10d01fddb34bfe1323bd3bb19403787235034dcbecf1fcb865f863b84000407995a20f931d8aa063b034fdeebf91e1d650388ecf0f3ddf3b5d167533416dde296f938c9b49216e53b22970fbaaa2df13069ae8c4c1f0ffc5a6e79ed90ca0d481cef7cc85810cefc53981ba07c28dcfb89d10573681f9c421bbb9486549dab865f863b840e55129e18a68c8125c60f27478651a70d943c70ab50bfefc419bd97c97b1933d95293d4fd138d65d9d6448a7c4bd830bb51f7713c9a446b63502ccdfd9a3f404a0d4d372bb2122dce38506e0dc3ad12189a4f4c0087315a74a9e75fea1e35b3b39b865f863b840974ee37fa103f894a7bbf583c57956340a4f9a0e97756669a7ad0eecc16dff17b23a5fef51844ea1ae7ba2614caa3205fd3e1b728f8973b60c45cd4292695f04a0d5183907d381837e1ff361e4ef60d90144e04829cf13e71a099aa29a5e913f02b865f863b8406fa1edcead87850a912a859cb7ac7e8e1bd596fbba1debd46cc2e09b46efd7323096b53a61b55d547d8d8a83dbd2bc72e2de95f96b337f69b7e51e300ed4ca0ca0d5750412284dcb0b01f31c7dea726e73bcebf87829c68ee1b0fde883ee39f8afb865f863b840a7b9c47b54c6cafd4cb7e0e80a50d652befd2c696722546f1b77360ec5cb1b7dd280ffabab3d49c221d8328334cf21c94b9779169fbae395e3fb95e159903a0ea0d592f6236034cfdc773acef60e1160c61c19f589c9a488a2bd91d19f411831adb865f863b8404f5e993dda650bc9947ba41c91bbf9a576193d1070b850172a07dfa1fd3441eae06fa8c51929c90740ed7516229c765848570ca292ff7d56377cf24970336000a0d674a87c3248100ba574ce340304ddde06f724b9c79eb7cec69cb2535d15c8cfb865f863b8400d19c8bb92dbf83798dd8e4a0b1189427e8f2def654055860cc3297b6f6a31ac76a7ce3529bc7994e6a8dbca94fee29af70453e8ae76d7aa48d3a73a645b4407a0d7e39aa06109955ed5f814180d37907d6a630c9f1021449a1619fbd607dd7f7cb865f863b840f7ce90c80995e02966d1d616e81fc76f7752a12538bbca3f2aea4c168f3b21cf7aea29ac6b48812e783f8a6e36032ad22db72f6b462cc1ada997d17ca1249e0ca0d88de450d4adb9af19f1fb2bbcb7183fd94c148cbdd493838c26bbd166aeb546b865f863b84056aa85d8f61678aa55c6fef18ed99468119d53cd711396e809b49a68ced8fb6829289019df35b06ef71f81d63014446b839a15676b742177bb3771ea066a0805a0d8a9f28359c127e5f3768c6b2441ad65bd49871d9c3c10aabfc2413b54c705a3b865f863b840b6caf5dca993eb9de739f6212cdc9a81e76b05555e7a34b61c05ab20741bd1e3d992df1f1488c55dc02d4bd549b8fc4a5843907f5c4948e242e277a3a08f9800a0d94b0051ab8a10af3d0e6f3dc2cb2f77823f603a57284cb009cd6813db99a685b865f863b840747dc73d02b55f745aee12e2e2feca9df63794a1712534fe000397d6a929c04eeae2c7c0d9337cd49c8abbbd32947f7e26b1d75531088c1f275bd1211226a90fa0d961ca15183c5dac08e844378fb1ee30f0f33390af4e4807569dd1a9bf3043f8b865f863b840741032ae6a5c17ba8497ac6527126e1a45ec0b7f517c6e11b08be5e5058ccc424ae4fc32f3da26f1d38ebbda90210f183e4c0f1b3b3877d41140be859c40d10ea0da41b74281cf2c965b58ac92e9b1e4310063156fecdd6bb652efb6b8affe5b53b865f863b84074610394479a34e423a338be4f66e26e23a3a8b31df333332c8163fad669423e6099ccd2b6de662739a15c875f4cda3c7cf2e032065852ad364d4864cafdfc01a0da5b6360b587c48b3cd3ea07b46003c5db14a8742651170b9525e138d54332d0b865f863b840c33efbdf689c3454edcf74858dd5484f3088c9034c2189b35ed83505011ffddbd30c135030bd940481f288aaa54915eac7c772ccd7cbdddda1d06cfc18e4c604a0db7dd8256c43c726b27dc6fc8feaa6618d1097ccf964350297b51f26dfa7898bb865f863b8402f9c27025cf713141e9ea4411636ceb665285ac936fefd84112abd7f6b66decee6679780d463d11e66c46853c1fe8d41a1482e4c7d9a487b0e80f492ce215300a0dc04544011294afc730c2308eec8777836ea4d719e01488d010ff8a61fc09d50b865f863b840d444a1649277050872b414beaf156921eacfa61fa3a1a10bd5df7b3eb0a9a38879fb969054714887e2cc81e63c889455e0856b7d38d7fa31534c3bd44ff22802a0dc5807c8459f4d66844532b036894fe6604540a850481a71404a6b2e0114df24b865f863b840e38bc531c300fe050e49bee95c4f5168e34d388ae04fce68bafcb280b10e04c212e050fba8c67dddad24a0add0b2dcb8d730744ee1e184a13fc14513735ed503a0dc73d3e546b98bee82a3cf1e72e3060978bc68d47e75436f042b9b17b3e2de0cb865f863b840adfd9fc02b2c91e916fdc3f32c630ce6e6842717f4881334145da4466a4986f5fce42904160b129e0bb669d1feb38d73d79adfcc78596aa3ddcdc2e6a12d640fa0dc9defb6659a54599c2ab0e88579c709a3d0d2bd9fb656f6f04e284c156336d4b865f863b840d451a4832f32cd7d73792ed8aee926ebbea2876a6272506fa7a51ae491ee034c0e289d16285b331356c37d44227c3b45276f4fbbecd079432215bd1e3d610402a0dd433203012445ca6e04d2b9eae7ae6d83b70a3371ea1883b5225072c52520b6b865f863b840499938cfbdbc2917272fd20c3d45f5724810b9cf3f5b0cf208355c87cccc32c84efadb15cbf09472d889fb3be1c1646ba24fe3300478a74d37bfa1936eea0b05a0dd9539b6b8cd6d77844d27953c7d61fa6b1640be55598cd7d2968fabd16a169cb865f863b84005f6c4e8080c63701df8762bb9d340e19968ba63d13d0bf956e78a5eea0764478c1fc4f5d3d8f9f5d27cfadcd2f7cebf872331677b3d9c953429a682d0c3e905a0de918a1920a8559e3263eaee7087f111a7d36e2957e496716a4e57768327da4cb865f863b840333919a34df23a0291d75f71d0b39e4986cdfc4b2a9fa3bd1fd22fe5149dc38c249df38f52f7791364d516d85c7ea224edb925ab25dbb6386bdca01f523f190da0df84d54a2a9b1ee1235cf8c9d2bcda4d9f58755edde9cc9f472efc7ca7ebc369b865f863b84012cc886bd324a570e4dda89207d86f01e17efe8d23adbd0b4026c05de97b32f96160a335b7c325a680452ec949e691133a14ad25b11cce1461629551d35cf90da0dff8d1fad896452d619084d0a1629ee7e9c54c87b826fb4d8c7d4e52b88cdaf6b865f863b840fe51877d386e927c113bd396a9d089fc1c7adebfeabbfb23e415c4b048c8c623a2f789f422ab3ff56b66be4626a4506331917996dc1d858992c4c73e97e5840fa0e06501dc67333d2f3c72c87dcf1cdff591f0e201d6367a5fe98b92006bf9a43db865f863b8409f111277d0e728959532b6a96dd5b6d775984133a3b1ff23849187af5769f2f5a50c97f8ec844eae9460f51ad6e3ef8f1173c18fca536eed47e35da411832f07a0e0845a51f4cb541635d90c6204f9740f9cc6373cefecc3f839f2c587cb314addb865f863b8407e87434f5514bed387f074bbad96343a12befe583dae0b5f337bd28c73c0c870821d094fa12953e6a0a1c5943482eda7b535d9a811e71145838cf45aa52fcd0ea0e099e41ca65898f4d983843c0fe9bf50514522f8308cfeaea99a80a51613fc8db865f863b8405a90f4cca3d3ab8b28780d02e7e3fb8f18032002e042692918a263d0bb44af3b6c0f2bb1fffff44610e906a499bfce17a7ab75e567fa4f31eaf473ca3698530ea0e172cef11edd7b5841173be146448fa7e8dc782ec8390393cc598e0ca727d635b865f863b84022dbc13c851b82c68e7db5966d14063f1aa292d8831166b0330e5b6be4f8628d0dbf10c24d546cd76666aa3c061d2d3ced1b4245f9194b6cdf82322a25cbf707a0e2ba59fd54c53fef7c9455a1e5d84eed99ad9dc3530995b7b661c3e25d2180e4b865f863b840b1f437789373b31aa3d5a13fdf213f32dcb085f3a07982f86d01b2eecfdc4adcef93cb742c010f1c5048448f12fc4bb77358f7d1e4c6c2ec57cd06a33df21205a0e2c55a6d7c2693ab9f4bfd9fe94856fee68ee8a62c7a8716f237a1cabdd76949b865f863b84087882002823ed8eada0126795dd74c66f2611600fb024a75bd463f846711a33157075a36e6eddb570e17cc3d4d2d456974871c4630e0a65fd53dfaea5b9eec07a0e44893830b8e8cbccc43b2b5683754aa186a00787b865e4112eef42306b02e97b865f863b8403159ab1b90d6b8286aabc2baca573412797a4968df9bde25886eb3170a87c43082bcd1c6723236c21a5159ceec7f5697dcf63feb2ca6954a71e8b67b0e1c480ea0e48e7ba1545973cf8bb9ab3ac21e850ff0b50e22d5cdf9cfd3b6f38721d4ca67b865f863b8402d49bcdb0c668d6c5982eece65bc4cb46efd1fc992fa9b9e337abe929006f8d71c5ea1609f39caa28a16d9ff7e8cb18dd2d34d52b42db8388d3f7c0d42563c0ea0e4932f43c1be02f87c1774b1fc5eb9b320a0d34c48e145d6e472714fcfcb0d50b865f863b8406fe142108dd1bcd04ce4695ae96670c737b421af28f5307d870ca1f74d3064e1abae03982a010fca3ab6645b4f926ee6fbc61e603f74b2896a5772843cf78400a0e4cf3427bd73002d54d30cfd09550e852c1c2869a31dd2d16b58aa92eef32de1b865f863b8401b0e936e8698a68ee8ee554e9eb7379910597c9297825f24e9ff369366ba2b2a47b498aedd8eee162dd67c0c0eb7a84cedc57a6ca4ae8c28321cc462734bb10fa0e565d348643bb1e32ad984699ed67392a33486735759e5c61b274bf24e1f00e2b865f863b8409db5d0426cdae916a2a1ecd609149cea62764090d575cdd0a2c28dcbb579abbbd259f5a889e115c11bee2f14fb8b42a418d4fc0b4aa7bb941d0b40d5d8de2209a0e609103ca434abd43c1b17cb1253eb54e65364eb0a1ac7749433dbcb07f3d69fb865f863b8409544e2b43f5c81cac9b27124bed0e50947a3b89bd8712896733cea6d3c0a85bb1f9e36d18061f6106ad80c79fc7a81da088b476a3ee0a504f52912c66a30f90ea0e64b3e77804b1e4cae39c8956c3c976f3886ee1ccb1e89a62fa129a9ffae0c3ab865f863b8409c6cc110989084545e757b4786f02a8992b1d98df3a62275de4f84ccd22e7c33d82b76fd99eadc90b3fc12115cd46e8a686bc64a22cd46405fcd28be065ac907a0e6c11e5c80f99cba5744104d4756629a7974fc9db8c5941518dcdc963ae3fe7db865f863b8402560e10c0201ad7214c458d2d175525db66c0b419c5b1ecf0a25a2f31a421cd4f3fda1116bcc352177b6a32a744d6790b7e8ac9920bc7cf69cbc2db14a0ded0ca0e6d59d33c825486645c8dc4bb792b628853ffc1db37971055ae122a2513f22bfb865f863b840f49b4185b2e88d11439a363fe252f2965e80185e7e68287620f63a8557ff8815ae7d18dcd4c0589a780b183bef4ddedb91d63108dc27e5ca0c47e0006b10660da0e6f40610dfa6a569e46eeef7c7cd768e412f13d4e4a6407f9a1cf049a352b68eb865f863b840d7423428ca8937a361441267cc2a95d46575808a116897bf986884ce1824a535ff4d15f5c66b57ecbda671958d81230b02775eba7b9f110dd1bddc6123ba400ca0e6fdd70d2a5afd9d969ea9187de95fd9c401b8005bc5bda61abb67547722be56b865f863b840c3a63fd4607f4e66c4cfa3eddd92af996d43e835530413a86e0af6eb668fdff4431d2d6ef6d51d5ef1049a12deda478562b48acb73cb69e639b37b85f4802105a0e71935c38e10af9d9986dc348d6b8601a533e339471c23b79f7e57bd941ae8e4b865f863b840d508d720449ad89bd9f446423f13495b1beb6b9afff9e7640892f47fff4a4a259dc3f2a636b6f0653315695009a6f201917a4f7b7d252339281e3ea8acd61e01a0e759b907acca962ba6ef4d342117a10e66fbd9dcc479f175b3c03ba261e1f47bb865f863b84023fd1bbef21b7bc5416333cdccd936905f6494391ce2cd8f41ec7835a794a00069f130e28800e1769f58120345e445f050571e6bd1b9d62c2d6970b2cf67580ba0e7df92ee768270097ba6aa61a548b7a4460ac6d35c2601de292b7245c6310a75b865f863b840231e2839d3cb11212909dcaef14444ca0f36abdb6da2575e3b4abaa780abf34dc96a0bfcbd12b187ac0639382421e3b063620fb398bb6445b0006109f591190aa0e8856888f6a3c4ae92f76adadc496e622705f517747f6a4219c53e12642981a5b865f863b840b0111fc095c0f16185bf0692ea456d1655983446dfef2d36806713e490308cac19174fb7948e6e0dda403927a3027084004c7ad4a76372bf9956558ab7703d08a0e8a08af0c91de30804a2b5adad71db6ec62846b8c523633e81ad14bdb07afa67b865f863b8404af3180c243488c80ab4384341c3d9096a6515dce3637b96ab7358066733cd87fdef76872d1c9e37398e743ed8a44b23739019daa8e90b97055caac788b54c00a0e8d4a2da4eb2c3c37f940b26c6e43cd8432b0ab3a088f593989098a4680bcff4b865f863b840d7cc78212e82f649ca84a98a0328fde9df599f7e23430f3f1d070565a77213519f5c7b690e2889c7e58db49100dfabff2da624ea8b5585ebd6a7131a79f40404a0e8dcf49e49e9883c217bad75b46ebf05186f46c65b50f89fcf4f991c3032389fb865f863b8408c3c977d15be013cb8516c6d66d05d3086b11a416181c643ba72087a2d2b4d5538df1e5653c25d59423850eea01d0b7da3772fb2ad87646e37bebfb99347730da0e9fc035b14e568bdedf2bcad9d7a78126880a580a8f853934a9b87e5cf8c2911b865f863b840cea3703595c740a4a8c0fe552836ad57d6f6fdb75bf484820a7f45f16abf3edd89704c2f765322ff7d6d6b42d0ec40f1d0ca089b8ccfd5c197382f2ac9fa0304a0ea5884e0167645f54813fcc7747363594019151c4f52fcc3dea93dbbc9cd6fc5b865f863b840306eb86fac03e4e213416d9e50f4119aee50f23430574f7991bf73c019316479dc5d7518be89f2f54eafcd50ddd01ab6629adf59b586a0830ff71b42f0518107a0ea6e33767f007f5e63cccf0daefa1b528c2ae000809fc2f42847e34ae81371c6b865f863b840a881aec443e61d67a4800258962d67666d440f5f3866620318f1e2a76ca1945d74a14d08693c99c2d5bb0d52f510afece2d8e05b87c79ec4caff896be24cf804a0eae0b7d0e15435fb7b4f2de75aae3ec3badf4aa4c908a85af073776805f06768b865f863b84046abe4e8a18df7f5c8c84f7f7d2fef5a5c9987480b1bd8dd9b711043672a96ef28ef8ea8fe6dce4a7890cff83418b7e12d42785c73331490b81b60cdec0f2c02a0eb7e19dcf651d628d972b91b10ee8a95c3aad5fd0364febec3995df77292e03eb865f863b84036d27cd5455fa48fb01297f10cb75bbbea8c279adc990aaa44112e2a712541776dbfc9eca3c6298f8511892dafe5266268285f050e990ea4d5e6dc1b9da62c0aa0ebefd3256199b8001a82fb9456eba66a055843a29d9e9d449feff5a158c8c7cfb865f863b840aed61371ecd8abb8297bf1d960e9519f6abeb25b8d29f6f19b32711f84f23d0d56cceed4664408752097f5663dd75f1e954fefe4d54b19186d047210d1ee0b0ea0ec0d9939257652084199ba0e9a366a95562d38f391d34b4ae4fb3cb224d558dbb865f863b8407a1a291fe545e06a17669dde5ad1bd5d8638430a7aa4f094ea47ec7543c73ff1fc10b2d89f3f5bf37120ee2c92df9a001cbb039ed0faf7431c3d98b4d50b8b04a0ed0fb06e61b4c5c4a0cd1b113c4343bae101d5cf8326c5c42e6a841fb18006f2b865f863b84009e4027ee387f786c0817a92cc9a947fb9b19b30d6c36b29e9c6921f6a1ad0b9b0a6c6026deb8c149118ac934c163c2990c1f950045f9398e330163068de680aa0ed848689945dd829a0e36d67f038e0a16286e531c90214d036dbab13d1c841fdb865f863b840ef50b46c9cfe2579ee395e335e0da639e9f919bd98550a11387502f0316bc1d53f5217656161bf73eb3e99e12dc7752eac6544643b3192ca8a3bea6a46431804a0ed9184c7e71882631b1f1114b2d86452231851671f7114290fe27c439f3f68b1b865f863b8409e1d9ac6231367a6a4c24b2ef81ee3d99a7e66e02b6c36d50682bc1aa0da0a2a3111a877c2937607c3594441557ec3064578414ce4769dbaacf1fe0070c2df0da0ed947414e09dacd79484396c98b2f4f64c6d4d99347cb4909d46c5e47f33f6a5b865f863b8407e98e9fbc59abd6aaba61fde0bb2c5ff8cb753e9f2b6db8e2f89f988013070a0206326517701dbb58b019998a00c5ada0d8caedc1887bc69f17a6d5a9e860201a0eece43f1649c53f7b4ab57fbbf451458be4030ca9ce929e1c4beb3e4d011034db865f863b840323e88ef9a18595e0c118bdc74e3568388ca29fdd551489886db0e268b8b7a724dba1c57b3de9c053e65a954c8942ff9169379784797b03b5a01fb602068b90fa0f00723b7fccbd08677e37963ac30411f408b3dbb5e8c43179ec3b6df36a44aebb865f863b840b8e5cc2222e3e6b09364471a19e37577c4fec7021ce585b14cc8849fa2cf2db628f96332ef4a4a2b5e8945b41979746d9bbbc5d8e8c404dabd7c72996873b20ca0f043d8f26dc0ac211466f2073d32bd7b4b1125783e98ddb037d7947571719acbb865f863b8408a184527c62cd09d99da175799732c09708112e720d77397767f3921504774105e2812b3ee3aec23ce82415fea1ddea3ab29732cca1e3f4e92e40f5382c0ca02a0f07a7954f65339317f812d09046484a73ebb6ed518776df5c47d1b9553d39c0ab865f863b84059ef423fbe078f8d09048c6d3d115761ee710833fb5928bd2443275b1676e73cb40203f64e1b3a064b75e7476f478a8875ba54d3f5bab7e32aea5ab1c8c9220fa0f07dc64178894ad2415e43a17f8c097259c254e0460b142eee801a205ca30c6cb865f863b8409c2c89c92fdc9f4b06d9638df55a637b1e9fa06e41044efdc715b9b8c0fb094773ee9b473d2dce8be8aad7a22954210dc490d7df19bc9ab6f55f6140118a730ca0f109dcb4456390725f2ad7a8f6829194a075b5d7fb7c715aca6bbebbbc25332db865f863b840a174d79d79f2814af01ad9f8685fdac00d2a928129d3d03f71b5470438f4a759e71281102935b34c97023972b679a4982ae8e902164dcbe8fcb5e43c37a1bf02a0f145c30be39bfa9e9d0bb9f006f0444130144b06d5573601771a568d9a61b829b865f863b84024a69c087cd8f6147ed6ec2f309c54b1fef3c956564144014dadbe5e52024d7b3921d67bc65f886f6d336e3f8de9e28a0edb3d4bf4523a579e63cdc5819c660fa0f183fc5dd4c28b39022847996ae945b780c1e7ae7e3803781dccc53ec1ed26ebb865f863b840477a579c33b7cbc8de78c60aa6a69a8a8917f6e7c5edb295bcab0d31aa3cb7ebf8eb4d43c7d47d7abad4fcfd40a0d4d583f6456471020e888b2f2665d8181a0ca0f1ac1a9ea982933fd5189dcfefb7384457afdc2580ae7007c83537ed780b8a0fb865f863b840b3ae42b14aad6e26cb3f38a7180b84320fa2e1b28ad105405b37160f5e010bd6c97a129137eff0386ebc441393f6748eea3c65ba409218e676999fa7ab40f801a0f2158c754d7297bc9d65b6e85962e563aff44f2fbbdf0da76c64b35ad9ff7e48b865f863b840729aed8905fc601e4742c6b1541d20cca6c4dddb1aaa6731c1f89f0bb21d6b18d4cc2dbcd07b3cdf39ebe35baa68da29c904f16a405d64f4150f1969fd607105a0f2814ac72031eda8cd762458c1f67caf5b589f3fc5d2fd5506de46fc10020194b865f863b840468d6a3c74bae936bc1738685c250772d50f66fe67e0901117c196001de9a2f5585f755c47ab8aafa26302628957f9c1761d49cf07bda5e5b70e6331dadf9400a0f28fadfe62859d32d868b2dcb7804d1617959877ba735a1c020362c4faaf8e18b865f863b840351ba2a19cfbfa0368cf31c6e25e35e827036ebbaa59c6bce0d03b4024d0276059f0ba9b8ee0360315ab1807465ec061b5d0a049698ac97eb91c86ba6e04c10fa0f2a704f6004b9b59c5c5e37bcac8b1a7225c0d1d9c2f80c3811c8ccbfa86d624b865f863b840e828371f93a6c34234d0f7bdf220710c28751a8849b06f5900120f736d1470d027734e9118f2ebd8b1c1ccca7b7f82edfdbebbd249316db321dc477c15bcba01a0f2b26e68b59da4ddabac0caa2d57eb2a012f49cef9ca7f0dedb96fbb61b5d4d9b865f863b8409178b3eb4fb0a4301ca74ff3d376e424328838ebe9d394f30d971fc8ef8734ab59f1b9aa13fda39c1f61b2ce643bb6dfcc1de916f26c08df232d997d1408c907a0f2b3f4738ef0e1db1719b15bf04788429a799f5ad08c8b6fe80e1a58561fa779b865f863b8400a958f752fd442e36fd24dda212a7b21e90fb9ac8067b1e8926e13ebb11310b9f9ad122f7ee5cb69286170a03e3d187daa46adba1c1e39e9e51080281318f405a0f2bf0d6cb2709317caaf754a6cd52231a3476c22bde651d7e658c9c5fb809c3bb865f863b840efd9fb37fcf0827d46ec60d03a070739bdd58878a0fe35a064d94073b0242593e0d91db424f6d3775c0d2398262ea1da9e555c2e14a11a207bc0e96f6be0960aa0f366cbd2f95beffe3daa55dd52f8cfc98f9dafdf66dd9a9bb50bfa5c45f7a6b0b865f863b840693e1c1ab44a1698b786c2ad44aeddf1525bcc8e088b912e68eccd2373f4295e74e7f33c5f8deafaf67b1cb326e333edd5921170c43884b7bfa1e4e80829e807a0f3fd7dda76df1d4049f4a9c89b9f0123bd9a98167fb4e8e2d58af47b24c5db99b865f863b840a447a45ffb4eede561e46dda713e892dccef2086ffdbddfcbe86e858d3ae95945fed1233a65ecfc51d5a354767732828e9e4107d4180949da936c72601b9df00a0f41127de00c7e87f09c2f4e5225f95eb79fe604350b8e5cd83a0bbc81d20f1a0b865f863b8405763836f74a922d81500e7670aa8585d035fff6c18497278f4fad7bc380d0ee11301bb29cc8b5965a72004fe96e8498e7a88ce3190ca659148a57859912db603a0f491df1216c383c87803eebb9ecd959882f187032226885385ef0ca6082c416bb865f863b840c6d7ceaf8a0a2d2e8375a1b8b5ce7aa81778a8097f93c9ace18cab0d530da92e537f27ef46f32d2734e390021b3c9d1a837142e58065041bfc14fee6aaf3540fa0f52bf0c5e8dd35828dd1e19f69a3907dddc6bbe4d294cffa75930bb101a1e95cb865f863b840b43426fcf013cf2d1194392fa360a3fa3ea36eb7bbade677f3c1bf5cf9da068f0c021d9ef607cf946b6875c1e892a41871807cd19c4a105f8b830a6a5ce7040aa0f5e938b8bb48dd063df032c120fb5b4361836fac0ddd162c8ff33121a9b368f9b865f863b84087866874f4a32a1dcebf8cc8946c6d83c9ca06111eb31ba9390f01de8f4f3f6859c7d7109c313672dc4fae8bc2380d117b40409b312015a896f75abd7f055f05a0f5f55e9d4aa34cf0ca68447789be38d3a393cf2f6b7fc9166bd8bf4133968ca0b865f863b8408f5c9ed161635593b524049af41f555aa93ed1067fddebe58b53c7797a977729ff1948cea76337fa0d32a23167ca7ab08723dfda9a35fa50fe2248073851bf09a0f65695d41b887fd3ba581ebb5f3cb5b33ff3d11b2c43f961f1643baaa7e7ea2ab865f863b84074fc5fcec425c52a16ab9c45aaabf03595557c9b46db79aecbc59bfc44b4bda69df9b14361404927a963a9fac4f363e44ae5e0d875cd493bd0922e6b73291901a0f73fed9c9c7f15783d2270a48c6c9d18d266ed5b8b11a93f151d7cd488fa388cb865f863b84066a4522a916156218e9623080171ebe4ca95f59dfe725ccf0adf6a47b9ca3892ef66c30307f62584cc70d2e782db3eed9f681d2b3b7f66957d085de4d1ed4200a0f78f4e589d61fb39a0ea4fdc3f0026dba41644f1d3069be03f767a131e22de78b865f863b840c6f958e7f0087c8c35607db9b135b04b3874490aa9ad9a0bb936ec9668e9e506042c1cc7faee4a92583e3bff3a74923178c41cfc11bdf140d351a620c9077507a0f7a35644f7d9a2d429d3e88d4b99442d1e1bde717bb9e12599cefc5b18a85696b865f863b840d74952af3215a238412340081f3fdcde6b233e06772001849181038c2ed123b7174971f93e0b65656baaf8e7ae380ac0e5af472cae5f2f8118f106891835890da0f7b55c5f30444ad1ba0b9bb8161d8ea68ee658b0386eeb1053562512914347bfb865f863b840274e3f3d5df9aaaef78800c81941a066b19df24f0de0aacecc3c37aa6a9c6be8cf16c1e37a89d9f44b7d21e369defaee3fe33d732d566c981ca0a9bd534d550da0f7b7f589570014fe1c10610a3687d292ec317f5c52b3b1ccb36f96ca3fc2052bb865f863b840d5d1c864a4c215bc5a886c8a32fc0c3ba7dab506e0adbced77de3c6553a503e9491bea19f35e503910a511d7b0a1a56cbdfc4b5240ce4d63d74568636393940fa0f866cccb71c2fc1bfe37ef0749a6fb46092265bdf25a7e256299aaa617a0e82ab865f863b8409f6ecd5aaed07cd2bf212fa8cfb0d3961036304ad3a1fb3c5c3db0e9e1fa25f96324fb0268ec60bf2aeac78ff59dd9ae55a6966dc21ae70af707689034312d0fa0f8f5fb74e9007a15660e3c52ac47db525bf2757cec1ebdf68a762aa67fa57230b865f863b8405913733b16bc951aef25e805f19c1001b2ae08589b7370b3fc1306a2d8b43bd17e9034432d5355242af9a2dbb4875713912909704bfd8ae72db4114ab760cd0fa0f950653edaa72fedb7b3f9eb0fd99785d80232c192ed0e0bf6789f107eacf6e3b865f863b840054f932edf288619ab2933b8a14f321923648d1feb98e67807805306d4aab1481f4f2fcb99561c4c396838dc08dbc4ad300a23e31a12f1bc36c8a2b1d918ec05a0fa5cda1c05f0e7069df58344fa2ed43133b4ef116acbb2b4f0faf280962a34b5b865f863b840466d8be3aded65d15ca5f520dcb497b42cac3c72902e586b64d45a1d6d254585a4ac0c9c82cb0c540fcea98e0a772b66ad6e011efb4a7d209157be19a8c60a00a0fc32a83d2fe0f86266565086b16b98caba2327731e33e4091e4c6637dbd39ab7b865f863b8406e6d9392fff443b302cde97d8897c5657641e42a146886464e427b3dcc395c79952f0afbd0e6bd931f47cd6f6e79012b71aea919fb5ebb49fc77d2a4dc8a9105a0fc55323dff57839026d76b95c143d658009f3950cc1eec73cf7f4ad1aa885feeb865f863b840f4b35d80683f561a7d78fcc98b2bdb95ff5bec35f2345b52d2d43689265d9beba31345a8038fa82628e5e36da80f4377b0dea096899d987938411d015436470ca0fcd998b754bdaa1c99701caa8281bd20871e77f5a8991d13a3004d35c93e9fc0b865f863b8405cac40c4ff6b0fff2d97799b5664f63519877b6920a4b01cc1f3c5e6b954dd02d34c417b5867a9b406b73edebdcf128141c33bbcd1945ce325f7c98337030509a0fd148f8f3a9bcfcc6b6965a6b5c46b9db1b95017e6cea99e6af0cebcc9fb9c48b865f863b840d967093a84f95d151d6f10bb42557836dc63d34aa1d54ef3e76218706d9c52038cc86463990919b1327325e0a335aced4c93d5c58b11e22b0d568a6377e72a0aa0fe05d07f8467afdb18f5bfd1ce5135dc857ee839755afb0523175ba01757b4c3b865f863b840a95764befa086a01c5228e5d15cf5389d09932eac3f0bb80ded375d47495a8ae22c272f0579bdad164a012eecde9ad7b80570a744030d98cfb634fd471bbf001a0fefb4a6f7cf7b3d2eb1f1482aef42869047a88c63306f5a623517068915c4aaab865f863b840783a27173838643aaf95fe7f96567c933a66f994a47280c3b2f9e8ddd6ae95ecf1dbe6c25ccc9cfc6e1b9f0678b998a3b4316a48756c1465aac8af65e235750ea0ff48c3c4e4e22fe843e5016a8ea63c2d4bf7063261c0e9c994bfa5b487e6d50e", fmt.Sprintf("%x", v)) + }) + + t.Run("should build 1 finalityproof with blockInclude as same as Justification Block", func(t *testing.T) { + t.Skip("Manual testing only") + require.NoError(t, err) + + r := relayReceiver{ + c: subClient, + log: log.New(), + } + + finalizedHash, err := r.c.GetFinalizedHead() + require.NoError(t, err) + + finalizedHeader, err := r.c.GetHeader(finalizedHash) + require.NoError(t, err) + + r.expectMtaHeight = uint64(finalizedHeader.Number - 1) + + path, err := os.Getwd() + require.NoError(t, err) + r.bmvStatus.RelayMtaOffset = int64(r.expectMtaHeight - 1) + r.prepareDatabase(int64(r.expectMtaHeight-1), path, "kusama") + + fps, err := r.buildFinalityProof(finalizedHeader, &finalizedHash) + assert.NoError(t, err) + + var fp1 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[0], &fp1) + assert.NoError(t, err) + + var lastDecodeHeader substrate.SubstrateHeader + for i := 0; i < len(fp1.RelayBlockUpdates); i++ { + var bu RelayBlockUpdate + _, err = codec.RLP.UnmarshalFromBytes(fp1.RelayBlockUpdates[i], &bu) + assert.NoError(t, err) + var bh substrate.SubstrateHeader + err = types.DecodeFromBytes(bu.ScaleEncodedBlockHeader, &bh) + assert.NoError(t, err) + if i != 0 { + assert.EqualValues(t, lastDecodeHeader.Number+1, bh.Number) + } + lastDecodeHeader = bh + } + + assert.Equal(t, 1, len(fps)) + assert.EqualValues(t, r.store.Height(), lastDecodeHeader.Number) + }) + + t.Run("should build 2 finalityproof with candidateInclude smaller than Justification Block", func(t *testing.T) { + t.Skip("Manual testing only") + require.NoError(t, err) + + r := relayReceiver{ + c: subClient, + log: log.New(), + } + + finalizedHash, err := r.c.GetFinalizedHead() + require.NoError(t, err) + + finalizeHeader, err := r.c.GetHeader(finalizedHash) + require.NoError(t, err) + + candidateIncludeHash, err := r.c.GetBlockHash(uint64(finalizeHeader.Number - 3)) + require.NoError(t, err) + + candidateIncludeHeader, err := r.c.GetHeader(candidateIncludeHash) + require.NoError(t, err) + + r.expectMtaHeight = uint64(finalizeHeader.Number - 5) + + path, err := os.Getwd() + require.NoError(t, err) + r.bmvStatus.RelayMtaOffset = int64(r.expectMtaHeight - 1) + r.prepareDatabase(int64(r.expectMtaHeight-1), path, "kusama") + + fps, err := r.buildFinalityProof(candidateIncludeHeader, &candidateIncludeHash) + assert.NoError(t, err) + assert.Equal(t, len(fps), 2) + + var fp1 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[0], &fp1) + assert.NoError(t, err) + + var lastDecodeHeader substrate.SubstrateHeader + for i := 0; i < len(fp1.RelayBlockUpdates); i++ { + var bu RelayBlockUpdate + _, err = codec.RLP.UnmarshalFromBytes(fp1.RelayBlockUpdates[i], &bu) + assert.NoError(t, err) + var bh substrate.SubstrateHeader + err = types.DecodeFromBytes(bu.ScaleEncodedBlockHeader, &bh) + assert.NoError(t, err) + if i != 0 { + assert.EqualValues(t, lastDecodeHeader.Number+1, bh.Number) + } + lastDecodeHeader = bh + } + + assert.EqualValues(t, lastDecodeHeader.Number, r.store.Height()) + + var fp2 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[1], &fp2) + assert.NoError(t, err) + var bp chain.BlockProof + _, err = codec.RLP.UnmarshalFromBytes(fp2.RelayBlockProof, &bp) + assert.NoError(t, err) + encodedIncludeHeader, err := types.EncodeToHexString(candidateIncludeHeader) + assert.NoError(t, err) + + assert.EqualValues(t, lastDecodeHeader.Number, bp.BlockWitness.Height) + assert.EqualValues(t, encodedIncludeHeader, fmt.Sprintf("0x%x", bp.Header)) + }) + + t.Run("should build 3 finalityproof with candidateInclude smaller than grandpanewauthorities smaller than Justification Block", func(t *testing.T) { + t.Skip("Manual testing only") + require.NoError(t, err) + + r := relayReceiver{ + c: subClient, + log: log.New(), + } + + candidateIncludeHash, err := r.c.GetBlockHash(uint64(8751755)) + require.NoError(t, err) + + candidateIncludeHeader, err := r.c.GetHeader(candidateIncludeHash) + require.NoError(t, err) + + r.expectMtaHeight = uint64(8751750) + path, err := os.Getwd() + require.NoError(t, err) + r.bmvStatus.RelayMtaOffset = int64(8751749) + r.prepareDatabase(int64(r.expectMtaHeight-1), path, "kusama") + + fps, err := r.buildFinalityProof(candidateIncludeHeader, &candidateIncludeHash) + assert.NoError(t, err) + assert.Equal(t, len(fps), 3) + + var fp1 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[0], &fp1) + assert.NoError(t, err) + var lastDecodeHeaderOfFp1 substrate.SubstrateHeader + for i := 0; i < len(fp1.RelayBlockUpdates); i++ { + var bu RelayBlockUpdate + _, err = codec.RLP.UnmarshalFromBytes(fp1.RelayBlockUpdates[i], &bu) + assert.NoError(t, err) + var bh substrate.SubstrateHeader + err = types.DecodeFromBytes(bu.ScaleEncodedBlockHeader, &bh) + assert.NoError(t, err) + if i != 0 { + assert.EqualValues(t, lastDecodeHeaderOfFp1.Number+1, bh.Number) + } + lastDecodeHeaderOfFp1 = bh + } + + // GrandpaNewAuthorities + assert.EqualValues(t, 8751752, lastDecodeHeaderOfFp1.Number) + + var fp2 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[1], &fp2) + assert.NoError(t, err) + var lastDecodeHeaderOfFp2 substrate.SubstrateHeader + for i := 0; i < len(fp2.RelayBlockUpdates); i++ { + var bu RelayBlockUpdate + _, err = codec.RLP.UnmarshalFromBytes(fp2.RelayBlockUpdates[i], &bu) + assert.NoError(t, err) + var bh substrate.SubstrateHeader + err = types.DecodeFromBytes(bu.ScaleEncodedBlockHeader, &bh) + assert.NoError(t, err) + if i != 0 { + assert.EqualValues(t, lastDecodeHeaderOfFp2.Number+1, bh.Number) + } + lastDecodeHeaderOfFp2 = bh + } + + // GrandpaNewAuthorities + assert.EqualValues(t, lastDecodeHeaderOfFp2.Number, r.store.Height()) + + var fp3 ParachainFinalityProof + _, err = codec.RLP.UnmarshalFromBytes(fps[2], &fp3) + assert.NoError(t, err) + var bpOfFp3 chain.BlockProof + _, err = codec.RLP.UnmarshalFromBytes(fp3.RelayBlockProof, &bpOfFp3) + assert.NoError(t, err) + encodedIncludeHeader, err := types.EncodeToHexString(candidateIncludeHeader) + assert.NoError(t, err) + + assert.EqualValues(t, lastDecodeHeaderOfFp2.Number, bpOfFp3.BlockWitness.Height) + assert.EqualValues(t, encodedIncludeHeader, fmt.Sprintf("0x%x", bpOfFp3.Header)) + }) + + // t.Run("should 1 blockheader when call pullBlockheader at UnknownHeaders nil", func(t *testing.T) { + // t.Skip("Manual testing only") + + // subClient, err := substrate.NewSubstrateClient("https://rpc-relay.testnet.moonbeam.network") + // require.NoError(t, err) + + // r := relayReceiver{ + // c: subClient, + // log: log.New(), + // } + + // gj, hds, err := r.c.GetJustificationsAndUnknownHeaders(1091908) + // require.NoError(t, err) + // require.NotNil(t, gj) + // require.Len(t, hds, 0) + + // fullhds, err := r.buildBlockUpdates(gj, hds) + // assert.NotNil(t, fullhds) + // assert.Len(t, fullhds, 1) + // }) + + // t.Run("should N+1 blockheader when call pullBlockheader at UnknownHeaders N", func(t *testing.T) { + // t.Skip("Manual testing only") + + // subClient, err := substrate.NewSubstrateClient("https://rpc-relay.testnet.moonbeam.network") + // require.NoError(t, err) + + // r := relayReceiver{ + // c: subClient, + // log: log.New(), + // } + + // gj, hds, err := r.c.GetJustificationsAndUnknownHeaders(1091000) + // require.NoError(t, err) + // require.NotNil(t, gj) + // require.Equal(t, 908, len(hds)) + + // fullhds, err := r.pullBlockHeaders(gj, hds) + // assert.NoError(t, err) + // assert.NotNil(t, fullhds) + // assert.Equal(t, 909, len(fullhds)) + // assert.EqualValues(t, 1091908, fullhds[len(fullhds)-1].Number) + // }) + + t.Run("should getParasInclusionCandidateIncluded events", func(t *testing.T) { + t.Skip("Manual testing only") + require.NoError(t, err) + + r := relayReceiver{ + c: subClient, + log: log.New(), + } + + blockHash, err := r.c.GetBlockHash(9765080) + require.NoError(t, err) + + events, err := r.getParasInclusionCandidateIncluded(blockHash) + require.NoError(t, err) + assert.Len(t, events, 5) + }) +} diff --git a/chain/pra/receiver_test.go b/chain/pra/receiver_test.go new file mode 100644 index 00000000..ee6c6112 --- /dev/null +++ b/chain/pra/receiver_test.go @@ -0,0 +1,194 @@ +package pra + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "testing" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/rpc" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/pra/binding" + "github.com/icon-project/btp/chain/pra/mocks" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/log" + scalecodec "github.com/itering/scale.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +type blockinfo struct { + BlockNumber uint64 + Hash substrate.SubstrateHash + Header substrate.SubstrateHeader + ScaleEncodedHeader []byte + StorageKey substrate.SubstrateStorageKey + SystemEventsStorageRaw substrate.SubstrateStorageDataRaw + SystemEventsReadProof substrate.SubstrateReadProof +} + +func readBlockInfoFromAssets(path string, bi *blockinfo) error { + b, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + + return json.Unmarshal(b, &bi) +} + +func TestReceiver_ReceiveLoop(t *testing.T) { + t.Run("should monitor from the given height", func(t *testing.T) { + subClient := &substrate.MockSubstrateClient{} + r := &Receiver{ + l: log.New(), + c: &Client{ + subClient: subClient, + stopMonitorSignal: make(chan bool), + }, + } + + bi := &blockinfo{} + require.NoError(t, readBlockInfoFromAssets("assets/moonbase_blockinfo_243221.json", bi)) + + subClient.On("GetFinalizedHead").Return(bi.Hash, nil).Once() + subClient.On("GetHeader", bi.Hash).Return(&bi.Header, nil).Twice() + subClient.On("GetBlockHash", uint64(bi.BlockNumber)).Return(bi.Hash, nil).Once() + subClient.On("GetSystemEvents", bi.Hash, "EVM", "Log").Return(make([]map[string]interface{}, 0), nil).Once() + + err := r.ReceiveLoop(243221, 1, func(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + assert.EqualValues(t, bu.Height, 243221) + assert.Equal(t, bi.Hash.Hex(), fmt.Sprintf("0x%x", bu.BlockHash)) + assert.Equal(t, + fmt.Sprintf("0x%x", bi.ScaleEncodedHeader), + fmt.Sprintf("0x%x", bu.Header), + ) + assert.Equal(t, "0xf90141b9013b4b6ca5b74e19d4bc04280edf20a53a4ebe1402cbb2ef7ed9a1611fb8411a33ca56d80e006415ef11020701d83ae5456ccecb3685eb39acc6b52d3e481214d8f1aa6b5465e347562ba9c7c993862047e1d6f020c25ab4139676afbf7170c254e2f36cefe70c046e6d62738060eed538a43e6738f4c560c5d950be96c72ad591f0c16f564c003b5c7b895c0e0466726f6e890101f7b9c5fb3f5b72f937ed511b173e5a39b9fb3ffaa1cc4dd024a4c7c36c7da8610847fec28647d5f0806548f385257170d76cf6e890f7467ef33b36dcc5b9be1b15d0fdb267aa2fce057cc81a0e2397f5a32ffd8637753f7d0c0c0b7289b002dc3f056e6d627301019a1b7069e8aa71015a15925595589999890dba42cb87dae2b5aabfee791bad47d380392c626eef34701ea3a95d7dd4fc891759cb375991aacbc1ee3663742289c2f800", + fmt.Sprintf("0x%x", bu.Proof), + ) + r.StopReceiveLoop() + subClient.AssertExpectations(t) + }, func() {}) + + assert.NoError(t, err) + }) + + t.Run("should call bmc.parseMessage N times when Parachain emits N events of EVMLog", func(t *testing.T) { + subClient := &substrate.MockSubstrateClient{} + bmcContract := &mocks.BMCContract{} + r := &Receiver{ + l: log.New(), + c: &Client{ + subClient: subClient, + stopMonitorSignal: make(chan bool), + bmc: bmcContract, + }, + src: chain.BtpAddress("btp://0x507.pra/0x64b22d2b8c3ca311a0c2de34bf799f8101c89362"), + } + + bi := &blockinfo{} + require.NoError(t, readBlockInfoFromAssets("assets/moonbase_blockinfo_315553.json", bi)) + + subClient.On("GetFinalizedHead").Return(bi.Hash, nil).Once() + subClient.On("GetHeader", bi.Hash).Return(&bi.Header, nil).Once() + subClient.On("GetBlockHash", uint64(bi.BlockNumber)).Return(bi.Hash, nil).Once() + subClient.On("GetHeader", bi.Hash).Return(&bi.Header, nil).Once() + subClient.On("GetSystemEvents", bi.Hash, "EVM", "Log").Return([]map[string]interface{}{ + {"event_id": "Log", "event_idx": 3, "extrinsic_idx": 3, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x64b22d2b8c3ca311a0c2de34bf799f8101c89362", "data": "0000000000000000000000000000000000000000000000000000000060ec0616", "topics": []string{"0x0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271", "0x00000000000000000000000000000000000000000000000000000000000005f1", "0x0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 4, "extrinsic_idx": 3, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x64b22d2b8c3ca311a0c2de34bf799f8101c89362", "data": "", "topics": []string{"0x92e98423f8adac6e64d0608e519fd1cefb861498385c6dee70d58fc926ddc68c", "0x000000000000000000000000000000000000000000000000000000075cd0d636", "0x00000000000000000000000000000000000000000000000000000000000005f1", "0x0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 5, "extrinsic_idx": 3, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x64b22d2b8c3ca311a0c2de34bf799f8101c89362", "data": "0000000000000000000000000000000000000000000000000000000060ec0616", "topics": []string{"0x0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f", "0x000000000000000000000000000000000000000000000000000000075cd0d636", "0x00000000000000000000000000000000000000000000000000000000000005f1"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 6, "extrinsic_idx": 3, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x64b22d2b8c3ca311a0c2de34bf799f8101c89362", "data": "", "topics": []string{"0xfe25c73e3b9089fac37d55c4c7efcba6f04af04cebd2fc4d6d7dbb07e1e5234f", "0x000000000000000000000000000000000000000000000002b5e3af16a8772360"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + }, nil).Once() + // 4 EVM_Logs event + bmcContract.On("ParseMessage", mock.AnythingOfType("types.Log")).Return(nil, errors.New("abi: could not locate named method or event")).Times(4) + err := r.ReceiveLoop(315553, 1, func(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + assert.EqualValues(t, bu.Height, 315553) + assert.Equal(t, bi.Hash.Hex(), fmt.Sprintf("0x%x", bu.BlockHash)) + assert.Equal(t, + fmt.Sprintf("0x%x", bi.ScaleEncodedHeader), + fmt.Sprintf("0x%x", bu.Header), + ) + assert.Equal(t, "0xf90181b9017bf1e8f0653422859dea6705ec5d86015a3c9f4c7c03eccbcb4bf858682956fb2886421300932c9abc4e9966ecf08dd3a5aa7087b448a88e54d18b3caebdbb2559c3f8744b25385e8e912f3965b85334a4524a6e15b21fb3c1b355d9e64df395b856a635970c046e6d6273802485ca9e9427894cb1864d725977e3c168171daf22bacf64c7ad5e0674c331730466726f6e890201e93015e1d2195ae2d73004a790db2e4bf394e40f14df3e0a2edd9dff0930e8a910ef0e6bfa9d8bb055f94e873f1df551b42df11d2cb053811279a1101e9c03fcf9cfa1b41ba3027ac3bbda9af6685440e8d89f12c7aca5987b334e91dbbaa4aa9356cb1e07577d7374463ec88709ce945f280b072b89f4bc8c0e70abf4b9b267c090d9ec032f7d7121f9bc2b2a8b9a6a06fd7f434cbebb963d6cfcb2e4206d2f43056e6d627301019cb74fccc8c86d67b5766b1c035e16ed4de18ecd091d4dd8724185eb28dbd1612983e904aafb984f05bd27443b6f8a56fbea955e208718d02e52eb28d0e62e89c2f800", + fmt.Sprintf("0x%x", bu.Proof), + ) + r.StopReceiveLoop() + subClient.AssertExpectations(t) + bmcContract.AssertNumberOfCalls(t, "ParseMessage", 4) + }, func() {}) + + assert.NoError(t, err) + }) + + t.Run("should build StateProof when EVM Log events contains BMC SendMessage Event", func(t *testing.T) { + subClient := &substrate.MockSubstrateClient{} + ethClient := ethclient.NewClient(&rpc.Client{}) + + bmc, err := binding.NewBMC(EvmHexToAddress("0x0daaab07b078fd9cc8ebc7f980aefd0e2aba2ec6"), ethClient) + require.NoError(t, err) + + r := &Receiver{ + l: log.New(), + src: "btp://0x507.pra/0x0daaab07b078fd9cc8ebc7f980aefd0e2aba2ec6", + c: &Client{ + subClient: subClient, + ethClient: ethClient, + stopMonitorSignal: make(chan bool), + bmc: bmc, + }, + } + + bi := &blockinfo{} + require.NoError(t, readBlockInfoFromAssets("assets/moonbase_blockinfo_814054.json", bi)) + + subClient.On("GetFinalizedHead").Return(bi.Hash, nil).Once() + subClient.On("GetHeader", bi.Hash).Return(&bi.Header, nil).Once() + subClient.On("GetBlockHash", uint64(bi.BlockNumber)).Return(bi.Hash, nil).Once() + subClient.On("GetHeader", bi.Hash).Return(&bi.Header, nil).Once() + subClient.On("GetSystemEvents", bi.Hash, "EVM", "Log").Return([]map[string]interface{}{ + {"event_id": "Log", "event_idx": 11, "extrinsic_idx": 4, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x0daaab07b078fd9cc8ebc7f980aefd0e2aba2ec6", "data": "0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f63783837316166613362643061646661313339663638616338383831396461353361643434656630373700000000000000000000000000000000000000000000000000000000000000000000000000f4f8f2b83a6274703a2f2f30783530372e7072612f307830446141416230376230373866643963433865626337663938306165466430653261426132456336b83a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037378a6e6174697665636f696e01b86cf86a00b867f865aa307844303764303738333733624536306464313065333566333532353539656631663235303239444166aa637838373161666133626430616466613133396636386163383838313964613533616434346566303737cecd83444556880dbd2fc1379b5ee0000000000000000000000000", "topics": []string{"0x37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 12, "extrinsic_idx": 4, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xc06be3322f7118b14843861b424698646875ce64", "data": "0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037370000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000dbd2fc1379b5ee0000000000000000000000000000000000000000000000000002386f26fc8a12000000000000000000000000000000000000000000000000000000000000000034445560000000000000000000000000000000000000000000000000000000000", "topics": []string{"0x50d22373bb84ed1f9eeb581c913e6d45d918c05f8b1d90f0be168f06a4e6994a", "0x000000000000000000000000d07d078373be60dd10e35f352559ef1f25029daf"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 16, "extrinsic_idx": 5, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xcf88a8d7fc1a687895fc8ffaad567f303926b094", "data": "00000000000000000000000000000000000000000000000000000000614c0ab4", "topics": []string{"0x0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271", "0x0000000000000000000000000000000000000000000000000000000000000b3d", "0x0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 17, "extrinsic_idx": 5, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xcf88a8d7fc1a687895fc8ffaad567f303926b094", "data": "", "topics": []string{"0x92e98423f8adac6e64d0608e519fd1cefb861498385c6dee70d58fc926ddc68c", "0x000000000000000000000000000000000000000000000000000004027f32c45a", "0x0000000000000000000000000000000000000000000000000000000000000b3d", "0x0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 18, "extrinsic_idx": 5, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xcf88a8d7fc1a687895fc8ffaad567f303926b094", "data": "00000000000000000000000000000000000000000000000000000000614c0ab4", "topics": []string{"0x0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f", "0x000000000000000000000000000000000000000000000000000004027f32c45a", "0x0000000000000000000000000000000000000000000000000000000000000b3d"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 19, "extrinsic_idx": 5, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xcf88a8d7fc1a687895fc8ffaad567f303926b094", "data": "", "topics": []string{"0xfe25c73e3b9089fac37d55c4c7efcba6f04af04cebd2fc4d6d7dbb07e1e5234f", "0x000000000000000000000000000000000000000000000002b5e3af16a0620be0"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 23, "extrinsic_idx": 6, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x5425f5d4ba2b7dcb277c369ccbcb5f0e7185fb41", "data": "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000614c0ab100000000000000000000000000000000000000000000000000000000000000034e554f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034441490000000000000000000000000000000000000000000000000000000000", "topics": []string{"0x8d468b5f823f8d38322e9c4433d184adf453fd3eaa28cef280056aa0664981f0"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 33, "extrinsic_idx": 9, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x81e42baeee09de2880d4a8842edb1911755ac48d", "data": "00000000000000000000000000000000000000000000001c3c1b11702bed53c7000000000000000000000000000000000000000000000000000002ff386592790000000000000000000000000000000000000000000000000df3ed9d5c44a643000000000000000000000000000000000000000000000005ac5f23236b6319b3", "topics": []string{"0x4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 34, "extrinsic_idx": 9, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xf3fc5f877e9dd90277c072ff3d07d076337fa29d", "data": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "topics": []string{"0x2caecd17d02f56fa897705dcc740da2d237c373f70686f4e0d9bd3bf0400ea7a", "0x00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d", "0x00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 35, "extrinsic_idx": 9, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xd50e4638b5f58a66a5f4ff81f092db2357ecc6fb", "data": "000000000000000000000000000000000000000000000000002386f26fc10000", "topics": []string{"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b", "0x00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 36, "extrinsic_idx": 9, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x81e42baeee09de2880d4a8842edb1911755ac48d", "data": "00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000237c1b53067785", "topics": []string{"0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 37, "extrinsic_idx": 9, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0x81e42baeee09de2880d4a8842edb1911755ac48d", "data": "00000000000000000000000000000000000000000000000000237c1b53067785", "topics": []string{"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0x00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d", "0x00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + {"event_id": "Log", "event_idx": 41, "extrinsic_idx": 10, "module_id": "EVM", "params": []scalecodec.EventParam{{Type: "Log", Value: map[string]interface{}{"address": "0xd50e4638b5f58a66a5f4ff81f092db2357ecc6fb", "data": "00000000000000000000000000000000000004ee2d6d415b85acef8100000000", "topics": []string{"0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", "0x0000000000000000000000004bd9e71910a5526375f417232264b6dcf1c21e49", "0x00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d"}}}}, "phase": 0, "topic": []string{}, "type": "0a00"}, + }, nil).Once() + subClient.On("GetSystemEventStorageKey", bi.Hash).Return(bi.StorageKey, nil).Once() + subClient.On("GetReadProof", bi.StorageKey, bi.Hash).Return(bi.SystemEventsReadProof, nil).Once() + + err = r.ReceiveLoop(814054, 2, func(bu *chain.BlockUpdate, rps []*chain.ReceiptProof) { + assert.EqualValues(t, bu.Height, 814054) + assert.Equal(t, bi.Hash.Hex(), fmt.Sprintf("0x%x", bu.BlockHash)) + assert.Equal(t, + fmt.Sprintf("0x%x", bi.ScaleEncodedHeader), + fmt.Sprintf("0x%x", bu.Header), + ) + assert.Equal(t, "0xf90201b901fb7c5eb3821324777aa80bcb46570cf52dde89b136c009b5e51c52f1efec2d75759aaf3100a7b5be88098bbb994a595b60e4d33fa1167d9b19ba0ef242e3092d81059af071f715dc6d161ee4f970dcc197a0393d3f9692ec6f8b3ef21e71f92971d8514c730c046e6d627380a0074c2df0eccdbfad8b29b3a284fa597e7e48ffffc86c842fe75fdef9d480710466726f6e8904018239fd93348272db330c1e3901b78be4112ab61d19519ad4a2e808d88df0ca4f207ab3cd8d71cee1aa5f90970b7c35cc1c523c02f0932572e4888ce7cc643509a1d48e3dc4382221d839edc8988309d2f51d6c2c24d61854602bfe5ee4aa945848eada3b8729301971b526b555143b78bdc79ab58a5f3469c9e6cb3fd7dc2ed0140a2c98df470b9c67e66b9d1c56a01134f4855c945ca6a5cb0db20088bad2dbb7b0cec4eee2a3b90afcd6bc9de569743251b8a620f65be7e3a8d3e35da5c456067239b6b7115de5d5aa73729cd621c95b7fab968f44128a7bf77d8569589c69e7fb98f13c088231b45bb8e5c0c36b1ededad1b64ea30407c669ac88f03cb263ab29bc13947146e7799e4f9f6f76e3f1dca42facf6b4eb28106085f8fb46bf1eea056e6d62730101d8eb51a193bb725b141d0eee00adea6f9b1d9c83c340c4ce785bac5987059d0e96fc680c8b5af10182be5c589b2ca59dce3fc0f738b248ded6cca1b3ade50c83c2f800", + fmt.Sprintf("0x%x", bu.Proof), + ) + assert.NotEmpty(t, rps) + assert.Equalf(t, &chain.ReceiptProof{ + Height: 814054, + Proof: types.MustHexDecodeString("f9148aa026aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7f91466b901f280fffd800d67dcfa1e31cefafb35ca4c38cb7e1cb7d3a617fbf18ce007b7903a10368f5980cef19c9297e3db7ed185f928343333f9358485c7370d57a19dbaa80860db55e380d4c4d199d79c1190046c48bfc88c72fac9732919566d2566dcd010e0aa2315c58009b963e6b51d0fc746b4d0ab91672cef6ee4992d928f83dcf2a4025cb4def42c80e1be36528d6a79518f5c3c67863cb8afcb74539880abb508158f3b2302f32a0580ffca98047216097d347be172e1dcf8f68017b4a5d6b386a1ff9833befad5f2fe80ae60a8538a53ed8ce68a4128f7a3148befe5e748e926d4ec3afd82de03c7003c80a487bb6555298c63125b2128ed2e66d644b86c66d7873b5e2518be6890c594b78093572ac9ad43de2f80f8d6bf6444a19c776dcab734020bda35ddace02ed3612f801c3e100e1b48463ab52ba1380c6da19c99fd2b844ee7cc478d4d67b2beaae418805d3d1fcff2b49723b3da0dc655c5570c7d87b8bb24366aca972b5508b521572980e8f22e7a1cb280b29e75a1d59f62d08dae14cfd1e41c39753d2ad789892e5be480628d28e1ebaa968678f56970faa6b14f2daf3db427190d64e209c991cdcc0ff680d108afb00798775dfbbc6e95815fb87d29eec5b4822b9f803b34541fe0dbd4478015bcf68c353f320fa40d1f6cdcf90c9c3f5e57f3ced5426d43ea2244c8d4cbddb845800104805b127e9edc12e57e62b63cc287372aec129041e27cf536af12d9c19144b1144a8057f9b3c58731b9265e2805dd67413a71d455345e3868da268575e8edbf7302ceb9109d5ed41e5e16056765bc8461851072c9d72d42b000000000000000585f8f0900000000020000000100000000000000000000000000020000000200000000000000000000000000020000000300000003029755f1ea7a6f6058dfc02f95bc932c3c261c1f314e0078423a39efbc1f8b5104540ac2650a7565770080e590a1a554450000000000000000000003000000110600809cbdf9fb020000000000000000000000030000000b009755f1ea7a6f6058dfc02f95bc932c3c261c1f314e0078423a39efbc1f8b5104540ac2650a7565777ab3cd8d71cee1aa5f90970b7c35cc1c523c02f0932572e4888ce7cc643509a10000000003000000000040dd4a1f0000000000000000040000000003f798331b1d2000d483977593ee6e447e4760c63f0000040000000300f798331b1d2000d483977593ee6e447e4760c63f000064a7b3b6e00d00000000000000000000040000000302d07d078373be60dd10e35f352559ef1f25029daff798331b1d2000d483977593ee6e447e4760c63f000064a7b3b6e00d0000000000000000000004000000110600e09f906384070000000000000000000000040000000a000daaab07b078fd9cc8ebc7f980aefd0e2aba2ec60437be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b81070000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f63783837316166613362643061646661313339663638616338383831396461353361643434656630373700000000000000000000000000000000000000000000000000000000000000000000000000f4f8f2b83a6274703a2f2f30783530372e7072612f307830446141416230376230373866643963433865626337663938306165466430653261426132456336b83a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037378a6e6174697665636f696e01b86cf86a00b867f865aa307844303764303738333733624536306464313065333566333532353539656631663235303239444166aa637838373161666133626430616466613133396636386163383838313964613533616434346566303737cecd83444556880dbd2fc1379b5ee00000000000000000000000000000040000000a00c06be3322f7118b14843861b424698646875ce640850d22373bb84ed1f9eeb581c913e6d45d918c05f8b1d90f0be168f06a4e6994a000000000000000000000000d07d078373be60dd10e35f352559ef1f25029daf81060000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000003a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037370000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000dbd2fc1379b5ee0000000000000000000000000000000000000000000000000002386f26fc8a120000000000000000000000000000000000000000000000000000000000000000344455600000000000000000000000000000000000000000000000000000000000000040000000b00d07d078373be60dd10e35f352559ef1f25029daff798331b1d2000d483977593ee6e447e4760c63fd48e3dc4382221d839edc8988309d2f51d6c2c24d61854602bfe5ee4aa94584800010000040000000000e0cd3a1403000000000000000500000011060048bc1a79a2010000000000000000000000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0940c0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac602710000000000000000000000000000000000000000000000000000000000000b3d0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa8000000000000000000000000000000000000000000000000000000000614c0ab40000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0941092e98423f8adac6e64d0608e519fd1cefb861498385c6dee70d58fc926ddc68c000000000000000000000000000000000000000000000000000004027f32c45a0000000000000000000000000000000000000000000000000000000000000b3d0000000000000000000000006ffa2707ea1fe81c8dafa755e74f4961333b75fa000000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b0940c0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f000000000000000000000000000000000000000000000000000004027f32c45a0000000000000000000000000000000000000000000000000000000000000b3d8000000000000000000000000000000000000000000000000000000000614c0ab40000050000000a00cf88a8d7fc1a687895fc8ffaad567f303926b09408fe25c73e3b9089fac37d55c4c7efcba6f04af04cebd2fc4d6d7dbb07e1e5234f000000000000000000000000000000000000000000000002b5e3af16a0620be0000000050000000b006ffa2707ea1fe81c8dafa755e74f4961333b75facf88a8d7fc1a687895fc8ffaad567f303926b094eada3b8729301971b526b555143b78bdc79ab58a5f3469c9e6cb3fd7dc2ed01400000000050000000000c81468ab0000000000000000060000001106007aa9a3aa06000000000000000000000000060000000a005425f5d4ba2b7dcb277c369ccbcb5f0e7185fb41048d468b5f823f8d38322e9c4433d184adf453fd3eaa28cef280056aa0664981f0810400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000614c0ab100000000000000000000000000000000000000000000000000000000000000034e554f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000344414900000000000000000000000000000000000000000000000000000000000000060000000b00f516bdbd32a0b4cb07d21f26b9185b336502027f5425f5d4ba2b7dcb277c369ccbcb5f0e7185fb410a2c98df470b9c67e66b9d1c56a01134f4855c945ca6a5cb0db20088bad2dbb700000000060000000000f0cea5310000000000000000070000001106002eedcda848020000000000000000000000070000000b001e9eb23057adf446777c7fab1a6c565cb8977ab50daaab07b078fd9cc8ebc7f980aefd0e2aba2ec6b0cec4eee2a3b90afcd6bc9de569743251b8a620f65be7e3a8d3e35da5c4560600010000070000000000189d87b5120000000000000008000000110600d431ee3948020000000000000000000000080000000b001e9eb23057adf446777c7fab1a6c565cb8977ab50daaab07b078fd9cc8ebc7f980aefd0e2aba2ec67239b6b7115de5d5aa73729cd621c95b7fab968f44128a7bf77d8569589c69e7000100000800000000009055fbb1120000000000000009000000110600fcfa0ea518000000000000000000000000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d044dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04010200000000000000000000000000000000000000000000001c3c1b11702bed53c7000000000000000000000000000000000000000000000000000002ff386592790000000000000000000000000000000000000000000000000df3ed9d5c44a643000000000000000000000000000000000000000000000005ac5f23236b6319b30000090000000a00f3fc5f877e9dd90277c072ff3d07d076337fa29d0c2caecd17d02f56fa897705dcc740da2d237c373f70686f4e0d9bd3bf0400ea7a00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b0101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000090000000a00d50e4638b5f58a66a5f4ff81f092db2357ecc6fb0cddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d80000000000000000000000000000000000000000000000000002386f26fc100000000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d044c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f810100000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b000000000000000000000000000000000000000000000000002386f26fc1000000000000000000000000000000000000000000000000000000237c1b530677850000090000000a0081e42baeee09de2880d4a8842edb1911755ac48d0cddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef00000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d00000000000000000000000045451aadad03ed94b94fcec603af11f2e8f16c1b8000000000000000000000000000000000000000000000000000237c1b530677850000090000000b0045451aadad03ed94b94fcec603af11f2e8f16c1b81e42baeee09de2880d4a8842edb1911755ac48dfb98f13c088231b45bb8e5c0c36b1ededad1b64ea30407c669ac88f03cb263ab00010000090000000000b0cee3c900000000000000000a000000110600c88e1801080000000000000000000000000a0000000a00d50e4638b5f58a66a5f4ff81f092db2357ecc6fb0c8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9250000000000000000000000004bd9e71910a5526375f417232264b6dcf1c21e4900000000000000000000000081e42baeee09de2880d4a8842edb1911755ac48d8000000000000000000000000000000000000004ee2d6d415b85acef810000000000000a0000000b004bd9e71910a5526375f417232264b6dcf1c21e49d50e4638b5f58a66a5f4ff81f092db2357ecc6fb29bc13947146e7799e4f9f6f76e3f1dca42facf6b4eb28106085f8fb46bf1eea000100000a0000000000a031924100000000000000b86680410480a5d1d10f719b25189d69cee1500b2bdf5d747a5d834af2fb7bf3c8009352047d80c6a0dbe93fb0d6eabc13a415545fc917583db2ceb3132a37fd2b577ca984ff248079a049df09bffcad30d32c4f69ebc11bf1732ef47defd27ac368292273dbf460b9011f9eaa394eea5630e07c48ae0c9558cef7398f80d7b3e23a3e88acc21d0a65e825397d6315df2f04ea6425697229be4b759d2808809ad724b847153c8f72bc55c93456c5a925bdbb37aa4de1d201f99bd9b4afb1ba505f0e7b9012096b41c4eb3aaf947f6ea4290800004c5f0684a022a34dd8bfa2baaf44f172b71004018045ebc4af9c052c8a75e2df8f8324ceaf4d855606c819a00213e3d26e199da2fe8084184d6de6a4e5d74b4c37f18d7c1d4b4102d8a51abffa7766f6e45902c1a79280de82b080f4ff4307862cd2058e48806ce66aa3a7373e754c39369fd85042a04780777f917207078879034726deebc7268366f961e9c6ba053aa7c9fd0b5983449f745f09cce9c888469bb1a0dceaa129672ef82cf50a206d6f6f6e62617365"), + Events: []*chain.Event{{ + Next: "btp://0x42.icon/cx871afa3bd0adfa139f68ac88819da53ad44ef077", + Sequence: int64(2), + Message: types.MustHexDecodeString("f8f2b83a6274703a2f2f30783530372e7072612f307830446141416230376230373866643963433865626337663938306165466430653261426132456336b83a6274703a2f2f307834322e69636f6e2f6378383731616661336264306164666131333966363861633838383139646135336164343465663037378a6e6174697665636f696e01b86cf86a00b867f865aa307844303764303738333733624536306464313065333566333532353539656631663235303239444166aa637838373161666133626430616466613133396636386163383838313964613533616434346566303737cecd83444556880dbd2fc1379b5ee0"), + }}, + }, rps[0], "ReceiptProofs incorrect %x", rps[0]) + r.StopReceiveLoop() + subClient.AssertExpectations(t) + }, func() {}) + + assert.NoError(t, err) + }) +} diff --git a/chain/pra/sender.go b/chain/pra/sender.go new file mode 100644 index 00000000..4e1ee5f3 --- /dev/null +++ b/chain/pra/sender.go @@ -0,0 +1,460 @@ +package pra + +import ( + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "regexp" + "runtime" + "strings" + "time" + + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/log" +) + +const ( + txMaxDataSize = 524288 //512 * 1024 // 512kB + txOverheadScale = 0.37 //base64 encoding overhead 0.36, rlp and other fields 0.01 + txSizeLimit = txMaxDataSize / (1 + txOverheadScale) + DefaultRetryContractCallInterval = 3 * time.Second + defaultMaxBlockUpdatesPerSegment = 11 // this is for Moonbase alpha testnet + defaultGasLimit = 10000000 // estimation for 3 blocks MaxBlockUpdatesPerSegment +) + +var RetrableRelayReSendReExp = regexp.MustCompile(``) + +type praSenderOptions struct { + GasLimit uint64 `json:"gasLimit"` + MaxBlockPerMessage uint64 `json:"maxBlockPerMessage"` +} + +var ( + DefaultRetryContractCall = 10 // reduce testing time +) + +func init() { + runtime.GOMAXPROCS(runtime.NumCPU()) +} + +type Sender struct { + c *Client + w Wallet + src chain.BtpAddress + dst chain.BtpAddress + opt praSenderOptions + log log.Logger +} + +func (s *Sender) newTransactionParam(prev string, rm *RelayMessage) (*RelayMessageParam, error) { + b, err := codec.RLP.MarshalToBytes(rm) + if err != nil { + return nil, err + } + + rmp := &RelayMessageParam{ + Prev: prev, + Msg: string(b[:]), + } + + s.log.Tracef("newTransactionParam RLPEncodedRelayMessage: %x\n", b) + return rmp, nil +} + +// Segment split the give RelayMessage into small segments +func (s *Sender) Segment(rm *chain.RelayMessage, height int64) ([]*chain.Segment, error) { + s.log.Tracef("Segment: height %d %s", height, rm.BuRange()) + + segments := make([]*chain.Segment, 0) + var err error + msg := &RelayMessage{ + BlockUpdates: make([][]byte, 0), + ReceiptProofs: make([][]byte, 0), + } + size := 0 + // When rm.BlockUpdates[len(rm.BlockUpdates)-1].Height <= s.bmcStatus.Verifier.Height + // using only rm.BlockProof + lastBlockIndex := len(rm.BlockUpdates) - 1 + if rm.BlockUpdates[lastBlockIndex].Height > height { + for i, bu := range rm.BlockUpdates { + if bu.Height <= height { + continue + } + buSize := len(bu.Proof) + if s.isOverSizeLimit(buSize) { + return nil, ErrInvalidBlockUpdateProofSize + } + size += buSize + + if s.isOverSizeLimit(size) || s.isOverBlocksLimit(msg.numberOfBlockUpdate) { + s.log.Tracef("Segment parachain blockupdates") + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + segments = append(segments, segment) + msg = &RelayMessage{ + BlockUpdates: make([][]byte, 0), + ReceiptProofs: make([][]byte, 0), + } + size = buSize + } + + s.log.Tracef("Segment: at %d BlockUpdates[%d]: %x", bu.Height, i, bu.Proof) + msg.BlockUpdates = append(msg.BlockUpdates, bu.Proof) + msg.height = bu.Height + msg.numberOfBlockUpdate += 1 + } + } + + bp, err := codec.RLP.MarshalToBytes(rm.BlockProof) + if err != nil { + return nil, err + } + + if s.isOverSizeLimit(len(bp)) { + return nil, ErrInvalidBlockUpdateProofSize + } + + for i, rp := range rm.ReceiptProofs { + if s.isOverSizeLimit(len(rp.Proof)) { + return nil, ErrInvalidReceiptProofSize + } + if len(msg.BlockUpdates) == 0 { + if rm.BlockProof == nil { + return nil, fmt.Errorf("BlockProof must not be nil") + } + size += len(bp) + msg.BlockProof = bp + msg.height = rm.BlockProof.BlockWitness.Height + s.log.Tracef("Segment: at %d BlockProof: %x", msg.height, msg.BlockProof) + } + + size += len(rp.Proof) + trp := &ReceiptProof{ + Index: rp.Index, + Proof: rp.Proof, + EventProofs: make([]*chain.EventProof, 0), + } + + for j, ep := range rp.EventProofs { + if s.isOverSizeLimit(len(ep.Proof)) { + return nil, ErrInvalidEventProofProofSize + } + + size += len(ep.Proof) + if s.isOverSizeLimit(size) { + if i == 0 && j == 0 && len(msg.BlockUpdates) == 0 { + return nil, fmt.Errorf("BlockProof + ReceiptProof + EventProof > limit %v", i) + } + + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + EventSequence: msg.eventSequence, + NumberOfEvent: msg.numberOfEvent, + } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + segments = append(segments, segment) + + msg = &RelayMessage{ + BlockUpdates: make([][]byte, 0), + BlockProof: bp, + ReceiptProofs: make([][]byte, 0), + } + + trp = &ReceiptProof{ + Index: rp.Index, + Proof: rp.Proof, + EventProofs: make([]*chain.EventProof, 0), + } + + size = len(ep.Proof) + size += len(rp.Proof) + size += len(bp) + } + + trp.EventProofs = append(trp.EventProofs, ep) + msg.eventSequence = rp.Events[j].Sequence + msg.numberOfEvent += 1 + } + + if b, err := codec.RLP.MarshalToBytes(trp); err != nil { + return nil, err + } else { + s.log.Tracef("Segment: at %d ReceiptProofs[%d]: %x", rp.Height, i, b) + msg.ReceiptProofs = append(msg.ReceiptProofs, b) + } + } + + if len(msg.BlockUpdates) > 0 || len(msg.BlockProof) > 0 { + segment := &chain.Segment{ + Height: msg.height, + NumberOfBlockUpdate: msg.numberOfBlockUpdate, + EventSequence: msg.eventSequence, + NumberOfEvent: msg.numberOfEvent, + } + if segment.TransactionParam, err = s.newTransactionParam(rm.From.String(), msg); err != nil { + return nil, err + } + segments = append(segments, segment) + } + return segments, nil +} + +// UpdateSegment updates segment +func (s *Sender) UpdateSegment(bp *chain.BlockProof, segment *chain.Segment) error { + p := segment.TransactionParam.(*RelayMessageParam) + msg := &RelayMessage{} + b, err := base64.URLEncoding.DecodeString(p.Msg) + if err != nil { + return err + } + + if _, err = codec.RLP.UnmarshalFromBytes(b, msg); err != nil { + return err + } + if msg.BlockProof, err = codec.RLP.MarshalToBytes(bp); err != nil { + return err + } + segment.TransactionParam, err = s.newTransactionParam(p.Prev, msg) + return err +} + +func (s *Sender) Relay(segment *chain.Segment) (chain.GetResultParam, error) { + p, ok := segment.TransactionParam.(*RelayMessageParam) + if !ok { + return nil, fmt.Errorf("casting failure") + } + + txOpts := s.c.newTransactOpts(s.w) + if s.opt.GasLimit > 0 { + txOpts.GasLimit = s.opt.GasLimit + } else { + txOpts.GasLimit = defaultGasLimit + } + + s.log.Tracef("Relay: TransactionOptions: %+v", txOpts) + + tries := 0 +CALL_CONTRACT: + tries++ + tx, err := s.c.bmc.HandleRelayMessage(txOpts, p.Prev, p.Msg) + if err != nil { + if RetrableRelayReSendReExp.MatchString(err.Error()) && tries < DefaultRetryContractCall { + s.log.Tracef("Relay: retry with Relay err:%+v", err) + <-time.After(DefaultRetryContractCallInterval) + goto CALL_CONTRACT + } + return nil, err + } + + return &TransactionHashParam{ + From: txOpts.From, + Tx: tx, + Param: p, + }, nil +} + +// GetResult gets the TransactionReceipt +func (s *Sender) GetResult(p chain.GetResultParam) (chain.TransactionResult, error) { + //TODO: map right Error with the result getting from the transaction receipt + + if thp, ok := p.(*TransactionHashParam); ok { + t := time.Now() + s.log.Debugf("getting receipt:%s", thp.Hash()) + + for { + txr, err := s.c.GetTransactionReceipt(thp.Hash()) + if err != nil { + <-time.After(DefaultRetryContractCallInterval) + continue + } + + if txr.Status == 0 { + //TODO: handle mapError here + s.log.Errorf("failed to send message on %v with params _prev: %v _msg: %x", thp.Hash(), thp.Param.Prev, thp.Param.Msg) + return nil, s.parseTransactionError(thp.From, thp.Tx, txr.BlockNumber) + } + + s.log.Debugf("got receipt:%v taken:%.2f seconds", txr.TxHash.String(), time.Since(t).Seconds()) + return txr.TxHash.Hex(), nil + } + } else { + return nil, fmt.Errorf("fail to casting TransactionHashParam %T", p) + } +} + +func (s *Sender) GetStatus() (*chain.BMCLinkStatus, error) { + + tries := 0 +CALL_CONTRACT: + tries++ + bs, err := s.c.bmc.GetStatus(nil, s.src.String()) + if err != nil { + if tries < DefaultRetryContractCall { + s.log.Tracef("GetStatus: retry with GetStatus err:%+v", err) + <-time.After(DefaultRetryContractCallInterval) + goto CALL_CONTRACT + } + return nil, fmt.Errorf("fail to get Parachain' status, err: %v", err) + } + + status := &chain.BMCLinkStatus{ + BlockIntervalSrc: int(bs.BlockIntervalSrc.Int64()), + BlockIntervalDst: int(bs.BlockIntervalDst.Int64()), + TxSeq: bs.TxSeq.Int64(), + RxSeq: bs.RxSeq.Int64(), + Verifier: struct { + Height int64 + Offset int64 + LastHeight int64 + }{ + Height: bs.Verifier.HeightMTA.Int64(), + Offset: bs.Verifier.OffsetMTA.Int64(), + LastHeight: bs.Verifier.LastHeight.Int64(), + }, + RotateHeight: bs.RotateHeight.Int64(), + RotateTerm: int(bs.RotateTerm.Int64()), + DelayLimit: int(bs.DelayLimit.Int64()), + MaxAggregation: int(bs.MaxAggregation.Int64()), + RxHeight: bs.RxHeight.Int64(), + RxHeightSrc: bs.RxHeightSrc.Int64(), + CurrentHeight: bs.CurrentHeight.Int64(), + BMRIndex: int(bs.RelayIdx.Int64()), + BMRs: make([]struct { + Address string + BlockCount int64 + MessageCount int64 + }, len(bs.Relays)), + } + for i, bmr := range bs.Relays { + status.BMRs[i].Address = string(bmr.Addr.Hex()) + status.BMRs[i].BlockCount = bmr.BlockCount.Int64() + status.BMRs[i].MessageCount = bmr.MsgCount.Int64() + } + + return status, nil +} + +func (s *Sender) MonitorLoop(height int64, cb chain.MonitorCallback, scb func()) error { + s.log.Debugf("MonitorLoop from height: %v", height) + + if err := s.c.MonitorBlock(uint64(height), false, func(v *BlockNotification) error { + cb(int64(v.Height)) + return nil + }); err != nil { + return fmt.Errorf("MonitorLoop parachain, got err: %v", err) + } + return nil +} + +func (s *Sender) StopMonitorLoop() { + s.c.CloseAllMonitor() +} + +func (s *Sender) FinalizeLatency() int { + //on-the-next + return 1 +} + +func NewSender(src, dst chain.BtpAddress, w Wallet, endpoint string, opt map[string]interface{}, l log.Logger) chain.Sender { + s := &Sender{ + src: src, + dst: dst, + w: w, + log: l, + } + b, err := json.Marshal(opt) + if err != nil { + l.Panicf("fail to marshal opt:%#v err:%+v", opt, err) + } + if err = json.Unmarshal(b, &s.opt); err != nil { + l.Panicf("fail to unmarshal opt:%#v err:%+v", opt, err) + } + s.c = NewClient(endpoint, dst.ContractAddress(), l) + + return s +} + +func (s *Sender) isOverSizeLimit(size int) bool { + return txSizeLimit < float64(size) +} + +func (s *Sender) isOverBlocksLimit(blockupdates int) bool { + maxBu := defaultMaxBlockUpdatesPerSegment + if s.opt.MaxBlockPerMessage > 0 { + maxBu = int(s.opt.MaxBlockPerMessage) + } + return blockupdates >= maxBu +} + +func (s *Sender) parseTransactionError(from EvmAddress, tx *EvmTransaction, blockNumber *big.Int) error { + _, txerr := s.c.CallContract(EvmCallMsg{ + From: from, + To: tx.To(), + Gas: tx.Gas(), + GasPrice: tx.GasPrice(), + Value: tx.Value(), + Data: tx.Data(), + }, blockNumber) + if txerr == nil { + return errors.New("parseTransactionError: empty") + } + + if dataerr, ok := txerr.(EvmDataError); ok { + rawerr, derr := decodeEvmError(dataerr) + if derr != nil { + s.log.Error(derr) + return txerr + } + + return mapError(rawerr) + } + + return txerr +} + +func decodeEvmError(dataerr EvmDataError) (string, error) { + i := dataerr.ErrorData() + if i == nil { + return "", errors.New("decodeEvmError: ErrorData is empty") + } + + s := dataerr.ErrorData().(string) + if len(s) < 136 { + return "", fmt.Errorf("decodeEvmError: unknow error") + } + s = s[136:] + s = strings.TrimRight(s, "0") + d, err := hex.DecodeString(s) + if err != nil { + return "", fmt.Errorf("decodeEvmError: %v", err.Error()) + } + + return strings.Split(string(d), ":")[0], nil +} + +func mapError(s string) error { + for code, name := range chain.BMCRevertCodeNames { + if name == s { + return chain.NewRevertError(int(code)) + } + } + + for code, name := range chain.BMVRevertCodeNames { + if name == s { + return chain.NewRevertError(int(code)) + } + } + return errors.New(s) +} diff --git a/chain/pra/sender_test.go b/chain/pra/sender_test.go new file mode 100644 index 00000000..217032e4 --- /dev/null +++ b/chain/pra/sender_test.go @@ -0,0 +1,708 @@ +package pra + +import ( + "context" + "encoding/base64" + "encoding/hex" + "math/big" + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/icon" + "github.com/icon-project/btp/chain/pra/binding" + "github.com/icon-project/btp/chain/pra/mocks" + "github.com/icon-project/btp/chain/pra/substrate" + "github.com/icon-project/btp/common/codec" + iconcrypto "github.com/icon-project/btp/common/crypto" + "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/wallet" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +const ( + TestnetGooloopURL = "https://sejong.net.solidwallet.io/api/v3/icon_dex" + TestnetMoonbaseURL = "https://rpc.testnet.moonbeam.network" + TestBlockNumberHasReceiptProof = 273042 +) + +func init() { + rand.Seed(time.Now().UnixNano()) +} + +func genFakeBytes(n int) []byte { + b := []byte{} + for i := 0; i < n; i++ { + b = append(b, uint8(n%127)) + } + return b +} + +func testWallet() wallet.Wallet { + privateKey, err := crypto.HexToECDSA("8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b") + if err != nil { + panic(err) + } + + w, err := wallet.NewEvmWalletFromPrivateKey(privateKey) + if err != nil { + panic(err) + } + return w +} + +func TestSenderNewTransactionParam(t *testing.T) { + + prev := "string" + rm1 := &RelayMessage{ + BlockUpdates: [][]byte{{1, 2, 3, 4}}, + BlockProof: []byte{1, 2, 3, 4}, + ReceiptProofs: [][]byte{{1, 2, 3, 4}}, + } + + ethClient := &mocks.EthClient{} + ethClient.On("ChainID", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil).Once() + ethClient.On("SuggestGasPrice", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil).Once() + + sender := &Sender{log: log.New(), w: testWallet(), c: &Client{ethClient: ethClient}} + p, err := sender.newTransactionParam(prev, rm1) + require.Nil(t, err) + require.NotNil(t, p) + assert.Equal(t, prev, p.Prev) + + d, err := base64.URLEncoding.DecodeString(p.Msg) + require.Nil(t, err) + rm2 := &RelayMessage{} + + _, err = codec.RLP.UnmarshalFromBytes(d, rm2) + require.Nil(t, err) + assert.EqualValues(t, rm1, rm2) +} + +func TestSenderSegment(t *testing.T) { + f := txSizeLimit + txSizeLimit := int(f) + + ethClient := &mocks.EthClient{} + ethClient.On("ChainID", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + ethClient.On("SuggestGasPrice", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + + sender := &Sender{log: log.New(), w: testWallet(), c: &Client{ethClient: ethClient}} + + t.Run("should get error ErrInvalidBlockUpdateProofSize", func(t *testing.T) { + segments, err := sender.Segment(&chain.RelayMessage{ + BlockUpdates: []*chain.BlockUpdate{ + { + Height: 1, + Proof: genFakeBytes(txSizeLimit + 1), + }, + }, + }, 0) + + require.Nil(t, segments) + assert.Equal(t, ErrInvalidBlockUpdateProofSize, err) + }) + + t.Run("should be segmented by over BlockProof size", func(t *testing.T) { + blocks := defaultMaxBlockUpdatesPerSegment - 1 + + rm := &chain.RelayMessage{} + for i := 1; i <= blocks; i++ { + rm.BlockUpdates = append(rm.BlockUpdates, &chain.BlockUpdate{ + Height: int64(i), + Proof: genFakeBytes(txSizeLimit), + }) + } + + segments, err := sender.Segment(rm, 0) + require.Nil(t, err) + assert.Len(t, segments, 2) + for _, segment := range segments { + assert.NotNil(t, segment.TransactionParam) + assert.Nil(t, segment.TransactionResult) + } + }) + + t.Run("should be segmented by over BlockUpdates", func(t *testing.T) { + blocks := defaultMaxBlockUpdatesPerSegment + 1 + limit := txSizeLimit / (blocks + 1) + + rm := &chain.RelayMessage{} + for i := 1; i <= blocks; i++ { + rm.BlockUpdates = append(rm.BlockUpdates, &chain.BlockUpdate{ + Height: int64(i), + Proof: genFakeBytes(limit), + }) + } + + segments, err := sender.Segment(rm, 0) + require.Nil(t, err) + assert.Len(t, segments, 2) + for _, segment := range segments { + assert.NotNil(t, segment.TransactionParam) + assert.Nil(t, segment.TransactionResult) + } + }) + + t.Run("should get error ErrInvalidReceiptProofSize", func(t *testing.T) { + rm := &chain.RelayMessage{ + BlockUpdates: []*chain.BlockUpdate{ + { + Height: 1, + Proof: genFakeBytes(txSizeLimit), + }, + }, + ReceiptProofs: []*chain.ReceiptProof{ + { + Proof: genFakeBytes(txSizeLimit + 1), + }, + }, + } + + segments, err := sender.Segment(rm, 0) + require.NotNil(t, err) + require.Nil(t, segments) + assert.Equal(t, ErrInvalidReceiptProofSize, err) + }) + + t.Run("should get error ErrInvalidEventProofProofSize", func(t *testing.T) { + rm := &chain.RelayMessage{ + BlockUpdates: []*chain.BlockUpdate{ + { + Height: 1, + }, + }, + ReceiptProofs: []*chain.ReceiptProof{ + { + Proof: genFakeBytes(txSizeLimit), + EventProofs: []*chain.EventProof{ + { + Proof: genFakeBytes(txSizeLimit + 1), + }, + }, + }, + }, + } + + segments, err := sender.Segment(rm, 0) + require.NotNil(t, err) + require.Nil(t, segments) + assert.Equal(t, ErrInvalidEventProofProofSize, err) + }) + + t.Run("should be segmented by over EventProofs size", func(t *testing.T) { + for i := 0; i < 10; i++ { + numRps := i + numEvents := i + sizeLimit := txSizeLimit / 3 //to avoid this error BlockProof + ReceiptProof + EventProof + numSegments := 0 + size := 0 + + bu := &chain.BlockUpdate{ + Proof: genFakeBytes(sizeLimit), + Height: 1, + } + rm := &chain.RelayMessage{ + From: "string", + Seq: 1, + BlockUpdates: []*chain.BlockUpdate{bu}, + } + + size += sizeLimit + + for i := 0; i < numRps; i++ { + rp := &chain.ReceiptProof{ + Index: i, + Proof: genFakeBytes(sizeLimit), + } + size += sizeLimit + + for j := 0; j < numEvents; j++ { + rp.Events = append(rp.Events, &chain.Event{ + Sequence: int64(j), + }) + + ep := &chain.EventProof{ + Index: j, + Proof: genFakeBytes(sizeLimit), + } + size += sizeLimit + if sender.isOverSizeLimit(size) { + numSegments++ + size = sizeLimit * 3 + } + + rp.EventProofs = append(rp.EventProofs, ep) + } + rm.ReceiptProofs = append(rm.ReceiptProofs, rp) + } + numSegments += 1 + + segments, err := sender.Segment(rm, 0) + require.Nil(t, err) + assert.Len(t, segments, numSegments) + for i, segment := range segments { + assert.NotNil(t, segment.TransactionParam) + assert.Nil(t, segment.TransactionResult) + + p, ok := segment.TransactionParam.(*RelayMessageParam) + require.True(t, ok) + require.NotNil(t, p) + + d, err := base64.URLEncoding.DecodeString(p.Msg) + require.Nil(t, err) + rm2 := &RelayMessage{} + _, err = codec.RLP.UnmarshalFromBytes(d, rm2) + require.Nil(t, err) + + if i > 0 { + assert.EqualValues(t, rm2.BlockProof, bu.Proof) + assert.NotEmpty(t, rm2.ReceiptProofs) + } else { + assert.EqualValues(t, rm2.BlockUpdates[0], bu.Proof) + } + } + } + }) + + t.Run("should not segment if there is no new blockupdates and receipt_proofs", func(t *testing.T) { + segments, err := sender.Segment(&chain.RelayMessage{ + BlockUpdates: []*chain.BlockUpdate{ + { + Height: 1, + Proof: genFakeBytes(txSizeLimit), + }, + }, + ReceiptProofs: nil, + BlockProof: nil, + }, 2) + + require.Nil(t, err) + assert.Len(t, segments, 0) + }) +} + +func TestSenderParseTransactionError(t *testing.T) { + var encodedErrors = map[string]int{ + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b424d435265766572743a2052616e646f6d537472696e67486572650000000000": 10, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027424d43526576657274556e617574686f72697a65643a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000000000": 11, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000024424d43526576657274496e76616c6964534e3a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000000000000000": 12, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002b424d43526576657274416c7265616479457869737473424d563a2052616e646f6d537472696e6748657265000000000000000000000000000000000000000000": 13, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027424d435265766572744e6f74457869737473424d563a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000000000": 14, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002b424d43526576657274416c72656164794578697374734253483a2052616e646f6d537472696e6748657265000000000000000000000000000000000000000000": 15, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027424d435265766572744e6f744578697374734253483a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000000000": 16, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c424d43526576657274416c72656164794578697374734c696e6b3a2052616e646f6d537472696e67486572650000000000000000000000000000000000000000": 17, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000028424d435265766572744e6f744578697374734c696e6b3a2052616e646f6d537472696e6748657265000000000000000000000000000000000000000000000000": 18, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000026424d43526576657274556e726561636861626c653a2052616e646f6d537472696e67486572650000000000000000000000000000000000000000000000000000": 19, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002e424d435265766572744e6f744578697374735065726d697373696f6e3a2052616e646f6d537472696e6748657265000000000000000000000000000000000000": 20, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001b424d565265766572743a2052616e646f6d537472696e67486572650000000000": 25, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000025424d56526576657274496e76616c69644d50543a2052616e646f6d537472696e6748657265000000000000000000000000000000000000000000000000000000": 26, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027424d56526576657274496e76616c6964566f7465733a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000000000": 27, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002a424d56526576657274496e76616c696453657175656e63653a2052616e646f6d537472696e674865726500000000000000000000000000000000000000000000": 28, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002d424d56526576657274496e76616c6964426c6f636b5570646174653a2052616e646f6d537472696e674865726500000000000000000000000000000000000000": 29, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002c424d56526576657274496e76616c6964426c6f636b50726f6f663a2052616e646f6d537472696e67486572650000000000000000000000000000000000000000": 30, + "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002e424d56526576657274496e76616c6964426c6f636b5769746e6573733a2052616e646f6d537472696e6748657265000000000000000000000000000000000000": 31, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030424d56526576657274496e76616c696453657175656e63654869676865723a2052616e646f6d537472696e674865726500000000000000000000000000000000": 32, + "08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000033424d56526576657274496e76616c6964426c6f636b5570646174654869676865723a2052616e646f6d537472696e674865726500000000000000000000000000": 33, + } + + ethClient := &mocks.EthClient{} + sender := &Sender{c: &Client{ethClient: ethClient}} + + address := EvmHexToAddress("0x0000000000000000000000000000000000000000") + tx := NewEvmNewTransaction(0, address, big.NewInt(0), 0, big.NewInt(0), []byte("")) + callmsg := EvmCallMsg{From: address, To: tx.To(), Gas: tx.Gas(), GasPrice: tx.GasPrice(), Value: tx.Value(), Data: tx.Data()} + ctx := context.Background() + + for key, value := range encodedErrors { + mockErr := &mocks.DataError{} + mockErr.On("ErrorData").Return(key) + + ethClient.On("CallContract", ctx, callmsg, mock.AnythingOfType("*big.Int")).Return(nil, mockErr).Once() + err := sender.parseTransactionError(address, tx, nil) + ec, ok := errors.CoderOf(err) + require.True(t, ok) + assert.EqualValues(t, value, ec.ErrorCode()) + ethClient.AssertCalled(t, "CallContract", ctx, callmsg, mock.AnythingOfType("*big.Int")) + } + + ethClient.On("CallContract", ctx, callmsg, mock.AnythingOfType("*big.Int")).Return([]byte(""), nil).Once() + assert.EqualError(t, sender.parseTransactionError(address, tx, nil), "parseTransactionError: empty") +} + +func TestSenderUpdateSegment(t *testing.T) { + ethClient := &mocks.EthClient{} + ethClient.On("ChainID", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + ethClient.On("SuggestGasPrice", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + + sender := &Sender{log: log.New(), w: testWallet(), c: &Client{ethClient: ethClient}} + t.Run("should crash if TransactionParam not a *RelayMessageParam", func(t *testing.T) { + segment := &chain.Segment{} + require.Panics(t, func() { sender.UpdateSegment(&chain.BlockProof{}, segment) }) + }) + + t.Run("should return error msg is not a encoded base64", func(t *testing.T) { + p := &RelayMessageParam{ + Msg: "rawstring", + } + segment := &chain.Segment{TransactionParam: p} + err := sender.UpdateSegment(&chain.BlockProof{}, segment) + require.NotNil(t, err) + assert.Contains(t, err.Error(), "base64") + assert.Equal(t, p, segment.TransactionParam) + }) + + t.Run("should return error msg is not a encoded rlp", func(t *testing.T) { + err := sender.UpdateSegment(&chain.BlockProof{}, &chain.Segment{ + TransactionParam: &RelayMessageParam{ + Msg: base64.URLEncoding.EncodeToString([]byte("rawstring")), + }, + }) + require.NotNil(t, err) + assert.Error(t, err, "InvalidFormat(RLPBytes)") + }) + + t.Run("should return error msg is not RelayMessage", func(t *testing.T) { + b := codec.RLP.MustMarshalToBytes("randomgstring") + base64encoded := base64.URLEncoding.EncodeToString(b) + + err := sender.UpdateSegment(&chain.BlockProof{}, &chain.Segment{ + TransactionParam: &RelayMessageParam{ + Msg: base64encoded, + }, + }) + require.NotNil(t, err) + assert.Error(t, err, "InvalidFormat(RLPBytes)") + }) + + t.Run("should updated UpdateSegment ok", func(t *testing.T) { + rm := &RelayMessage{ + BlockProof: genFakeBytes(10), + BlockUpdates: [][]byte{genFakeBytes(11)}, + ReceiptProofs: [][]byte{genFakeBytes(12)}, + } + b := codec.RLP.MustMarshalToBytes(rm) + base64encoded := base64.URLEncoding.EncodeToString(b) + p := &RelayMessageParam{Msg: base64encoded} + segment := &chain.Segment{TransactionParam: p} + bp := &chain.BlockProof{Header: genFakeBytes(13)} + + err := sender.UpdateSegment(bp, segment) + require.Nil(t, err) + assert.NotNil(t, segment.TransactionParam) + p1 := segment.TransactionParam.(*RelayMessageParam) + require.NotNil(t, p1) + assert.NotEqualValues(t, p, p1) + + rm1 := &RelayMessage{} + bp1 := &chain.BlockProof{Header: genFakeBytes(13)} + + b1, _ := base64.URLEncoding.DecodeString(p1.Msg) + require.NotNil(t, b1) + _, err = codec.RLP.UnmarshalFromBytes(b1, rm1) + require.Nil(t, err) + _, err = codec.RLP.UnmarshalFromBytes(rm1.BlockProof, bp1) + require.Nil(t, err) + + require.Nil(t, err) + assert.Equal(t, rm.ReceiptProofs, rm1.ReceiptProofs) + assert.Equal(t, rm.BlockUpdates, rm1.BlockUpdates) + assert.Equal(t, bp, bp1) + }) +} + +func TestSenderRelay(t *testing.T) { + DefaultRetryContractCall = 0 // reduce test time + + ethClient := &mocks.EthClient{} + ethClient.On("ChainID", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + ethClient.On("SuggestGasPrice", mock.AnythingOfType("*context.emptyCtx")).Return(&big.Int{}, nil) + + bmcMock := &mocks.BMCContract{} + sender := &Sender{log: log.New(), c: &Client{ethClient: ethClient, bmc: bmcMock}, w: testWallet()} + + t.Run("should return error if TransactionParam not a *RelayMessageParam", func(t *testing.T) { + _, err := sender.Relay(&chain.Segment{}) + require.NotNil(t, err) + assert.Error(t, err, "casting failure") + }) + + t.Run("should return error if got error on bmc.HandleRelayMessage", func(t *testing.T) { + bmcMock.On("HandleRelayMessage", mock.AnythingOfType("*bind.TransactOpts"), mock.Anything, mock.Anything).Return(nil, errors.New("HandleRelayMessageError")).Once() + _, err := sender.Relay(&chain.Segment{ + TransactionParam: &RelayMessageParam{}, + }) + require.NotNil(t, err) + assert.Error(t, err, "HandleRelayMessageError") + }) + + t.Run("should return TransactionHashParam", func(t *testing.T) { + p := &RelayMessageParam{ + Prev: "string", + Msg: "string", + } + tx := &EvmTransaction{} + + bmcMock.On("HandleRelayMessage", mock.AnythingOfType("*bind.TransactOpts"), mock.Anything, mock.Anything).Return(tx, nil).Once() + r, err := sender.Relay(&chain.Segment{ + TransactionParam: p, + }) + require.Nil(t, err) + thp, ok := r.(*TransactionHashParam) + require.True(t, ok) + assert.Equal(t, tx, thp.Tx) + assert.Equal(t, sender.w.Address(), thp.From.Hex()) + assert.Equal(t, p, thp.Param) + }) +} + +func TestSenderGetResult(t *testing.T) { + ethClient := &mocks.EthClient{} + sender := &Sender{log: log.New(), c: &Client{ethClient: ethClient}} + ctx := context.Background() + fromAddress := EvmHexToAddress("0x0000000000000000000000000000000000000000") + + t.Run("should return error if GetResultParam not a *TransactionHashParam", func(t *testing.T) { + _, err := sender.GetResult(nil) + require.NotNil(t, err) + assert.Contains(t, err.Error(), "fail to casting TransactionHashParam") + }) + + t.Run("should return error if Status = 0", func(t *testing.T) { + p := &TransactionHashParam{ + From: fromAddress, + Tx: NewEvmNewTransaction(0, fromAddress, big.NewInt(0), 0, big.NewInt(0), nil), + Param: &RelayMessageParam{}, + } + receipt := &EvmReceipt{ + BlockNumber: big.NewInt(1), + Status: 0, + } + + ethClient.On("TransactionReceipt", mock.AnythingOfType("*context.emptyCtx"), p.Tx.Hash()).Return(receipt, nil).Once() + ethClient.On("CallContract", ctx, mock.AnythingOfType("ethereum.CallMsg"), mock.AnythingOfType("*big.Int")).Return([]byte(""), nil) + _, err := sender.GetResult(p) + require.NotNil(t, err) + }) + + t.Run("should return string as TransactionResult if Status = 1", func(t *testing.T) { + p := &TransactionHashParam{ + From: fromAddress, + Tx: NewEvmNewTransaction(0, fromAddress, big.NewInt(0), 0, big.NewInt(0), nil), + Param: &RelayMessageParam{}, + } + receipt := &EvmReceipt{ + BlockNumber: big.NewInt(1), + Status: 1, + TxHash: EvmHexToHash("0x89807d988e91650d41c6cf1127c3106a0d281b0c0523e6af61df5859e1bcd935"), + } + + ethClient.On("TransactionReceipt", mock.AnythingOfType("*context.emptyCtx"), p.Tx.Hash()).Return(receipt, nil).Once() + tr, err := sender.GetResult(p) + require.Nil(t, err) + require.IsType(t, "string", tr) + assert.Equal(t, receipt.TxHash.Hex(), tr) + }) +} + +func TestSenderGetStatus(t *testing.T) { + DefaultRetryContractCall = 0 // reduce test time + bmcMock := &mocks.BMCContract{} + sender := &Sender{ + log: log.New(), + src: chain.BtpAddress("btp://0x2c295d.icon/cx8e270cb0610d67daeb0de5fbaebbbd812de28e4d"), + c: &Client{bmc: bmcMock}, w: testWallet()} + + t.Run("should return error if got error on bmc.GetStatus", func(t *testing.T) { + getStatusErr := errors.New("GetStatusError") + bmcMock.On("GetStatus", mock.AnythingOfType("*bind.CallOpts"), sender.src.String()).Return(binding.TypesLinkStats{}, getStatusErr).Once() + + _, err := sender.GetStatus() + require.NotNil(t, err) + assert.Contains(t, err.Error(), getStatusErr.Error()) + }) + + t.Run("should return chain.BMCLinkStatus", func(t *testing.T) { + input := binding.TypesLinkStats{ + BlockIntervalDst: big.NewInt(1), + BlockIntervalSrc: big.NewInt(2), + TxSeq: big.NewInt(3), + RxSeq: big.NewInt(4), + Verifier: binding.TypesVerifierStats{ + HeightMTA: big.NewInt(4), + OffsetMTA: big.NewInt(5), + LastHeight: big.NewInt(6), + }, + RotateHeight: big.NewInt(7), + RotateTerm: big.NewInt(8), + DelayLimit: big.NewInt(9), + MaxAggregation: big.NewInt(10), + RxHeight: big.NewInt(11), + RxHeightSrc: big.NewInt(12), + CurrentHeight: big.NewInt(13), + RelayIdx: big.NewInt(14), + Relays: []binding.TypesRelayStats{ + { + Addr: EvmHexToAddress("0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0"), + BlockCount: big.NewInt(15), + MsgCount: big.NewInt(16), + }, + }, + } + + bmcMock.On("GetStatus", mock.AnythingOfType("*bind.CallOpts"), sender.src.String()).Return(input, nil) + + output, err := sender.GetStatus() + require.Nil(t, err) + assert.EqualValues(t, input.BlockIntervalSrc.Int64(), output.BlockIntervalSrc) + assert.EqualValues(t, input.BlockIntervalDst.Int64(), output.BlockIntervalDst) + assert.EqualValues(t, input.TxSeq.Int64(), output.TxSeq) + assert.EqualValues(t, input.RxSeq.Int64(), output.RxSeq) + assert.EqualValues(t, input.Verifier.HeightMTA.Int64(), output.Verifier.Height) + assert.EqualValues(t, input.Verifier.OffsetMTA.Int64(), output.Verifier.Offset) + assert.EqualValues(t, input.Verifier.LastHeight.Int64(), output.Verifier.LastHeight) + assert.EqualValues(t, input.RotateHeight.Int64(), output.RotateHeight) + assert.EqualValues(t, input.RotateTerm.Int64(), output.RotateTerm) + assert.EqualValues(t, input.DelayLimit.Int64(), output.DelayLimit) + assert.EqualValues(t, input.MaxAggregation.Int64(), output.MaxAggregation) + assert.EqualValues(t, input.RxHeight.Int64(), output.RxHeight) + assert.EqualValues(t, input.RxHeightSrc.Int64(), output.RxHeightSrc) + assert.EqualValues(t, input.CurrentHeight.Int64(), output.CurrentHeight) + assert.EqualValues(t, input.RelayIdx.Int64(), output.BMRIndex) + assert.Len(t, output.BMRs, 1) + assert.Equal(t, input.Relays[0].Addr.Hex(), output.BMRs[0].Address) + assert.EqualValues(t, input.Relays[0].BlockCount.Int64(), output.BMRs[0].BlockCount) + assert.EqualValues(t, input.Relays[0].MsgCount.Int64(), output.BMRs[0].MessageCount) + }) +} + +func TestSenderMonitorLoop(t *testing.T) { + log := log.New() + + t.Run("monitor from current finallized header", func(t *testing.T) { + subClient := &substrate.MockSubstrateClient{} + sender := &Sender{ + log: log, + c: &Client{log: log, subClient: subClient, stopMonitorSignal: make(chan bool)}, + } + + startBlock := uint64(1) + endBlock := uint64(10) + expectedBlocks := []uint64{} + monitoredBlocks := []uint64{} + + for i := startBlock; i <= endBlock; i++ { + hash := hashSubstrateInt(i) + subClient.On("GetBlockHash", i).Return(hash, nil).Once() + header := &substrate.SubstrateHeader{Number: substrate.SubstrateBlockNumber(i)} + subClient.On("GetHeader", hash).Return(header, nil) + subClient.On("GetFinalizedHead").Return(hash, nil).Once() + expectedBlocks = append(expectedBlocks, i) + } + + cb := func(height int64) error { + monitoredBlocks = append(monitoredBlocks, uint64(height)) + if height == int64(endBlock) { + sender.StopMonitorLoop() + } + return nil + } + err := sender.MonitorLoop(int64(startBlock), cb, func() {}) + assert.Nil(t, err) + assert.Len(t, monitoredBlocks, int(endBlock)) + assert.EqualValues(t, expectedBlocks, monitoredBlocks) + }) + + t.Run("monitor from a heigher finallized header", func(t *testing.T) { + subClient := &substrate.MockSubstrateClient{} + sender := &Sender{ + log: log, + c: &Client{log: log, subClient: subClient, stopMonitorSignal: make(chan bool)}, + } + + startBlock := uint64(1) + endBlock := uint64(10) + monitorFromBlock := uint64(5) + expectedBlocks := []uint64{} + monitoredBlocks := []uint64{} + + for i := startBlock; i <= endBlock; i++ { + hash := hashSubstrateInt(i) + subClient.On("GetBlockHash", i).Return(hash, nil).Once() + header := &substrate.SubstrateHeader{Number: substrate.SubstrateBlockNumber(i)} + subClient.On("GetHeader", hash).Return(header, nil) + subClient.On("GetFinalizedHead").Return(hash, nil).Once() + if i >= monitorFromBlock { + expectedBlocks = append(expectedBlocks, i) + } + } + + cb := func(height int64) error { + monitoredBlocks = append(monitoredBlocks, uint64(height)) + if height == int64(endBlock) { + sender.StopMonitorLoop() + } + return nil + } + err := sender.MonitorLoop(int64(monitorFromBlock), cb, func() {}) + assert.Nil(t, err) + assert.Len(t, monitoredBlocks, int(endBlock-monitorFromBlock)+1) + assert.EqualValues(t, expectedBlocks, monitoredBlocks) + }) +} + +func TestBUContinoution(t *testing.T) { + s0 := "f901bdb90141f9013e028303f48c8705c73f8a874b499500b6b5791be0b5ef67063b3c10b840fb81514db2fda06624a505f5781074eff5f822eee1fb5702341e05bccb46b105fc51f09c7ff4d9a0f9f36e5b5486ef0dd3ccce85ee181a7ef4dc4864348584258570fac4cffc86e9a0ed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757cf800f80080b8b1f8afa01474a2eb5c9b0502f2d4f3a8ea3f9d1e7b8d534117a08623f73e6fbbb347ea9af800f800b888f886a0226cb7bc34e56a0743b282debdef5b8d70094514f8b942593dc0b3a9ed9687fea0ad1143249979c2887875944681a7f3747467a750ae6b50a2cd2d4d6b48fcfb60a0266dc753fb2e92b774873ba531d1dec92cb12ce32e227f06372dc319667133a8a00a8281f2f67931457e64e02b23bff0c9f5f8f7d13a72bd6163f456dd85c54a5bf800b875f87300e201a0cd28fe742528e29b35a3ac929167c5bac928d3945a959111faec41df8d211c49f84df84b8705c73f8aa5ecafb841e9744967e09326ee9d3f1b7fbb48667980e4bb282d3214230dc2bae61bf1d0c51cbb2d779a76cdc728bd2870e119669d4b88e971db654e1f3f68173717af6eb900f800" + s1 := "f901bdb90141f9013e028303f48d8705c73f8aa5ecaf9500b6b5791be0b5ef67063b3c10b840fb81514db2fda0b358f98a851fc6d5520687e6af2de6bd117b3f03ffa1caaded131f73e9cb94e6a08287fce416c4197c0e3052477489a1f2eb7f657e7a417b0f760e91d215c0b25fa0ed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757cf800f80080b8b1f8afa01474a2eb5c9b0502f2d4f3a8ea3f9d1e7b8d534117a08623f73e6fbbb347ea9af800f800b888f886a0226cb7bc34e56a0743b282debdef5b8d70094514f8b942593dc0b3a9ed9687fea0ad1143249979c2887875944681a7f3747467a750ae6b50a2cd2d4d6b48fcfb60a0266dc753fb2e92b774873ba531d1dec92cb12ce32e227f06372dc319667133a8a00a8281f2f67931457e64e02b23bff0c9f5f8f7d13a72bd6163f456dd85c54a5bf800b875f87300e201a0e16a716239df33cdaee5a7f7a8342ba673e6b4b3aa90caf0b35678124ffcef98f84df84b8705c73f8ac470d9b84126b95cee5c2533578949f0190618301fd718920a28b3dff8b582667790159bfb4a7905ca591e5895b40ca62b15b3c3a474d1e32f0117100c072bc6012fae1fa400f800" + s2 := "f901bdb90141f9013e028303f48e8705c73f8ac470d99500b6b5791be0b5ef67063b3c10b840fb81514db2fda05639bb8547b320f2534155d3472459e78910a7ff253eea6c8d662166d9563899a02054a1a176939920e9eff36d00be0f6f143bfeb09f9c13dcd1fba54a64935edfa0ed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757cf800f80080b8b1f8afa01474a2eb5c9b0502f2d4f3a8ea3f9d1e7b8d534117a08623f73e6fbbb347ea9af800f800b888f886a0226cb7bc34e56a0743b282debdef5b8d70094514f8b942593dc0b3a9ed9687fea0ad1143249979c2887875944681a7f3747467a750ae6b50a2cd2d4d6b48fcfb60a0266dc753fb2e92b774873ba531d1dec92cb12ce32e227f06372dc319667133a8a00a8281f2f67931457e64e02b23bff0c9f5f8f7d13a72bd6163f456dd85c54a5bf800b875f87300e201a087f05c8a6e56372d152fd07b6b779e28bde76283fbd4ed4d56ff54bbc6acfd1ef84df84b8705c73f8ae4d95fb8412f53f86cf4979559daa764024529ad94fad6e8620bcf2cfd5e9cf628770a550a6f91cca4e6c4dcb49dd92d7bf7254c8d96cbeafc676cd13980c589607f4d40f700f800" + s3 := "f901bdb90141f9013e028303f48f8705c73f8ae4d95f9500b6b5791be0b5ef67063b3c10b840fb81514db2fda096424b18d5a53354a75b4cb3b49c908b132409e9775b7ac70a746cfee5637944a0ed1246c04ba4a430e10dee3ac760053cfdf112ac8fdb899baeaf52c2a2465122a0ed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757cf800f80080b8b1f8afa01474a2eb5c9b0502f2d4f3a8ea3f9d1e7b8d534117a08623f73e6fbbb347ea9af800f800b888f886a0226cb7bc34e56a0743b282debdef5b8d70094514f8b942593dc0b3a9ed9687fea0ad1143249979c2887875944681a7f3747467a750ae6b50a2cd2d4d6b48fcfb60a0266dc753fb2e92b774873ba531d1dec92cb12ce32e227f06372dc319667133a8a00a8281f2f67931457e64e02b23bff0c9f5f8f7d13a72bd6163f456dd85c54a5bf800b875f87300e201a055f8a3320ea19ce31036a333049d1c90dd264aa04a9647fbdf1201e0cf555265f84df84b8705c73f8b016097b8414e5d5635d3d5d3ccd771ac45c6cf5193ccecd33a8eabe8c325aca6bf291a1d9818e6f502acd88d9c41d1999e1d646171cf70081aa006d6c8ebbd8099d3514ef301f800" + + b0, err := hex.DecodeString(s0) + require.Nil(t, err) + bu0 := &icon.BlockUpdate{} + _, err0 := codec.RLP.UnmarshalFromBytes(b0, bu0) + require.Nil(t, err0) + bh0 := &icon.BlockHeader{} + _, err0 = codec.RLP.UnmarshalFromBytes(bu0.BlockHeader, bh0) + require.Nil(t, err0) + + b1, err := hex.DecodeString(s1) + require.Nil(t, err) + bu1 := &icon.BlockUpdate{} + _, err1 := codec.RLP.UnmarshalFromBytes(b1, bu1) + require.Nil(t, err1) + bh1 := &icon.BlockHeader{} + _, err1 = codec.RLP.UnmarshalFromBytes(bu1.BlockHeader, bh1) + require.Nil(t, err1) + + b2, err := hex.DecodeString(s2) + require.Nil(t, err) + bu2 := &icon.BlockUpdate{} + _, err2 := codec.RLP.UnmarshalFromBytes(b2, bu2) + require.Nil(t, err2) + bh2 := &icon.BlockHeader{} + _, err2 = codec.RLP.UnmarshalFromBytes(bu2.BlockHeader, bh2) + require.Nil(t, err2) + + b3, err := hex.DecodeString(s3) + require.Nil(t, err) + bu3 := &icon.BlockUpdate{} + _, err3 := codec.RLP.UnmarshalFromBytes(b3, bu3) + require.Nil(t, err3) + bh3 := &icon.BlockHeader{} + _, err3 = codec.RLP.UnmarshalFromBytes(bu3.BlockHeader, bh3) + require.Nil(t, err3) + + assert.Equal(t, bh0.Height+1, bh1.Height) + assert.Equal(t, bh1.Height+1, bh2.Height) + assert.Equal(t, bh2.Height+1, bh3.Height) + + assert.Equal(t, hex.EncodeToString(bh1.PrevID), hex.EncodeToString(iconcrypto.SHA3Sum256(bu0.BlockHeader))) + assert.Equal(t, hex.EncodeToString(bh2.PrevID), hex.EncodeToString(iconcrypto.SHA3Sum256(bu1.BlockHeader))) + assert.Equal(t, hex.EncodeToString(bh3.PrevID), hex.EncodeToString(iconcrypto.SHA3Sum256(bu2.BlockHeader))) +} diff --git a/chain/pra/substrate/assets/kusama_encoded_finalityproofs b/chain/pra/substrate/assets/kusama_encoded_finalityproofs new file mode 100644 index 00000000..20ae6d58 --- /dev/null +++ b/chain/pra/substrate/assets/kusama_encoded_finalityproofs @@ -0,0 +1 @@ +0x7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856bee6040020140000000000007412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0081097412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f8a3b4e6712ddd01a33e9e4516e1824f7439d4e399b97b1585da6568550ad630905246a4e608536dab8c7915c192520a884f3426ea55c34a87c93211c08f6007000e441df11d2886c8134a93a68dbf26e82d5eb862e0b652dac36452d1de7ef57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f0a0dba9261c4fdec2638d88fe7b270747632632224ce645131a62ea9401409be7a4aaaeca6ca1e2f49b9c5bcee37d90a2aaf09280c4b9dc48783fac92fa6008010934937c0ecb5ca84b298a6be8a05aa2739db425fe03ce195744b9c751bebd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00919e55f926139f6c662bfafb2c00a58e289d26ab871df16466e0c8e6c5241c7d5194b95c5bfc8887ccf77a43414dc0d2037d49833516a372cd4dbe6dbe3c300a011295d0cec16375dd645e533e11778d79760b1328239d9b8845a48de8e3daf87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b6f2de2bc2d6f3e163d0d6d4c9aceb303e7eda7f98dd569f3cfdbb70f34cd305751757a0d831ede690d12e6f366dbc294510e19281bd3ac01becd783ae9db9050244364beceba2a73ddff1eca2f19e6c212b92e33796afa29ece15c309d0dc017412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bc9f4b4f0139bf95077a45d133adfab518dcdc3ffafec235869bc760ce5642249e787e8cb666d799633cf4990b568358c2f364c52504348dc705503fdf0ec20302c79e9966db0947a30acd331e753aa883e758ca3645dbdb19301c4eba95a6d47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cdae639ab3b86008e959226413fe1c70b84822d7049f61503c1eac6a5235eff3f0b78347b91e668c1b869df27aa63f3577549c6fe386e2d54a3279e3a425290f032251cc887b1db49b021e033ab4438bf26f69dc78202cd9e035fbdc62b9782a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ca2cecece16a9000a96595d93a093636c20feb5fb65fe70eb4eeb4bb05cde8b3b7b2a4bf60aeda0cba0c0f730a286a4899b59c3a5732dd8a8317487b0039b50e035eb6fae7043b47cdd23d2faa0c65a67c4e5b6c5590f546da78ed240ece1c707412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003e4ab01d3dc9762591fe6a96b163b445720da13f01683c6d1bde3c82aa77447cfb7fd240399283a6b228335dea534f3763536eb22ee169d8e8763aff54ee8c0b04cbadf3d7638796f99321fc02006649e6faf7143d1c74bdca8c63ccb688bb347412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00caa02fcb2fea7981dd0816b9cd6e8e8c180a6f555a75cc4d702f2b6eb668caa3bc2e2b7886298892356a66bc6f55de1f6af53fb76e8bc9b8233c82b9b4804f0405073dda3c462b94815e11fbbda3a021d792b7ef58e49be9c51413cacaae22927412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a62347b5d9ef98152074505750709ed855413e3ae518a4f01f040541e2568ec8c7e2bb36bda88d423ab9e7b826d4037341a402f67f153ee5be22861f4b47860305c401b4934e3f81c181da546eeac19c0bea5aa3e4f3c0322ab4c05513a789ba7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00af745a5ad9b81d2d642be8d5637e792e5adc8d0726a19bbe0796ebef444c3beacdc937964f3dc3bf1d68313b7251f2055f8c41d755f2138d8f3dbfb09fe6bd0f05d9fa10ff7ad73bc75f401cf2ba4c69271d0a6cfaf5ea98f2b81f1817c3e5d47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d2683bd9119ad06b6a8b6eb2493dba77ea7e3aee8813a701b61dea5595c082cca65e6e109ee8195db18dfa04207d55d16b279915d03ef27d96b6c1d8a194630d06918fbeea920656f3fb0f71f22fd896a8e9d6549d3e60bd30413a457c4f9a687412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001f3ee7a98af8fa6f3e3861b37eda0ceed19c9ff5f08cdb5deb610295102c5227973340694898e097db5478f564ff00c6a5140389ebedfc587ec09b5d8373470e07059c40884d31b801dd5733495a3c9a0102728ccfc4db5fa8eb9bcfbdf24c897412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fb430f10e61795591bc0e30902f7620cf0056b34ab9cb1d36e0c3e5aa850c789025f6b8f047cc8c17fc51f00cc88ff1ca6d5af3c2b6e657f739508e1346d3f0107197f87de414de14666153a601dbea8070f80964fc99ed58f0fcfda471e85607412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0083c46be6195466b143ade4a8c4d6e11b404e4d85e34567fc63e57968928b570cd555234f4b62647958979797a18725f523f33a95ee8a0cdd2ac8762a354cf70f07256e10a14dea5216ac1594ca108a5da57c224f12aaff57ac139c95984adeb97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fedc542dc845055a09cae2456bd4de86a57d02ce3fd90968b874051f1f40761d4108e5e624e93a22ba0a7217e4e03a9a8391c2fd3a95e2649fc85d93300d800b07a6e225b63d830bcbe5d0fe9f2478cced8db0f1c01d40d0864ff4163d61db1e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a063164ae7b982530dee2170bb83d06d3fe592430d2bda74e3d88cc0ef9af3be733e03b36a8db1e7efb811fed082c736dc818996482cac8e2e0570eac33db40807f00c1582571966d460bfb41c2b5f2e5144e01f0856c15e6bd3b43beab7dbc07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0090e108e461d1ed272ffe9baebb35933883dcaad390179c8b70a5de2151f986cd5c944605c7166b72cd4585d052e65854760690343e023233fefefd4e1666cf070842cf210a31a977d49e0cca95f903a410009663c40f3a57abfd32e3636191d27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002f0c1b564413adc228240539aec27215b78c19e7c685e78dd64999d32f82651550143bb940afab6c18838e5489a0a02f885beb2388ce22205d1aaa53acf72f0808575524a0cfb5d74dc0b9a8e16ae0d8a51a076732cdf87c248cb7b9f55633fc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00730d4af0c25bc4cfb502a9edae757c3f731991ea19f1bf7c54c752161447ff30c78bb1086cb1849cf1b7bed0c49d4ac59ec8c27d06560a6db046499cf9033c060880eca6a462d2d8daf7bb35af246df0da742d5d532562575ac203d965a678937412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fc27fdf89994b3f603ede68a1153bc7203114cbb7a9bccfa579fa34dfe357fc0cc3add37b54a818287ae9ea8ac81e3688679d0c01fc63c9f8955cbc35ff9240c08836ddb7c53a60022f25256719c70c16a42924c14db61787ac87e5d390370377412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003e31b4736ab4f4d8d04a75e199a4a54ff8f6d50fb3127c0a5f4a48b2475201f7fc7ad871f00307a7023f1010a4ce80366af3e46aaba2b94090ec69d32964380508e3d6d26c79f4711f4881590a4fe13cef4ee3dbdffe9358324e8d3139923a167412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00812dbb65b0c418562d97224a4fee3fd63992ca149d0591e513530887b666883eec7ee75968316595029c24752994a1c7f1463f630b1b89bcdc6b08c560f23f010934f5f12034f9df3e95a2b4aaa009b81b9597a7b12d11503d38a21745d838df7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009fde1a96ef268304401559a8bce26e487c23197844f487cff41598ac5eaa6a4620b79e94b165f8c2da6992472ef8016514c0e4c46db1ae63a925f899dcebfc0e0a293f665105de4dc0f73a4c05e23e85e99495cd626604c001848fb6ad28c23c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008ffbdc7b995450ecfa59c2770913ee3dd63ec1392775b234d29d8093fb40746cade26963ed8d96159547cb772ac1d7d5e6c33d404d8c114c0ebca5cd555e8e000a8002450dca58abfab495af10fb5c63539c281321413956a50447565da2601f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e1d437a711aa9bd4358aa741adb603accdfd9b932926e304b41ef23cd0202a1873297fd901d21556c8e18d58e8e19a78424c4c7728197b6248104d2ca3d285040c585052f87854d6ae4308d3c24807d3ba34ff7c9bd348244a7606ff913fb2c47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006aa7982776add277697547948061f29a906b8affe30b315bce4c06ae84506920b21783099995aa52b2269cd6ff33b7209c009786efa5bdb8d286035c3cd9eb0c0da51f531108ba182be4fce0f270913688935fa4dbd9ef99a01d12dd4ec4351d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00979da9c61ac726eaa8019376a3cc88f8026847d259c8feb4f5d34d45c1d892646da4ced60368be53d8f854573714947844f0948bb5e4e8fc2de5f10454b1ec060dcd8f189b05b1f230e78bb22b4f76236a731ea2e64857bfa802f1bb511e58ca7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00328f5c0e918d9e9a703c8de6ed966c19509c338f107ba319a9d033026045682cd249c7356e26f04c9b17727ea52c66683a1897707ea8919511bbf14a851fa10e0edae107f4fb04b110afb1ecf53d4243a66f15c4c69876d6b751f5548f926b8f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001aeb58fcc48b72d7123cf2908d9eff13fef9443cdcd6e0ce7c4f1e2fec136618d805648646d1c106b6ec1ae4dd53a003e466a1e4ea52393e3a32db9c05fe35040f4ae91d0c81c7bb1027f8b741c3162c33a14e67d258a02aea3615d41335285e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001ca11ee4fdf474c9cebb6cd202e423af643f66d989e51d46efc27800f27f0ac4e53098a83809637a69511eab6521a8879ce03fb52c05add64311d151f4dcbb030fae5e80cfed04be08a8566ef8ca22a40bd429f23f44954a3df9c1c1603c339e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c9a3962333a36d0325c09617d86747c7f751ce178fc42071fad3592ae583c2f526d009539f1a403c9de6059d82316deb91d5fc23faa4f0206ad72a2f3eddde0e0ff5797efead90c9a065afefc72709e0a5ea2ea98cffb9059f995638b45ff77f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005e980e1460ad8a886cce66d87b224779c08bdd6d1687e44a60a6fc9c16509b5187c13cd2d91228d98212d3d86ba1d05c89e9a19de1c36d92531d23c8c5af060610180e4023674a455295547a417d515d167c101fcd03110f5607e076dd6f90eb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006435725038cd86984989f6547105becb20a72a192d5ba55501a2f2f803ea434a785ebcecd8c5c3b4c4f59aaaf43c07cd1ae7886fde417ead9095ea37d4500d06103e63d2855685e2c7082a5f2ad28ea8c72923a9e73ea7cb8e014213258a760d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003ffb3db5879dc3d7df723e9db264f34b647538b14a8cf4ec785b7282fc4750d048fd6559bc7eb2fd9a0ccdc7525e1e5e0d562dd832a7b23beba90baffde2c00110f6d511ddd2ce18be483709e72a906dda0bca20ba5609a564eb3cd8555215cc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0067a76d644049b10deedeadbc2ee6f0a666034e27a5a1fdb2dd3222a4cf2255085dddaa85fc3c73f389742a30ba06a1937dada9703772eacf8ca309a8a315e00d11186b5717e21360a51e6ba054de53ca7078693fb9e1c30cbbb166023d7b1e857412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0083cba7e17e043b0384f8b475731f248983626a7cee0730cfbacf695acebeaab311548d582dfdbab4f76718ff6c6e2fa6f160e8aadac7c283230e9b16a7b8d108113743e1317d516f7938153594e3ec05a58a30db8214e6665c2272432fb102597412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c82dafa7848caa298feecdde2a78106eccacfc58c50651b125ef7173a383e665e3a0e6171c315ceeebf43a8d25f74d6e69449c19b4e7ffd015099fa53abcdb0b11786c3c28485db63801ba00096fd41385239a7d98abd3c3a559149775bf364e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ac4ca0cea46ee402cc0e4ed95f07e1a56cf2f57553126d20404f0e8411f4581e336fd6909404eefe0eb51c4fc8de6f6d736256af3d0ef8751040672e624e650f11e96804dd8b6b436e12a1c9b7a4e9395a373cfd80280f99433ed42eb583e9837412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003116a8c3a2095e85f974bed14c614ddf5fae1300f2dbfae9fe0d02f8e25df8a74baa4aabda3e24f5211e5f581d87409b25f11b1282542621f1852479bfd629081276bbbe6034c52980b03b3afe1bb1b50abb6fd0b1f1b8636c4d8ac82e42c9377412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006c8f8f1458aa70dccb7f9a3bd52f7e190eb083a8519839fbe5e277e03c71fa9824c4b0340ecfb4459ec2bcb70282f39a7023dabf43ab77e9c6bda08e3b37130b1332b62bfb86742317bb10f17fa6b4225fcb31cb4f3429dc709c8120ac841ae87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00efaf13f22dd842189864fe57fcfca8477b8ba2af2e5a4d47af23f868e8991b41f86564123f2e05d5417f2e31cebf022229cc3ecb01df46a999e50bc7a35a030213936b39107386249f83bc407a047c78623d4b83b72fccdcd7b753d7a091d7707412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00965a9c03be09560f9cc52457ae56d5ded1edd91c3a4dd33ff1af584b96ec16193003f81ad6bf77a883eec24511d25fd0ed9181f58ba693c7cb33664e8a929e0c14205545ee3a0a8577ac313e8b1430cd79daf877ad9aafc59f43f6718bbb648e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009869fc3d02a37c1702159f5f6e2b79c8e3b902f60b56a8241db54babeaf6ebdc4aa4a5d35529f8d27108d02c77c637c5ae10fa5f40c7ca1d8788fac5ddec5c041431e7c61d0cfead281702e149818c670308d85099bfd6526a764806cdc82b527412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a36f3c46562162d633c8c340db072a5fe1e4920109ce2de26e1fdbb4278a78a6fedfcdd9506a1da19a8817d633326ffa17859c1b187b83a598db712f3bc1330814cf21c5d206353d0a3ec887c2a90dffd0f267e9ac78ef45fa99b153116ab0b57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001e232cfe09241840a3f1ac2380b612a92c1f6e7fa4d37ef31c272155688112d2810bf99c3529ae21f9424a4291241d72109c31caf4aab8721a6f0a4e4348c6071575952139d99bfc450335ca368d690de04d951e75473fa94a9d9e7a6e39808b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00463f8d0716ad1683eb9bc3c3fa7f201e4c17b5feb1c432a14e26faa8115d2c4886997ebafe44617cbcdc3928694dc98d24b52d00fad2b25fe86ec87e84602d0d15db890d6ab6f3b3d7359364c6a93c19d122dbe593007619d72da3fac72542fe7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006ebcc117c7917b42a5b85d1cef039ebcd2c6fc6d108963d041824a4a19fb2ff42d8b29bdd63f669031aec1fa0dfb7c84e4ba14c3d97b01ecb853fd191388860f15fe3df6ecabb57bb90cb9b10b79569f1cdb2f7dbd6bc3e95cec8d9c62dd5efb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006dc0542a1b61394d773ab2139d01213600d11b3923937210a3f5696457344a8f9f96cb236a0cd9c5af3658db03d8c3c7cedd962ef0a4095b2e92b1bfcd59a70a1629a134ad8e3c2b2d107d170ff8783a58af27f4387432a294ac7f16ef5c8b817412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e2bf4c751323759042fceddaef24c3ddafd82cfc83fe76cc59c13105f6f85d615b472d5b9b1db3816203319c0075758688cc4a96a1bafdb5b6a0b628317c510016ab5563ff24c62a172247d0022fb6ec0fcfcc013996368c1ead68b29effc8687412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003297df652b706897b9384cf4ae7d064e24bc71e2b62f17bed375a7c4cad5d2542092af19631e5d5ff9c4da7cad77b8972bd7e7118c171e5c1220fb33c2a3870f16de42b0da1b44f8921af23c1adf65169e827e6ba63cb81930b77ac3c090f4947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0028e51e24244336a8d7b0e1b4c26da3f271d170784018738406fa35fa277e98e466465c697529745188bc62509fadb6ea725e8987f4d662c49a6fd9add8d2000916e0aa37d33f049c260f21fc0dd3c5adcde5a2adb039e2d4fe04dc40ea38f4847412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008351f39010ae4b3b474447396610ce855cfe3468d1ed8601d04d2c2868c8b27592f09dd94b658d9b68948de0e297f76338070be92e0b531eac5b88aaff6708001721b85adc12f3f21cac0d4e26ce580af00bc0482126538ee92905aedae6ce5f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a0ac4bd8039fdfeeb7489cd470116b937f0b50adf9de0042cab66f9141d5bec9c0611c422a0c713d232e7431923299fc9e6e3a72bcd98b24d39326157b331c081737a839e095aafab0ff8412e0bde45070b16c38bab99a8f14581625c87184e77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001cb3f2aadc26f9b75f1481c629e2cc1773f0cb0ca52a04a26990f770ed64d0a7b0367f5e59f78395e0f6cdd32e04d093b6c4c1849bf6bf8fc67642fda76c130e186da0f65b865b067ef9c0617558bc5f4cb76f1bf9115093d4fc688c799b26b27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009b7ba53710cc2c3015307ac5991dba94eaf66fe352fd00ea838c3538bfa1da01dced5d528aceed475c1c33b42d12ede9d2897b622d5a65a8419ef3bedefa8f001964933057fa15f03b807dd35816b81d06be5407eeba37e2dbeea0d2758cb7bf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0058e2e7239cc307a9dd333deed2c465c96151e2bac40a5b23beebcf1818cb76bd60f35575b84f74d794b5cea031dc8dda9e3f30a0e3640eea3b5d5a675ec5ae0719f3033cbdac4b7b650dcd0d22e9c6c8349e2561c48c904bafab1a9d630eda4a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00df5ccc3aaa9b0ec72c4b9da1c959cfe2d7267d520f61b14e28f394f2dbfeb52a77396753bdfae0e217c03eba12606e5a1c851ed3f228e3f96264a16a5951dd0019f674198627ee586c93f538f4013ea3624d9ee78176a65ddb9b81bb2240769f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e8554736a86ed3afd84b76ba6e1f368658d523b28bef5e202541b78c9fc824e0112858aae6ae0d26ec0d2ca56c4060f55d9465a1d611d16befea5a7cd31fc1011a1453da679611351b7abda6a29d2305fa68392f48b2a9ce27117202e63fb0b27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ee40544faa9a651cfda05b440771d65f330f794c86df9c108c2b338297a0abb350402f8219406d6b71c2d411e96871deb2b83f1389609ce4d51c71313e51f60b1a621619f82c1498d100c25a49f9e5a41ae1d549abafc8ad919082a5e39227737412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0017160b64d2ba20b29ca0b9a1a74ec91d2869198c7979b8c5e304d0ec1090dc6290774d1d817c946c9360600d5be27ef91772b9d7b8de04f87636f7e1228782081b00bc4259bd38dba94ce98f0c7b8564645a929232ac09b0988e21549c070ad27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c373a87ee129a7869e6d4b6d4ec36c4aecbcd552e4c8f7ad30a0d513959ec77140d3cd38d545975488db26cfe97b18f3c6cf854273af4b3a0c5af3e0211feb0c1b5274a1614c68cdec40868ae34336e6c8e2738b1a4974bcc2830645f24af79f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0065f3a18d8af532f59193fec1334728841e8b4434da5866d1f2fb4c45da92358a9ca92f330d8a983caa3f6646fb3a8264810c73567382e1d3d39700340153f60f1bca3939a0da1e8fea6f673c96bf55a27ac431fef7b2e14376cd5c3e54a185677412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e492ff8c153242b172bf0a0156e5dd05b1777c0dfae441b2091a77ab2c8481e8c27c45a263767ada7151a1512eddd88030b923af6266d4d763065ecaface19001c84ffe679c52b9be6fa56992429da5b57852ce168bdce7b69d8ef2be027f77b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006c34e655964de9df8fec3031973ff81646de6f6c4856526caa2f4e11e5edff17eaae2cab67675c5b2e898391cad25f537549eed0de463e6cecbab0c3417b53011d0866f96bad61ad548632a2a38c62fbaf449fcf601edd37f66c8eae052f82637412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002a3241e1430dc3ff43ac77451e99336ac4e3baec8c8fa62eadfbaf2b08b2042fd4fbd3856ec8947eb633343368038f5297a786d5bdc71fac30b63e1fe14a8d001e59efb1ea0992ef40c7903d74e338db59a7e5cfbb46ced1971ffab848fc8c6e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f970f387088957f31812f30427ca67c2e5e197105ae7ad05af3e88f253ac4a60fd2cbd2838e4000d75c5dbee90a8cd12dc17a74d23e145e265ca9771316fb0f1ec3fe0ea67c80f6fa1c94f6401a67517beea40fe497c98fc594d8ae68a0023a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0080db68258970174fcd847f78e5be4d658e660633bab9c67827a706d6ca60e67357c18348a0daebc79f553ade04fd16c1196a7c5282c362096deb45bc2e3d47081ef94e359e2b8bc0df722b4d3b83321ef968f22d848233611470e514dca6e23f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000fe40587a170cdc05bb43f7cb34302814203e255d53fc5dff75237d20de5684fde4ac138d2bf8ab9aa33f4c70f435fcd7aa656a28f668f9c20090ce44308e40c1f0bf1d32ffa3d521aa29e2c847864415ab738cbbd92faedba6044610502a0647412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b479e9bbb6343e1bff3f63f5de3910798c04fcad584fe25662461eedb8d865cea824ef0a8e1888470bfce896feddc4762acc8972f728f6198385009639f53a011f0d090aae06e1216ff251f990289742fa042537db56176029296236873b2e2d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005013c0316a5e6baee73672bb9ba38a29023569f7659c377e8e9e1026eee543ab34c8aeec0aa367b4a81b5c4f4773d269de50d29600d4a2ec8653bc3884c8dd0d1f834777e70a38b7d68db63f5b3b876b6d059cd2871a2e060534f4c49686a3d07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0049d620562306f52eebcb09289b81844982c7b4ddfde379a70dbae61070609096c2569eb064f1ec3f4822cb8bd24941c08e50c01664f6a2f3bcd6896be19f89071fab7985c3e86ce8b0e7edc19b0118e97529929ffcd514258d29c3e445408c967412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ffcdead63343420658822f2c24011c1ed5d2342219ff6d39386cfd988179d43dd1a0202e0fc19f2d67febfbf39264b49fb6b05b118f6c209b58486e34c5ab40b202342f70f6f3667268a6e997526ac8a12688e4f7d487e044f7a651dd6af5fb37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009d02cd63ebf49efedb35ea7b80032428b9f42a0e84e80e8d54f5c3e5acec97063d5f4a304017803dea9f4f6b5e033e2c3f38e938250083b65c6884b23a480602204ecb11e2cbe2fdc760b77c2708163d99a7fa6b25c1032332fc2f792d50d9a87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000e87b815bfa3205da3ae74c577515c7297d2e19fe074008b7f3fbb2e40007391128f3d1d691dbd2ab35d3deda39a289e4f8b8d4b38424c00e09b2892875c910820fb5eaba86692e24c64e3c2f7feeec80a76ccef9a122e20c59ed0c459b4a7a37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f9b3b7e49ae1ebff47de49b415db368270f287deb013b2a57ff74964592d7df7ebce5df5fa84b53a6263a4a82008f7e819cd8d790870f792bc990ebc8a11540e218f73f89206b94b66229d663de9327f7c3a326d74cc609e1146aa18eb64c19e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00598239993d7eb2536064c27bec35a49c3a46f0119dc2920077a9aa9c98e130c16896977cb7b5abda512d19911b8994cdc5f382e84eb7519077af89e7950b8a0c21a2c7244635d9a632b872b7f88bd407efed5932de44dc6bd8a88b6178ba7f4b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00df05edd9ccd2abaa552a8038e07477ea54e78dca4eba1bff944246467723e1d20ae4bb37f8536f34e6eb8f7bc2e4c5c9b875fda8ae017bd417fa694c75fb4d0621a6331abad55acaee26f5f1843fa6b34356c3952cf8441e3ae430e5594fe03c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f51034575960c92b106c1360906236a57349430557e2796f601acab58dc3c48ec4167a159b9d306e7513f449c3cfe58f4d4f0e2b2a51bc9166b42073084efd0421b1e00b9f893c9d6c0897811284b2b312f5bb85f566df2aacd736f62e66259e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0016e7fe9e8ee27ce0f23ca492f4673ee3c5f23190ad293a629a34e8a56ecad7f2abb3f635a1f1c0ecad01f22aa5a3415843f366475c3e68df5708aa8ce54ebc0121c67cf8f405be15a1204690dc99b75867efd0e4965a76d0c5055e2cf93636f77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0059f19aa2032c84dc0f32f6102fe2687df23f75703b6f6d7f92749112e9e391b4ac75da5213a72c5e8420c57a282cf6eff42447f3124ca4f982d85abc6b2dc90921c8c75426837b740b7fe6f5e7e74af33d58852adbc595d73557757927e6e2c47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0029b98427c07fede7df4241a8ec43e116898e71e99a0930fafec9ecb0a42aa7dd1e88ea3f0242cfe15597c6aa793e34bd1ec359f13d790930ff5d5fbe9b75af0122041166308736f6a8327f85be60fbfa2625aeb3cf3908d2133ad51759b8d53f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b3087ee7824fd785258ae943b474e1c8969b06e60de56d5b01fa87c103420bea2ae99e3ad0b2fdbd58ee79f5dc249a934146c1b0989a7ae2a51cabefc9937202225280ca91339a751f61a5b8546aa0ee10e75201445f267b0e2c2566796d72a77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ba4ca82af26d02f7fbb13f7c8921ecf4d413a40560a50dc1939f13b66c9b6e9c24b3288b12418ff70bd27f7fc471143952e3b2cc1b7af6e4b9abb464daf0800a22debd07671469757bb62c902564bcc850f10cda1da8e78eb7caf79c9882fab57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003634488711f39fd1394443165e9d4ab906760724e01ff68b5ad65fbc9d8616c9e16d492a7f662c3df50f6792fe5a0d0e70a0019da8ee9ca1057cbd8aed3ccd092309e8fa28e5378178652ff984995ddbcb301b73e416106ec69eceb3b448d7367412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b5af961b0a17b1381264096c26a64d34934b6302b992754b250d8a44d0f51a161f492fd8b39115567b6d2e8431a761a9765779cf6577fc29cfa76c0870ec030e23270c415db05fd6d5c184e152b3d75a8d53fa9a43b025b58925caf748c9a23e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000541049f31708b373b625f5717e28b850e64b69ca0b18959db123f5473484d78f2cc8a828178b03526c201adfa579c532e0115455ee69d409caa4491b4a7670623d2fd575dda24f66dfb33da885b714848d8bdedbee3702e7af08e4ed97dc0ba7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00513c6cc5fa6dbd8abf521d63e6f43e72ce77887453ed225011cacfcd049c3082fe9ec73090726a18122a8382ede0d0dee28cff7b193d072bf7580d01723b0e0824453bbc105fea95468b3cfa025ed820cf33192515d7c062b543da1d452d49167412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c47818f8bb4e73e20ef084258cfc566c17164144d42aa26c4e1e3c6ff5e1365f8636bdfaeb59100498756fe7b7c4955e0878e438ffe697502823123b0ed9230624d36064edf815632b7d07e10886ad1dcabe78e923d1f14f7c73b21a0f3a95b17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0036015a94e4a345ca5ec9ea59bc9c8f52c72ecd25e0f7096b91f8e9eee4c1b7a3fa075645ac5d58d492ad3d84f77f6a9fc73fca3030f0aa60e68fd926063e8c03253622dd0e934fde3d1f8ab9308f027a6155152d0410210fd257a1e521e8f8447412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b7ead6a4f88398525c39949525a2737a203c9bb90a42b547dba0a6878d7d678fd1bb8405cd340444f933d2a1a814da7ace644eb291a1111d851c4690d352800426be8d259a81badb0b0e53bec4cacb34d1d6853bc99394c02629766200946f847412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00adf657744553f6a829d2d25706362c044fec4c675d4d9355f03ba1796e63bc3af9d6d387266c71b7c5c67bb20f2c1fa6f3eca47423bb3b2b01f489e5ba35df0526c264a070c0799212702ea08d9c30efff41d199302b7da89e976aed338929287412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009e88c1cebd9511afd5caf1b7496ff3fa40548f8c3a3184fbf5526132aa3a755755e38c3cd9b5e9897aab13dfca51914977284d71db66bbc54c394e7936c2960626f54492e909b90a4d83679717793dd90da6b690ea67f0bb50cbac8b9845de717412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004ea9ca7b257121bb3b5ef120e941407a7b638d86f9366d36cfc3e319aff06a6103199b30e564f886f61e63fb61ba6c0b6e73cecdfa621e1d18fcf658fcec490d283122aa6a6aeeb235b7a2385f75f015b9af4ece967695e419f63accd67037377412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f80068156a721cf129021fff2d94374107754e39c1d506f81634c8d995ba152f20f01e50b0c8c812aad1e161942ff90f19af0d294106d6100e916f9358f0370428a6ad0258633548f3111512f2bec89d9af385a28002a0dacaa95e40d681d4de7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e17ff7352687a9e1c3c331c117c9642a4f8d00375fba16f92666b74c2462b0ea3ddeafa89a8fc53ef384379b40790d1646af4fcf293f6ece25e19eeef42c5b0229139bc1e3cea954ca9ea3c4c19aae309ccfe8f6536ad6e6a7c81031906929cc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005fc23d552143a3e056874e95b3ce4908544825c4b4653795367dd09de3a13723bce04f417ada21f43412fabcd5b022a8a636ddb4b48e1c570afaa7b653f130032970c966cfa1d786631f4b50c4ca8bce2d61c3719f0cbe663f2c12d2aee498007412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cda6d8a155faf239f61cc33732e36134c5bd8cc91002386e237ac9051dcfd2ce65c706c3229fd1b41c9ab0cca2a7f30c3196c067b4adf53bbf63d72c3821d0022a53a045f66449948ceda879ddb1a3c7eb74d438963905407bc1068d5adad6307412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00245411b400600b24a3f71bbb63a311b49e1f4cadf2ec670979be70d1cee0be98dca71c9419be2198d381f12b246743fcb20c37841b2719077613d586f83ffc042ab31b2ae41579b19899dc1a5cf88cbc064a13b9d4e5b8609504bb91007366387412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00393efa37ca1f8517fd308a9d3aef55e027dfbd1d6f579939a295ec2673c25e049d151af6bafc9b546739a68383b0144b6f9a8d28f90c336b9d4ece806378660e2b1dc16ba4794f65e3b9a80acc4c621cf3771f137ae66a10777c24e64dd939157412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a043336d576e050c3ba364a4fcfdeb30b79a4aaa7e559101fc712ac2242708598313f2fe1d09040e6c7ec9c35e4bfb1ba442300e6799f60d0636ad8e2e809a012c3e4f6da2743839a2d5bb56bd69325aa63b7bf075729180615f16cd13c573557412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0087d34661c1c232ddcc467771d5a60182674b779000960313b56c978ed8488343e17a6345b9c5c734d23c6eec55521a37158fb461e5680750ed86390859d674022c3eac29c084cc7f9b3385186b09fcaf3199412d01a254e4e4cc092f41e6d8947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cbe8250e7c85abda600c1e083ffa2ae6e60072f2b7097607632833fb0097d585e3c2891a4b3d294f19d1e395b4d2be459253b31c12723de83761017c7f934b022cd4047d533ee477213ceeed6a1e0d92ffdb576b46fb19f88641116afe993ef77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003be173ff931120f19f72667110c1e4336484cb3dd5af75914224b09f3d4f5af02738a9b31d3a81ddc37208301cb025d9c818ca9b414f40fd533f5d291dc6e9012d7046462826854856153545ea81e9b22b133c4c3ad3c17828cf755a2b8769cb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004444c90358ff72e817ca2a9d98ab85f3b1c7121b1e7617c4d248fcde439e9eb1e7135b4fb8b11a2d1d2a36746fd2c98c7cf8c8b0d113bc6f4db1c0c689b8a70e2db2358096567b7bdf89f7b55e92dd1137648685efaa69f9d207bb9ec829bec07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f682a524629af33ff8e0aa9465918fe90f0b87b58afa863311437f0b91287d65ce1eaf47feff65552dc21971ebafc3515617ef885843098d9c32ed0803de8b002dfb3abb18baec3f897802f3f74ce2251197c70e36b4d1f0d4eab81a5416725e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003d01c02d02846ae8968a321bc778bba3177927dd36827e5a81c028176a034cf6325447a7a000d5790b0c38d394db32984de2f1f28bdbe9694bb18880432a39062f3c7a9a8f310a2d0425ba6a32270bd26f1cdffd989dbb730cd27ad3960d9a817412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00085e6d8fe3660dbd340115ebe285dd5171c6a822b1296509496affb6d815c64ca788582adef8f06d9cd605a67039f020678d4202e985e72bcdd8afde9fa5b9092f759c1e5068bd51cae5928b7b35ec904c8e3648ece7531503ad08e69af8977e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0097b0239c3c11dd0ba3749c15a8a381496801646c15ce373afce47e0c2b1849b0ac35ac3950763448a666bc3c74fa7bb2033d67e55bf72e7e2c9651c51d6bba052fb55309a83acc38a41da27befd22353f8088e123b5fc1507cfe3f646dcc188c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00985519a9201b877375d7d98f25a15b8ba4dc73bb29181145d7d4e478af7ad877784ed58a21fa503ed2b10e3ebd30297975cee6a4dd1967918b3c4951a6c8d60c2fddf0ac69664b427a82afc4314acde474cb17f9f03ea675a2382db413892df77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002d3f3865204d6c85de87024806b0b238d383530a925a7e639250fe0126585145d94262358ebc48d4241f5fd4ce8751fbf255159b6f3b519f7a02a3877e865601304ae76636739d9981396037b5cf68e06d203245dfd2e2621fcbcc6064bc37787412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ff0958c8dd5a52dabcbc6778e10fbccc2e8abd7dedae109dad764c1c9b3df52d21a83a8e181fcc5fb866e1a7d4b93b83225c846dd0b18023ae860eada338d703305b275d503f1ec6fe226c84620f7f95e8669e4db5e9ef051cb2415b19a1d2917412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003944fdf6c4a5e26bab95ccff2cc740f16d3f1d996c456ad85098b7190a9659ee9ca06f61a3c9d3f66d34662ca089525a713d51cce33f7c0c071fa21c8a09200f30bdcfce24e42e80af515891400731958366af9fd180a4d578d1c15b9de174977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0086254b432983cf2df151364d6b43defca2f5a89faf48101972e29387e969fb68db893278b7a299321f1f9fdb5373cb2b9a5a537bd4849acd1b8d5d469fcb0f02326a7dbdda2921799f6636335772a1fb9a4897c22b16721b0700e1bb9bce72137412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00360f16a2f39931579f1a4cbf9b429bab33b481932520280fadbae9a69af639bdf97b12d5196ddc8e5478257d4e6e1010129de704312e9a2c8a67f10c232ca80b32940116b194fa6d290105599fd178fa7a28c293fb27640abd23c53b53b268017412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b14004bc23135328786ff59fcbb3dee547700f85cff63a415c9607182cf5ab07ae488f834cf7d950f973b891c26501ab22df96f88e9eab98ab86ca060749c20833a4f301590b0a75ea41a24f65ae64eedf9e9934677d5151d6282974623270c87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002107dbf63e46f9fb282df91ee0f107f967c761ac8d12adb4a2f18dfbfaa76242d665c1dbf0ca4880715c0fe71a5a4e104e8844f2a2293061dc4518782747eb0633bc56e557f9d68a0e80999c3bcbb0afca2eee221c611b88bd474daad0a159957412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00614379db9e5c1e9dd8b990f216211ce73218bcad2d2a60e1198179447b99f84eddda6491ef7cb668b5bcf18ab98df131da63305a2b2482974b3828181aec010333f6e05d1d79b8534474c6bec3b3934c8235674b37131afbd7157d6c1b49787e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008b47e3536750516de90a5947d95b9096a2cab43986da71ed186b75f538eb2bf200951ac7a51c0eeceed8948c4c4311557d4cc9d7793f8cbdd100520bbeffb4003412008d64e339eb4a3a763d7ba24af096616dde67dabbddc741f0b07833a87c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00022c414a730f76a4dbffdf7fc2cf319f25bb205db34fdf5f1b6ee6c2c67dd7f95814e38723c9662293758b3b8d27897602204a9195b25e51fe5a5c43c782130834396d0ef461de6a067837b71e0aae27bd3d5fa63a1ce2b9169fdcf4b71c3fb47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002979f06d33dd65cb3aca70e2fa42846f0242325effc607372d96dd72b303c05e847faf98caa399e7483cbf5fc9503b783c1968baace090ff67c396269232c90b3496dff90280cd98397e43ad0f42bb94a31bd613f0df1b33f708a99081d701557412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e82df5295a50d80c5b25ada65727ccb66e15fdbf3202bf9c9a1e2b4d0be4abddcd1da00735dc79d059eacebe1dff598812c12d6b5c455792ff6a8aa09a92d702353c39bc2d6ffc87b4587ab34ca983b28c23076d5f9e22a33809be375d86739f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0037a878cea88276fefe42b4a08d1a99b6e8f28ce794ce2cdce124587b9ac721bcbfab8184f417e1f8fde96116cfaa6c7f6aa89725eb4fdf4e40e6a6064bafd109359b54946f5dc8f4c6fcc54cd9b68bbd4b67e00d5f2687d7e1b0136a60caf7287412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f0eea57d7e03852e20457f7a8094f5c43a6249c00daa4303033c807b216cc2a309e0e054b6932812b761591a74d101065e3134936a03a3d9c62f24a161eb7a0335c2f0a6843fb1add0a7dcafc4d0c37da14a4a9f869de803ae7419fb09cad0e87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ee351528744a3dcd6977d6a2f5e01545c6af547fb9c4d6fa22fcddc2b750943900e6e8e1edfb7d64e0a6350c034c5b62495cbe47f832a1fb35ecf27ca6aba90d361c5b336ba1098582674e32dbe2f0abe02a0c09ab40ae5e28d66f828b718ae77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002da4accddc33ec03cec5d3c4ff88f80d0c06fdb762418f5bc23064a9622c2825ba4a7e1a3f52227fb5907d829fef33c5c6bd526b7aa51fa17993d05b0f54ab0f375d6fcf1c75bc35eeb4bcf41356e21d797ca7097af0f5293454817e6daca7b57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004b336f13b069d9f2932cb0f9b5a626703d83739d12853659096e7778f231595db6a012e535541433c80c04b5ff8bf86301fad23c48a1277de7a0195cfa66c90f379cb36d9bc0ed5523df498018874190e839ee50092b1578e22d5afc891ad24c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0021974ac629db0bfa521d14063b4a00fb0881d128a27fda37d4554ee3fe6304e49f2354ea8792bbb0708c86fa59a9794b5c98985768ff0b44696c2b5e47a22b0437d604831863e4ff3f264e39b38a2a3ee32fe81b50cf02ae61ca1022df58f2b27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b7b619263219081e5fd2fa9240decfc95599202db70dd6f60b6ab14dd5040ca9da61887e508277a551f7fce658e83c7c3f048f39dc34ab3d0642660c0981d8033803107577b82e8f20cd20c228eb814193524ee92c9f260954f7f46fd54c874d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008138ff7a22bc04065c10f00726e4990cba8eebde8dffef38cc2ff2712e5f8171663fd9c708dc376c58df8650fdc06122dc86a1c51989b14f27ac684ce8405507380f0c705a821343bcb98cb060bd7faf9b1d21b84eb7d9d29b48b9bde01f99737412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0058ee72d00dc874e4bfba77950657c079efa459fffbef16f57cfb60ecf335fdff2cee0fabd572016f546203a7bbf1a3d5cbdcd5fc99ba66a16033f93aefa41d0838aa6a77dc828d66462074eec66882b37efb0691c879d1cd1364f7626f1ff1f07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00076e521cbfbf35e66001fba4d6342bef0c89a5831d0dc630ef4ecef2751fab7bb9c6551cd41f237fb3f052acf9dda1cd1630df25a1d503db6e00bc1f59b6f20738f4100d5495dfc9f0f1a5efc786446db941934631128fcc040fafb679e8138a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004806806244d79cc5f7695dc5992b273606135811495927f28d85186c13c3ca76d60e7d1604b6ffdd47de951fd595497ed50b0449974e79c0bb1c8034a306ce0439d2d8cf3d9f79fbc7d7fd3440e987c2914c462777105a1ca512e3d74ad54ff47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008c637340e16151e44f73127d24d0392278f8479088aeb756a7c348c55e45abec3fb1f99b3e2343dd2e5a173c0b9e3b26dad2b80937c83f156e7bc80293a6f0023a0c5425d01569b13d8fe13fda74859ac4fb6d72f607fd461f25c4ebf7e98cd47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002527739548a81231f568671cb8cfbe83e5b83c08f74adab262c9f89b5063e6b7fdef42f256a159cb9ff1f7bf3262ca0f772f931ce901129287a76c1f0981c40b3a1e7190ab0c48a10028ac7c9cdb0025126fe68dc6a06088ed6a03121d3057357412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008fa520905398feca2ff9a8726b7c71bae4128fccd7ff1dd239b5a9c910dd3ef9981058343b2847c85921d3e6a8259c5f5527383e0813ff5db8f2e7d3010752053af509aa20017d20f865999ff56aa2a4c38f643014cb0f8b1038359d210f11067412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005fc8fa727b6d87a83bc6681e9cb915ac711531404acf10b29d4ee975489c26f1c3f564675bc03e7657b238e37dc27264f9f17b7bf9224d45d76b58c26d5997093b62a9d2df9885f9af0c15057e3d03969dca0690b91abe26ec70f400ee5f21eb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00886cb3d53181a2221b11efa4e89da4df9d625f732f9a5d1751c18e47ab0b16a8012d25c99b5d5216e82f849dad83e5d629f9bf4b2050aeb82e4dec3c5fe14b003bfa274a31d381881971d92b6aec6b2b6d7475c8caecf66d9d0d3daab8223ff77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d155719a121e62ea6997eb3a333d06fe7f7d1f5f3f1556ee71a457282a4e9a8bdb341cca12ab6b91e921ccbeec9917a2b3da4822cab9646aa6506f64363405043c0c399e9977eadabcee0f5038b1746d0aeca846c8ec3a2c4489c38a5885eecf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fbf050268474f064b37870b2b49298ad90f6c84d267b754ba6d4cbc609e8d6c3f27e14b8577fdc0cb9454a79d63db42867946f216222e907739558464087de0c3c52628a9f4d7aad5eab967a259e609dd9d32b149c614e9d954cd9a17ca72fb27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006c53b40496219d6f71986f176f0f9cb9e08efd61dfab307eb4969a5c54e8cd791074b813d1ec80aac8560cbb6acfc6184555c1422197cc7d0ec9233bc3b8ac003cbb3b5e54e92a209b7107a0dbb4eaf65fc7b529e56ad68d3e21c4014caf3c877412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fafc14d0b29414fdb913d3962bd3eb3cda1607011c841be51da9f33c56f57026d7eaa2d695edc893e1831c626de9c304e7837aeb2528e33004e9ba8ef50c7a063daf19d13486111c2f7ff397c97db75d7a0780f5ea3a29ff9e30bdb58ba91ef57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0078ad38035faab7ba25d72920173012712110c4493f450cdf53161e392ecd8f12edfcc5ab55b30f9ee1440fce77fad18ed40830c04eea22ba5252b2546ee128043db091de7d340fa29193d647a5086084434764ba257ab1a1d52de901acb444d67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0059f226ccc90a18485d970bf5b655e4263e4324647faaf1cb129b5e307f2f4fc3fd97f148bf6e7421b909f71d39589cb463c71f30b0801725e76b7313f3994c023e286fd02bf1cfb10bdb52397245dc6446b48f9dae0a0919bd1e4e793d49bda07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d01088fac39bf6d97420a88c158a658708c60fb471f7ea28ac98504b91c0fe3c28372ae35f47557d3dfc6da54f0307eabd2acb207dfaf4e7775323a24fb596093e3e7abf910f06e75aa5e8c3e3f04935ea0648fc64a241a85044e461c24afefb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001311cf233cf31745d9c54894083f0d9eaccdc0639b4adcaa2a602fc734d23a5aff5bee9f81b2d3cb2b9cdcc613bf106ae7145f20901ac24bfe29515d9f8623083e5e47776b2f943726df1a547403d4fa114d5c04ab3557e758bfcd4774020a8c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00417ac01baaa1de7676f7e1bf841d37902a25184e6c80cb7ad76c2863d807a1197a02a1741745dcb29b1de6291c84f675e01101af1f814ecbfaee944346350b003e713e430285f05f5f5b5aa810113969cc88dfad7e6a7198c4bca55c322e95547412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a4f5c3db37141cecc49eb3333fb5ee74a9953969ab661294300a3de770e781803a89f792af6f9c76ab3c4dac20c1576d1c1b7c46bba2a474a6919faacc205d073e89ba8a647dfef3ef8e54bb106a40db4b56d49818f673c5f71391b2ce822f697412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bf8dd042b9bb69f2e88afa907d1c86e5667945915913f3239e147dc354bcf153c171ec995cd84df67056b57227601769ec68114a9425b45e418263c419c04c053eb46abb27a83dd13ec79f9f06736f0992f39b3fd2cd14f12a635c09e507c15b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00737e3392c6fbc6a7cdb723a10936fe03730c0483d36e16faecbc835db995d0f224c69d699ec95fc0dbe4ccbc3b96187a0e0dbdcd7711aa78139db2316d2f92013f4064978bf74c7b93d3890f97f4062c39837a83e0475465f2381b0ec34df3c27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003db8f5f9e93efd6efa044df9d639e3c5b4685f8810207c16c3f82690bc47897631a7159c35c051d4a36156940511904b28afc193a2c1af5597a0c571bcf46b033f4667ae05eec7353bf6623aae8faf05047db1aed2807d1d336c04c21d79b9c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f154f462c2a8d761c4040c64590e15f52237432d1bb2ecd62a1d6f4738ee31994405eb0026800e98e7fef238161c296fe9acce1300829116f3a2814a402d3f0b3f70e4f56e2aba2477ead6d3eccb6a5c9be147381d12ee53e31f2c0e13dc6aa97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fc4a153a1877b18231d6e621a39cd6958a0967f90cec5a624e935dcaebb2cbea4a743200fb9fad97e3e2604c395758cafee49b4a8abc5a7e9b1875c565ddaa0e40365e14834690d417b9f0cbefaedb88bb9fe7ab1d0332ba3a3851e5c30d9a187412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0086feb98e4e38cd6ba012ed8a66a0e3de553e67b9d7db99dac49ee73a872dcb6094b0c8a27714c47d578a92dc9bf4a17a6628b9eb8c21db732b0904bb2ff4b50940960707fe94477ad1f764aff50e467b9fafab66cf713566268df50a5c5ac6c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007d7d7eaedff7a15e5b86866247d0083846592797001b6f9cdb6264decc1e9a5fcc43dfa5142c8dff0b68313f4d011e659ae217edb9624d37f786bb4880b8a20140fd1ef66fb78b57869f701781b06209662152e15196b39e96f64c14f1ea64047412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00954e5c81a6b7e48926bc61781799dc9050ef3011d02a7fab6db5ea5d5fbe07af5e165d5134134ce1bd5d0d1af7f4a6b75fe09bb1f294b78e8a2a2d170aa19b0141b5120a0018e3d9b6b51dcc0a06e438fb9f97750ad49430cc1c6f865ec7097c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005f54d3764412c172a4803725fb35fb97c23df7a7556a00831a75cb64bfbdef8b335fd77c9f3367a1d994475414b8eb26f5b0a13ca11678d7c1096c1721746f0d41fc0d03aec399311e556614afee67f1e87ca997b072e61cfc60b22962be08d37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00660e1b588b01296f0ffff2df1c84d9d84bf1173c568af715fdacb718608615b5024c3bf1b5be1eb85b58142937b70014184181803b26ccca1b647907ad027b0742561211c18eba2f5c8a4beedbca78fdc72151e5f3e80ee249bf92af960076fd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0096e888aba86e147a9ca2a238c4b175f4ded8f72701ecca609f2c5dcac84d751db04e3e6261d233651b1c00151f5cf6330b707a25694491a2c1e44152be423e0a429b0b9e4a5696e259c0a8857f1f8be9583f005b7bfea1373429c53a919dce047412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f15f6dfe415bde851d73bb0186e41659f62a540da2b6e3c649613abc7dba1e9b3fab6ecab9b9ac9293e1162bbd3ce34da701a3a222476bc6ae03fd53e5e470b43184792bfdb9d45804ccb5fb0e4552cdc3b13afcccf0efa222a4fa7f28f35757412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004ceb2dfba2ff44192b60a565f8d9ec8d6ace4554a539263d8b249434f78f52b52c9abd0be6eb0eb6fbaf02f92b3df2fe3013b936f8ab6c8ceaef9b21545f050f43446bf453ba593fd178be36b2e995e69528bab0bdc44ab589c820f22c2f05397412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000834980caf48de5b9818956a7b070c752653e785e7f54fa859d4f0aea27474aa5c6f039dc062ded8f2b9606e5d0e289b287f0b4efa07721f298af308f3c8c00f43891eafd4434c9addebbc20ea4191b8828fb70562c01541bd33066f474a19f97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a0553c0d0a41fb054614a5b0dbb24019fb96ff165e063ab18e98c7498d73c60245c88ff8216c6a9b1c689f503e3ca0f5485c6f343f813158beed3347aa95b80243f686a3895023c6fa1ceee1ec0a9a982e644611516c4ad6d617b99a859a9ba87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005cec73b79ee785e95877db84bfb0346c67fe9b96d593d06b412a9b3bef40c9ff95413f63ea7ddb2ac89ded9b2250199998be0960dd363bb1db8b444698bcbd0b4400c7b8264677b90a0bedd06b785db94aab6f49c044200146b2e6fa7897c0017412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007f55ef2a1c40858e1046dc5bb0fd1f60dd44a039a4c158627f7495eb09e0a4ce8789201491d627f2374a76e4d68ba4607d6398839fbb650d318f19616e1d620f440f7fd2202aff96fec10e0dccebd183995e4619136fe2b07d3b6796b67e36237412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003bd9975b4b1b26402a479563ffb38743abb462118839c48578819b6b1653e414708d5ece0dbe844eecef651d6ecf8899002faf7a46a487f16cd3fa8b21db790244608cad21550591119de80028ce45dcf43d4e644b3a5f4522bbc9c8f1d689db7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00679f1efb82fca1550d2707242b8f44af043e1aec4da7fb84432ca5d8479c6ba23355df9b307806f19391a21b0bc2358687a9cad21c590cd09b172cfc2944f00f448f1982ef4fe90e4e5605050d4f5489a9cf11f214afa13a907b62526c8285587412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003087c3c38052186fd1b66dfaced98e763336160a7b274ec7e5f7808dd656edae494cfee33ad6556dd0d7a39750baa7ed8f559602a509b4b515735ccdd7f10e044564e0bfbeff8c865ef5d48db991f6503303be12ecb56382128451a1d5bb5dfa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006348a3a14f3f0cac67c4d47fb92e3ed1c5e31201274923faa2056d1cdacb03186376f1d01799f4baf7b106bfd1f4234bcc61bd19ee5317272d58c80120fd720046074de41299025a6d2d07d73c39089142843ede2fb749f37b7387b9d00b0cf17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e3a99bb87c2565ae253474fc5d99409749f533fdd529929bc8e3ae86dcd74d5fdbc9eb353306b1fe37485bfb703a1c2a932fd90e918f03c1ff20dd7c0ce8750046865091b6e11557f254c05b26de7f15eb851e387ea66b38f0ab148fb30aaa507412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0032126f428a20217434c09eed46c5fbfdda2cac0569486485456bdb8d2cd0371a49c240ae2419462d8b4cf543dabe0de350578edf0e8d09a76c05a2ac7be1c00447276a7e970f22028e7d0107b7e4d20165b2c7ffc536f38d55ab170e4bc4d6e77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00117294c9c0eb4909d1c374d6c0746fb534e61fb2f5f507d7673a4f8726956bab7e3d567935b7367e675a8c75ab56d8125ff32adcae89eafc89f5a6960567be0f47b05c0a92b2977fc8af13303be9356102d5e9202ee6bffbc6883f1ddd92e5dc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004a62ab45db2444a3a7d4cc1829061ed737ab1ccdc445ab7d8b6110f40b42dabeb793a4b23b1ea66a4ac918fc660debb6db8785ccd5ca29cb43ef07b4d87ed90f4856dab87d66f24db52d94e06d8d543d635f4aa6ad939ff5233939493b3387357412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f18e3048503f73bc08b62d6bd133f7d1ebebe3ec4bd1b4808880420b6583f35889e1482382dce8142de1b91cadf1cbfe7230e9b5e30a7bed50eebb4eb89b109490bcf479a778226dfc7b823230ddc52873a69d077d52974250b778d382efbf17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00aa136f06548754a88f1e9137b62e1540c6ae7c86e80a05ecea3a2cc458b3ddefe41afc6d9f61988b64780fea6307a3a02bb3988c0d24e1c40806ac13728e550c49255d41d1f619c20b94c516d89791cb41475eebf5684abda7c8d4b07d7745b67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fc43e83e6a6897ce2fa95f1a67a4b0e224475627fcf7143f47b4d46491be31059d4b713fd655470a1456bf5703081ffbabebeacac8a9363f6c2322863a9e470949bc3aa9209291467d377d4b0ffd9de0fce6ef58ee5bdeaf03ce85d01e97b0637412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0093980a4faba11d7af2472284358da9ecfd33354f029dde1600b4cdeb961eba89a517bac10a8889513db50148e6cfebfc177cc66f8d95b667dc2c2e79035e6f0a4a2a5bd3982f1e6c55659c399353769c6cb1663bccb1e3c109e1338fc5d0e6d67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d87e17328d157b0b903d1897a98200a5b9a3153854f3142e9b2ad0184758b27f1a4f81a31890479701659014371c031c78f2f7912297bd50a67c7df9e9980e004a565811312aab87dd8ffbfd01902f9596aff03dcee6659ffcc00296383e01f07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b143b57af4f7d91693f91caf93825e603fed97913effb6ffc5e92af6b1ffa9fdf31e6de6ac9031d52e75152a0813528a6daf7a6212285333d2a58275894e4a074a8b3f021bd9f94473beb64359c0434f40b7a47f8e146e87067d01bdba51efd57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0072987f5c2ee7f0f04d178c42358a402badbfe4c14f1d7de787606e6e75206ef4504069b98f8014b8e9ff0fbb95b095e35bf10241517b286cb0099ba1b3a8330d4a901eb6da8345dff7e59a4d0b84cb36218835ca2997de099870519026b532867412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00212b000e68dfea8d63208ac00fa8d598876becf801ed029f909cc0eab3ca232ca0136061003774e3d79b5d812844692af43d1191e30735a6399684cd9566a80e4a90775307346c824a4b8d125cc825fc94214c1e4b226d84f28e794c3cb4caaa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0090dce85fd2d29ec59b28b2664fd474a2c56d78496888ce9907d257f2a2df9ae5810fe69976997c5417df17458be7e53d5d964c6984a6b8bf9730073f8fb46a044a9a44bdfb838d1f9715e5430b6b9d36a184355cf43ce417bbffa67597652a997412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00135f0f26298180cb9cb44ee1ce16b1af1bcb3738309a7493842efbe241ef3521ecc6bde9d8965310bb731c43e5f532d0eb2fc17a341f0bd6ab6f9bd045ebe8094af586cf1105ad073193a842f1d1b23143d050c54158df67bc272c0b83d2d0f17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b9d75e2e39dab43a3d22480568d21c4b8c523ba8916817d9fab2e731f615b05ef3b8694e13950c979873d90e07cf2bf676a52d44edc2766636ab3f94a94de80d4b24b54151cb10627a6533a9740790e3000104be718da94c1a836653276300767412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0098b5453040138d29971c1eb96c8edb3fd6dd70915e1e5bee7258c1fbcd257f4a173077f2e031497ec2ab10db1d0a5537ad7208046be6bb17ed64c2fe026b2c084b8bc8228499fa756a2d072e275c8546264f17f88a3df07ea786bcaa62c103457412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f322a86e6f96ca680c32dbf5ccdce7403d021dcb2170d06153b2758666ee8ee30e90b4deb7a5a4a8bc7347fbba7cee77c1211868f20725fcdfddaad6c7612c064bf80cb235d2be39045e6d83d95d073f9f22c806e39e81a4890923be707eefb07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0068b159e65b83f350aa14b107e65029d3c28fb10661bc78f7ff809d5240951d80538bf891cb83692c33a256637751f123522d32ee274a8fc0f81ef4c9f2321f0d4c22782ee1bd4165a7a48f578ceabdf94af60bda2e1a5f5354edd480b7896fbf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c776268e48ccf465bf63b55caf531c0aaec9bdf27b14e1a28ea376af462e16333f5b824a70632d74ac6bf315f7bec42b5d5a3f8003baae7821604f06d57fb3014c912ce07ea5eb582045b0ab2528e6a1b624f6035db2ee8e8b013edd59d9d8a37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ca534baf9f31eb81c1cd22109cd847dec8af08b43e68623fe81840759d7b682cba8af65fdcd64b34876bd6d4e8a43a7777ee295a88341f79399c51330372c7004d98ce8f37fd944570179d42c8e1e7cfc38a40ce02875347d6c914281ed9fd3c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0056bd4d5208065541fe188bee915d2b03a81741386e0c6a035e816397c518d424edd39f5462f6cb8295d03b0529670412cc9eb79cd76935100988033d4bb28b0c4dc35a94f5f4454ff25518b4f6de1c86581a997634c240b5672b2b49104e55c47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e2d4edbf6ef92e7636123a0b317d4608d1b71130b4ed870c00e4df54259bd77ed9676bdfab75fa9573ca91f9a18ddf3298120886f7903c5c3875a5ee2bebf4004e2be3b94e292f4a0d2428fddc8829fe71fb0e31a83ab1560bf12bb249a3e5d07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ac3aad08519a9d400ec58a71b784c57dc66179c070cc3b35f2fd94352ef7c50f86b4bec7d0af5c69a354d7aa1c8c0eeca028b53f4a788f6e1a2b4d2b2c184b0c4e6fd64100eca46bba08a38a453089ea7622a4f7f67d55c5df1280e2faea35307412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006e9b8077c7093b5cf599975a5cd57f1f27144e1fb794c57c48a655e750593a26ce7507d6a15af18be72cdd7a53adf238aa58738bdc4db1af724c7bb0616ce80c4e8341dbbd838cd2ffb287707b5257f7753e9ffc77bb157c235703fef6c885e47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0009be33af4f12011ed4dc67d12cdf6c0d5bc99e15a306d879c700443f3b66fd793b3d5018efcabf38c4081059e7fe50544ddb0276f46de084c602ea66182510054eee1b610420fcb8180d66f548a01f4bb3899fd9a41cf3c075fd17fc9c9c64ab7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fcd03dc3c284be4fc8f6ac09ea44f079a4506abb06fc0d5d156698a4eb84e234434c09aacc6252d50f23491a8b2b589dbb7dcacd5b62a141cade25af96700d084f3f4e2f62f2246baa2cf1e8928ea617b2c4e2e2172b80bd633a1d336d3590a07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e31ce0eeb03927fe2ded81e52e15610b147b08addda54198cab1556f9f787dc7c8f93a8b4f897652f1916b64f038a47f6422b206f191f25ab4f7c0f6167059034f55c96297d0826f2c06a387cca6d20e5107d53674d5d0e70035f79cdbc3e4127412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007636b8284266b68ff0684bb5d28a453bb1aa998d4b4c13d5a67f7c4450bed9d964fea42b38c76e994f0ba7243f6614e0a42262ea14d5adca51e5f4be6b2f7f054ff2be6fc896ad5c5360330efdf46d26f881fb1cc3c589ea407db7c06b27fe9c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fc8b2df0fd2530f800ae7b23f4906ff9c9b3339e085f4c4225bc28887ffd55ae50811679564eddcc02e61900edfe0f283a8eecbe9e33115112652f30507e5e0d5087dd41a18af353a6f2c48d07a7491a1bf81c03a8e5a4bc9d81f0135c4699e17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0089dae716b520d55abfce9d3475761f04b25c7c70d94d6e1b4a259ede093d8a613825837888bfa09490de157e99ff3af96ae9f84f60c74cdf681b897e02afc003511eae6e782c850632e0a6b138e5bb4235e83e9cef23a76929815790bf7a06447412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0041d5948f3f56144f56e641f9b14945a42fd34147ee50f5f6c94433c8214159dd36189529e189e844db51ae35aa0123c7b85b3ea1d283f067d34e64ca591473015187dbf2c8f595ac73922fb5253513e0475e80c25b161ddfcd56895f4f7a48457412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009c1a6dfc129047b76a23914d972cdc08b73169dc10599b70c5f6dc0ff6ac82d8bc56470147df943ed2bb35963fcc558b03fbd39b2c4667f424cbb89795621706530acbf1b93c99d7a6970be2179809938dc8df68b1fac4109fa850b8ce9e26b37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ce8cff8e18f3c1ff255dcea75ac12157419e53b8afdc4a3e29920086c5b6fdf3eb96ee4149d81d7bbb74665f27d49e72905fe958daab9f7438a51bc00aee6e04534826e9e480f6bc4f6625f450bc052be80fbac272a195241ae5c70c58de610f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0096101953e6daa794c99a2dde215dd627070b14f9563035bb6055c72527a20d2b25a36030a54eef51ad561c953ba783925a3cc303d1a3042b3d896b82435fce0653e767bb54032aca54a343182ae3269578bce30ac9205a8d1d18dee301fd9d777412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fe82a3ce4d20b5cd3c43417e1e73a7e6c506c3cf9d9dfcfdd06571da4f696011967c299c29be647d5fe356f745d74c83b5d172936635db35ad3758ed0b4daf0154c3eac557155cd833ea69ba4a6ba752cd5168b351aae397d234b340a5ad03957412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0056358fc399c2c68b74d77f6ee84cec4146e87e0ce848172ac54669720b174ba03db59e4524b45b1d40fd2df3260f5cfe29dfa3a3d2771b6676f819d5052e8d0a55439c22ef4133af0dc7efbc594a05851925f608f6dda5b91ba3f2ac30e086ff7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00adcd425239a6dc92fb7f0b029e949e958bd3895e241eb29d1506fc13f0b4c5083702fabd44649514d50a715206406bce7ebfe46715233e369fdaa15c7a37950c55643edc287c8be779b2f36eee9921c5b205d4fb48d7e9de026802d742d09df37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ee77cfda4b4562b467f0c2960be1a5f9ee80279c9bf36d2306943cdf665256db2dbfbd2ce5348c77a305fc877ec4cb9477cb8b1388068dad10df172266677a0255863d94ef9c234b891606ae1c06840314ec1b6612d4f5b334447f53d2587d197412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00539868b8f5c21fd4619f76fa3fb1607587e2111728ef407ffea73c9fcc2982eff7f22dce34ac61374a344148704746554de254f17b21a5467ad280265afd1c0b568280011edba62374ce5a2fdad01c1bff1b09f9c1961e42d7848505f1c0b4347412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00adae31102e1392af6c22be74b7268387eed455c32c721f5a03bbbd621b8fda67ee50e44a98efd70b217b9b8901f09a5de96f4de38bf214153790515b8e502e0056d2787fa13875b10d1144dc0bb5bf596bc01cf68ef539ab28053581a6a9617d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003a4c931f8d106415e9676e425c425f18ebbf8588d3f8651f611cb41a76503d72b4658283213794b7fa0290f67cc69a9987fd5929c0571788888bd4d57e707e0c574d311ee9b8f6412e94f33b280f8d1c9db75a6dae79bd98176155aa101cb99f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c7d5e94db32de92b844d90482db37db635dd4f789cb0802016dd37b0e6ea0e13b80204bdfe1a315ef4760ca49bd089123428135c1a0fb6557318a862d0d7cc06576cfd896ca75daadd83777a43741eca3ed5da2ff5b9ef11418cd5956f48b1df7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005e304b2bb4d398675d7fd5a300cc2680f8ac969cf7afcd169d7c5481c65562d0867d9b01922c0c430479c3cd9dd2d9a3d9fcd88e3c8adc46e61022f064c1850957988cd8d0ef29afce154a6e0fb28137e971ed9cab06784b99c50e97e443be0b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cfcf858bdfd1472e2c763255b437c54d7582d5546d547daef0106c2803338eb46b45e68688efe2cc0868d07b106b1819f0b382f6d9b3418b0025eb1dc1b1910757aaface7bb681c831496495d5b6c08504381343fcbf8ec0f0a542c7b363480f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005d06bb08068fd99e621dfdcf3d9c5a019b1e0871a27bd31b60dcea1793c10fded943cab693a1f251aa454fdf08344d4e7e79e334c0bb6024b440577fdedba005582c42a4813d5a1e71d1cfab67a81315a6703800ce0c8d4a63dc67a47b75e83f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0005c19c5c0748e34c213bc006b32ac929b591eacdcb552c8b90d2279e3b6201aec245710afc43005cb2ef3eef7c86f9bb27bf7e07c580b28d37607f32695b93005833f9c4c9cfea2583f1e554f5ad92a24463d37cc10135c96bc170ee98e1b7f27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ff91d52606c6a94073f90550dbf949af75531de0af75769a8be75fedb4965ba68f8f05e7679fce070210102cf4cd1bf4813d590844825215e88d3bd27ede140e596ec665b0fa30467a2430e227b7a8c9638a8a9fc94eaa82fd39323c40255f267412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003e2cbc6bee16744eb82622e870095abfad1a3c61c93d94c5487f31a349b71b1de0fc7acf0e3a0b5d87a53f5bfd52d008e200c3ea86088deec4ad78dc78c6ab0e59cf0b3e53a440bcc2079b3cf1224972215ac7f243a2c9032e7569a025988cf27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d0414eedae9bca997b61f3fd353a36f82e7cb03736006d4954e6bf3d107a15810e8ee694f25d263efe2a94ade8cddea937cdee7af19bd696eee710ca66da5d0359f9d3756cc564bc353571a07d364e3589bb15fefd6ff8a0f8e5fa319a937d717412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002f09a1d4bf6d63ae1763a2f99aa1c3f9309d95b4ace2646e7bff3e580f5f0488108dc2f9cdc36edfa6629a6d79971c3e5049afc375ed35e5a5dd3a65627bf00b5ad83188b1047fbb4284a4c93d3ae2b4583cd486ac02440b6f641a8adec340af7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c15addb432eaf2f95223167a7eb764e5523c771eddad727905c04555edb3d7b675661e9ef57ef2102ce24d41b9027dcf2f26a94c6fc773e3a70ad3bef47920095c435ae0e7c0649d19979b3d8daab7e5e355f895e9ba5f60b4bac3b1419642537412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f8ef123f8f343d1fee289e3ea5d44e7edb13facd3953f125690934e2380caa823d29d2e8e2cd0f0abb7698b74dd9d972597ef4a889a49b74436b0f05bc58110b5c5cc6edd22658d7f53fda685417f4a013a2af9ac83772bc7ae8c996f0aac3ef7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001f3bc9d8336713b03dd41f529e6e8565e02d2f393d6b045e8bcb12fcc5e04d0cae6deed298bd6926ddb07fac34fe44217392bf38f272b23ff98ba7c42ac1440c5c7830737b8750fe722d0005a35220d5c127b07060f8d0155d13f8795871f66e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004f2ce0778b221846e634b688045e86bf09c240f032cbeafcd84c6e7bbdf5ea523184c89d58bcdea8525b725dffdba222e3f50588595c75b1960d6d0adfc2e6005db809d5f7bc2511156ef12602a10a56e20c942f2042d0a2e33d77f4b395f6dd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f674a43f9e280c1854d39514fe75ba626e3953f572c63850fadbaef8bc425a500a99f8053e7bca24944d48a1ee22f6d03a874bd81f94bef346cd1d2f4a63d7025dff3ba50c1a660bf944a055f4b26406b262c2bb03978f7cb00d073ab0a318277412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e42076d2234f62833a37a0a737d818600d589717bbdc12d9bc2671dc81d0d67a0c6ddae048a164b99f1d1fff92ceb48295c065c3b5dfbe6c6f8e0230a2b3150a5e1c7747ff7e6b67cabb457df0ea82ca827e514033b5ed18c97bde71d00349a67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007586f9d4138a60d57df526efdf06ea20ac7a8499fb223cbe6c9db697c79a0c86dc7f13377227018630268b4259ca646eb05cd3de8c5af5bffe90ea40642678035ec09f6ef4baf679ef603f4a6aa61d40f176953e6b03fc7c42bb85958b51350d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0047106b095cad86c6eb240aec0f913c33479c211f28aa33ad75c27848c4c38c5857c9e0afb7119d2d998cea2310c98fc8acfccb72d4c180cd3e269ccf0e47fe005ec705b77e999b8edae1caed1ceaeeefcad946dd79d73ecedaa064c24d4292ee7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007e1950ccbac1e5bcae36ea006a43d8ba5485924a056ebfd215c3a7db4be50a0bf4332a9f643da096cd82fb0ce21f42c00759bcca4db0382e1f7a20cf015f110b5eeabc5e404cfd4edef8cc92a75815b86778a129c50c13923aecb177a13a993a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00dbdb493f0d9a2dc5d25c548fcf98d0b8bbb2067dbdcf62fddf483090a382b3dfc038ff2772da4869f4e4f60c7e59252b30019effb044a10f07fd656acf4cf90e5fd8b483f7c9e962ef658f54ccc310d8a83e0760fca816b0052ad938e41204397412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002158300d1e2289312a94caec3a6e7539684485fbfb9bf67152735515de46ae73b9ba88eab8294e35324210c2cd689bc24ba5937928acdb15af9de1f8af7d530660148b39a7a4d383a2a3072f84ac526e608f89210f50fa61f48d38c4bb0370977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007ddf7c870d9d3ee8a6e726e2fb870fe5e3ba4f6422240b9b4281a9c2bc5c67eaa246128078be34cd2f8c23977def2f2cb42fabfcfe4321cde1fa9ed6ff1aea0660e5146c0ca238b6f39d96c6012c1999189f871d7b31dfa09a3c4507d872704c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f125b22255f75a5d27467f75b5a46af0af14e2e4000f05a528422b31f2953ec289dbc1ef5e3991aa492746d2c47717f8ffae4df69b3ee8e8ce409826a0ce13066119b8207b905c55ae6107b924a47939049fea78ce967da1f3ef3ef9ed1533e37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fe6dddf20b727ecda123f2e115a7832db28dec75ed680e16c46450fbdc10ee7677a5188f76d93ad0cd482074376d640a453ed8f882bc5cdf09a3ab33db928c07613344adc4bed6c29a8bffa0b797324e90ef7e297af8ffc6a7524c313d11d4057412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f9d97593fea2bd2b0ce1b29abadc642b6f880cb073f1a54fb47653237f6bdeb12c2ea9cabf53b8096e5a04c6a0382dd150c5f12fcb9d7612ce425f53333630a613aad308f149d696255e29bf2ee326089fd3ac59212cb11f4b2473bc19801cc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00237a42bca4c57124f2444d34840ae5921f1d5ac6ca416362c91cff24bddb587946fe6e8f8fc9f94faccedf454aa213cb4570a4ccbbfd47d97a2bf7523bfa6d066175208a6061d7fd1c9f0728d8a4a44445703045e6a2f97be321c84946af20357412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f6e12753a86334544cf10ae4ed757e9aee2afbe22fb4d1a87565008f1f81dba6349b5ab02c1aed3d46d7238d17003e348f72e7467d3725caa661573b46676a0a621dc2f9add2ac0165af497f1d0f873a7b61bd26de61bab0db6049cfc79ce4ea7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00be24ee7c8395f7856216d873b944ef7517d9bcd0778ec1eb89560139d4b660cf1195cd780c2df8247ca1e8d868e669ddd5b0c0cb1004b85bdcb7c211fb32a20862f015934a6bcd3fec141264a7e8497131cd2ffa2cf6522450d058c798c9eccb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e0f7a64e653c54aeadede909675af1465fbbb92039a46b95baa9627166c95ca1a77a980ea8382c75dc1d40303979cbbce1d2a9793f4db2927c46407b75406800633ff03d0221001ee36f12a30092f977b6926cb50b3ca2b36204beddb8fe2e627412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00991bb89bbb0b2fc717ea29c8d838149ba30efbf5736036152b30e91e1fd5767eb79027cf4b913c0e799a36be15c47e4ba9e05931ceef5c9b91729524cdf4c40b65a52bfb05ff5b8bdcd3baf401901497443439c0807abb056c2e242b114d13be7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003ad447d258a73dff160f135a4d070e73fd1f7f96b38a14f164111c897ec476ed8d81ab361ca7db5de0da7f6fb5d3d165e37c8de4b00be3cd97d301f2f5f480056660785068475785ff105179854565924173c18214fd57ca8edc07390717289a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d62ba3f361c71d1d34f8c4ce8d94da53e711d0f30eb0171c8237f79aaa3ff41bd2c8176fe7262cc594a8d6cecff54e3b6a689d6f876b1ea268d4b3ea5bc421016685deca424d2ac70550117ce316aea82fab1a1bcb9f88b984f56ada58c9e6267412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006eecd8e22c40fc7572176caf17307d14ca277e5de946152bea200ad5b70d7bcf6f111d4a276f129dda6a611aa7f1381e6508c19829296d9a65c74504175498046739149e1d0aff1af497ab1c9d8e78e2ca6d87ab761fa777c14ddd285d62168f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0011ede09c4f4df9dff92bbe8c5b19087733932fa19113acc4c8df4f73d266154a9c3cf8ac1a347bdab7d5ac2c30e7020696dfaea4be646c69e7ff9d12459b1d08673e90633b273b9f6766a2e21f1c665151d3646cf3a2cd3a4b5b036eac26adac7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0023f518318970fd33e082818159fc6757abfc7609f8685dd07897a8a085be4f1cead0e00a0c8ba2eeffbaf4342d50bae33826dc9de5e2fad67b172512d0a51b0d673fa06be490959aea5df6bf2526b7dccacb4647470e12ece01b4f3f3b6675167412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b1be9c0c897f28f07dc2ef30134ccfd263767265ad43130e37804c8c674eac94b69c2686870c5369a296ab86b9b5ef8c323ecc7c45dbcf31993dd4c99f97cb016783db38a069fcec0260c6282648e68c76fd22e918322897c782ce87f425ffe37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001b6b624fb27f8bfbfadd708696fbf5a99dbb98a0a6545c47679adb569f7fa350695943a59854acb7a73e7b83c46079d1f1d7d57855d0e22b2e55ea9171e1f107681d0cbdc65fb57da7ce62ab6c3184c1f32bb4f828895a36a249b91b7e720bc17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0013a7def3a80545db8d26f586ff8146282ed1093efec7efd42629e8721b9b4b7601a647173369359318e40398620e4fbead4a73ffd78729809790ad6c8152570f684ad0bf43ba729e3547bcab78afa53488ed288aabcc3b4d1cc4525e008fc0807412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d4584c8ec0997ff5ddb522b2df757c724dc3a28a73f93bc7c6a024ea356846d90e7e6c74877f77e5183688a14dced9bc9da987f2cb20b977c52bff38c9a0bf04685e13a5a3067b84cc9f2c11b88d46c300a17fcdefd2a817cdf67ff26211f0447412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00819e4cc22cf1ec68e7137d3c16fcc2e4cd7ea14903b28c1dab5a2d4bc0ae50a2b2ed2e8182b188f67a1c8ce532400f1028cb6f87e7178eb6c37e5654f0209b0268f18f66c890885232950ec2cbdb963c711575c9eaa7f2b62386d9ea8d31b7a67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007ec78437f56d1b0d54c8d69bf5a79d9ed3885884366839dde5f0fb9cb7cc9f1e99247de88d81239745c9f99338c92551c0b19ed43661ae672373e1b7f7a52a0b6907563e0bf7299f501d1e13f128fa1b71714695e522abff5d47eb11bb61fce27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002a064242d0883fa9dd025c91c43a6df54d2999f88b663a4b137c0ce2bb06d587ed01089482c99377aab9e6ab91206d26871194eb8f081ede1dd3224f10284a0f6a194c14f9909bccbc63892708f4ba815f2d50cf0a90fb61260c1ed358d59e1b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00dcfda005c48a801bbdacfb9c5efa999ac6c80a4a1046d44da884887e85559076d03ccaff35a23c1a8c9f846d1bbae26b733a7c14cb2e8bf531e841902f7167076a3a54d4ce2550488de3867a7c7b453d2816bf6441cc237c60433c26aaa2e81d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003f441f66ce93a6a1a0eaf73aefd8fe5255717e8d753023c8717cd8c042b0c997cbcb5d94470dbc82daa1354d2c62a30cbddff2b2c6382a38076b518412e46f026aafd971e86cc44a92aa26ccc667e6db300a2f8af4463b8d96d29deba9078a947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00081ad2415481e3f448ec20aca51f0dd897015e9678dc11b3eb264173e22b8407128967c0b28c5d7985ae7fd3716d422e1f1656e510ea54c4ff329153025156026b4b0ac0ac5e69c7b1ed46470a658bb5da61335e8be7f38fb6ec97da66128c4a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c6b57448c006c1c67d4caff8ee20e20fde8b65789d68f58e58219384ddd893ceceaadf9314c541baa9181140ca6e8c77ea11fd570b6444ab9c2f376d3b86dc076b6a19f15c174611066de8720f768b6081a5d6886f152ce34fc77e21cdb59a737412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a363d31cb386a9f6c68e1c43f4d22ae690670e7cad717f3008ae8944da25a8e5104ae1f6010b1b71ba4f5b711d3e7657a8cd2ff548f5404554385612d4abad0e6c390d6645c1cdb92be1bc732520fe9c9eca06d614aaf11cfbabe41e13305ddc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a107a392c3f211ac83a3cd5d8049d49664fc82a92d1a2c4a84ccfdfb10dd8c4a7f5364692d1a4d68eeb4202cfbdf242d72b6c01378e1a6169479656d8054dd066c62907831c967bb4db42ae20d648db8fd0146264aad16e508ddf888e3a985357412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00410e8062ab443f1ac984580c913c47c771e49f13b5e50e1c9af78393529ddeb28717149fbf151f08709b13fb4b2852a13c1733260f33c88fc730f2e02a78e1076c99bbbc4ffb0804ad8df786eb90a9d49535465f3715d649c5d278f1fa8def297412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e1bef7c722ddb69632a28fac02efbe21a596be6041ba91015fbf6de50851e0a367c718c25691235a2fc60c62564159f297b427be4c4552c0890455a2cb5a7e046cec43a2e692f493c2a395ee052b0544f33ffa09ced6abb1cd2210f332db525f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0052ec31fd77491d8719b85488eb3344e0bf44b01c6aed7ebcd984e137e823d4297c68a71d1d39d81e2fcd02381ae07e86ef7d6f582ffaf6c830409c0b29d93e0a6e0cb7806a2eb0beb624c136d223f3c21f0a58faf5f15e3eda1dc747592942477412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009a0b40f01838e3dfb27b0016349db92fe5248ef315aacefa3fac67b34fcb3840cd3875e47f8b61042d512622eb822d9a98dacf9c574854fe8e20acdf013d1d046e34b9840761c7c6e6b1deb0ac67e204f6b59449c52c538ec018fcd3b1e1e72d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0023899844238a545a2d67f2da4648a64ebc5abe191585e6397775b6e4e86f88f76fbd827d03d478a2e55e2e7478821c19cdf1ccba8332f89de8e766453a4c88076e49c862bdea504631c48eef8a6a9c2a3d1b8e5e8a410bb4e033509840663f9e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0035c7aff88e7acce445a6c8ebe8c7100a4207d2cc8a8f7cd64e12f93443643c5953f47d6dc0c6a196ab837691199914a7fd34dce57a34d1f80c1029ffb267720b6e613b022cea1b970987ba57e63b8f0e4d5d005258370cece607661418b228c17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005f60863e006b33a521eb15c8c25914d701f57dfcfa11e84a019d53af9055f20f30c217ee526b6767e5c7f8135df23d0eae0a58148769467cc36394a6fb1a73036ed9b3aea78843793d02b0c86baf16fa9a3690461f4f4e36dc54436832a2484d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b2ab5adca0a0d1ddd157898626ccde2b15e3ad12b93fa3a9873e861b13bc66cf7080ead76d45e75c585b1b3e11e84b4f46f3ccf2a4111db67adbec67f837f8066f48967c6e23a57d5a49050022d6934a9d8a8a0f2f3c349368c52791763911e07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00693beb11969a8d9c9e69f9788a5fc52e33e1a6495b65b38038013105229c17db3e5ef57dd2307077997407318a53fd218f4cbcfd40ef3bf40eb51df260ec52086fdfba86a5dfb0a34a14a78181f8f5d035e670aaa804a9e05fca15a1d870f8477412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f342ba1de7520b872edc60e03acd4de4f0a5636b1595f2018c1c2d1390c166a830ad2f3a740971c32f67c02fbdd8ff34ee11c91d0155545bb56245f896b6990c700223b64491477f429ddc8ca14942b29aa4be210bbe40c1c56a3c833a540d237412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00773d8c15efd536a24767d7b96b6253b7fca695c3cb768242ef3f732df6690898fc10c64d6ceb65a6be3d94d1c7c27a0b41821de0c6662f98865327ee92444509718ca158e1424e8e30f606dcaf65aecb409fdab5246a9c8ff91323ed4d4667fc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00111d119d4598a34440a20f1866a13890ec7ab09e74706fbf3711e103328477a45295dc94bc15e0dc0b97f1f08f29c5329169a67d562fd6acc529c6ab88effa0d71bbcf98df63123620e1f99d2a6ab0a30e9a928d788698ba7d30b13756dc79fc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b79babdcbac331bbef7310d4b5bf92e8882f9c69be39a0eefd9e4e517716ad818e94361f4a9908ed399ab53e85fde1ffe354ce4781f0a472b753c4ab37a1df0272b06fef1fd8952b4f929b2f550cd3234d313fe4e1d6393aa01c092f34b11a847412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0011f55db40bd72a69220e58025a0ebc727ef110080e72712914126768fe844f9408b06647166efd3dcf3c04008bc1d6181d7de19551044ed2964fd0de5bfb4901737978a4671cbde483f2f603463bc1218b7c7fe5089172bf1fa83fc850f83e7c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00718d8ed78023044ca8a4505d7b0c65b33502c82655f58e9d5044f4ea272fff9b266f0d2471307f3a87d773006580718da1930f8becf7d441f834bf1887f549057441764ae201450364d2c89533ef302c3c4ec040888265d3bf0ef2b101de12517412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0074b01cf2d7be9d1868aacf77d072c6297177cb1db4fdcdfea969f9dc2a613b50f17f345cfbb8ff7876725819b5be298814d66fd120d6529ea0d353b95670fe0874d61698b1059634844c0bee9cf4d058dc0e21f30160fb627e90f462093c06187412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0078b7a224aaea26fa4c61c7235ff3faac82e35e3d947d9d9d7abb69160b2bf2af179efb8d7ed450f846c4c522195ea5039c30c62849a40edf0cadeb26d646b50d756edbabc1c36767b191589ae4ce322bc3a5b851a67141cf9603ae5c1d41c01b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002c0c7afd7cf965fbfd54b89db767327607bdcad95e491fc1797b598cc9edde5dd5577c99a6f5e7abb3d5cbb86a8b6324a7998c2a373718f1136c3004d619b30075ae5467a8b261be5fd72b0fa5b9b2553a39c5ccd8392c819c14e7596cc12fbe7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006a6e1ce24e7fa3b76ad2517870122aba442edfccd31efab4a462baa62fe740e3f87f8063684dea0f49da742fd0d16e4cb43b7c1bc619a327b2d48c314ffe930276802d0c4d5db17e9a82d75695770f084be40023cdf4b172e75f4bc0bbdc35d47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f761e25a3e843776d2dcadec65500bf36617ba5e6427d405207652c9387fb2a77463aab40661ac43457a044e85685e5e4a7af54b15675f0fab26d4d6bcb7b70d76da76100d843e94ae878d288b5692567d2185e93b817929f030cb696e3d95fa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c6bb997441ea02c33281e9949fa1d0afacf311c3d4272097987162b95d183df966b055f7bb55c9ff40d337b6737b999ede39bbaa91610b3489a6e2beb915680177baf3d84563069557df8f4b85203873658a90b9ebf83e5b61900e911c4005c97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f756d180ffa50307a9f623de505fd2704c141f66a1afd8c0e8169d0fc56f71fc9a78059f99f0f2f346ed64333eec88fbdd25ea91e29d78c60d0d0a9f0fc2c00e785692c450d49390007b93bab9e629d4785cbed20583769ea1d470c0870ba6227412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f78d6ea651e8e24df2154205b12d0e73048732735197d2a64560056735c669bebc22b62f8fdf21cb206bde30c0fe06ffcc8466929c2afc04af2526b55e23cb07787c542fbda4c4fddc72cf3506e0d6479e193a828a1bc2b2384861cb74a4010c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0008d6b165fcbf8dca3db19967c0961361a883cfd2535436c1d0ce8f05cc690af86067ccdf19b8d5026bf07e6caeb418a657cfd35243d7697f24448c390e755106788aa019130c26218bef62d3717d66d28ded0a312c86e42c51162ef4feb928657412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b2ccb13c5702f38c66cd3248eef56e3dff68f5a2b66df8b4df3d870a96a58afa387ac8eaccd7d79e0e75fd850b4c5e79636b7c9b29b3fcb742b29980393bda02789181c18300a85f40aa7b8c2a1638ba99d71fb4b5c75b26a199111032403cdb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00986b138a39b8b0f475a2968f60e16801c5d7d918642c903d0f728eb9345bf492b301af54cbbffc3770f8a9d87810299d958a9351319ad2a6897606f002caa10f78d57fbaaeec744e3409809d1225faa90275f603714ab8e2e732833fb33514c77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e0889858e3c68d3a7e5207e86a0f9ef5e1cf119efe879dfdb2461ad48a7c4b0a5a9868750a07adbc23f6afb830a97ad149e8359707ea468aea1d099522eda80a79438c21483e3383e22984f877bf50567f1a6504f255f1813050a05c8f7f3e7e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002bcdf68b99a1928ef4b7151caf7db6d2ef690c6f06b11d5c3597e94359443acbd4636fafb4d48980389e3b36f1017a20f7df26dce3999546868f9453d93f2a087af05fd9d5a3f01efb73a814c5e0c1eb3864a1a992c292200a3ce2c5b82533777412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d39f9ccf3a3f4b33763ea3658f342619b43753b1fa0d67ba23cc716e981dc0140c338a064fc5ee5e6926b95407488c66a5b274776425cf823c719ca3887d82037b6600bd3f549724000813621a808b1d6224a871fe0e1dd460c3fe9ca946f0307412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007b867b82bb1baa0be8c60c4ee54e0d46ddd932ef578deda7c6807b1c3457b5a73e1d0ca326180e970b9ed2925a116e82fe8a4a761b747d65919dd48b13b4b5097bf341e1c4d0deec119316a8849c8d4e912a8b5dadb38575db14e937f45b7e277412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00099c2ead7720234c7a195488d3887fce50004c408ae3e79657c2fa5fd31991d9c1d97bd0420c951cebf86341cc45273534d648d0c1b9823ff4317f5c86b22f027c601959f37a0f0bd567f8ba246a0af5a6e4e2514b3a7dc438c3dbbd098912667412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0056f6c63d8f4672dd9d1c5f1fd91d0ff4d431b51974419549018702aa6b9b03d9893a3081e528112e1c4c43802dd47d469ef4ecd901d22de117f3dc60400570007c7f87fdca644e1e6ba1a3fc5cf27776ee3bae1c0280754a0b0d2bbe0e395e607412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f3aad9f611a667b8625b34c8675d01e1eae87c19c9fe46d2400663a064d9cb1acf7ec16c7c4d205d418f0a69b467d0ae4f206e71b6d295920859952f85a0b60e7c9415f4fd22e5aced5385d9ba56d21dbcdf10df8382dbbfd2cf5dc4448032027412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ef2ab528aaccbc619f459e2197f62b85ee2df28c9776d2fd2bf390a823780562535120429b91d48949c5555793b65e3e4839503a34985f518fb1c9a2bd40210c7c9df999a1c4a9c1b5477489dc0c223a32a7f21eacffb6e937e7e1d7d1eee7047412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f6384109dd446c28b11abf8609f51494e3a85b907644de19adfa32b3cadd5e368e68b21171340a7754221f49f317e6e3ffe88e5fad75572a563b7f03533375007cb9c4415a2544ab08649dac164edb5b274204f337dc44e5827af6ef3d8a50a67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008189b351b7eda92e0d9fc9f1c2de84f564e2b05ea5b3bb28c8dd6e08713dee045f6654771feda5fc68229e40fbed48d051c3d2981741dd1cff6cd24b83ced7047d09c7a996e2b21bf33cedc6394bcf47fc94ae83e5f027f121839de3903b39b37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fab57a074275a41e5c519f1b9fb477d526e0897558fd88985515cf464dd0b4826497a45fabaf73cd7233a67c5799b906631f03a062960867da8d31e0755ef60d7d3e5d4bb8356453c3430bc745863fd1ce138b9a9e586e647f34c4bea232e57b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00369668b08a6e26928ed687e34b2a9165eada1a9783bcfb57056cb7ef810c4af4dde9add0289a57dd9912fa8285fd5f8bebcbd1e13711c4e96b8441a79f0fa20b7d694cae8e241c14d2a2d4adbc5337732a014acb9861702eefe7d94e861a650e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001d78e20d6f0d2891d8c074357697c70fe36b8c20e64e27cf9897d4160d35b7ce063fa34f6817b4bf3b85df09e0677a0f2adfceb188a5174874480d565918130c7de5eca494ed60b231920dc3ac991265ec72e4922e75c50b8a1dd8737e87a9c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00df3232000f60264c9f2fc74ccc180495cdb7aeb49a0c4610dbc218a017f6b91960bedd36230b0204cc8504597702dbd2cdff6935d59516ebf2314d85cca83a057e09f2e050c4f868e62eb530f3e27bbe96c2aae037f979af89af5162b5f3a0567412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002a43241c329a50b1ef7979469e6c327e5fe46c2d0cf67190abb67e5910e4127ff653769c55f8816ea4828cc1b5bbb3249886cf786bdeba27b917ca89d190ef007e5c6dfe75783f2dd0a78473662a4158f99a7883970171cfa97cb55afe8a0e207412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000f591f511a8c850c063f5c810f5db873de7d8800241d174cdb60fb3ecfe1eb76c7c4ba84bf32171fbcf69cd23f200000129ff11d26684913d312c5bd5ed712007ec79d621c3ef96464329b27439e633f43798b8da05729310ccfe5fd602aca247412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e2198fdd85f864d3ad41a6e4aa4abbf414d59a9ce2f56ba4960600837aecef7df5a7a36fafdd86f35222a8243c69c1eb51f0e26533788692e235b15916cbd504806676371ed3e5b4772a390f8c60c73c84ef84e7ce1d9596768883d2185af3b17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ba531959bad2d7f54288c1776eb3409e80c487f2dec30fcf0af3ae9f813d10eeea525ded252563d01ae75a10c454eb76bd298e000ed9a07bb039296f5f6f7f078079949be9bf709a0f8f80f2ede6970faa000172a89d7800284acc90c50180e37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b25b6509249c84c21e8bcfc990bcfa5f4b17bb56c19f80783889acaba14c1201ca444e76eff0e9bc257427b160bbd062e22601243795411c9b00aa7943920f038147eed34b4f35e6f68f10e5d452736be46b6f94ad328e37a7734250e143536f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008949bded5983a1da83614a6b006ce8f977f167e57a7289df8736d1dcc9900970d3abc1e41c1eba252e200664b3ed2edf0c1a051f024b69f8aa792bf8f955740881a1b892765b2eded1751ca78ec80c1b1288e4e42c918bd7043e9a81ad9085bb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0034e4e833ad96472fcb545998d9d15060d17db9d091369f80ad8d8cf2c8c836b33a1c7d2c42556921d6fb5d47c626c970d22d952beec7a4ee340147b67fb1940082355a7ac8ff56b100f146a370566d6b6e3f82af39402e5d7df61393264fbb987412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fdd2916950d5d568573aafbbc1ecaa9c581c44b49987f96c3113498764024df48a55504acf320dc54d98736cc83eb7354671b54a839f65e80dcdbb81a5169d0c823e9ce884c8acf839d5aa6fa8a0f61bbf085595c545c34bb71767c2bb2e0b577412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ad73d6046a2e09926557a4da701a5310627551e34772a33d58ee77dfb6da69e473c2efaf29213d9672d1377d1f1621976956bf4cc74b4af26853e64a15df9c068315fddc0cac4b20982abcea40ee93b5b524f1dae736a9fe29383bd0bdaecba77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f4b4e68e530d264daf59cbed2d8691a2c05f29318d3006311620c3d8376d7d5f081881da3571c2fc3a028a87685d348b01b058b73f2be658ddb46100bd55ee0f834a62ff8daa95f63d81a7315907db602560ee717818c27497ba6b497af864817412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0098386993241d461c7ffe07f6bf6567c00c95cd5677f01ebc1ca022cd16cf316ed67756ffb9ad122a41fd89a7c029c7866c5861a4c5f7a42e148bae94407f030b838b4fc81fbce9e8c4f1733c8e42bd676f1c6e9fecf2d6d6e9d9e6d56fc3b6827412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cc72a05beec1a7bb582911c97d1c8c8601e6f01acad94fca13a720badc966e3a0793a59864ee799a25870c526d07e57910fd302faf647cfb9e56e560795a770b8400bea0359f3e8a1285aeb1268e524985e7046ad330d486efed20753ca51f077412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ad7bcbe77a5895992d6c9ab586e7102048d116550de0ffa35472e483ca0bc1539c495fd95205ca670e10cafac2f22b8615ad0a8b5932dad3b471f6ead500cd038425aaa4a3a0eb35643fa678a90851f3b6daec24a5e8a63df2119ae53082d6377412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0052cd5c0826f0b597b4e9b56f7f85f5f948b546298aa0139f75eb5c270c2dad24eac6bd279431e7ce4a1b4b0bcfa75a4bbf0b805a1711c0ecd6ca7e74bda20b01849cff700b439b6ab6fa13e3036bc1ea023e214a8b7cccef8cbddb08203df4c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c698529f3a1950fe6667baf5d57a61d3899dc0ab8d468d98aa2c76fe17eb9359d4960505fdf8177e742591de448d607cca21e5facf79fdb3261c1efaca87230b84d2d37e08262ade7a57eaef4d6691454bfb33678be52dabdb34f75f0a48e78b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bba793bcb8c2c677325817faa55d0ad6f960b9549555981f7406b024e83d1b7a7c03c560f1bf39ab96de0d1b0f80c3ade3e7b62729d773671729b9ef20eaae00859cee8097a433d9d36faa96a3896d81dfc1efcdcd931123b1c5cf0f60d94a2f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f10698c612e77ab63da9629957a9405cb1dd9431d42fa4bdc610df20340993667534b7308833bb40472e6983860300a492d57d47bfe85e71702e0242fc326c04863948b755f8a20c20cd94643a1c24adaa748e3d14d17e1ad9e5045ee89d5b9e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000fddc9549ddf4fe0ceb5b9c83f14557d6eb5ab9fcc69602f139be66ad3c13181bee8fa08830aca23a5cfa225785cedfd58c74355876367f018a91a85b6052f0c86476968265e08889345ac22c9ce3434e34a005186996273726a6ad0d3be4e177412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000c1cf771be1382cb8d413b7f8d9d32cb866ffa11e60876672c0c736c93cf44e545fd511c706d8e5961a9aa59e742322a0523c0b580e71db9c6c218ad24a5ff0a865b352972db47341ecf0679c3e556f21f1acf96130bdc5fa5b8667651b0799d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003de13c89aec25c90e5c61e97582390fb68dfff0073c2922f42436fa5a17ec3d37bdaf8f6ff4ab15547d2fae9ed4346cbea20a41ba1dbb2f7774c42f2e7234504866e80ad56a1f0b962210282480c097731ecaf0587efad21193181095fbf6b117412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009800181403283d4f573081557f5b41d0dd4c80e7bf867811648d6c6ffdae5c9c464dd977d219282990e17d1b2eac90dfe1007abe481d4fef9cf3792ae8137b00867dd8888743609b4894b0afbf40ce62a2415b54b4e7206458b3019435ea3c467412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001156e940eaf740a94706c63bf94b975fe6efb09d837e851e63e2f92a322556b4c4dab1bc78e706931d3941a3805079953f64a0e682fcbcebbe4f54b80b71690586c1ec938634b944f9bccb3326cb1f09621577230812e2771cfd95bbbc4d2a277412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006ce8d0349412d518a01ba60c5d220fd999b62018dd172080b3fa0e25fe305cda554bac327579060d37dfb895cc7d7f98eedba75b00741aa2f94ddda8f62d3d0d86d07882373aa6a5efc34515b70ae649ada234850f7886d7617e392dae0371657412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c31b10c8308794bb7a613df22fb124ac4f38d50736232f81380311817e2403bb97caf065f9e3f4223e2367d19807bcc5ddf3a56a3ce981042eb93993a9bb740a872f1f668e60d55dbec0088fb9afa0d1e4dd1383feba48cead619f2e4db2f3a57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0062e6fa28985b44322555b7e1457df4fd997ac840eeba97add5cd847e37f410ccec8e4e2b8e936f8d1eb72283882efa9573442448d80703284fd3640c2a48fe0d8746e1d07a12a67441f01cb8613ad5f517a716450482a60866cd9ce58d8007867412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000078a879e35dbd49187cfa1635fcbd643f21dfc4253f62753a46f783b52b34d0f1d82fc8e78c838e5d9a83155972427c320775623f9af4bb87e996900eb14b0987a8cf45061d3b0092eeda15be8c40d0c04fd24cc57552c5f42e35b19300480f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005d78528fe8e26c1bf955702ba386d37857d0b9a985f4696a930c313c7316598e10b51756ffbc9c809c4f549c87fb383de2bb93af04444b780b83b15c1121d90288b6c18aed539a125811322da1278f3695ef4484c59d8c33a9f5934bead2a83e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00819fd5f286c9dc2d08529f85af6f03b6ab100daea6ca2647fd92f589be010007f4db3918a92e3139b9af9ba680d1e6dfa454abafb7c7acd3da7e219b33f3570b88c21cc835d5bcd7d7e864d6cdbdafad58b92090b56cbeeab40469879bd1fcfb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001dd3b67d84a4bfd03a005f777b65615c8bac9ec09f855a121a59c7a6c6a4f6a007ef0f2c8b00d8222f781bf39db1fc13e90bf64cf6530ce06510aeb5f471d20588fba241518b05e47dd6d063d6922c91354fd619c18f5f62efdb3b749eee6fe67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008fd4f0595447b87c6da2b4586058dc68de3dbd7d742a1da817fbc22d6f47f27bfaed4b6fab27238ad6d8cd6e64476a97007c1ce37f415ac52ac26cc0b373300d89d38a1e70eef588f846dc9a0bf6d665a9f1fc690addf61499929aa97dd387987412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0047855be7a8d64a02817d1fd82c01b6ca7e6a31d22862e618aad21cc6b49bb5844f850482ddb420fb4aa67b45fc1ea0c3376a0138b105004d487004e81896f90d8ab765bda6e24809df0d447f7657c96ea5562bc7cfbc848dc14c44b0c7f5d73c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007b21328b38f823fc612311113184623eba5270246056a53f6225cc3967f6d2d54863488bc2866a18b0f881e98dda5c004dfb3aa61e47c63511e51f6766d0580a8aeae69850306b97024454ef4ebb7600adc211a479ea04c57d60dfde36a7a2ae7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00baa5970c4746915d50da8f388dc5a059217796d2188fe84484a40e72e5d87c49177a89236b5a5c10196bf8ab2a1ac43f4fb16ad3f4bb3646ca9785840acf6d048b436b8f4ae43ffb64ae83136b91a22cd32029f06c21d70beb12437c1bd751017412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a8bce4f8dfe11c76ad6a70f565b088d5697c8258b137da89afcc8c467849699a40778b80006bd3e5f7179fbc3e27013702e42db6c6c6854969912bd005f2bf0e8b46bbd10e610544f112d385659948acaf7098bca31210b409c36a2f04797d777412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002c27a85258227d501c93e9ff7b1f214f5d77bf93e57953cace9036e6e49ec672360c49dd13eec877307b38005a79bfe9af1b8842288d4bf4b1c5a3cf1ad74a058b98f3d09929b8d8c1963b67000056e6b44c70fb0c04986c9e0dde0850e986c17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cd0cbc20dfc42509e7e1735d9e02a187a815d266d23f1e69bd22b50709eb0bd79d124fd10e3f5deb23baf9cff349418ddb2029095db1e3bd6eb589ef9cad5b0e8bc0ad9f851aa355db0565889d6e2b8f8ff1451374937ae2e8d6dd973b0671037412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007459ad3910df48f642f028e77892cd0623338623d112cde29ebd882c69a7b6d1a6c0569552a6b12689faa104dc55fa8c35e466a399f397d20e36942c757dc40a8c994ac4a4f13bebbbbf3c178c52201eb0fc1c1eaea148a7168df8242d4b1a0b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ed1ba5d923a569909ab647f8f80b4ca50915986ec9bb107c5d1a5d8074b90c02fe419e4d939965e49c3cf9e835d682f258e0700540ac819bcbdd4193f68d94088cef064802f8f0b91f05c7955e0d7b8e55857a5a203e3a72bcc37b1f15c0582c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a3368d2b3db7e2e5d356699f7de8db93def73397f2911b4dcf722f56f2ec34aa49e9d76e67a3daad0442d3049a139d7f1d04e5b9d205ec57f6db30e8b7fe2c098cf07ddf0f6499ca7d8ba13a3d4802c144b57f1959198881cb1e79195c3430717412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0055259d369a8159e326c809224ad54a35a7f29bbbfad4c0100e17121d25163bbda4040fbfa89865c74220c2559324afdcc1db584ec17d10f00f5c6df38874bb068d62524b3d56c18c6e75dfef038edfcf7ccd08eaf4aa95b9262de28e58fece107412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0092f06c4607e3d115b92a7a44e6e0b77897821b1963d88a9f1ae41a0d5aab322be8ceeda2abb8e7dae4c42c6fa069ee9f62a9b373b08e33c4c15135949cd55b058d8022cb348ed8ed40a06fa45ca18ef5e91433464ff8a19c29b0d9f42b8b44817412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002324f989ea101dd9cfb47124c8b2fd75344e41a864764b51f9e81bb73a8e96be2d9e8620fa5e19f110b0d8dec76af4dd3cb81115058ec71c56866db5ceebb70f8dd7e1228f9cbcb02e6cc13744dc5b219d5353c74080f3332073d72bcaa100a67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d92cf4363f582b2fe24dd56e5c7fee666e4b6e2efdc067dd0ff4a85df5c6cc2182f80f6aae8748b52a78cb7120a3e14e84e4e9f7e2d0a503dae91562cb090b0b8de6d0a0345e7556c0f97a7a7c30f26d6ce0ded318afd2f808e7fcbfbd9082e57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0037a4a4a93ab68f0c533841f821d425d523374479dbad56ae95adecb5d2e6dd30f76fc5ac0a391da8c07a6c4638df314007b0a748ae2bfdcc75fff5bd4244850b8e166084f313ef5fe71dc8aa0a082cff2a135983b2b9dc07bd14e451da3729a57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0005062b727bb3fbcb2a80395622eff6611929c603980d56bf685dfd05673b104ca84ee64c02bd1ffef960fa2a41c4ac95e93167882fa29f83f723ea7af40d97038e24aef8ad77dc29a92fa0618c83ebe16aa3d1c8b8ac1e499059908aa384a8c67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003260e4c9892c047e5a9652cfacd44b4a5e17d3d630fef0a7e9bad94c21cc957b5978f28a8ad90d02f0be46ee0c5a0fe64b0ef80ce049a3a9ae8743f976ddf5088f2e7b543a7d3a2231cf7d2dd51606452431eae3413c9cde087b2f32baa345087412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009b243bffe34a2fcf4674d491928d89c51d4fde9885371809ce53308350fad502477e6b9aa3da3fa2c351fecbbb0435a3831c6da72b3d152e7a8c77e223b272038f7f55c6fcf428a34ab2c01f74401ef0306b8528f9c6fe4ecb44539599cf170c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006c1c2cea0502db7b8df39910857d851c947d6dfc71bb4706540d33813dae80da28c5ee68f812d7af884b8fc24148c4afcc371ee94b0df0b8f6ad2936b1d366078f94514e131d81e94358970430dbc2cfdca7605fe70848b6011611bf1709c6ae7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f71363b104c3e64a5dc25e18cd93b58246de5c637fabcb2cbc45bcb1ca26784ee70ed0be9d0133d31b36eae4363bf79a0c894a57280b128f9afb4661bae19d0990d05b5a62309d67e40a31cdb7fef61194b23809e105b481eb62096f6afc146f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e0bbb7413be15718481ae4202cc7108c8b26c722734980303001b5d742f4687da60fa633ef872c281cc1ad62c9efea8d0dc23ba702c831793e9dc42a22c30e0790ef50a1c931d0f1e40f7812d390a5639028298c4376cc323552e2714a779d997412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0034a308309d694e783ffadd45eb373de56828a36bc1dbb66ed4fe044c245a4daaed32c369ecd6393f58833054238d7786ba186665f569b700049321fcfd14600e9121777694a833eab58ff374b5ac7679bc8d5443d74377e7f465c630c334d9c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0055b97600d5b4921db62ba4e1db4174cde4834a30f53c3b140a217e4c5f3f81c78195f8232bec7ca28545cdc6f66b22f9f2477d84e1c14a460ee11856168932099129dc0162c2596f9c5f90df9f11d63bf9ddb6d714f32fd7d045e1a8a69a169e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f7fbc18d9c23690056f5982613359c241348874c3657352d7a79a1e638cbd1b626c824bd011222138094672f530bf9f55327e72501777db5d0ce0d86ebb27d039136b5b2c6343ec492d3081dd7c6e81d26e32b7daaa478c1b9b0e7a16be6511b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00854c61740ddf2d0b12cf2d34869f9643241f7be549df1e7b6eed797d887031c1ac32e9b0c33c3bd57a5cd71523025c4a9f37d597b70fc6f6f4c9d9522b7cc10a91a5421d7b9317a93e0f04b9014f07b71ea4139b7cef7c73d8363e2fdbda0bef7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d12bb5cf40cba39ba796b6440c52a45443dca12369fd8bbcdfa70a6ed3bc4df88226405f751c60ec7482c32f79d7619c16f2e93a9769e159f0d4d9e8fd6c28039222bbf46c21869db9bbd87f1d6fad66f2e6c74c146ea2f8b33779b175f9d0ec7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007201bd53b5d64ceafe8ad2794ebc3c279616ca934d1878d3eaf5deafa857d53dcf30748c05632abc9681bcd2b521833fd0c350b7f83229bc6491b45da4ee460a92e0b7b5e98974739f250d8dd641559b01e6979f23829289b1b109d5ffae85b37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ed2a30a507d28fdd704df7a150b60b166948704b4b8526e975cd1f708bd752b3d7ede81ae71e89fc24e435508a2df76ed7e207af1394abc1532daf8bd1e3480e92f21f9e78b08c7238f842364fac39b3ec2c47b9534fe1ce7003ab4c7594cb857412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00266c381295fe7974e8eddfde8c9518f05942c709526787c5c6649d80cbea6b30f44720653b7c88e32a954fc523506c319967b220bb0e32ae6133f3d2134bc30b92f4bb5443e977b9b5c4ca9a40323c134edd90c3b6e9f52d6731f1c83e8cc6b97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000c52ab5e3d62a00b68abe995040a0c601afebff8af1ee2580770d261a3009eec615420fc3798ebf41dfa7584b19cee45cb73596324282c1d52f576b924778508931a85a2f74b6bd670fd7e4407388ab76fdd72991e45fa0f426c5f697540e2c17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00477097e31b72e5f2018c33ece290a48e01105580edf06b66eb52b6ca93e4a39025bdbb4a895701178e7766bff0b3714c733e6d488e494c71c96872c993709f0d935092b3e5066a82cf818a62536d88d842ee67102c1e1a6db0a57dc56f5b56417412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004266b132f8378995b6560f7d98a63f63357fd6690b82854b4b842491afb8eee05e82cf150e4b6aaa383783a0061b5582fdbca3cdbc3bb57f507803e5bbc0ce049395033dd901a0d13e0e6f71a6cb3b3c95acda28d89bb008f5acd7d1239429487412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0036b391579de804cad405ddba3d436f68af5dbf1dbdb74259bbdabee0319b9b0ec83df03cc69f64e41bf6ff319d34569d7c41374d221b2461febda9cb5cddb50e940bd9d799f05c51b2dbc5cd4d9585f85c74658216ed405647e76db23aa792677412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bc0cccb30ebc7e8611dedd216bdc2779ae0153df50fb36f2681f3b5bbd4a58c5319d8a89764226a364566eb46eeb1312ee767d74e706331abbae6426428a880d943a813369d3d3d464312c58e93c4bd5bc3d50c9694954c126e0269bf2a0a8db7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0069ee8f2f36d44a88c3f40db976ffa31e8d25ea4fc76e3c11247719365cdda6efee8258b675089571189c1546f79c7eac96cfbe9226282a0a07887c33474db00f961ce00c5b6f9edcb760e2a841debfc3883c624b9153fd2d12d7180107765c827412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d2c06b998678244f9ceae2ad9aeff189765cb63cd4747930e6ff0022956602b984c50bcddffc4640639242cd9b3ac6f0833a5227d1017dd3646991babb237802966b56f0ae20c78c0b332728c842dd5a14cffaf181d538c30e20cc4cc41c4e957412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0045dd203779cc819ac23990cb9f5965153ef6a3467bcabd9f16804161e240a9a6c02738bee53fa0de2404042ee34756791dbcb07472600d6c6349c77fbfcc27019681dfefe4599c287026535a56f591620d3e42159639eaeb8112c2eca760e41b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cdbb3402eeed84853f74b7fd15f02517c429be80236a5041eaf3b8e9823c3ec521185d17ce7e8290da3d4ea978a552d2fcd1f6a4f0c99c74615bb899082b7c059774f75ddda9f906fbf2d6f4c8479010a47d42e0362c62e0b41e0c20af7759ca7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005010b78df0ff6511a2b5a6d52285ff4a11a9807f904b87d4b6050312c6a62bbbb4b8527ed92f5b3696bd56994863031ec9860be89ff8fb1a99294e1344c91d0d985f05f92ec9034bb1936bcbdab54ed51b6d98ea0213862ec8678fcdf5265df77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0011ab98e5f5fadbf2c2550449868f55c13bf97e996fb0b341c0d19554ad23f2742848e8408eafed21552940d9711d62fcca12a72b3586fe4ca6acbb47058cbd0b98dec4d87976105e47917142f1ea793a9ddf937de57051b13a38b019097a32547412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a1da275c354be1f051f524f3b25187f064836c02db519a72384e9313d7b6332925aff289a72dd18e2c8a68dac8a4f0d0fc659c9d91ffad436211b0f2d398230f9a0900e8790faf85d4a4b85270d9814ae4f4fe365ade06d11da2c0e0493711ca7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0030aef012c552d49c53a845fea457aa15e19a04e7435403170416d514a3ba7cda4e2ab4cde6b8dcc66b736350c0450263ed951cca387c65041981df6b54cae4069b5c8dd1f08b3106c1761cd0d1e1c7a914a77c290df2ecb132f1eaf601e46f467412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007c8a0e7ba6ee0c852605b729bfdb90b5cfcebb240d7fbb474d9d60ac40c3c1310cf778f10efe9ea050d2b41d1bd2dd57c28d3f65c6f944d1f3c2170dbe537c0f9bddcfe82d3d0feff63ad7d8cd06f33197f56b2a4db91cc15248758ae2df1e8e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003de630288ae858b093a6ed35f5108d3b9667c2f842cc56a70a0a63e452599a22568ed5585c0e52ee19d483dae8bd6b4e2dd1482f3c4bebd4ca8afcca0ddd780d9ce56193ec04e02678964990b508bed9f483cd1775b30261994e01b033237c297412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002e4d7488bd2ca38a9748d35780e48457eedb8db1a098abf4c8d3fa538e8946e30b6d59628e6070533e26fee39d66c710499b996ac42c0278a4b59449b595a5029d06e18d1e5f6cb00840c4d85bd8ae1ed5482f580bfd50b0726d725882cf96a37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005fc1282162d96fd4f8f9f44cb78014e7d2dd63e99439ae09ce9f98edc95d7ec0b07fb14d874abf94570dd23f04a464738893040b0f7ae5fd8d436071ac48180d9d26d4c59d4c0b6fd1e1ab3814c8d17c79ea0af849f2c605817011169af14d587412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a05228078f75375c4b41de2b1dd884a45a8b52c3d2b7eb256c11a70dd87204ea6e4ee4318ce074c2852c9a44e4cb31c8f8db9f8b8eed533b171b45ce512d820a9d38599d913dcf75c0cf0ed05ea8391b154d850c176172d05e19fa5d458f87b67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008e892f98ebb6716d2a8cd169cb5fae49b8bf6a4617a826f227d3f81c7b79bc7ca6e8125f5488b9fc43cdfca7e24c28f5b0f40109493933cf3e944313c53cb90f9d5996b4acd2540ed963922a54f58ed8e6f4ba7e54fd99cf98e0d5e616cff9cd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00225787332f8b9e4f514ae90dd20217301c63bc30a7d91817448ea12e1f91cfc2f50e6d48961548af6d39f751abf47c7144c81e127727f350496e00c7bc461c069e3e51f98f85152018170765153e7c73051c224846f8f97ffb4784fe6cec97167412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cb9817398792cc22a9c1fdc94ea8ecb13599bb9922e5aa3f5c6e07ad425e253adc854186761e7a506e7aa8c389fe52b9c93693c1ad93c79bc2d4881d6087c4059f1e709208ea44927151589eeb60d681bf40411c58345dfcc53945f319c2cd447412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0055a09b5f4256df4c4b43cabd1a5d1591ee25a1d47d8858ec6dbc206a79750bea0e288f43ff8194dcb38145e579217eab83c0b70d689837c39c84641938533d029f5ab655f25e2fc1202af6fa6fd8aaa23d90d58270344dbc4029d164e7114a4e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00894f6cb3c578de57151a96bf832d12f3aa1cd4e0cfe4e92ad0420c7507043e555ca6761f6f8e0cdd1aba7dd282455b1d0ef7ca654fcff638d8c5cb73a02df1009f6f97bbce670bd39e73f6c7302206b073c18f4b11c8ed28898019264e17b29b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b3121f3a306405447846ff6338f1f60edbcbf956e966856afbdcf3ca9003e1c3fe4acecaefdb41bcb86071eb4875580171e1a4ba97bd4d518fc24f5da58f81019f79e5cf8ae4b08e71b51481a361ac7e5cee5a64c42252f997d9aa102e3c37337412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b69a3db2df4bede8372fa1897267dbbc9d9b792bed057c627f2bcd557f30cb2408673a8d49cd852e0c7f77cc9da82963c1d3a736a4f0bf1bd991b7be09b6cc029f8d3b64d16c8f30defa53135d8a1e7a66af672256006f202dec3434a2d33f947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00265a83330298138c066345df584ab951d9271a29fe1ccc126cd860c40b59b9539e9cb9e464a592b1b86e9abe415bcf8dbabff7dca9fbd99b77064f83bb5b660da0204a4ec85cb752262952ee60e410ffd17f08cf1232d96e940ab4b33d0db1127412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bc1985495f28a02ad119645f7b5660a90b40da637e8698d177bb4e08794f232c11ccab3679119ced71401363d1c32475f88e515469b0d5288a75546bf90e6e0da0396d88aa19bd81ea72d0193fd22fb6d0478b9bd3756b3dd2a7977f0dbb978a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003e8dd8a76f7ba051b79e77b34243400f80f3e799d14c1b7eb332416f27107b338de6ab30205bb4c92d1db950925a70c9fe9704d0d551f2107d1ab2d4fa8ae906a0c2fe8b44725caea506a24c4c453c4ef82de882acf0d1393285aee2767d51f27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000903c0190279fef53b34f4667c50407a500d3a46139078c1772e5e275ed679a8e2408ca50e3b7a5cda5d757eff087bf780a6166d9b458bc102e176bc56a32c05a12af7f6211bfb76f1056d2b2399ee06b7fc78db2ddf6fe01be5a3ef024510407412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cb8f86b9b55579e3d4147908d8d37b086a51165f8944c2d722ea8c77e432562f5c2755c1cd71c37701cea4dceb1ae9a9cabc2f91d38c67586c5854ef0a2b5d07a131370b8ac30d7c9d1ae404f06d100bb3399108e254abb60c7f9b996af2a7c57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f9d8984fcafe20f636f7a64e3cd204decb7e6e9055ec31743dbc6e695594af3f5b96c7f89d28b1b8add0aa6efeddbee32116ba42ac4e40dfc4c993b01af59a0da21976f58656ae4e2e40809dabf9888a7f9503564ed0854d9d73c2690ebd68f27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0070b56f34262b5c4a05ab64716ea9cdf9b6ff3d97691b641437ae485ce5dd4bd4890fb014fe5e5be246c19ba2e306c0f9369bb2bd9cb23ceb031545b66d064208a27550e2a03f99f1308f5abee215451d283b357d5576acd283e44487e7e6aed77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ec8b79d2444742844fdf0bbc00fbec2fe7f13eee53497b394cdad71bbef857ebd184c5338436190e4b2a73f505fc3ee6e97d30e8c311037ed20c71800a2ceb02a2876c0d1376c2f71e52d61b21cb7753562dd57f5d5a3385da6b13ffbb1ae0af7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005db5ca2524f4202718fd1b80689acf4823060d1b0229a9d38b5714cd94e5af13f6fd506d3ded6d934ba8830a3195534afcea2c84d059033f3428cfcdd8b4060aa299ac12c664db7152ab0251c1276bf98ec7247e3275a6564f5459d950499c827412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d50f7849e654a2b4cb9e63ee1e7730c9123857d0ba94f2d52d1f37ab27b60268947a3eb13e4677857f7562af1209e873514045de63c89b5f84f5aa4f0b79ec06a2d6f27b74b8fbaaf666d67ddfc3c3617cbb7d9cc77a358329a2a5735646ad927412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00628a426386126d93865f8b804f89600ae952c7e3f54733a3ee7a51b06060ca3843b67430013c44d821a927e65ca3f5c411a9e7f775322ccfe82028811d587b0ca2d7344bd60e18fabf7c81a3f24e5ca3fe7a281bd5ce42b11c2d7857b96d6e977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a058b81fbe41c774062a369b285e11a526f3ee76ce09d0c053a7d1fabb6a8713442e5fe215a1c69e242d26884447579968174c7f59b704be6eb87dff8b246300a319f5954c8fb46a9a9d3f30b72b8b2413f75c8385393421fc55b8a83fc16e177412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cce69d9cba7b2129afb18785082a0a4f94dde5a5443250538f452b691cbc38e00c23f129e89d5113131267051d17a8593023e8f21597e10d23fabb24d899730ca3e0d754d858a9ee8ec6a09741806d416f94816ef2a7ec1648d377707b64d83b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f3a7a17274b7940ec94247d4a5f367009154151c6faf300470816abedcd655d1d9e541b1fa6ff09e812d867484e342cbb2529994b2ff472b3e80f5d7d02b7707a4ab152e8217389a7762161e2b3aa9c17297533bc6b63157f2b3038778d6183c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0056cbe5e92c69113b2ff5dcbafa103ee8c538aef48c9e4b1ae71e425409ef378d5e8f325a3c9d4b0c1542611f46d54096ae53fff118d98f09997fd23fc7542f00a57f4ef5d8b2faa65d10005966600a5642308665a49e7dd3739a7d847577836e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00174f38f80b67c3cfba056d92b0dcde76f5be1168c95db27a914c52f3ef36ed1d53230aa828387c8cb242b623009d1d21b1e6e52a84f4d65c3ad3c247b344fc0ea5c1c12800b85f715bddc4185a30df728500854c339b7dde2dcd83384e88f7aa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001c357a4d104813e941d9246932563d4bf482b115dd036d2d269875832fbbab0286b5d3d90524744a848fdfec8d337645b60c98067bdfb5fd27b836128ee68d0aa6200c8d1eba0c02b69a4cf77db7cf425cef22b73d1ff2c421150017a0032eb47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0084b2984c7178a070207d36b71b2ddc2f535bdf2b9d45aa4606fd762960d001af4564ee8b6eacef054dfc0c9ba9640c48a9a100118f31209d837bfcd64de2d808a66e70a9149e82a31c6fde0a8c884215e4a79fb7335c00ea97f383195fb5c0fa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002cee4847899ef94cd404bb06d34e5efcd6d0157eef7591921a768d38dbcf81321f26f0f05b5c43e87ab8735c616be118b9c14a1611e628115550b48037eef703a66ed3182a2075e89ce53ab00189e041a7984debca51385efe6c3447e2f3c11b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d80e633cfdc2e14bdf6c4fac91c13dc8a652031ec6cbec2d29319ec8f1735553d3f29d5934193281fddc998c0bab8e9d723e0ff908f4ddda5c74e1daa21b940aa678f8c4844e42b51f8cf7a5eedb46741e1c27df766e40ebeab9847283ef58127412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00eb937e8d19fe1e93cccb00cada6b7572782f3b9e60a75f02df7e5e3f22940255fe2755d8b697c14ae37e20f08102338fd66fa9d3aa571359de6d951c76fb750da72875685b414acae14e60d9e80a6995019b8795e628311c4315f22a8ab7d0977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00617e7572da827b51792493ce256611d8cb9385827ee80203ea2f015025ea7124d151ef3a76e611a9b0f31a3017270cb571b881c6309e6b0aeb46e1ef07073a0aa74539130fd91001439cde4016398c36dd19afc23e53672971ad47f633086c917412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b29da906bd5781488e82abc95f5b9956db9d0aff1e50655981d5b50635dd85bb3bd55863296849e666d2d650e2cb3e7741dd268a7ac2a5bc8c4bfd86390a3904a74665ddba3dfd94ea6f01974c73bf680c222c87441ddacea5e99699933aec397412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00aad0ae89082254c31537cc539b1b50824ef02c2ad4ccebf6249a3fff41c090c3b528ea21cc2fde49e752c60dd3e5dd6c7109bdad3724a30d2f27c00e4369fd04a7524a19e867aa1174ebf5cf819b028c4082bd6681f6366e25f0028d46bf54f17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b1663c46fd1a474ec26f75f2fb34ea44cc9bd074698ce8f02c48c1c0479144e00736d5e45c0fe7aff70245284bb8476a2923f0218da475179415b61d7e687101a7b6c6d65df9aee65c7a9324847a4217cfcdc24b57725d7c1d50a705482749867412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006c2df9b3ce1f8345efa1e7f4e92de6cf4b76aff0d965925d03bd9a114b8b60ceffd36435692e12491a162539e4475f02f8407fc30bf18cbbcb4b8a7dd9f89407a8926e96f23a2bed399246d8ddb5cfedfa56c03a0df12ccfec08034c524063877412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a409831fb01e8a7aaa48bbee2f314046093f5f7bbf1cc510323a8cbbfa2e5c72dfdd8fa8c7d54b624ca95eebabf5623703be2b4ff0eae2def3d697e5d8232600a8dde508bcc0842474451ed9139f53995833988e9af34a38f8a8a85191a4f3777412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004026b8e6f61fe3b73c2a4359d4fcb6a5ce82e3f648420bb44e7b28f9f84be30cf656c5efe67576cee7fced9d3c1b33a31745240429e3afa8f84574f57e3f8f00a9c90bcc71d596ac5329a84807fefa1e91cba5130aa4c33546dd47520418adef7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c8bb69ed9cf16c7d63c1133de20400b95611ace231f156aec556b839c7f2054247ec799ecc9ee9832aab669e971d599968d14d2a4d5a3fc46f8d98cf5eb18b0caaf08be2eae68fa730030691eeba70ca0f8962bdf030677624edff892cdc71917412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009c395e0314afa892062b4f1880508463b174c7137bc1fc7b0320fa02f038fa413cd1161de69dc8fc70477df285f6cc2fe89d43b056e1fd81b5b9a611adeb9e04ab215a352a0297936881d90af86cb6b6c4d4d37d3ceeca39af64ff5385a2a2d67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bc8fbb785617af1604b07b5766f89ca153bd9c8741ce6e53185bd6fc6eb66c7c770c35a2542257a8fd5063099bb87f9b86e5c2835e690f590e427a3a3493760fabe520e51cc414cfa7c66b0ecb74f8307014fe9bf73c88edde443eafbe4f5e947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004288422b51e29bcf53b1171f1ba95cfaf6cb6b49ebd24794a6cc54e7765c6e36f2f8ed0bf4175eb74076ad69042b2e7cef7f6e54980c42f225b1ee0bd91d2403ac25cbd2ac6262f60f1a32bbd658b3f5974bf0b6eaf4cbd2f9e7e90c3d8cce7f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00af6240b04aabc291ba04d4d71a70402cda8147e10e79c660e143e071eaed1b7ceb9c82ed11f0376b197fd2ecc1d0bf4f9756bbbffe212994736b2a48845fe00eac894a56d59cfc3f34ccd338efd2ca9566dc3b959f37234aa390801ccccbfa627412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00663e0405e1e361027d9a2e4d4be371635f3561308d93a14a31416fe892ad8ffc3fedc99aeef2902f2b86a3051d2f7ed6f7c3dc707f5d23e7c2820b5170a0780dace4d13893db4511557374af1882f8b8d859105e7ef8dd0121d4d5da9dd1587e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0000fae888c174ba5b883165b79f93fa563b159bff059ee0dbb321c27f2011a9ce98fe4a61e64ea128e8681520c3de32b6690a1bcadf122be31616cc4f076ed009ad02d319dd46887a6b101b85899941d6c53b50cfe860d6057d8e1135f9f49f5c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005e1b7914ec830b24f3f9c2418c0beee6b68fbffec74b4c79169636e27c8bb12917554b8ab9e948bb1dddaf047d523df375e976f1db0fa10e467c2a97e738ba07ad055482f247e14bb474ccb7d99d8010c7b3c8d2b26a64a213f5621b8818e6a27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002e16aea8323d7b8cd3a09ee9891b9196bddf8f6672196775c31a7cc4557dfe9122fd72f8a4a31e5c237e42e02822f9f0753acf4889003f677e7822bd73328b08ad10229fde173b0120795502716fbab5bc44eea02707798ba1f4b95c1a2bf6b27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007cf07b00f721a1c9de164ead55a0346f06369295d9c6a7deaf60344916ce746d3451653fb6b85de86e8b4e61bdbd7e764d08e8bb8e3e35dbec3b9b3883cc070eadb55f11cc68686f2d6b83a002d68f1d820ce67445494b8aa7ec19ef89ca1b5f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0048eb8e348daeb9032b21841ab86d8b7fed6f97383131b8369f59b206185f7f4060af4d929a6f0c4ab045c552ca98880c209e4e5f273a2f97340be8b8f8f3150aae37da60940e7919c60c3d3719aa0bad05f0116fe8f2e7ffdb24bf6ee22a76cc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cf893dbc065a2a7ba14dd2a1370a1c0d8a2e8a1c72720b9cb7760cdacc442bcc25fc1cdb339e7cbb789f7cf61fa1f804d9541bb04670e166aacad5981c9e090baee90dc5562fb2d5a966aa0037f4e0676df1cb4a055389c4b7f025bf58f5c6957412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00221dc00d11f2468b01077c04d436de9553624cb52b4b6498065acd17c21850aa9939074e2cfb9db16f42c142ba7a1788f17b79bbbefed8124f2a015f7358a40faf572a74dfbf8a41cd025961ba0b3544cae23c7e1c3ef4f27f06706e14ac115f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cc0e8aacb5406f7eb2f3c71f484d71d5c0c19f1fd2f62b686b659a4630a81a1c355b4652652e92273a1277eb119c089550e28c1a00ed552599e8e2c3d6cc8701af65bf47b48d0fc3e920b3d99db62c93698759c5aa467a612cd1fd30e2a91bb67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d081118606904b76382b13d39941cc68e2ae7aa4cb5e6f44a422218543565cfa278d373b37b3c6de953a759531b18aa835ba2e4f13e6a280783bc1ee125ce300afae4682dc8021cfd88cd9a6ad21e94d59a341fba9ab9a576ee26c37450a0c217412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00772688a769082c77c3001d9f4d67986141c683b4e7b204d674a4358420f6fbb552141d40d3b43f4a535b89c3f0c98e4cec315e26d3bc7ee42da7c7fda3bc890cb00eb77a5716be0feaa6fcd4c2ca1ee2f67d1f49284b6a6871746707b7269d1d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0012b41fea5358b318894f8a911a494270712389c94027a37955674d9c8ff142057c92b8775f96be98bf51610c0c5e4c3a1bb66a3dbb1f642890a1b63c89d69605b07108bc0c406bce6b6e3a6c511badbb94846970ea03c5a9571e6bba22105f227412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fa775bf7056774ad0ef12017e01bf7c67f2b40b3b9d57e5d83cffaeb5ebc76a16681a94324775c6125bf660ea28ae04fb7c92fbf81a74bc4dee0b6ceb5a1e407b0d09c25c58aba24de6fa8cc28468832b9c57ca423d37c214414590b9826c2af7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d5bace21bccbc9610dff2af1ea46ac2eff53cacfda10a0e5b8c26cc182128d912bb7507eafa559432a7e67eefb51b6a3df6ea15d855dda0ec23ed050d87e5102b0dcabff3c67fc93fcee2c59aa2c5543217fbb821bac73f0c48153914e49d0497412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e91049ee4debfd2adc9a28f4537b9d85a1a4bd254b8ce23a6483b37a2318854173921227073c3ec6bb43036e979491109eb29718389055cd64411a8349b12007b0e0061804de004dff8b16421345738af191e4e5fd82ade83509cc0ad97ff5df7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00838b58c36fbc601a374cabad9c64c921c34af1d72bbc334f0a2eea8b0f162fdfd69a0f8fe506f628d30946e2f834cfed94e7627849b64d0d17557bc0fb76d306b0f81e9c4c670b51db5b990c7cf1ab617f4325f0e49ca87a3d3d0276aace80fd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005ffe489ca340b0dd9386c7162e6e6037692dd33682dd51f0b874b6478ee17ac50a0f1a589517dd841ec1c17cb5c10eae2941e7e6c3298fcc42367f909e69dd04b115837b3eae48d82466dc106412a70b6f893c7799849fb6c136bd5d831cdeea7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00aec2ef664ee05b910a073340b85ad09f186f6bab65caf8dac9be8ee2e78a95dad763995bb7082bab77e7ae36a58235ed3822f721d58aadc5ac189b4d9dc8ed06b11fc43bd5e2b238c0d0337be8285193b001101c9a8a2110bbe35eeafc30ab8d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0095ac6674cec0ac1555fe0be6cd4caa3d046c5010d7da6057cb82aabc265468e834a4fa38b68d96131f7f5256966dce035e76b5d04462eb545d7e2f2920275e01b14fcef1f5bba5a60792c8ea3d3ebde60942b79c1d34eca9f789da14cea27c277412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002f34311285a871ce428d9d6531477edc53803cc51077bbf712128522c08903410acbf546d4ece34125458c607d925775028048a3631ea0a078710fd55b467105b2c0e57cfd006bc4a31e9d1ced78da3590d43ebcbd9179d6ae4ca50c2b84b4d67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006b5e8cf9ef75896f2843f1635dfb7053643df3c806e96d7db0f2dc56a0ca4df26350f7882c34ccc000ea681fb51704d43088f4f3119734fd490a64544aa80f0fb2d7b70cf3c888c7d97296dd813fe50083b95cb81d95260c94a2d629edb4e7a87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008c9a3bd1f3cfe8f2ac36316c065bb9ce6a9bbb412c1d58558383fcd7b0f16a887afa3633bf2c57a2e6ed2a54f5b5d479d9e29a11923a969e2f85643ae494f807b353b30799978fbcf275ab0af188be27355b9cedc94a7ec7bc76c0c32e07ac047412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a13f696b3bab6a70ed441ed461b93b5c3ef1e53a97b6cb7e159167755750fe2be763e3e4e82f1de40d6e88f426eee96da657b751c6b94f8291da62fd17309f0eb369e06fa9a8518d817273abb92adfa85cd0c3592f3bb68c53d32c51bc9571a97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003326a42afc72107140ca743fb74da4f323ea45043ea0c6ab76c9da3bc2feaa18e1882a6d5985bd5d0806ebe063a910bebd3197284c33ad983f1d4523b6c62508b3a3ad0b9fc2bacde1f590f1e1079f81204d7983a5d0eac972c480b3f203b9fe7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a774f69181881f06431d45e105e55421dd8fcf65cb2154283796feb76bc89091f364f84d22800e0cd64af0af41c0eb26ee5003e673ebe40d6a68687ea98f4c07b3d131d4391544a459854b4cc2fafc54184d49dfd1f48332edf97aa4b6d3e6e67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00600be8428c0f27a15759289cf625777322f89e08282f6129bf29cba6f1f1d54a9510ea9d692b7f6948e09b2e10ce0524f4677e59a03761129acd4943473d9105b448549ae4c0f9b3ac8a7f2a04a646d867dccbda656b80ec62c84768a0f3390e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008f76a50926418dd67109dfc4594074dff34ed36f42c0ec79584ea3d62f23bed78a2ab586e3a771eb2ace99047af46054f847b303c6c238d73ab6c36640aec307b4f1f98fb7fb551724f107664aa75fb9bc9ea5c94e6e0671843a3bc6a80869977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007199084190ff55500e33ae520e4e0dc0100e0ec365ae4c6cb4b7a11ece916acc2c034dde78b9c2348e5936bf9af21a286119b92f688cbd2f74124d103ff12b07b5305b4dc8972fec6a4eafe2977f2b80b6601d49672ce48a6f6c5bafa53f070e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00233ed20f964779360bdbab945838223422af2d7543ecb810d90935d1c53f4b20c3151103363c885209efc845f7090229c4c534924fb5ec449a5ba68a2ef04b03b74fb00fd2a352f99e02671c07f22de71298b3b5101f08a78ba2f438d77ffab97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000bb8c43b003a480281a026370a9cf5484106791c08616eb0918c21633af1fb6459999800c98d163efcc1e11245e45880808ad074c1cf065db253556767072601b78db05da773a4ec530bba4eaa9e40376547225f65ec87bd6dc793d9f6728fd87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000beb15b83b968a7d2137e71e38d8b5ff78e6fad7bd3c9c7da4d7399d18000ffbca6495947d33ee414e22909ff55f99fbc344cccf5eddf1820baf263945c9b80eb814f1a2c043f377d6221e57a551d0333d54c8f0b983a03611fb92c42c31bd547412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004aacd9642333248c38ec215d0a9a78ee94e952dd65f0c2c0b0d0b1f20eeccdc092efc81643a387202795018d17c115cc4f0f555b80b4541b5920727ec1ccea09b8479cde71991c00078532a159000e52525926e98520f98dd19f8e1bee5a211a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f4c64d713ec744b31437a5e3af31d9b7ae409661f46da65e023aec8bb03784fc2d6f789c931c522e0167230e231667fca2f5277797d0a781162580547f080b02b8d4589f88fad4eade69539a5edb61b5666fc9fbde60e91660ee8b5fa2f828cc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f38174122b3a171e524fa2fb641996d7a8100673da07c6e67c02d3d148b5c2b91f4b6adb823ac76ab72cd9735c408dda05a6775072bf02dced2bf9f09442ff0bb995396e80067ec48dac0597a14549f56c4ff0bafaee702a3254aa262c34ee8b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00eaa47febed1b0caedde6633ca3a7cd01b8d048b791b07c1788676e94df0d8136b3dfcd978d1db13aac20de4d2586f66d11a88d434e346017e806f885d524090cba0e04fc1845507fec83d480ba04d488b536cacb3ec02505d25a579db07543377412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fc70b9c98323d44558a5f5e8b1b0713ea17f801b526af04893ffa41105431adb44811aedcfdd6ec468afd28a49f46be28f1738b36ef85cec3b1cdd1a77a68b02ba731644e4df752af2b1fcd86dee7f5925f69c450f1f829f7d745741e782fac97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009d483beb072778fb47e118fa1851fc845539e1fce4b4e05cd25ffab335b36faeabb810409854f92b8b24caa91e117d434b04d54aa7c5cbdec3fb818c9fc1f60cbab9bcb14cb509e46e3bfff35ebfcbe4411e0d99625a41fd43aa4ea4cf09b5c17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e3ac214e0ceb710418d5db4b2d1f13ec97aa82bc7774d2f4c4169325db2f9921ae06b03f87e684f18b8c0f995450e6fafd77d7ed0925f28e267e5a3844afd606bb08b1df805d94ce82d4db0e2554afc6dc5769f0d742222a981f440e70ba21f47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00121d1b6864fae962b1d5cc0c786336cd0f689323e6e4fe0cb77933285d7ccc4d12386c5e9d2d43e6db7d02373645b0620ba31cda396973d6893491e76666aa07bb29100974b8ca522740fffabbffa21d2b9ff3a02187e2cb21a08aab2ac14b2e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0063c461511efcc8c6df1dde9d9043061a509b7197b036d58093ac4e599ec90b1640bb7a6c103095be85afe349a20790268f701db55e5b7944228dd0895dcb7b0ebb83f16032279139614340761befaf857f2ea2a198b1561790b5de31664ece517412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0000fc837ebc9484e2cf68c61aebcc12b973b51603b5042910459732f921baae061b5727a5ef3406fb13c982c3db415c5db4c40b71511d9d39940c06ca30888205bc1cd6a00cbaddaf3dcb9b86db199416b1b58fa5ac5f372e4b3d4347f4fb2f267412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0051183fb600ff04e06a607916157cb4ac3bce10b1642368a63ec72b4e79797b0d1028d2f57ef1dd933c122ac842e8f54b0afacd8d31c74192645858a9d3883409bc253d7765dc282f082b0c969a58facc0313c9fccbe37b60aea969fbc8418c0e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e34b2cc4176f4b077bbd0aa84218fc439e6638ac93d6eb00ac6532122896367ea8f8d2cd2312e55b38706b11a0973510e13a5324115465ab7c643acd6713370bbc565a620c9fa13a975f6b8b37a25c6eb1749776ed5b1c3308a935a83b4719f87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c0a54543086304184e848b23da1bbcca377eaf86de247ad8eda38886127ea82fa90b8ff6acac8af69ad549dae62c45d891fe755ccef9f5fa4cc8a8f52d0ebb0fbc9b4c9af1d5130035e8c3b3c7043d0a837af74472f9adb2afdbf602612bb5957412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0058f1e14614880fc263110b7c121ac228dc726c1f174be1d5928d7d9ae3ba8149c4e75e9d7fc7bdbe697128a700897ce20ba36b2b29453bef954295b70e761b0ebcbdc30eda2988dbc3fcf6aca2f92fe640864df1eb3fe89c1dfe7fc35aa417b57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0040abc1ab6555b314e9bc8723ed03fd69fef3fe82a8bb5f0c97865c2fb3c8911d229a8f0497f5338c18faa723457f5722c5f39aaeaaebadc21c96fa78c30e6b07be0a17bbe3dba128d996f6b057cf161fe47a899201f85ea14585406ff7f63a5e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0050e3ed05580cc539ee09477fe8cc928c35ad317d3a89e6ce58f810d2068be29b61750637c9c0905e1ee9b63cee879db02219c701c4453cf2bca69ec7c1b87204be6c659a264fa4283e80deef8725369d2191fc0c44b0ee199caba1c6223b7db27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f08ed60569fb5c0c97a6be3dcecf528a388b9d4425b8f66daafb77b900dd2453a9ec721b2c07a0c653598ecd3c0cbba4c89990391d7ed959910b7b6fe53b7600bf05ad6855fa0c2caeb606384e08af4b6885de5cbc5973937bf0ec9baabfe3c27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b077a7272ad9b483cbc21f01f6c9b46ba972a091f30d7b392c65e320ad266c214a0ff047c6be793dcaaec1ebda5076fa086beb314f9d8ba23a2ed312ad723b08bf2c5678423f2197fadcf2e7e1167e713aaa651f6d07edcd842d4c137328d2977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c5c86cf1c80a288494603b3abd7ca75cadecca2dd99b5ea1c3b8a8297f1fc3bffcba731c1794f0b624d3031f8189f369409f33c6dd3d8200bb218b9dd9636e02bfada9adeec1268accfa432231a6bb49699c038960fc5e75b2fef94759d6f0c57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001f785c932e76e9dfa1c8675af5a4276cec2b3367621cfc78afe17ace4ba3872a5cd5bce130c008de62a886b2b9f6f33419e6f0f78994b3a7d023e00febd19200bfb6804ca72cb4e32dc8bf8ad30a5defebb90fb79871bcaddd266028694413b67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00677a0d95fe459dd7f8876180293f7f76bb57f484eb340639c1999a0150a4efdb08e8eaea6e624c89174f99c9d85f1e983bce419ddcb13f3d4977600655f5a60bc00699fc0c1712b9b73f06e32f7adc6020a92bebb50268c14547704477319fb17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006f751233f892821af2ddbcd738a01255c927addc0ad8f5f8b10219227ef827cc0e252f6ec91a85bdf5a661cb7d91be5358a81709317a03d7393033769a23470ec09ee215905d9b3796467164ba1c1d8d7a8df228f003d528b2c5b6da6940c7327412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00bdfcdf9b72841f84ea99f664681869b81c4630d8caccb042980d436373cf79578aeeeb241f0618bdc99bb69189528190432b736912e996b7a312c057bfff5d06c0a1faac2c3fce942577d07dd7113b58e146149f627f25c86a526ce64d9d002c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007b9d8e6356e982130854216cc0f19245703e4109275e30b1c41abee119a802b94893d138fc1abafe676c32a1105ec6f6dc1e7a3caeea427c9c6f5c0d361ddb02c0c8b899b2fb3daf9a7f6b71e887141a92da1f473a3d63790d28a25c30f9799c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d84513f8a07af8653d68897526edfedd38ea8f1429f3d6604bb752aaad0d26ee7dcc1fc8395ed7c3e3be062ce92f6848c1bc80f0e356ee5599785ed41e390c04c13ec46e6e4c05622b2103c215c7814e48afbb789362765c709b9afa366e39aa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fe65f80474ce152e8066b687be7a24e8ead0150b110917e859c79dd39a4056aefca87b22a58b96192be770b8665b8d7360408482e7d0341077be2f972ed4080ac2408e30d9a4867b4f45eedc81c5550db900086af0fb6501d6786e9082c936437412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ccfe58ed09fc30045d0ab8511e554d3c87a6190c82c01af820511666a0524586e90b30ec2dd587782b3f509ca3a75b137e197c99180a8e30c893612f414aea02c25069b7d51e3077be57bc11db23800b79c972056e5589c54a0b1b0509713a5c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f9d39d7f45f0acb6d596224dbc5268dc92c674344eea6b0965b66f8aeda0416856408736c7c5223c8e418a2f20ccac6f98b3207677db7cf7255956abd85a430dc277f631f7215a61fee7dc682c6962b40226369a9fa68d04fd1b5d0ffca668a47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00544c246eb0341e91ff0d12cf7758bdbda797ee64e79b351a42635383ad07c24b71938fe161d88d6c4f39470581afd240d004deac2b8daa8980213ed9f9d14900c45b5870f33be25386a110b8178ddba4c073bbe1ae9ceb277515bb3837916a827412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0073ab21ca37f38b248df433df4e9146c9d73f2cca6a690a1d001898a3c8159e890c8d085fdf4d194c43c7c4154eb5fda133a50422fd87fa6c40f53fe71740440ec4c6cf5e15dbcaf804ebb370b3f34d6c88317c199870d5793f84ff14198f6f647412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006d680f67f1f88bb407308cce55da3ad66ff224f480df9dd57710ee9f8479fac1d904230f93f8d7c781b6e551d39fd04f0f5f89761b851d297d2bf97953c66201c4ea85b552adc87d10d1dd976a6aeec6e07a1f22127a846e8d448b2cc93d10e87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00919f32dffeb050919a11f05b358375778975535f159518c9b23b32694c39893c6c215b078eec546eb903d9de693e6a9b81d1548c0725bbcad7bb6112b87df000c511ee786680f69643a8219504d27d4b1db66d53a67403cf4591e2aab3b099827412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000dcca38df3df830efe34e80cd33d7c0562ba25e31c5e3054fd82b45cbc1df9554a43b58d4486e41865a53011fa3b60bb973e074c6be9a9a1d7b1cd6041702505c5bbfd947059a580002d769bd6c0b8c4095a11b9451710674753b25a88f3b5f57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c9b0928cfc8852ccf7f8e8f916698d71fbce90ec948230bb4a1282e6787c7bc2835280a22b3121425deefd1ccc0fb9ea19530cf15fc09622939dfcd28317200cc6691c3cf5eebd250d01013b93d5d4dd6e406c38ea6a6966fc4d2a34956c4c4a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006a160b012ccd548cd52050adfa2deb91a4f9fca2dc0ca08ef2b1df506b2055913ec43ff256acb5c24b4b6c60c696d148f734e4d4bb1f97b2668f5f9e427bea0ac69c025f13a202d8e5fff9b0b209eb185b6ded25593530eae893c83036341aae7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009992acc32a3d31569479776320ec83b4b6453d3b7263fde6c8858db70e54644ce6dc5590b25372a1248bd51ce5dddaf288cc9bb8f06d87aad47157c7be352205c6c7d5c50023bc108ec8af8ec260715d19bc72448527ef4bbe7f11e3d877df547412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e4bc9fd4bdad6bce02867fe462232b64ed7dde386d33003a0cbf86817612ecc90288117d8029fc6ea74c92ae403f6c2a658d7ba4fb320d0fd25cd88a6f35f504c6f0c0135aa15a67f14bd670ca877085c7f263f811cff7db248bb130d34a48707412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0068566da842abed5e04b1f09df38354932b2b2651808384bcc060cf9fd228226c9a66999b4d6de7d7adf0f954205d5339f7c786577d9afa49450517a77fa79503c80b0c7d6df417f1fba2e532590cdd2a98a7bc0e418ccd5f326dfb0b89c354647412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f74d0bf72b4f2a95503e93a9403f1b20e76e16593e483507698488d839a72729d65863ee3ba67bf1307e78addaa5cb3e2830cb17960b782aa7806044be04410ac84a5643fe7a66dfa5133d9fac89d58dfd37b4d3856cdce379bbb1a4f3cee5a37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a424810db5c3f1e7b5d09284a9fd29e5a368a921991930fb46952908b140d9241cc911124f5d8df70273046ebf1412f79b6346448db0271673851e34280d0700c8d5cfbca144fc4aac29dde930f9b372c61d3d1efbc1496dcd563e20c45a280d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004cb200fb20700b3a9578b49a988ed38c5e563f58e1a741d2ae29e92a381abfc2bbebd9f32e4d6e4fa8522ff48a74bfc51dc775835e166457c0bbcf5741cd4501c98dc6754b2cb297986bec40c51907a9a7fda0d2ac808a03e4cf36d2e7986e567412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d73c08c17bcacfdfd0e1c37172770c2aa03fc0b7fc8fe4e08eb16d7aa0db990d162c3d577f9645601dd996029bdae829cb353c98219430fb392bd2f640daa303ca01f68ec2720c5d4aca9b9120ef2b3158b6435596a11adff45f4348aae1f7657412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a23f31716b06726b91e4c76f440d2787360a9ea5cb551a9f1a4badfd1a2d7690b60822a6aeea74b6d89264b0de0e92fa81007af3bd17a1728926de5ea188150eca347a9db580cbb32128af8a26fb5724a21ec7547cd004d231233220658433507412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f100e9a5e32af65be88e4d3bc64a72ccac82dd00eb2a53f5bcc6bfdaa5d77b8d3a05d6343dea97b406a84c0f21c88a156b28e05efb9480f91f6604ae780e20eca77e7a167aa17939810ff2e6e5e4c66b997b214b8ad41ccbba8be7c202481e37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00dce74ce7486c6b0c0a7a27a505a97cd12bd4fc2cce662c0968b62df82d72a32f3afd5dfb82f9b8933c8f355e518700b8f51646d5175f7b03260c86179d9f800fcb023376af50df3b6d8ad5b61ba232c815a91105e3f73e945678f248226a8c1e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004389cbb47aefea9df3e57e16128644808e81bbf8a3121827280c768effabe95958b7bc81c6d5a07b7101d1fb49bc2cf9d5a3e9fc5aee569d1bf979969a96570bcb092febdd11ff3b9077cf19177f9c8494898bd36f4ce900d21ecd2d73c9330e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ebc7b1994bf686714b826cdee65e3b7c88ec37a35f604ae9e1fd3eb57b9a8d422aa1a28d832436794983818b1df0ccfebe9aa4d5b6b1b1325e19b15088b01b02cb29686d490f5cdffecd907fd6b189317ebaa1cf01b916e4eb86e340ee3da01c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001562923f6bac6917ba6ac99a737d83c5ccc0f643ba58e0eebe974d9ec86c1be50bc19ec012a63494e97d8be706f9f31315099121fde295e0b512beb0e235d400cc3b077b2c20246f3b5a658ca418502818cc833c7e07c8c0677caea580dd45967412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00855df22e5713845b812e3ba971564e7901b27406490355a446e6f40daacbcc89301d7475fdd01f23e6729afa653e85458cbd0ada60a6d267de9b73775b9e4203cc45569e5ec31f0d258c9a323de87bafd9100d0c3deb05a203ad74ddf2690d907412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002ac033e7a83a447f05374887fcae947675ba0d5a3cfaade158305b0d9b87493017857cdb190b9eaa2f3dacba38a743d6f8af740bb7697d69ba0b70bda0a61c08cd9b00dadbfdb24477e9a9bac83a783861e262651ec28c3d027b8c399b2017bd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0049b9cd134b46303aa6f946133bbbf5c3e01bf8ceddee77cb9cd9ccfb769d57388d2e32f65c5cb78ab9a8465591f041eeab8912fdba9558147201b09a060c810fcde31382e67335289f8f62f08e3ce2acc022abda0ebef80fdf7351028354d7ed7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ce7efcb07aa252288aeb007d9590ee4eea766d752113119ab038b7c804d1d0dc90a96d033ad21c6ec58c2595f23219f34e8ae338d1345acf8e995331077cde06cdef6778461a36475a54b64d3c68035a18dde6c13ad5c68b71a45dbccffc00f97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006a99fa36ad201662836ac55c7708a3b87d4f74a0eca438e0d4bc185a238151c61c920eb3997f235a2dafc01faa2b612e16b652acef2ca367dc4795ac72cb8f0ed032999ac80ad859c086354dea8cf45b1ffb569225d5724cb08cd7fba990f5217412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001496d1093018767fba010425b31e4a1b996963f8590c43135d6e26285b6c17bf29f771a0cc8c9f235620b9314f6c1fb933ba3b4b9110665923deda915e012a09d04747076d8c7140d383e50d6d0eb20628e0ede74562189a934fe8fccb7a88d07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a3d0bbfd263bb94805ced55635dfcce6a05354cc66a1473fe02ac25f1636b2d7a0afa2e279249b7fa6f81071e610959ba456b0aa8f2a27a9a60eb2e72c57e706d047c90d446415dd16e5cc315b4c87c306f1db47ff592a5ac35ca4bea7dc9d477412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0034387a05e9c9f20232381caa10ad5df297010e44d921cfefe6bbd8281889c809f88b4f5e59797e45f4acf7ac9f998e2a2f2df00076522e1b5c4bba258bf50702d08d7c4133b81aa7151ed03d636786af200d38ea0e8de42e5eb1196065a6b28b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00687119a5bbc05c7b796bf27b3ed1ec303f8f01f4ff49536b6a0ede3f6dedc4a039d848c2031e35fac2d5cf1162a17a56de29248a903a3e85b6b2a43e62618d00d0ee6f6c9eaf4d7d07bbc73f5174b9e1be17bb7acffd7e4e163b9bef36f89c987412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b6979302b05087f1129394a76d5e0e51cdd04352e3219497caf7e1f45e0a04cfd15239e4be234a17a38f54b8f896a40acdff597c9e8a451853e52a6ba2ee6a0cd0eea5e4c798a264c2eb4d88a52f4ef92cd8ea328892f6f2ed4696759cbb44157412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ddcf9e9219c4543e8ca35039b2cbeb45cd41d2547d9d76a42d9502d5528b111448a244eab4495ddfcaea081da67dcf44b5b297b59c0ad3838fe662dc7ed3190fd129795011ae7ec4f692d23281f4dc5a9b77224404a2d7129ec17026b23825cf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0046540f2ce40b24855c4ee7c1dd12929feec2c00aca012fb789b6ee2befeefd9c5d0ea34655429ac0c81e567c92a9b5179b741784fa12f7ec3d68ce747cc28309d1388eec35655b3b51f0154bebef738858199df35486142fda47b2dd3b4c1cb37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0071b51bb3ab57f3c6d4132d8a9633c8185c9813e89088b37095b9375410e0cc7996893fa86582abd112efffdbbb64d8d5ac7057aa2d6601580811c3719ee4ce02d1d5ea16074e9141839bb433b2559732caf4d3e93da77f9b99114be98611325e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00918ee9ec49c8724f1cde48fe8d4c2a05a57e48e4a91fe02bc9734bf23e2541c6a3ba1bfdefdd0eda648a9fcf6fba5c2ede796827d3fecd1668ae149a811b4501d27d8804ded5971b644f63672db1cfd92402425f8c1c986b64fe19a9a3ac19647412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f4320e3b33a0972723317cce6e18e60d92347dffcb2ded1358a5fc8c5bba00f1d326d82aa242c307541e3ec3a2c283a3180dec51dc47dbb83b54148fbea0180bd377934593854d84cdced03f3115f754bf52bbc1729545258003dc9401dbf8367412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b1afd887b2e782925e0dae9022c3d90363161780c7467f169b5025b62fba61cb3029920ecb1757769a084e3fd4e3daaa42075cc04242eeb58515d2f8df1b630dd3e23f291216fb4e48d17ba0c7ac0acdf1867e0a20196fb27edfdc8b6e0304627412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e04c1f137f4fef2e15e4f15cf589856572aad795ab3c6ab4295345369d0e6908ecfba82a52e4f631d466bb195dc8cdbd9ac962e8b26573d1ac36ec4d804b5f0cd4152846e2064ec57aaa20576c0944f49caa910e9cbd3760285a13d1629291e17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0071b468775fae15f5fa4f6bd01eba82d0f51a54a70fb17c4f0a57d3042147ed31b73949a892b7f621b041a92a51eba47ec918b91dea8f995b6b1530bd704b5900d434cfc66ad9e8fe9f10d01fddb34bfe1323bd3bb19403787235034dcbecf1fc7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0000407995a20f931d8aa063b034fdeebf91e1d650388ecf0f3ddf3b5d167533416dde296f938c9b49216e53b22970fbaaa2df13069ae8c4c1f0ffc5a6e79ed90cd481cef7cc85810cefc53981ba07c28dcfb89d10573681f9c421bbb9486549da7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e55129e18a68c8125c60f27478651a70d943c70ab50bfefc419bd97c97b1933d95293d4fd138d65d9d6448a7c4bd830bb51f7713c9a446b63502ccdfd9a3f404d4d372bb2122dce38506e0dc3ad12189a4f4c0087315a74a9e75fea1e35b3b397412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00974ee37fa103f894a7bbf583c57956340a4f9a0e97756669a7ad0eecc16dff17b23a5fef51844ea1ae7ba2614caa3205fd3e1b728f8973b60c45cd4292695f04d5183907d381837e1ff361e4ef60d90144e04829cf13e71a099aa29a5e913f027412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006fa1edcead87850a912a859cb7ac7e8e1bd596fbba1debd46cc2e09b46efd7323096b53a61b55d547d8d8a83dbd2bc72e2de95f96b337f69b7e51e300ed4ca0cd5750412284dcb0b01f31c7dea726e73bcebf87829c68ee1b0fde883ee39f8af7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a7b9c47b54c6cafd4cb7e0e80a50d652befd2c696722546f1b77360ec5cb1b7dd280ffabab3d49c221d8328334cf21c94b9779169fbae395e3fb95e159903a0ed592f6236034cfdc773acef60e1160c61c19f589c9a488a2bd91d19f411831ad7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004f5e993dda650bc9947ba41c91bbf9a576193d1070b850172a07dfa1fd3441eae06fa8c51929c90740ed7516229c765848570ca292ff7d56377cf24970336000d674a87c3248100ba574ce340304ddde06f724b9c79eb7cec69cb2535d15c8cf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000d19c8bb92dbf83798dd8e4a0b1189427e8f2def654055860cc3297b6f6a31ac76a7ce3529bc7994e6a8dbca94fee29af70453e8ae76d7aa48d3a73a645b4407d7e39aa06109955ed5f814180d37907d6a630c9f1021449a1619fbd607dd7f7c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f7ce90c80995e02966d1d616e81fc76f7752a12538bbca3f2aea4c168f3b21cf7aea29ac6b48812e783f8a6e36032ad22db72f6b462cc1ada997d17ca1249e0cd88de450d4adb9af19f1fb2bbcb7183fd94c148cbdd493838c26bbd166aeb5467412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0056aa85d8f61678aa55c6fef18ed99468119d53cd711396e809b49a68ced8fb6829289019df35b06ef71f81d63014446b839a15676b742177bb3771ea066a0805d8a9f28359c127e5f3768c6b2441ad65bd49871d9c3c10aabfc2413b54c705a37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b6caf5dca993eb9de739f6212cdc9a81e76b05555e7a34b61c05ab20741bd1e3d992df1f1488c55dc02d4bd549b8fc4a5843907f5c4948e242e277a3a08f9800d94b0051ab8a10af3d0e6f3dc2cb2f77823f603a57284cb009cd6813db99a6857412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00747dc73d02b55f745aee12e2e2feca9df63794a1712534fe000397d6a929c04eeae2c7c0d9337cd49c8abbbd32947f7e26b1d75531088c1f275bd1211226a90fd961ca15183c5dac08e844378fb1ee30f0f33390af4e4807569dd1a9bf3043f87412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00741032ae6a5c17ba8497ac6527126e1a45ec0b7f517c6e11b08be5e5058ccc424ae4fc32f3da26f1d38ebbda90210f183e4c0f1b3b3877d41140be859c40d10eda41b74281cf2c965b58ac92e9b1e4310063156fecdd6bb652efb6b8affe5b537412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0074610394479a34e423a338be4f66e26e23a3a8b31df333332c8163fad669423e6099ccd2b6de662739a15c875f4cda3c7cf2e032065852ad364d4864cafdfc01da5b6360b587c48b3cd3ea07b46003c5db14a8742651170b9525e138d54332d07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c33efbdf689c3454edcf74858dd5484f3088c9034c2189b35ed83505011ffddbd30c135030bd940481f288aaa54915eac7c772ccd7cbdddda1d06cfc18e4c604db7dd8256c43c726b27dc6fc8feaa6618d1097ccf964350297b51f26dfa7898b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002f9c27025cf713141e9ea4411636ceb665285ac936fefd84112abd7f6b66decee6679780d463d11e66c46853c1fe8d41a1482e4c7d9a487b0e80f492ce215300dc04544011294afc730c2308eec8777836ea4d719e01488d010ff8a61fc09d507412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d444a1649277050872b414beaf156921eacfa61fa3a1a10bd5df7b3eb0a9a38879fb969054714887e2cc81e63c889455e0856b7d38d7fa31534c3bd44ff22802dc5807c8459f4d66844532b036894fe6604540a850481a71404a6b2e0114df247412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e38bc531c300fe050e49bee95c4f5168e34d388ae04fce68bafcb280b10e04c212e050fba8c67dddad24a0add0b2dcb8d730744ee1e184a13fc14513735ed503dc73d3e546b98bee82a3cf1e72e3060978bc68d47e75436f042b9b17b3e2de0c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00adfd9fc02b2c91e916fdc3f32c630ce6e6842717f4881334145da4466a4986f5fce42904160b129e0bb669d1feb38d73d79adfcc78596aa3ddcdc2e6a12d640fdc9defb6659a54599c2ab0e88579c709a3d0d2bd9fb656f6f04e284c156336d47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d451a4832f32cd7d73792ed8aee926ebbea2876a6272506fa7a51ae491ee034c0e289d16285b331356c37d44227c3b45276f4fbbecd079432215bd1e3d610402dd433203012445ca6e04d2b9eae7ae6d83b70a3371ea1883b5225072c52520b67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00499938cfbdbc2917272fd20c3d45f5724810b9cf3f5b0cf208355c87cccc32c84efadb15cbf09472d889fb3be1c1646ba24fe3300478a74d37bfa1936eea0b05dd9539b6b8cd6d77844d27953c7d61fa6b1640be55598cd7d2968fabd16a169c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0005f6c4e8080c63701df8762bb9d340e19968ba63d13d0bf956e78a5eea0764478c1fc4f5d3d8f9f5d27cfadcd2f7cebf872331677b3d9c953429a682d0c3e905de918a1920a8559e3263eaee7087f111a7d36e2957e496716a4e57768327da4c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00333919a34df23a0291d75f71d0b39e4986cdfc4b2a9fa3bd1fd22fe5149dc38c249df38f52f7791364d516d85c7ea224edb925ab25dbb6386bdca01f523f190ddf84d54a2a9b1ee1235cf8c9d2bcda4d9f58755edde9cc9f472efc7ca7ebc3697412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0012cc886bd324a570e4dda89207d86f01e17efe8d23adbd0b4026c05de97b32f96160a335b7c325a680452ec949e691133a14ad25b11cce1461629551d35cf90ddff8d1fad896452d619084d0a1629ee7e9c54c87b826fb4d8c7d4e52b88cdaf67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00fe51877d386e927c113bd396a9d089fc1c7adebfeabbfb23e415c4b048c8c623a2f789f422ab3ff56b66be4626a4506331917996dc1d858992c4c73e97e5840fe06501dc67333d2f3c72c87dcf1cdff591f0e201d6367a5fe98b92006bf9a43d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f111277d0e728959532b6a96dd5b6d775984133a3b1ff23849187af5769f2f5a50c97f8ec844eae9460f51ad6e3ef8f1173c18fca536eed47e35da411832f07e0845a51f4cb541635d90c6204f9740f9cc6373cefecc3f839f2c587cb314add7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007e87434f5514bed387f074bbad96343a12befe583dae0b5f337bd28c73c0c870821d094fa12953e6a0a1c5943482eda7b535d9a811e71145838cf45aa52fcd0ee099e41ca65898f4d983843c0fe9bf50514522f8308cfeaea99a80a51613fc8d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005a90f4cca3d3ab8b28780d02e7e3fb8f18032002e042692918a263d0bb44af3b6c0f2bb1fffff44610e906a499bfce17a7ab75e567fa4f31eaf473ca3698530ee172cef11edd7b5841173be146448fa7e8dc782ec8390393cc598e0ca727d6357412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0022dbc13c851b82c68e7db5966d14063f1aa292d8831166b0330e5b6be4f8628d0dbf10c24d546cd76666aa3c061d2d3ced1b4245f9194b6cdf82322a25cbf707e2ba59fd54c53fef7c9455a1e5d84eed99ad9dc3530995b7b661c3e25d2180e47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b1f437789373b31aa3d5a13fdf213f32dcb085f3a07982f86d01b2eecfdc4adcef93cb742c010f1c5048448f12fc4bb77358f7d1e4c6c2ec57cd06a33df21205e2c55a6d7c2693ab9f4bfd9fe94856fee68ee8a62c7a8716f237a1cabdd769497412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0087882002823ed8eada0126795dd74c66f2611600fb024a75bd463f846711a33157075a36e6eddb570e17cc3d4d2d456974871c4630e0a65fd53dfaea5b9eec07e44893830b8e8cbccc43b2b5683754aa186a00787b865e4112eef42306b02e977412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a003159ab1b90d6b8286aabc2baca573412797a4968df9bde25886eb3170a87c43082bcd1c6723236c21a5159ceec7f5697dcf63feb2ca6954a71e8b67b0e1c480ee48e7ba1545973cf8bb9ab3ac21e850ff0b50e22d5cdf9cfd3b6f38721d4ca677412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002d49bcdb0c668d6c5982eece65bc4cb46efd1fc992fa9b9e337abe929006f8d71c5ea1609f39caa28a16d9ff7e8cb18dd2d34d52b42db8388d3f7c0d42563c0ee4932f43c1be02f87c1774b1fc5eb9b320a0d34c48e145d6e472714fcfcb0d507412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006fe142108dd1bcd04ce4695ae96670c737b421af28f5307d870ca1f74d3064e1abae03982a010fca3ab6645b4f926ee6fbc61e603f74b2896a5772843cf78400e4cf3427bd73002d54d30cfd09550e852c1c2869a31dd2d16b58aa92eef32de17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a001b0e936e8698a68ee8ee554e9eb7379910597c9297825f24e9ff369366ba2b2a47b498aedd8eee162dd67c0c0eb7a84cedc57a6ca4ae8c28321cc462734bb10fe565d348643bb1e32ad984699ed67392a33486735759e5c61b274bf24e1f00e27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009db5d0426cdae916a2a1ecd609149cea62764090d575cdd0a2c28dcbb579abbbd259f5a889e115c11bee2f14fb8b42a418d4fc0b4aa7bb941d0b40d5d8de2209e609103ca434abd43c1b17cb1253eb54e65364eb0a1ac7749433dbcb07f3d69f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009544e2b43f5c81cac9b27124bed0e50947a3b89bd8712896733cea6d3c0a85bb1f9e36d18061f6106ad80c79fc7a81da088b476a3ee0a504f52912c66a30f90ee64b3e77804b1e4cae39c8956c3c976f3886ee1ccb1e89a62fa129a9ffae0c3a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009c6cc110989084545e757b4786f02a8992b1d98df3a62275de4f84ccd22e7c33d82b76fd99eadc90b3fc12115cd46e8a686bc64a22cd46405fcd28be065ac907e6c11e5c80f99cba5744104d4756629a7974fc9db8c5941518dcdc963ae3fe7d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a002560e10c0201ad7214c458d2d175525db66c0b419c5b1ecf0a25a2f31a421cd4f3fda1116bcc352177b6a32a744d6790b7e8ac9920bc7cf69cbc2db14a0ded0ce6d59d33c825486645c8dc4bb792b628853ffc1db37971055ae122a2513f22bf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f49b4185b2e88d11439a363fe252f2965e80185e7e68287620f63a8557ff8815ae7d18dcd4c0589a780b183bef4ddedb91d63108dc27e5ca0c47e0006b10660de6f40610dfa6a569e46eeef7c7cd768e412f13d4e4a6407f9a1cf049a352b68e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d7423428ca8937a361441267cc2a95d46575808a116897bf986884ce1824a535ff4d15f5c66b57ecbda671958d81230b02775eba7b9f110dd1bddc6123ba400ce6fdd70d2a5afd9d969ea9187de95fd9c401b8005bc5bda61abb67547722be567412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c3a63fd4607f4e66c4cfa3eddd92af996d43e835530413a86e0af6eb668fdff4431d2d6ef6d51d5ef1049a12deda478562b48acb73cb69e639b37b85f4802105e71935c38e10af9d9986dc348d6b8601a533e339471c23b79f7e57bd941ae8e47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d508d720449ad89bd9f446423f13495b1beb6b9afff9e7640892f47fff4a4a259dc3f2a636b6f0653315695009a6f201917a4f7b7d252339281e3ea8acd61e01e759b907acca962ba6ef4d342117a10e66fbd9dcc479f175b3c03ba261e1f47b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0023fd1bbef21b7bc5416333cdccd936905f6494391ce2cd8f41ec7835a794a00069f130e28800e1769f58120345e445f050571e6bd1b9d62c2d6970b2cf67580be7df92ee768270097ba6aa61a548b7a4460ac6d35c2601de292b7245c6310a757412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00231e2839d3cb11212909dcaef14444ca0f36abdb6da2575e3b4abaa780abf34dc96a0bfcbd12b187ac0639382421e3b063620fb398bb6445b0006109f591190ae8856888f6a3c4ae92f76adadc496e622705f517747f6a4219c53e12642981a57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b0111fc095c0f16185bf0692ea456d1655983446dfef2d36806713e490308cac19174fb7948e6e0dda403927a3027084004c7ad4a76372bf9956558ab7703d08e8a08af0c91de30804a2b5adad71db6ec62846b8c523633e81ad14bdb07afa677412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a004af3180c243488c80ab4384341c3d9096a6515dce3637b96ab7358066733cd87fdef76872d1c9e37398e743ed8a44b23739019daa8e90b97055caac788b54c00e8d4a2da4eb2c3c37f940b26c6e43cd8432b0ab3a088f593989098a4680bcff47412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d7cc78212e82f649ca84a98a0328fde9df599f7e23430f3f1d070565a77213519f5c7b690e2889c7e58db49100dfabff2da624ea8b5585ebd6a7131a79f40404e8dcf49e49e9883c217bad75b46ebf05186f46c65b50f89fcf4f991c3032389f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008c3c977d15be013cb8516c6d66d05d3086b11a416181c643ba72087a2d2b4d5538df1e5653c25d59423850eea01d0b7da3772fb2ad87646e37bebfb99347730de9fc035b14e568bdedf2bcad9d7a78126880a580a8f853934a9b87e5cf8c29117412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00cea3703595c740a4a8c0fe552836ad57d6f6fdb75bf484820a7f45f16abf3edd89704c2f765322ff7d6d6b42d0ec40f1d0ca089b8ccfd5c197382f2ac9fa0304ea5884e0167645f54813fcc7747363594019151c4f52fcc3dea93dbbc9cd6fc57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00306eb86fac03e4e213416d9e50f4119aee50f23430574f7991bf73c019316479dc5d7518be89f2f54eafcd50ddd01ab6629adf59b586a0830ff71b42f0518107ea6e33767f007f5e63cccf0daefa1b528c2ae000809fc2f42847e34ae81371c67412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a881aec443e61d67a4800258962d67666d440f5f3866620318f1e2a76ca1945d74a14d08693c99c2d5bb0d52f510afece2d8e05b87c79ec4caff896be24cf804eae0b7d0e15435fb7b4f2de75aae3ec3badf4aa4c908a85af073776805f067687412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0046abe4e8a18df7f5c8c84f7f7d2fef5a5c9987480b1bd8dd9b711043672a96ef28ef8ea8fe6dce4a7890cff83418b7e12d42785c73331490b81b60cdec0f2c02eb7e19dcf651d628d972b91b10ee8a95c3aad5fd0364febec3995df77292e03e7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0036d27cd5455fa48fb01297f10cb75bbbea8c279adc990aaa44112e2a712541776dbfc9eca3c6298f8511892dafe5266268285f050e990ea4d5e6dc1b9da62c0aebefd3256199b8001a82fb9456eba66a055843a29d9e9d449feff5a158c8c7cf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00aed61371ecd8abb8297bf1d960e9519f6abeb25b8d29f6f19b32711f84f23d0d56cceed4664408752097f5663dd75f1e954fefe4d54b19186d047210d1ee0b0eec0d9939257652084199ba0e9a366a95562d38f391d34b4ae4fb3cb224d558db7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007a1a291fe545e06a17669dde5ad1bd5d8638430a7aa4f094ea47ec7543c73ff1fc10b2d89f3f5bf37120ee2c92df9a001cbb039ed0faf7431c3d98b4d50b8b04ed0fb06e61b4c5c4a0cd1b113c4343bae101d5cf8326c5c42e6a841fb18006f27412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0009e4027ee387f786c0817a92cc9a947fb9b19b30d6c36b29e9c6921f6a1ad0b9b0a6c6026deb8c149118ac934c163c2990c1f950045f9398e330163068de680aed848689945dd829a0e36d67f038e0a16286e531c90214d036dbab13d1c841fd7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00ef50b46c9cfe2579ee395e335e0da639e9f919bd98550a11387502f0316bc1d53f5217656161bf73eb3e99e12dc7752eac6544643b3192ca8a3bea6a46431804ed9184c7e71882631b1f1114b2d86452231851671f7114290fe27c439f3f68b17412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009e1d9ac6231367a6a4c24b2ef81ee3d99a7e66e02b6c36d50682bc1aa0da0a2a3111a877c2937607c3594441557ec3064578414ce4769dbaacf1fe0070c2df0ded947414e09dacd79484396c98b2f4f64c6d4d99347cb4909d46c5e47f33f6a57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a007e98e9fbc59abd6aaba61fde0bb2c5ff8cb753e9f2b6db8e2f89f988013070a0206326517701dbb58b019998a00c5ada0d8caedc1887bc69f17a6d5a9e860201eece43f1649c53f7b4ab57fbbf451458be4030ca9ce929e1c4beb3e4d011034d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00323e88ef9a18595e0c118bdc74e3568388ca29fdd551489886db0e268b8b7a724dba1c57b3de9c053e65a954c8942ff9169379784797b03b5a01fb602068b90ff00723b7fccbd08677e37963ac30411f408b3dbb5e8c43179ec3b6df36a44aeb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b8e5cc2222e3e6b09364471a19e37577c4fec7021ce585b14cc8849fa2cf2db628f96332ef4a4a2b5e8945b41979746d9bbbc5d8e8c404dabd7c72996873b20cf043d8f26dc0ac211466f2073d32bd7b4b1125783e98ddb037d7947571719acb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008a184527c62cd09d99da175799732c09708112e720d77397767f3921504774105e2812b3ee3aec23ce82415fea1ddea3ab29732cca1e3f4e92e40f5382c0ca02f07a7954f65339317f812d09046484a73ebb6ed518776df5c47d1b9553d39c0a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0059ef423fbe078f8d09048c6d3d115761ee710833fb5928bd2443275b1676e73cb40203f64e1b3a064b75e7476f478a8875ba54d3f5bab7e32aea5ab1c8c9220ff07dc64178894ad2415e43a17f8c097259c254e0460b142eee801a205ca30c6c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009c2c89c92fdc9f4b06d9638df55a637b1e9fa06e41044efdc715b9b8c0fb094773ee9b473d2dce8be8aad7a22954210dc490d7df19bc9ab6f55f6140118a730cf109dcb4456390725f2ad7a8f6829194a075b5d7fb7c715aca6bbebbbc25332d7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a174d79d79f2814af01ad9f8685fdac00d2a928129d3d03f71b5470438f4a759e71281102935b34c97023972b679a4982ae8e902164dcbe8fcb5e43c37a1bf02f145c30be39bfa9e9d0bb9f006f0444130144b06d5573601771a568d9a61b8297412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0024a69c087cd8f6147ed6ec2f309c54b1fef3c956564144014dadbe5e52024d7b3921d67bc65f886f6d336e3f8de9e28a0edb3d4bf4523a579e63cdc5819c660ff183fc5dd4c28b39022847996ae945b780c1e7ae7e3803781dccc53ec1ed26eb7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00477a579c33b7cbc8de78c60aa6a69a8a8917f6e7c5edb295bcab0d31aa3cb7ebf8eb4d43c7d47d7abad4fcfd40a0d4d583f6456471020e888b2f2665d8181a0cf1ac1a9ea982933fd5189dcfefb7384457afdc2580ae7007c83537ed780b8a0f7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b3ae42b14aad6e26cb3f38a7180b84320fa2e1b28ad105405b37160f5e010bd6c97a129137eff0386ebc441393f6748eea3c65ba409218e676999fa7ab40f801f2158c754d7297bc9d65b6e85962e563aff44f2fbbdf0da76c64b35ad9ff7e487412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00729aed8905fc601e4742c6b1541d20cca6c4dddb1aaa6731c1f89f0bb21d6b18d4cc2dbcd07b3cdf39ebe35baa68da29c904f16a405d64f4150f1969fd607105f2814ac72031eda8cd762458c1f67caf5b589f3fc5d2fd5506de46fc100201947412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00468d6a3c74bae936bc1738685c250772d50f66fe67e0901117c196001de9a2f5585f755c47ab8aafa26302628957f9c1761d49cf07bda5e5b70e6331dadf9400f28fadfe62859d32d868b2dcb7804d1617959877ba735a1c020362c4faaf8e187412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00351ba2a19cfbfa0368cf31c6e25e35e827036ebbaa59c6bce0d03b4024d0276059f0ba9b8ee0360315ab1807465ec061b5d0a049698ac97eb91c86ba6e04c10ff2a704f6004b9b59c5c5e37bcac8b1a7225c0d1d9c2f80c3811c8ccbfa86d6247412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00e828371f93a6c34234d0f7bdf220710c28751a8849b06f5900120f736d1470d027734e9118f2ebd8b1c1ccca7b7f82edfdbebbd249316db321dc477c15bcba01f2b26e68b59da4ddabac0caa2d57eb2a012f49cef9ca7f0dedb96fbb61b5d4d97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009178b3eb4fb0a4301ca74ff3d376e424328838ebe9d394f30d971fc8ef8734ab59f1b9aa13fda39c1f61b2ce643bb6dfcc1de916f26c08df232d997d1408c907f2b3f4738ef0e1db1719b15bf04788429a799f5ad08c8b6fe80e1a58561fa7797412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a000a958f752fd442e36fd24dda212a7b21e90fb9ac8067b1e8926e13ebb11310b9f9ad122f7ee5cb69286170a03e3d187daa46adba1c1e39e9e51080281318f405f2bf0d6cb2709317caaf754a6cd52231a3476c22bde651d7e658c9c5fb809c3b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00efd9fb37fcf0827d46ec60d03a070739bdd58878a0fe35a064d94073b0242593e0d91db424f6d3775c0d2398262ea1da9e555c2e14a11a207bc0e96f6be0960af366cbd2f95beffe3daa55dd52f8cfc98f9dafdf66dd9a9bb50bfa5c45f7a6b07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00693e1c1ab44a1698b786c2ad44aeddf1525bcc8e088b912e68eccd2373f4295e74e7f33c5f8deafaf67b1cb326e333edd5921170c43884b7bfa1e4e80829e807f3fd7dda76df1d4049f4a9c89b9f0123bd9a98167fb4e8e2d58af47b24c5db997412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a447a45ffb4eede561e46dda713e892dccef2086ffdbddfcbe86e858d3ae95945fed1233a65ecfc51d5a354767732828e9e4107d4180949da936c72601b9df00f41127de00c7e87f09c2f4e5225f95eb79fe604350b8e5cd83a0bbc81d20f1a07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005763836f74a922d81500e7670aa8585d035fff6c18497278f4fad7bc380d0ee11301bb29cc8b5965a72004fe96e8498e7a88ce3190ca659148a57859912db603f491df1216c383c87803eebb9ecd959882f187032226885385ef0ca6082c416b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c6d7ceaf8a0a2d2e8375a1b8b5ce7aa81778a8097f93c9ace18cab0d530da92e537f27ef46f32d2734e390021b3c9d1a837142e58065041bfc14fee6aaf3540ff52bf0c5e8dd35828dd1e19f69a3907dddc6bbe4d294cffa75930bb101a1e95c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00b43426fcf013cf2d1194392fa360a3fa3ea36eb7bbade677f3c1bf5cf9da068f0c021d9ef607cf946b6875c1e892a41871807cd19c4a105f8b830a6a5ce7040af5e938b8bb48dd063df032c120fb5b4361836fac0ddd162c8ff33121a9b368f97412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0087866874f4a32a1dcebf8cc8946c6d83c9ca06111eb31ba9390f01de8f4f3f6859c7d7109c313672dc4fae8bc2380d117b40409b312015a896f75abd7f055f05f5f55e9d4aa34cf0ca68447789be38d3a393cf2f6b7fc9166bd8bf4133968ca07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a008f5c9ed161635593b524049af41f555aa93ed1067fddebe58b53c7797a977729ff1948cea76337fa0d32a23167ca7ab08723dfda9a35fa50fe2248073851bf09f65695d41b887fd3ba581ebb5f3cb5b33ff3d11b2c43f961f1643baaa7e7ea2a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0074fc5fcec425c52a16ab9c45aaabf03595557c9b46db79aecbc59bfc44b4bda69df9b14361404927a963a9fac4f363e44ae5e0d875cd493bd0922e6b73291901f73fed9c9c7f15783d2270a48c6c9d18d266ed5b8b11a93f151d7cd488fa388c7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a0066a4522a916156218e9623080171ebe4ca95f59dfe725ccf0adf6a47b9ca3892ef66c30307f62584cc70d2e782db3eed9f681d2b3b7f66957d085de4d1ed4200f78f4e589d61fb39a0ea4fdc3f0026dba41644f1d3069be03f767a131e22de787412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00c6f958e7f0087c8c35607db9b135b04b3874490aa9ad9a0bb936ec9668e9e506042c1cc7faee4a92583e3bff3a74923178c41cfc11bdf140d351a620c9077507f7a35644f7d9a2d429d3e88d4b99442d1e1bde717bb9e12599cefc5b18a856967412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d74952af3215a238412340081f3fdcde6b233e06772001849181038c2ed123b7174971f93e0b65656baaf8e7ae380ac0e5af472cae5f2f8118f106891835890df7b55c5f30444ad1ba0b9bb8161d8ea68ee658b0386eeb1053562512914347bf7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00274e3f3d5df9aaaef78800c81941a066b19df24f0de0aacecc3c37aa6a9c6be8cf16c1e37a89d9f44b7d21e369defaee3fe33d732d566c981ca0a9bd534d550df7b7f589570014fe1c10610a3687d292ec317f5c52b3b1ccb36f96ca3fc2052b7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d5d1c864a4c215bc5a886c8a32fc0c3ba7dab506e0adbced77de3c6553a503e9491bea19f35e503910a511d7b0a1a56cbdfc4b5240ce4d63d74568636393940ff866cccb71c2fc1bfe37ef0749a6fb46092265bdf25a7e256299aaa617a0e82a7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a009f6ecd5aaed07cd2bf212fa8cfb0d3961036304ad3a1fb3c5c3db0e9e1fa25f96324fb0268ec60bf2aeac78ff59dd9ae55a6966dc21ae70af707689034312d0ff8f5fb74e9007a15660e3c52ac47db525bf2757cec1ebdf68a762aa67fa572307412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005913733b16bc951aef25e805f19c1001b2ae08589b7370b3fc1306a2d8b43bd17e9034432d5355242af9a2dbb4875713912909704bfd8ae72db4114ab760cd0ff950653edaa72fedb7b3f9eb0fd99785d80232c192ed0e0bf6789f107eacf6e37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00054f932edf288619ab2933b8a14f321923648d1feb98e67807805306d4aab1481f4f2fcb99561c4c396838dc08dbc4ad300a23e31a12f1bc36c8a2b1d918ec05fa5cda1c05f0e7069df58344fa2ed43133b4ef116acbb2b4f0faf280962a34b57412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00466d8be3aded65d15ca5f520dcb497b42cac3c72902e586b64d45a1d6d254585a4ac0c9c82cb0c540fcea98e0a772b66ad6e011efb4a7d209157be19a8c60a00fc32a83d2fe0f86266565086b16b98caba2327731e33e4091e4c6637dbd39ab77412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a006e6d9392fff443b302cde97d8897c5657641e42a146886464e427b3dcc395c79952f0afbd0e6bd931f47cd6f6e79012b71aea919fb5ebb49fc77d2a4dc8a9105fc55323dff57839026d76b95c143d658009f3950cc1eec73cf7f4ad1aa885fee7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00f4b35d80683f561a7d78fcc98b2bdb95ff5bec35f2345b52d2d43689265d9beba31345a8038fa82628e5e36da80f4377b0dea096899d987938411d015436470cfcd998b754bdaa1c99701caa8281bd20871e77f5a8991d13a3004d35c93e9fc07412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a005cac40c4ff6b0fff2d97799b5664f63519877b6920a4b01cc1f3c5e6b954dd02d34c417b5867a9b406b73edebdcf128141c33bbcd1945ce325f7c98337030509fd148f8f3a9bcfcc6b6965a6b5c46b9db1b95017e6cea99e6af0cebcc9fb9c487412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00d967093a84f95d151d6f10bb42557836dc63d34aa1d54ef3e76218706d9c52038cc86463990919b1327325e0a335aced4c93d5c58b11e22b0d568a6377e72a0afe05d07f8467afdb18f5bfd1ce5135dc857ee839755afb0523175ba01757b4c37412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00a95764befa086a01c5228e5d15cf5389d09932eac3f0bb80ded375d47495a8ae22c272f0579bdad164a012eecde9ad7b80570a744030d98cfb634fd471bbf001fefb4a6f7cf7b3d2eb1f1482aef42869047a88c63306f5a623517068915c4aaa7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856983b7a00783a27173838643aaf95fe7f96567c933a66f994a47280c3b2f9e8ddd6ae95ecf1dbe6c25ccc9cfc6e1b9f0678b998a3b4316a48756c1465aac8af65e235750eff48c3c4e4e22fe843e5016a8ea63c2d4bf7063261c0e9c994bfa5b487e6d50e003d2def6ee3c6d33f2aba050a27d9fbc7a7b628ee5de0cc4207db6d4c39ad726cd8402ac1e801833063e32f3bb732852b86e7ba2b6d2eaa504de05f650b93f5c85e73ddfa15b38ddb674d14e82cc32ad00cbf9624a833eae338e7136483305c7407f31f0a3816080642414245b501010d0000004bb3221000000000bce2fbd29b6a992fe9dab6e120605bd5313dd517ea865c4ab667ba6d89af052b627aab90c7f089bf547e9f1dfd1a51db21980f91d2779472b322593bcfc6500cc1cd1d30bccc62dfd770ba9f4f7aaeb9dc7d610d9b4e2a11fa58ddfddbf0420405424142450101dc0f672729ec07bddd0a02799d8aa26aa9d7d7f8eeddd64e386b005bb3a20e6f961102d47c0f89c36170d176a923195fcafafb5f0ffa1c5727e8d5807b09c98217edd2aee21a00b54b46e2a919dc3d1152afbd722774b5a39463d5eac6680bc22ec1e8017d039a54ff79b13f9cd578f6b0d2b7066c0f7f6a6a1507b3112fb52ae8e21f3ce37081017779a6269490e3a39df65c579ea83fbca2237ece71affe75d1b177e7080642414245b50103ab0200004cb3221000000000c271f77044d7b9de3e0283438973c8abbc452506a3c2ce4dd8744c1e301292290982d0adea13aa6ff556982bc6b615cba03cc892fefa2402a041d4035243f706fe3240141b8ea9d3d4a6bdc957310e4307b1703d3c6e0103c0a14e0a7628d20105424142450101c4616838129cd79231c4eaed5ada68dad9921dcc1d1fdedc51c19421a40dfe46ab5241ecb6f779720f47cca79b591d3c2f2f473657984c79eb5225579105ee88906b17a0915ce1da00c98ff0d8cb45d22c477e461a9f2cc12c35ad6b9c9fcd4c32c1e801edfd04a1879a46ccf2c88b838acf93e998e4ae6ac2f974268dca707f0c58bffd814665792b1cab7567b8f7b742ab146669e86aaba67bfe450019ae505714229a080642414245b50103050100004db3221000000000fe21460850cab8ced9262703523269f0e2ebb6742a40ee15b9824fb41266f3107f3d5b94f8b8e9e9ae7e4ce4b2593f46f00a99ccb4b01da6f273cfc237d0600b04ee97ed40be05a5b9435550af5499ab10013b7b4587dc9ee30a652dbd7c5c0705424142450101ba7ac1bceb7802e64329003afd474cdd3b3a22a8c7f36fb74a170a5ceef0e222b31215d530ac499929463a41f9543f1f7abbffff8dd695d891e18fabb0a6d98a1a16dc80edc56b1b979f05097411ca1624b757b66b886108bf04d7e21891e38536c1e801619ed39e33409e5e97a6d8c30b0d1dfaf71e36125b9db7115fe03fecb362b919412cda1199a43600ad791ebd0ba717b48e6079e90e9c899108fc8651dd9e69e5080642414245b501033a0200004eb32210000000001ce63f9307789187e412964ebd9d89330fb6a9cbc1f796172df86dec49dd0e2234d897ddddd13ed6d892d2c6a9ee0d28f7b86cb1ad922f887b4418d6a799f203b2b1b307f850cd9eff67a0e4d0cb0a30bf5f015caa62a3404bbc3ed3f7b6dc01054241424501019225cde2bdd5454f7f415a647bc3eff9d83a2d2653f83ab9779bdabb9d426f26a3263f7676dbef2d083d769a989e0aed43a8d5b7f904bed8a96c126701ceaa86dc987f2328d1e06773c30bfb338fd688ff8c4c5a2dd751394d6d7e7043877eb83ac1e80194624e3fd700d136d066704abc7f0ee527f98dc9a36cc0da9d42b036d553dd3f692299bebab8616c9d9d197deeee831a49e580e1ee9842930223558db9cde989080642414245b50101f90200004fb32210000000004881636b897391e6d69b324059fac76c17a31fb937ec231f9cfb20c287c73f5836adf5043a5cbcd4385fdf9a36abb0bbaaf2b30b2ddb4c581e7e453644be77061d6ab258ef2faad30d59227d12636565d73f3ec6e6c23ea442d295b08b510900054241424501013aca5237e0df81aab76e961e0f4ca6f48bd4e85c056ecf45fc2591b78371d53042c4026cda22d6a5c10d1d85a60c08794d7a56efcc90cceb610e4abe5bd8588edd568ebddd701a4bf641660d8ccf4aff9e9c33d39d03936e8a71d969d70c6b6f3ec1e8019efb0b8d2d5969017f00b8429b2303048c2febcf232776ddff4a3fa21c81e820149263ade941b5692234c05aac97771ecbcb86780cdd215fd7059900fddc7bef080642414245b501032801000050b32210000000009cd0b2b0db635f8669b67ec140b3deff01693bd6620c4522255fee0dae50322e3663b09067a3fa31f5681274b2c9681d55ff4293c8e2b8654e55c98fbe2f750afcde084772c19be58a80f436f176d1fb4b49bd416d6ba27a8edb47fa2bf6570605424142450101e26b8c91067cc029a641e6f3f3138dfe6e4d6562b54d3371f7c7b7b681f85662a0c123caa732561da9ce7f9172544a8c957ae8ea55295513d3fd435652ed028be372c0cf981c1eea90526baee5b765fb22c81aa606e61e4f58c3b6ad7899309542c1e80174137e5eb8775eb457047928f394a54233409561584a1af5fcb9999005ab24f64f1ed5a9752418953046f42c743a716ede2c9fc637c27488817259c714d86fcb080642414245b50103d102000051b322100000000098f36fbe1aadbf0df604bc3a375f4323064dbdf2bc3a2a2b5713e0d13fdd5b038740e054538cf49aa551d0a4219e5749602e2c919f9e7508cc23c98953fef60fda40d6760a425db1254c9d73d7bfb32f012b3c7cd958f269bf9651a01411250c05424142450101c40e4a438cc10db6118a249820174cf39d8db68379ebd81cba374e8d8278aa7acb519267f8938ba22d1833b0b160c43ba488383c84b515f17ac2cc8ab486aa8d33cd023d0f98c0d187d6cdff9de583ba1fe4096bff7e798cf44a1bfb409a5cca46c1e801d502f382dc7d291bae95cfa33dbe48cdaa593a45c0a2f82a654c63f6bd9208944e64d292935a94a2dc247b13b16b2768c1e77d8c99ac04091502890737d85197080642414245b501036302000052b3221000000000a81ce495cba4d65d5e951499709d3ac1ea57064acedec43ac523bef8547631325b64a6165fc944cabe72b71ae5b8d06fbb98eb94faf0b9d0edff55bca2826a0c08511f99a3ed3af9d697782b0641145be90947956342182abeb869eb67a699090542414245010184aa2ff54019cabbdfcdefd25f4f400fde1136db92e8e280ebb1bffaf0bb6866f178e9bfd767324ab59899918dcd07e30eddcb477584b2cf65692e2ae7f7268b830660eb96db259ed2fae169aab0159a31224f84229ba4e54b3b90cde9e13b084ac1e80130c0bfbf084235655148410f4efba90d2b9bfe227b0885bcc3d2868e92db62331842c9ff274681050183c48befe615bf430a1fc79006f1b3ceb2c6fc063c2af0080642414245b501032d03000053b322100000000036d1ff946688df295839d370438879b729a01eec65c7296e1f035c80430bdb3cbcc168fb0eb022770d00f83a697a399b4d4ec627e1ac43073b0e133eb1cc050e65ff3bea1dba3da54944ce868b39e1c61790d97f490102e27b8b66b3b17c6f0c054241424501014e11186c2c348d1d6ae7a18bc081db1d76c95cc7574f1f064f692afcd7999e2837fb14693b12df37b71289c70e0e7aaa616118764be1102346ccfc2633db5c8f42953613274fa1403f5df91ed2734977271c38d98d7fdfa21237ae6b4236c6614ec1e80184bbd60ba5271df78a9adddafdcd315a57d489c8a095b7cea77a570b7ac3854f3c8f016dcccb86b0bfc9b38a4bea322f39db492f8483090f41b5cd5676a8a456080642414245b501035602000054b32210000000001475ae9934d1da0b22ee305721fc569889ef56fd6c47fe2b87ac81737561e12b6315326bf602ce942b58b7a414974cdee7224419fc64e3a614271f0e9dfbb300341a42990ded60e238501dc853ab3ed8beda6acfb1decbc1114c5b8a5de8e70805424142450101c4d06acb5b034a56377eabd1deb63b7de151ce9e9da8030f42c88f1b46fd305555b4ada452f4302da4bea1f7c98beffa94708fbab7593ccc155ee6149d38358c9d8563766ed8e9d28e2c2c063b9c19e96d28ca7efdf321013927a05d08398c6c52c1e801d89611916a215ced7b08333aad486228baea7a557f98bdfa5b9b677af6f3c92d9d6d96a01f98e79458bab5d65674553cba845e28f1590cdcc9aaae58b16fd162080642414245b501035e03000055b3221000000000d2be183ca4052d599c0fa111e0095ed54e58e211741634459a7ea9308c0aa13d5a33f6c1a9a1bce52ae41541272cc7cc0be9ebdf4e9005ae497d93c3d687260a7da761847ada7da2be01810c3de7ae1071d7225111f65e747f9112e8ea1b0301054241424501017cfd20792dbfcf87a6c67d56eff04c71ae869e3c264c5e234abeae73b2eaa44f963e544fa78b56313d3b37c41d59485a91a317f417cc065e3dd68fff189d5984004f38db487e6f7dea3a5fae1d5c7adc872b3d5da096456d2726d3d6adc7d04656c1e801282eeffd82274138b122416f044394229392c4473fa8cda9835478f01e50797ec201cccd35627cbb724dee5a68f202d0cf9b6de124b0fea0878badd1fb15a618080642414245b501036203000056b3221000000000d007ff98c1acbe09f2b32e8ad3d561ea2c1c3cce3bff495ed220555109c7185e10014dc9ffc48e84f9e05dafb903053ef4936e6bb5383c8bed966a9e3f7f63057ad8ac6798650cb830634e3bc8142aeb9fe172627ba62c8cb64d33ea91d1610a05424142450101ec5d877fdd47dfa25e57c34406c086d7c1d83694f7f4658bfb0a9e003c40fd08716266e632532158fd0c9bad9107336b670701197bab5d2b7fcf9e6f6415fe878ee18639de5e0fedd5a1b2b9237653455e4b9a31fc50cb6b912a89f16ee680045ac1e8018cd55162fdd595ec579aefc4de066e71fbe55b4cc96b760cd479b410e6ba28e8db9bb29ea6e993971f822e8f036937c1548cd015a300ee8190846facf99b0187080642414245b501036e02000057b322100000000022ef5b2c78d993bf780984ef47a6ab1a0d3ea822be97a54c5f6be6145d02dc62ee1a2855fd8abef9b2535b377400f999bbdfcb24fe0dd8c61f4f15693132fe0215877bc46e3b6c92e7a0d801f1082002d7c3e2b94b8bedcb4bb60e02bb6f040b0542414245010128b262d38279cc9c6a9124233a0f6b7eb0f20fb8d3d25946a07f52fb3bcb307e478d1e6076cc8bf21247da1b4a075bb25e3798c8c0dd8f8e3fb106114d195380b62d633d95d9ed28fb032814d9b514f806e2bf033f104c798eb39e640ba4b7095ec1e80169d765f6dd4a2cf06f99f9b4ebad2fb2804a1f53aba5466085a3d8602b25035a2986798112c23c66ef4743a86d1ada4c826780ad6ef0a31e9a47281030da0c25080642414245b501039202000058b3221000000000d447cfa6883f62be7f55f54492b540bb774c7000aebe1b18f5ccbc545f336202dd38419bd0418bc92c6dc07bf4ea88bac7f48f71c80c43a0ebb72b709e3a6d0f9e1f72573d99dd96668a5011f04a1caa37c650836f4b452f8f9d6b6f79f41e0105424142450101b0b41541bfaf9f24b40190acef20bb4ebffb3eb2bb06a0962d3b1e9653fb177f5462565f337f129561cb649463a7013879d7add2957afedcd56b5ae9bb268c8f562dcc684ffc81e2b3a5c91544944b0fde3bb2ee5d68212431d8a0b56ad1263b62c1e8012a612795c66f9ffc49cbb552b02819f8bc851431445aaefbbf5b4920227c4a2a7b5abad975caf284a866ef6ba5e16ad21403bcf4967337a9bd72accec12968f1080642414245b501030600000059b32210000000006a265e47f88eb5ae8343581de6def5763078818d887dccf76541e38f73c6f861ec57eb0468cf5efa675d55d545288d3f5f7e102b23b2dae8e3e586f30d63cd0a5e713c28d4e04e0af1b4f5d9727f69cc3ba25a147016cdb1c8f5f99f4d8e300e054241424501016abfe374defbf321e8e458d13bfeb238b77d517a0d916da347b19275b37fac7762f93d65691ef3aaa09217462d6c6ab60a0e05e742e7d00de11da3223c103c84d460066d208dd8adbf27125538bb00b0c8b1c8535a1599a9059437af7f3d2f8366c1e80159a1085b1bf8572b19e259f0c41daea3b4c68f3ed1ca64bcc0033fcd1a7df283ec1f1fe7c0fec90ffa8d40466d0bb0b3a6bf79dbab367feaab7299c461ff96b4080642414245b50101580000005ab3221000000000b8ffc143a3dea9b443c619151a8e1327b6f8782fc0530148f9504064e66ff20f6925b47aec12c9ce90fe1936b3fac17cf0dc0955a18347a6b5cf63fc967847019bfdeecf78c3e83f59909ec1ba6833311479d238331430c69e14c81c1f24e90305424142450101f011d7b725feb91c09ccaa97d38e9c6166b04f756e1c9bf47d67cc29ab25034ccec285d99f0e12244da69cbbf44d7a39756db24dec9c86ddbe08277d4718f68b8ba9c72878d4e80e89143218c8dbcfc91bfa35ffc398e715382f82fdb84b75f26ac1e801cc7ea3b013cb303e9e791fedbfe4661e4863d0164b3435fdf179529c588377a58aab6cfd87de86345e0b5754d70612854cf6b61e2a63496bbb8f0b3e074f0c71080642414245b50101fe0000005bb3221000000000be95a851c6be24bd6c33680eaad69770a872d8563bb055fea134b867adeb284fc1a50c123a682525e4a83405f2a828249322594ff2a5dd5c24e755c76f9e6b0b5cb0b5152641fa9120f31e920863d15e403cecdc816c695e17383976f4d1e90205424142450101c8a165867075fe08a011998d2e8fb187655a33a7d8440daffcb1e7f2f07c3709d1fa0cbdda1d0bc2e6965bc6ed7ec07a833dd8388b58011aadd2713d6b30f286c8a750502ae094a0f39100ca1d45a1d1f997d9e8afef266fe8d5b29d99a5ec006ec1e801fad9b3a2274589e4bd6e34fc2dd101745ef9243fab5c4708e2d8de61aaeeb859c7cd2ee6390d89d266e1a0b2b9b999ae962f8edebcdc60ca8f653fe240cd3d7e080642414245b50103350300005cb322100000000038fe060d9178b1cb5055dc90db715fbcc1c4d329592fbeacad5e81d44ed50512f19b8bb3a4bb4ff698d2f2766fe6e2d2c407d3d40cf110a119e3b4174fb8f202acdc9d3f4a08419250b595bc410d46dd57559d7f380d58782470d6541b48f40805424142450101d6db06d055f648f54a706efc7412c7d796d44fde5efeb7a1df8cd23caea04a0a148b75e095e648b1e068ead2dc67b0edbf239f4635e07e70e69981820de2c4834a45deb67542ce8e8678e625a56f74053a2e8295d5f379e3065e12f4f919ba3572c1e801bce8e351f0cc3c166f724fc1f45b7b487c293cdeacb4dedd40453e817cef64f44f142f87fa43261e96b221bf3c738964ca7204755ffba87bc58ff8da25809738080642414245b50103ea0100005db322100000000066693b5fe3b9274118d48dd2339e5d5d5ad387dc7329a515358a5016eedaa81fec5853359d389bfc883f5ab1f4228e4c7e075a04c640376cb8e8364cc78041050e08472dc4f0da78ca04d54f3f00980efc2997880a6df41674daa56375c5c90f0542414245010156dc348d5ca72b6c85f392a8f543ed41f81f26e48ce140205133b67df954262059a97236345723aa91c661613d3bb134db656c5e4a06304d942a2f03bcacf58b0f72dfcd9637ccafee753c0c41ea2744aef89f4d55ef35cb76453954ac51229576c1e801c1b6b71fe0dfbad511496f979887aab8bd117fb0cd03120520b7e38a34c965d155f80aa5d2d917f34d8d8950691675dcaa4f3a8fc7dd4ba91e4eb9baf906680e080642414245b50103db0100005eb32210000000007a74048c9c56d40a23d8d9d3e1832b94727978714d99bcfbd4ac5f1abca5f57fe595d74e0adefa8a1a2b4aae78502f2760bb694612fd45dfdd0f4aea7bb0a902af952a7cc37cda4c694d2c80b0040ffe5f0497470688c23d33c463c279fbd503054241424501011e2474a187dc4d1e88993db6b63790439d6c1ef2be68621672f77611b0955506ab0d751ee9db55cb92b65cb87ec91bfadc046510f5239179dbf17bc34fbc47821fec74e5a1b4462d52fcd1867fd0c6bf5162a8450ff037bd3ab32a57d9176c1a7ac1e8012ab172e19e744e862cebe3c051829ddcc1ac426963a90a42c86513490a5889105dbb8b661ec983f18d9f4408bb011ed10f971ff6a166dc068ca3f6dca31eacbc080642414245b50103520000005fb32210000000005c9d4e0843e826bcbdebee8039a176556162406305217aab5f812443be77734d70ec3dd51abdb8c164c4a8ffe1e06862a148d6007497f8cb9b30150678365a0ec56a86636aec627334a050828fe55cf9c105f9ef1422d334447613b5c0208d0705424142450101dc3f996a30bce53f079186f0d828d151c4ef5e1dd33651c456d5fc98044a9e7c227f8a8b78f7d250c330f5e9fb7fb89c8fb58acc107370fe96d29598c2e9b08b2888c906185d3ba9d3ae8445dce4c7d91543d35e612b22c93a393b1ca05051a97ec1e80191dc8ab693531c5fc1bdd497ea3f5094654ea949aaebfd7b3252cdb930f8df7a60f99986cdba0790665582bc595b53664e61f9f5f292c4a1f065abb841b01ed9080642414245b501037d01000060b3221000000000b665bdb99749c35c6bbe3a6b45a21f20f36be225725156ac55862547f6183e623bfd640848bf90849d4941d8daa18e86c19c90a9de51065452ec007eb0f05803b65e6c2f20d7228af0c7f43d114ff371a5b7ae76b0d4ca1bcfdc0369d0436807054241424501011e40877363d21ab942c9eeea6bbd3f4a396c91164f95677a3e9615f9c832866f00ecc36f4866bfa99bb0d500dc97e19a77daa97f909517a1a03c59151f1663815f7b5caf62bbccd170ce3ba6950f7f150fc937f899a8581b6b31c75957f4306c82c1e8013c205f635fbaf05c61f362f724b7c93e7521447cb24e86c3c97863c761de0e3eb8e32f81c1ab480257819c95ed8527c0abbc841849662516d6f62c35d20e4d1d080642414245b501016201000061b3221000000000ba8092dd7de3def8285bdef1993b4096256e3e79445c5f6be3240a9598fbd11ced14f281f937b5ed1febb4d7306cdd3d4cb824c10adce789c341e2841df17902968a5a5371d5d59e9c6d9aed9475838733ab98c8f963f883f884da2a61b7720705424142450101f4b92f692a86b5dd14462496d4b0898d2076e6ed4ee624d72fc096dc34ad2d742deb8da939af7ceddbd907d0603ace87829e379a000aa345358953e05d2984865f761bb116e4781c73415140a07e6992d0258ce957aac4fcc5eb30260d5ddf2186c1e801eb9251bb8c0a75239f9ac7b8785d6e043143cf1d4d15942451b2b620ab01cb81518de8cec44aec4a6fef7d6a5aeb3ec22c58a2f80e77faadc03b2afc416e7260080642414245b501034c02000062b32210000000003419fdd917f4769c3dee3947a784a77a41afcd1382bf3d358a03338392ee5e3c94c910250663742de21eb09afe1a924d2c027b56e4347fc03b2c1f0823e01b075d61d20c6e5dbfd58a9c0a3c64e595ae278e13abe4287d3296b8175284acce01054241424501013a3c42dd47899cbda1119f73f30eefaf0216bbfe9b908b7b51d7d06d36edc70fe514414e0a20dc264468ed69775417295e92e980e81e94b9612f724f0ec4b487efff4d91e71b7d5e28379eb17e63318a38093753b66aecfa6cbb5f8a2f383aeb8ac1e801668322a93de0a61aba494057f822d7a48dd1698738688fa7ff20b78614f89f1db88ba29002b61d30eca4c7c32b1166c93d8f1c0e7fe4c152dd00527ab6c13523080642414245b501018002000063b3221000000000fc73f3c3e297772388027b3867b76d22890d8ee4a4781c5f7838499e5f0287220fcfae0a835d0ee54970987705d59a0249e60f6ee536ab3bb3415af45a34530703c2a384f3aaf5608e1b994f52df20c228545453eb7696e82980610716748f0705424142450101a468fb3d0dd4d781dfc97064ca11029128334be8abb41d31639b68379363e53f3fa2c4640c049cc7902614aefb773a7e132a5bdb183ca10f8a2e96fd3fe53e8197ab28b6f0010fba1e70079a29c4f5228aabbe14adf6bc3c6926fdb34c39d43b8ec1e801229c5a7e483eb709043225b1b08a99a2cbad469ee2e8b1510eeddc5e089f0b84d7959a90174d25168e2a007c64a355dc3ff13c4b893af6424aaf3a257dc594dd080642414245b501036902000064b3221000000000de03dae082ac14ea4eafa042e2bb5823105052eed6fcc198d7cab36548e7317110889eb51ff1339bfa9d8c13de2656317c872d1a420540bb73a5760df655d80295ebf11f2e9aa27ee7f28de5d6199d66e6a8d7da87e49b75a322ed5480ff4403054241424501010099a0141118bd8110c2334fef7bd5177e3ef992ef57bb689c28ad0aa207892a267c67533a8364c2647357ba41e335a412587536dffae37443a39b93b0ee8786f79bb911f042ad9d0218ddd4de04042ed1c488177c7a8cc77d58836a713dba6892c1e8013f69845914dd561a5f7a199a6a480b6f45ee6e2298335d9a0294bbf6667ac8b8e674fe802a698b0249566637d16ec2874b79d21f0395311b30cbcec9e6a8cf80080642414245b50103b002000065b32210000000005a8bb3dfc874200480db9b9276e5562474e1096bdac2ac709a99634ffa46c95b1c190e706779e7a437615cf03b5eca192898950297e25de2e4cf1ab68acd690be987921c01fb3b1d53f3737bcfb5aad89cf4d3b1c1d62fcbbbd22a6210d5f50f05424142450101ea4071dff893b10da4e27933abefe6269489733f454d97e6d07198571f752579e816253be3919896fc2b5dbdf31b71dc19b84b19f9338b16defd67833fec07831182b0b480cbf6d0b0b57743aeeab3d007268cf1320c79b1f3c8413408c40b9896c1e80192d0b48e3d9d59d7001c2a88addf06a5985ec273de935cbf3351bbb55a2581ae78dabcd55dbf5a44f7e6166a99a1180f4c6220d99e36745bf898db926cd5ba74080642414245b501032003000066b32210000000009e1e2886d28837330e743121aed7fe106e683643e349520faf6c7180ebaa097a2e05b54c0357aff1f5751a6a758ba76d479ee71648250932d04453527edeee06ef25d2166168d8f5911a7db30b465a6405b03a62cc5d01ab1868ce8051c0b20e05424142450101eae8ccf1ff0289c0b27cafb43849b2a55ae73aedb1109ca991457b76297ffe6b3f7b7c5b08a6fb058f7f6a9ad378c94a3a03b8511b0f8a52d0511e44ecb8bf89b4dfd9ce0a86291f537ecc3db63325248dd262a8433d1bcc1ec2b0666fbb4d819ac1e801fdf5ac2edddc564bd479fb475fc68a6d2671c924322d17ae63952699fb858b8028283013f004ec99ab3e24d850d860806e2e7b7377e9211719eacc3152e8f06e080642414245b50103e302000067b3221000000000d2d1913f2c5db493f5b73085c0597793c7a43146b790668c5d9d01f169f429400c4b9b8407cee9b7ab8b6d6d638902de938689e6a5a159d5ef4a758da08bfb0834f78099a0781c7140d36e0398289cece667cd8b560fae3c98499149258a3803054241424501017e66793a6686434c246822843e317b41e92c7ce2b17fd97ecda1c2804a222563295bd85f231bd515ad37635adf057186e43eb06d719e5620a606c84a8e0e8a83844df9c3877d88793ccf4eae691be7e4a586d13c905ddffa9f6ce4e0426bd0d99ec1e8013a38ae36b07d0d95c8aa093b7d679b4f393df920329e183f313ec38d4f5506c4849dced70e8cfec33d0751948fd52437664a142d47f581cd4d931ade85ce5e80080642414245b50103d902000068b3221000000000aa04cd0dd818ec26ff75da41ce01453ae861030dfeeb8d14765e1baae5673e680cb8904bd31056ed827b12922710793072df537c5757a66d98d93c21da5a0f0f4bcc63f961707880720332b4212c63589d2f8194045461c694a1fdad6bc3ba0405424142450101fcd0e4e91af3662875c28afa9d30b8b18894e167d7ca3f974798313041cd786060928705dc6fe0a2ae44ffa45e0d75608f4a80d1fb525c2ecf728d48de7fa0835f506e966842fa89f3dcf0002333d1000d9fd22acc16d35fa1478b68454be8b2a2c1e801ced38f875dbf647b108a83fb992220eeabe39b62e7db147d6b7f4a940609ed448c019bed6d42a00d9906a2275b7208dce120f68909d03d65efd824f6222e0c8c080642414245b501034302000069b3221000000000464cad71faae2ec6f4084cec4d78d7bb083c7bcb1a4204494af9a07c65bb706810cf9b4ee5c6ad72213b16bc5aaf0af4e5ee1644de42fc943d769a20ee2ab60b46eb55b0f2e2cc2e7962e871a782e85d8cbb51ac7b02e66f189c57514fdc97050542414245010104e9b29e27b0a0a9f05b0fd098027acb0e6a407f3aa2e1b563a4d7288462ef3e683c02e1ca7f544faeddcea95d9f0b2fc72604d2c28058f4a93bd95682f8c686016d326aacb9039a71568b22ebb20669cbfa8a709e783652765ff8e84b9521a5a6c1e801b012b3bb0f84557af9f7dee2d08d53fdb36f5a2ab2a93736c95b8d2137e866c6bb956d34ded0ae0e078768af7e8d4e81a8aed0efa2ad0e47509d62853bbe4a39080642414245b50101480200006ab3221000000000b270dd274fc70080e8bb487716790f34e869e90f86b8c104fe236eb750922601f0f9f8a71105d91456051b96ae229da88993b6f9cebf2e5180f76147c656140470bbc3bbbcb290ef6451ad979b4c74ffdad85a5e546ab958cd2641630c3ed70305424142450101a661f79023aba59aa0758fdde813f73c5a4934007341adf6177f9b226afabb45735044e49cc4f984b2de267877b1d7b6a76555997c3a10458f779243562798884d137682815d180fd0ca90d9b665c860cca740d77a3f57be16f233ef77918204aac1e801f3328db659c97e4dbac4b0fcb205132273055308a96eaa497ef4fd04d374d479a198f071fec4fa7f6099d4eb5fe4b9e46f9c4ca98647e00481f13065e4249598080642414245b50103700200006bb32210000000002a1b766759f8bbe14e637eaf14db404c43e37f53a4d855e866bcb40525a5d34aca34c22931d9c0b458da859346b89cf11175b156050f2ba63cf63b007594610d56510a54955ef61482b762a58a231873ae848afb98192d607338404cefa1480c05424142450101204a6d73f02ddad1cfc7bfa92b7c52ecc5a998a4cee6655d0a2877afef7d93113417ad71c9dbe015555fd33363318da3831ccc30c74646ca9babf59d01320b8b72b72c5a16548cc7e88a1b382131b47b3000df08be030b91619139b8657229d6aec1e80179c1434a60eaf6be98fdc3999bfec34cc5bb8c0d9e47d2463ac633c2e4a0a5ac08e169614d558d3aed8d7c54e993c9620bf3b762e82d7e4db1c1c4ae63c89a6e080642414245b501037b0100006cb32210000000006ce2a4d3e7b67fa99d1f5ea93e22c47c4d1a9cae442097e17fc0eee7690b657bf6c7cb0472d04fe5f280954b1bad7bc2c3470efe6fe1b22c267854dd4b36b1097f5594f2c5deca7fc63b5426bd068240ba512b96aee92ca4a268f05e17f5040e05424142450101ce531633e5c6ab07800f85d8adcb27d437f05b5210e4f580e15deee9df9260198aa2d5df055bf4ddfed6b1c530bfb447dc872a688ec35cec915389ce3acbff8b7f4a65035dd5ec9a50a0cb79456b9aa6c1f8a4d5903b2983625298d12420e522b2c1e8019f4a86f6cd3518782dc4687233daf892378e077c17dd82bf85fea66277b10d04633a2b7f7bfc3300746ef4c2de63573d092197a394019765115130776b4e512a080642414245b50103db0200006db3221000000000682028f0e98283970de7679b041e9289c95b084c0f5723560ec477424f2b3927913b1f79cafdb53f33835bd6b6ceeeff7141098167c5fae190d0ded4d8aa94014fef3700cfc1d3e4b2d3c298539e5b5e6894fdfe551bd93eaeb77381650ec40305424142450101a4a232e7540b7a0974c0dcf9844af8a844e4497ad67093c38178c0edc2189e5864d22a0effaac3a3ff82b597a1b5ab235e991201b4af4384dd01410ee1183f8ff9ffa2df068b73d39bd098c47f676dbf7658aa47f814cc0def288beea5aed1e0b6c1e8013963f41f453dae9299c4c41fa76cb7dcc52321afa1e27d702cb2730649b1c417353add8fddaf7fb47c1793833500098a1935d3d5d54b5f870487c5f6a2c5d6a1080642414245b50101150000006eb32210000000006c2b54df9743c55aa5d3a6cac6da5e5808cff93ddab84f805cdd9a8eb5ae2437c0e1771b1a778a79fea506e87461eaf9a827268498f1ebf67637f6d9be80c909df76dd3fe0204a9b40553081e529ace6d5b471c1108536d80003226224ddab0d0542414245010144077345947f4cc29acbc50c1308aa99d70aa7dbcbcc8cc53e3f11e8bfbae04e3a47ecf3b8a2a3db2fb3f968fa183f14438aa33ef7cd63057254be894cb5a089825444aca8fcca9268d70bea0691b184da7f8f4d4d375fa77cf3a3d1d97e79f7bac1e8012acd39d48704bcba55d37b675725218c71f0419ddc2cd1e829381ae78710b4c7d574b5bb94ff6971ad3b2ce8f4733eb3c8fd49090f207add4d6d2adec8d6e925080642414245b50103e90000006fb3221000000000688f7269f0c896d332e20d28618b9c1d1f7817f6f553396118a4982cca3c8c73eca0aa5dad15e53d4aa7b6d3fae649b53cf68baf18b76fcda49263db432fc10bdd9ecff764db80c40dd9f7abb1a4a8535f58c6c723a6d2904c1d2cd9edd8580405424142450101e84463a131eaaef64f32cd3bb9ce0720d16d866d25164fa41fe4c2719f556c3464eb70313792043c80201ccea0ae96ef6e3f24f20f7644273daad4e85de21e833136cbf4c63fa77f95dac07187e9f5a3602d7cbd6a7a687163ff338bd517d1f6bec1e8010ed6d6787c347c60930c7d3a0dda90dbab99bfb56d045582a9b701b6da77b0e416415449e42251e34a0852af4c5b3672d8f4c09721e6fd3ae7f3152e7b6916b3080642414245b501035800000070b322100000000018d02b9f5ac276c9ffcc9b723b969598caa0c432979205adfc60dc5532c6605c6a04bb90638325ed16f6322d7772c02ec08dc92631cbd812c54d42bb5b158a0cac41981b7751d403496807700c99a9d0774e4adc6bd61bf5d38f325adb65a00d054241424501017cc1c8f26b43b79bf3d45768105aa7f3fee0d238c2db667bb1208b6d86ba1c14d276a374fa22ad8db4e93d7abedee1276aa0f26cc43a15523835d74ba6be29830f41bcc02e3bce64902b2c54235286f450085be831bd5efc50a105ed567993bbc2c1e801792d3c244f830ed4fb8cb99d706ca93a7106bb0370a4c96cb18d295a37dda24c7c0a116ecde66d3d214b83d14c5cf9a9bd5ab98042c33f12755a2aa5041588c0080642414245b501031a02000071b322100000000074da7aa9dfbe5db69ebc85b72ef9fed6cb034dc9308e3658387f026e8cf0644547ed380bed4f22baa8d4cd3185efe8a66bf749d115ab1456141322f84b3f3f033b1f078fcc6b6ceda60cd12441c561f1362dea1c21ebd34198697c6f8393090605424142450101aeca9452c97b52031c9f9cf025aaefd8775a341adc7f6b36b244fa69a1c29c5fdf7070729d52d575df948e8badd1ec68d5a8f27671a314c6da03f7697ea97d84ada9045b34bfc539242fdbabe5b456bec07b62089e5a662aba2e742648627aaac6c1e8014feac243fd9710e014345b17d7e70eed0ae94990484b6440213028b677ba03969b21db1474f9a28b28209f2e84fd534f519c4fa29c8e765e81534fbf062bc409080642414245b501032702000072b32210000000000c8cde7e0d593b8457139bb2b2fa7ea6f4952d237564de60d4ae2710114caa741b9eae52e788409c619772cbd1f66b78c5c8bfdd1ffe3608935b3ad7cf13d0023fbbcaf3f44efb13010db3aaf955a78d1e58f039021c21b4aebef1c8fac3140a05424142450101a660ffe76fd5ab0e19e08574356ccf58151895e7f890e55cc8fa4712ae557e31aab1c8251f3ecda2c4cb0bfd7a6e64d3b452b80588fd21d7569a33fedec0b28c9a93ea34bdaa2347e60fbb2793042a7c34933e967d1e8225b85295e553268d7fcac1e8011bc72017ec236f6b049b63697444acf895cc9d845b2e16048663e5521807aa2b6684d8c526a340fecdc1ff16a884a97a28d038e6fb1663a1e8e0060b72f9aa69080642414245b501030e03000073b322100000000098c1ef7b91e16b2c69154854cbdf246d5febc578af17597a87f610d518e96c780c310730b58fcf7e14687c13998a1568353d53ac33bb0c7f0f50f1e5a27a2506c4192b6e4b05e3851d25e5cf42e9ee9ed6f74810caa65ccb7eaf62d5464b5f03054241424501015457bfb7d9723e12334467b5a31d70f86ccc978033226096122c976e78134e5df117512702125004377c3d2202b953a633cf77b614a34b01ef9dc1423458548ffa925cf115c91e70432a9a2572f86f76d939d8cd088c95e8510c447ac7d53473cec1e8011bc5a73eb3b81e51cd99848687e98d3de458dd80b5711716fe023c6066f8c5a332f22db4e3b0151c61d8c6c51506fdd41302c046c284e9037b39506f5669c628080642414245b50103ff00000074b3221000000000a047344356af2dc54a8cc48801081d9f85774ed00fb9fd9494cd662a43f4203cb64c8c99ca0f9752c57a9c7fb943592d24a8c483c7f779ac77e1f520e790c50607d7585d264c6be21364ce658e5dfb69b2cdd5de7bf56e17ff35b5eb89d60c010542414245010188d28ae00090c9104505a726e4de04e48edcde90bebc2106faed92b43dd0bf72af5e33b2c6d3158b8c51f4a40a342a6c8c042b9ff7305c8ef7290ef25acf9f8d6ac8ec24d5a4121910e103008ef53e15c0a40cb32fe7a65273a396fde0223cc2d2c1e801ee5bcd4d3c4af101f787cb9a77f28cd89f5e4921aa22815c26ca9aecdc0236fffe21bd6102cd90de11b1dd3ce2175da0e51988ee57efea50c5a76699065b1a92080642414245b501018601000075b322100000000024c1ee474fd4c577cb22ef44e088d84b0670d740f820eea9af0307474a10cd42a81a75b178cc621857be733783f702ece6e374706cbb3fe3fdf852c612661105d941df35d20b15a831bc7512f663e499b618d6c2d16b8a93116f12ac8ddf8e02054241424501017cab309135e0c6c7faeaf02b6a1bcd55e4ff2a31b5bda1ff108e30cf4bf3ed06dec83c0a19096bb40de0d467fedeec3dbc460360707ae0e769d06d17f902fb8098bf9f36263c7f52514d2d4cf52dc3a4a68f1263e4a446a0dc7a1f372c3cb83dd6c1e801147ed1f79340d30ca4770a96292094b5a1e6ec6bff9c4b61ffd62c30ba253a4d8d9b15c404ae25fcc3c6a8bf9ed047d83a8e9dc4de4e06489a0d6f7c5dfdbda7080642414245b501010402000076b322100000000060fdecf52d18ba99dda23751ae053aa0f783f9a08b20a12a354af4863eb9926b39246ca5df5bf09ef6c601eca24b8cb63f3fffbc58b803897e16cbd7dedfb207e548e491cc55bee9ec2eb5c4e0df8c73eb39f09e1580c370c97cf758d2ec6e080542414245010172b23c29e3433c4df67e13e2fdcbfb9b13c01b7f6aeb293e009588766b1d96517e54b35345100136629f8bba54370099fef080682e6a015abd1a28b0bf778487ca322c2a5a6dd08871c9b8de4e37c3a7b7b51e290a0d06c185340df9fe66961fdac1e801614419f7df16dd2cadad9d8792d9e2c1b8bd405f7bed3d445a665336d180d44507d34638df70b661c9b2b686b0858ec74568742616a2769d649e9a3d1a67a0be080642414245b501037402000077b3221000000000d68d46185d5c475059d2c281f002980432651bfa427d683d105534bf49786a66a8b7a785f603dfc8fa77f761f95f0037c789ddbd2dfe21a40e275ad00603840b08a2e3ace06f21b6e8d3e0d8b4221221509ff6f4964d66e7302ea23c143fc20b05424142450101c206d89fb3dfde37a8b6f3c7127a68bd811b956bf0238ecb6686a01057c1d43141ba86dd048ba62ff226a339ccd767c7f7edec0a92c79127a91a87f46e2b198466b6955a845f25fe3d2c3887febbd4a5d0a55c9979f01c6b6580a8d21df8dd4ddec1e801aef354860159013ae8a5fc4e91fef925d45a69c616005de440718bd7acc2135b9a2cdc330fe6523fda461f12d64623d1f565d88d733874aacc18d580f79245c7080642414245b501031901000078b32210000000007878cdcc3c7f49507547ee104de45234337341eb1894e2b3922b881ffb8b607070323bc59d59e3cfff1a0239ad3fd63e2010a327a73b842813d8531cba16090888a37236793130c2ad6e837e8e4ffe638e2fe52591aa66443de8107bcf56b80605424142450101ce793dda81dd66aac352682f959cf12a294ddec359233d866b1d9fbf66802f2d2d4c90f0fe7bcdae9d23511d9f47bed41998465f42630d9c6f2a014bb4c9d1821505e64a639fdff58579a0e8c0d92ad76f91dfac181d1785e38e39b9af09f774e2c1e80119bbcb68a4fda40525f69945be141245bf10d412eb585ab6c6fb8c07c0c588ca0715d0df6c445a57dc5b61c4fbfb54ff9082f07e91247e508f20f876291bfe3e080642414245b50101a700000079b32210000000001e8b85f7301165c77f05915907c54165587d425169615be8d8987d0932872922f78305ec3b350567a8053c7cd85f4d56e6fbb15ad137a2e479ced13b337e71009f978dad47539b610c3a37c1161e3cc1a3d24f3426b05108351ff63215ed1402054241424501013c9649904634861c1db56f2c61830177b79bc9ca1deba40f403b4c78df1b3344f3e23a3c7a2de60c7ccc10db1c253276b89ee21daeb1683e9835968535d5c78cc8a5140cac9772f45775e5ecefb5e3abaf3d589c678d5285f4276877abba83d8e6c1e801a4181c4efe98ec43f07fae2db352ed0d3b3a889eefb68a1f81d0466c0892dff4e701ecf6ac4cdbc2b5013f9a139d2c86da08bae90914f9b05716b2d5d64b41a4080642414245b50101760300007ab3221000000000089eaed8a497195b60bb20a2018c90d6a3b61990512fe0c025a807480856bc0b09542021abe800b4fbbd69bca88a8f035f01cfb7eefb87914c47ffc004c65d0239855c8a11620c0c481fbe6dbb3676e72280e61d04c800713a278114158a2b0105424142450101408262077535aab5b60ab144ba8925ccafb33c30c865e44b53105e3394d91539d72bb3b0b1e3e78c9a387eeef3cbe5e8a44f41c46d9467ea0039b08ff6e58f8342225c0a6854a01525a9099df0fde870a033ffa4df1c6580418de1fa56b0157feac1e801bcacf9eb8ae1e15002df0478101c0600f04198b706cc0221fac73a75f271c53423326c7333a0a3864d260512d18f35ac02488a8b0c33cd32da937f823b9d6331080642414245b50103060300007bb3221000000000f4a5b6cbddc67209d463569c6680cc54af3a14861f87e2104f823e79d0a1c77a4ecc7078031ff657496192ee09b444ecca411ecf46fe1daa1c0ba12d08c5ea0009645ec36fc653c419c2dd75845f70d7d3e17e4f84102625edf36fe360f00c07054241424501018e6f30f999519ead47fb9c190c43529943aa7180ce4f4ecacce3aeaa8ba56406f9c97670ab4521b323dd2374eac2c7f3d73fec385e7e475246d8ab1114242b81aa520d55610042c6eb0b54edba327d013b6004dd07367d438a9f499c46aade1beec1e801aae3e1515cee7f88e1ad201634ac4dfb6269902d119372ec8b70d39a6abd4040292bc460588cf2b2b457b3c7b7328762c6e0b0c5e690140b4293a8a2043a1828080642414245b50101b00200007cb32210000000005ab312caaf915808943e912cbcbcf5ebae08a31b1fc97daa392554a50a9b40637b7bf71d62b7b813529fb5b8851b62c6a8f5c92d818f09994748ea38db10bf0dcee907ca0848642eff9110654b6bdfc0821934b47a4680e28edf8a9ff6bc850c05424142450101e0cd5a240d8f196c97cb392c90fe2ae51953961c3d453e7cf7732a05b8011538e3f7fda58db17f38801ae844bf4b4c9f453ddfbba1407cd91eb546d8332d7c8d5c133f7668af5ddd4d155b5c4f6dd13525a7544085679f9f57ce3946d8c592fdf2c1e801881768eab9583a574726f649226209da40e66578f8f047c5060daa0163e032bb3687717aa4b491608aaac66e51cf6f84c66fca7ef9dc5912bc38ffc572a3728f080642414245b50103a40100007db322100000000016166f09aeed27ac1ea73b3686ea26c324f7c4466de3d7a9b76e379d03645e7f8133da484861574a2ca859e791e43bcbce58ac39373019168efc57f83c00f702a44755cca5c45f078ba76041215c6e692c4602b85572d149751fb6d2db9d620e054241424501011420e113533634854a2ad613e5d66e99f6b934ef9f3dd3c80eb206981cb0083226fea0a8c0caa8d003f2c5a16a595213a80e809f8a58cabd71ecd937b5371f8d91ff5175434552badf412383111a283c765e10f181f9c335196fcb590cb84ce2f6c1e801e499f536d5b5f119eaf590dcbea350fc4dd9ae447f75af1121f58e26d7051586c944300856224cb2c5c91dc628a9989377c7b273f6ae7a70490b83dd4e144865080642414245b501038e0100007eb32210000000009ca373ed867506cbd1d6ee36b7b8ebca2940beca05d246266899e357c836975d93c41524549facb7acf469d3e2fd93ee96d09c114a0cf2301c9c494ef4c74002d3999f8f99ddb63dc01d2a9f8c48f75697fc876b40c7f10bf322b7322ec78c0c05424142450101fe8d94b9836f87c67c76850e4e94e669a61a2c28b427ea03d3e12c1b953dca77b001875223a585326fba72317fe6ca5a3a0b15444a30f34b83c6ddb4865062814e1b0c2406730a5e4181c5f5c3df18cebb3720a1cf9454ade6d2b68e739b780cfac1e8019b2b14e8cd32d64aef178b5444c8f07d649d4a9b6e7b953ea48013ae54c36123a7c3ddf4a399a14f27965ed0403be1fd1e3c0cebe15cbf7e61d0c950ec3147e6080642414245b50103750200007fb3221000000000706105cf95dd65bfaa67b057dd88f073628f2f75addbd0940e8b9db87897ce03ae42e8e3c609d7a0e7c7400100a6537ba825abede53f0b5cc9104b78419f880d5c6e5162b478f4f3dc51d87779673ee5e9d735fd72fceac59b75143e2268bb0c05424142450101f402ffcf4f19ba4c2a87255e4142ab00d156bde0bce767faef0b203550eea0548ba05f097b3566783734f683eab1b23bf24dde3dc5df0cbd0b7237f4b6507b8e113dfb90ff0d89563dc403085946206a12d43438fd30a068edcb976e628cdcd5fec1e8019f8dc4bfddc7d7ec505894b11b525b8419efd4ae707fb2b9db18bffc71dda142e590601f71ebd84e3948e71af27ad22e1522be67860865343db312fff39c6324080642414245b50103f300000080b3221000000000a6081d52ae2006b427f0e95820c710db8aa518b59080ca5ee8a69b08fa33b55c17bf5316b29cd69ad5bab3d69a225d7a3af5bce0404c14885ac6ae989908b0090d3f54f287ba2b8341bb4a8e300af265e2f1c2a4035abaea2f1299fcf8ec890f054241424501016ed65b37673d3424a73e84fc4ec3420d513b16383721d03ea0f406863238af4cb57da7a0b0c81ac1357f57ae3ac7af2588aea738a3c5d0798679ae53361fa089859d773aee62a5b4188d6bd5bef584e628437c1121d6d4b3ab9955855972a3c002c2e8016b47dc676a9fe85ab0b2b8960574ca895cb54bf1738c3b1035904d8fc3fa05d902a619893b373ef7ca05270f8f238ff314c05dda5f25118e3405c60b57d3c133080642414245b501036600000081b3221000000000020f401eaa937791eb5c85641fa64e963b2624cedc54815c0d8c8f8c0e3d2c5b9465788166e90e862865d2d75aef229a95e4e20b92e76dd75b691c213da3e20f2643def03e092c2fbc68a13efdb6b7c06f9ed1e8a7c10ab195184b32e7d1ee0d05424142450101086872648045282cee05f577c06fbde802c72bc501195732cece52922d651e3cbd965360a3132bb90fef433da328bf3b9444b8d046d33c2c55fa44920a978b8775b2f1802e688e08efea6b8a890b3ea3597b1d84113da712cf69bb5611b074b406c2e80102136fb82b583b9fc38978d26af589fc1c74a223cf3ef72a48f8d31cc6164fa9010058ebd36d5261ef636bdd6e468ac22315426a71a855bf1daa131a2dc3a4e7080642414245b50103f201000082b32210000000004ea22e93c1229f91a4c9955b9ac9096537028bd19b740265a61a58d27982013ab612a07dbbad0d4fe9d263408920f15d04bfff4aab6b8565e819268323ef5e034f6423c41d92671cfb090d7f035cf03984030b8ae1e2b9ed84273fe44251f10b05424142450101866ba9f07c0d8b4630a255102d4339bc0f8432b968943da0280c80548bfcee32f05730e3fc2eb0ae3c09bc557bfc319208b76ff39a18a5f0d5bedb56c294d58b620d3f4086eb7804e7455c965da39b98aa9de57c75bee79613e686a83122b5140ac2e801a2c2e667db90705b8e9511f0cf5daf548084319a99ca898b3fd559d326cf7b4da6382ab5bc02580ff7c2f80071b17148b358f52077f73c06cc71c46806c4025e080642414245b501033901000083b32210000000000efe3ad644e8688106afac791a29fa95645104254c5ebf76c2a3c40fee84b81616ff768c8d5e8b314cbf62ce8ed440e89f965fe580a8b5824c8787ba1fb777082e673d0c4fdcbc5379b6c65feff7e813b97981a92f2137ee985b52586a6bba0705424142450101e4fbcb32faa13da837a944d3a2e67871ae16e7fec728c5b537a8d3e0106c094f66888b6a66f5b4dd16cd9c2e0a75d73fc0b60d41877ed16a52b098344b751e86b05d45678747fa1867dc95f8e93e613c3c2051c723fb76536c3df7e4f0a5dee50ec2e801ed54145ab1c9bcb876c331439a07b686727a87403163f5420e565a986ba9ae218fcc673f380ff6e4e24500edee1ce4393028758eb96c13d1240b68502f6a2819080642414245b501038f00000084b3221000000000f66dfb2f148f9ad3faf0763c9a741bfde36746acc969023b3a3676b9cbd63c3d1d8ebed1c63471bafc708950165fab24123dc34e7178718b388c908f8ba5000d4c9af94d0904223bd9befa600088e32b9267c65c909f2cd1d1ad286960670403054241424501013c22e8fd0e027fbf2a84376d75c8004781e20a7853afa620f54a33b808f7f640e792a7f8828545fbdc74597512a5ab39637c4e412a42e4cfa2c5d123466d398895865a69aee44cbe76daf7343c63735da3ebbaffa6e5d58df539e49cd33d7b2912c2e801b850aa192e88ee6394b96031eb42c98a0ab713ce6d010eb75eb899ebfa61ecf7dbeddded819cb1df6c3d5cf9c92d5db61f740cfb2f15de42897b28df5a6ea014080642414245b50103b201000085b322100000000042f25f289e040cf5ecd0ec91e0c645e31cc17e3ad4f4d68da6f1448ba61f8a1297160b9f05d84a5b00e687c35fae07d9afc41dcae7b9dfe4ca526eb6f1b396059018fc82f5212feb00ac2ede2f8a9b6b765d5f5b3f4baee2a8352fe0bd4c930805424142450101e2e0aef9f2d8dc7050f6f26d36bc81a4560c4070fc5ac7424457050625802c0f96feb3b798864d1215e5ee391bb2519726732b10cff439c4d38f87e28fcec386e4fe7aac4356ae50d3810fb72841707ad9cae34abae890d382e63e60331127fa16c2e8018c87b645f584aaf22f90c63e6a8ee8e854b7eb67a0c04e003828ef4409e8f454d12b8528fe31bb666188bdc5d32146810815f9dfb7a43572f897549f18f4a9eb080642414245b50103f401000086b3221000000000eccea3df085cdc8e842e23191b1de7eb7dbe19d4c44bf39f6ddac73959b10664850c9f20b05e91bf24b8aaba19f4a97daf41dfde3b73313b041b1f334f6c2703537866a54a24fa9b17c12f4a32584def1ccb5deee50d9de3af7bfd82b0d76909054241424501019a4a3c28138e14095d4504f2fbafae91af94afff56afd218bafa237a83927c7d1b370a81e53535599346c10ca24174b97ceb029d6aabeeeb6c093543d3c0798518aade09dc2d98ea9f8f0b27a7819f0c151d3a4b1e4feba3c8e155e1cfc902ae1ac2e801d218dc7c117bc2c0a1af79e2aeb49ee1a0084fa8a2e0ea62b5cd5f076dc7506b1b1bab19a9f1d2ce9273f399e0e42c7e7bd39ce91bee2b2a2e5d08cca183be15080642414245b501031600000087b3221000000000eccedc361f8fe7464b484e9a8db4db5e18c4870dabda0c0c3ed417996856ed04b32a32f49831b12d3703989eef11c4d5687ab734ced936747ce8a079ead6c6022916e5f0ad784abaed057dbc9c149ceb200a259cdb5333b63d226164028f670b054241424501019c4943219d891cc2d3df7e66d2880e2923f74c4654fcfc0932da90da0515df0252ca733a1d5d9333918d0c9b2b20b1ad2ceb5fe807c3190522a7b4e60ff9858d3a4acfb859474aae9cf2c5b1a03ff2cd0817bf26dbd82fe1de4a657d388639841ec2e801a6c0b087b1b785cae1bffb9f9e35946060203ca1500c806c1d2ee89d175d27a6a9b0ae305dc247c8528021b865a2c8a2add6cf022a4e068352b08d6602ac780a080642414245b501038a00000088b322100000000002110068bff16494ee5d0dd075d4245e4c06720dd8558b3369c3a0385a4840272a5a0b4c10f5c6376478dffe69495a59c7e6cc7c0809e41cfe229c6daac9e409e48bfe88d6934950f3fcf4bd8bc6e03e36a9c1c89764f687dbe703f38579f10205424142450101c6881b0b52359eba75b517d4607736c7ea351b775d9a0af096c7cfc1cf9a291c55fd0d12ccf7bec5f2ed5e4f368d5e97bcabcaff365afdde448e05aa34f18e80417a23ddc424509d31af21d76795e14bd6f237336b9f9b0aa140f3efbde078e222c2e8013bde8f6adb911315fb58bafcbe12e446973fef0337240edf73eb978e99b3716118b3414834723c4120011d94c42934268840191f0f83b9ee9317cd34aa607d49080642414245b501017200000089b32210000000005400b79ff3ae76e3fe54ba3efcbb5f8666d3c6ea0470b8912036af9ee037db2dfd10f902e0d88fb169d20f20584005b7d1bcad222137f3e8cac69aa22bdb0b0ea5c7e7d05574d46dbc6521b63a4a608179d94f1f5979c5303e3980d1c1d20700054241424501015614d520105a3a81fe463e66513af87481137ffcf5db29845173ea07129d7031589ed8beeca132b747d695c035398f48ba28b008dac6723b8b243f00cd3a43894ffb2211066934d750106ca48b9d908445a6384c68f9301c24844eace5f17cf626c2e80188cb9f204302d3020772743316db0aea09aad4a8141d80f31443745c98cb4049c74614e8308996d77d2b10757bf95d5f0ddd6758c80febe6d0be0375bdcf3dc9080642414245b501034b0100008ab32210000000005a894cd11e9aa0d4ed6fc4e5affe4bdbd9679bfd834bab69906c7e9711f7ef0301ff976143752dad666f73c8d632899d6a47a4cd39debfc282423c79a11bf80296c97dd41974065b9b8ab0394362c1bba058a5857d1320eced8592197f07630e054241424501012a0f2485b4390a466a007361487c36cd033a4da90abd31c21c8598c6fc177645b3ce7890ebe01b373b7b35bfbc01bcb4ada7c1e9f96f399074cf9106d445b784e859870b6775bbeed494cd199b802bf3b61948653756077fb46b03abcb6fc14c2ac2e8018b9ca28bfb29635877dc02740f49cbdbcb2fd13799b97925611d8d1432df1615416b8dc4a1b2642c28a7b71ef0983ebdd331375d524712607c6936dc50bdaa81080642414245b50103d00200008bb3221000000000108b9a54247f5c36b1eacffeede6cb69a6b304d5f93f5b66084666cf545867217aac4f5a615cc0dedff9a857e60b7def2e6828ff43533ccecd81f0cfe4302308d144c065eeed04ffb72c55423c6dce8e1f04ec95a98c78d7d56ec07e8f4b510505424142450101e8a64ed2af1af14ecb0a3e33b93cff5a9a5c73a010156860927c218f316ecf1d436b9e28d2b42d433604a839c073d18aa3f3dfd900ffb09d411d87d602d75087bc0574995a8119a4fab1156fcaf06b4bef978a5ebd3ac57748d20e3be40675022ec2e801b34f4d86037702596c6db28393996075a0fd74fcb42f59523c6e46ebdd8b9cf043252e23e9e3be1422e821d747a1325143203f031f23a3e26e184987e87d72c3080642414245b50103d00200008cb32210000000007a62d6d432386b20a1da6d71a601c001406fb7cd054c4e20d355509c2dc1142c95de494172d8b2909c1b77e9b4d5a8869ce827f28b40ef6069652cafea1b7a02506147b90b01424934909f80cdd6c7d37daeb39ec19018f799a03ef72cdd200c05424142450101fa2abcfd126acb43be3481aab091dfbd994593377804530ded4f2d57904f372096af82b2226c18832bde04590efadeb0e9cf54cae685e7bfd3d39a4edb5af58351444b7c314b6ca88966448dd67b38f7c05b0f44669b74af985a48148cf7dc6532c2e80156f9c76116867f0cddd444aab686accc2e1987db0992129ae398262d5be85706de5eb62b0fc8939f7487d69aad9861007aad6b34734c78d6ba232a26321fa28a080642414245b50101150100008db3221000000000363a42a05b0b666bb0a168427e55a0d82a4507e223d2c7a41ab4de666b02df31a122e0f875ce8bee9e5f78e0c05e9afdffefb626bd3b9a29a31bfefd2452fc018bcb5dff3dbb81a7df23de97f7e5af686c40ba4e196697653a2b29defb40640a0542414245010138ff6dfad864f274c950181de8101d964c9ce0559e684050d5d9b5417954115d34c59aff2f3e11721e85d0546e470164a7b946165945caa42b3c744a5c34f98a6b634f4b2770a381fea7a660d23660d7a191d7e98bec299825959a9aae0631c236c2e80148c0dff0027e31bb80ae4e94ecaa155c6a159d4e0408b756e199609e0964e4096fa883075cdc3ca39f51890f1f6cb401fdfa8a7c079010e7b16cd3fa2b43f239080642414245b50103450100008eb32210000000001a8769ba36e0aaca62f11d893da9dc53d24091928669a702c957d4a71153fa00a986d27516d91ec3294dfb2262c78eaa795863b98727aa676a21a87e81a5920945762a0f008800c3471f02de1244a582bd994e9a7feb5053bb30aa3d89f6ef0c05424142450101b0e0371369f2ed68c576d4f411a1dafe706920179810dbf0470edd699fc4e05ba015e88da31c0f749e087c3a466e4ca90ef7bf6cb172243f517c642430d5ab81274075ef157e08300fd257ab30fbd54646f645f4fc2b5712524c89a6d9f53a6b3ac2e801792151b538c43a5f97492320fa0ed5f41a72966057278c690cf2b5af7fb6396fdfc67a56797c008281dee0ba139d0b61c1d3150d8405768e1a8ef0a96157419b080642414245b50103e80000008fb322100000000050e7f627c9c51fc5829ecf101da73c459a11847f1dbc6a83c0229ed3871a9d2340360dd4f192dbc894be387275037e2db802b20908408763437fa790c34a8101f3158dbee3e452aacd2b6105b783c0f31446340fe5cd61d1b339e9364897570905424142450101209a2f2af55e164502c780a1628500d5c831588187e9f6664283a5a117ca892dc0b74082e630ffae116a6831f90514379f64c9eafe29e68bbadc85cb028cf58c9ad9f1b67d9419434291f1d1ec96f31de969685a3b785912356fc978b00426c33ec2e8013218d7f43362b04365fe307229dc338a98ad4bd5d22ea185c2860a2f44aeb37f970dabfe26a04f35a277415bd659ee827299993f23cddb3c80846262dde90ce5080642414245b50103c300000090b32210000000002aeac25db8deb5e233fa26045a6bcd81048578b1322c5a75e4aae659787a2903867c6b91254ff3b513a1e2bf7dc77ce013fc406a2414e8305a0aa55900d0ed035ebd0b339ada2fbbeb5d7a029636b714c9854b3c672b6d53518af53bc12b110b05424142450101a658ebc69f2d10ac8c6778772d086835425f1beb0e156da172b219b0e0d626399dcae8b4dc54c54e14bf6808b526e719dac56f95ed5d82c22a72507f1a40a58cce4e660ee0424f3312872099e821a348e2de4d465c1b8bac7392b1ca3633008542c2e80173e18d576b7f622abb90eddc1915a1201ae98e302a9ed68aa73e7161d7a58e33a9350ee18ffe798ee1a0ff122fd6a2fbf33e64cd5d5aee96caa73bb853ed6638080642414245b501037c01000091b32210000000004ee9f9a8e6ec3c136eb91661db5a9b48e5c7cc2075be3b2cfb5c6191cc1bf261a046e9b0c2cd6bfc08a3c44cd9e271edbd6445a9636f08a4765ea7443b166306f6d891e8bcb36454194964c06c87b83cc0c951cb16c28b4597dab092f77b580f054241424501011275aa06e34be5d805e9953a07ea9458ed9a864faf326af6236b08cb21dc6301388ea1495d5a435af8f8399962ebac0561f03fbe784bf4a702f16697cae31f892ebbf8c8f3fa16aa81c3507cdc1fad722211a55d60247518fab3448c82dee44646c2e801702885ca3d265cbab20b744067957c8a521ce538baaee6052d33d8c8c36e2bf18474c6d281dd882231b08f54fd5075c30536f80ea13ea6298776b0a1bfe626fe080642414245b50103a000000092b3221000000000406460ae0b29e57a1f44f488ce90f9e0f153591fcdfac3f45ff6d158b23bae2df86ec6460725090a3120f5417d3bbcab9edcd997a5a22fd8ff725c575a390a0188f5ed812bc4a04a721c14b365bdc6073899d6e296605a073c0ec2363b09730e054241424501012032b55d18f0150a9ac851dce208c61a6e545018c98814b85a411d97fb1baa313376f2a6e5e08ab53b92116f4bdbe4878c5bd2127ba1a50bee967fcff82ef9827735d8fac063537da23bea31c59344d5aa01f547b173e66ea7121f19002df80e4ac2e8016f6deef6b1bf18aa863e227f79838a33f7c8671bf7556e44a458e60cc455c09692333a74c83c962fab13245ead249371f42429658a3ab1517c7a7b0bb32b3bc7080642414245b50101aa00000093b322100000000014b98afec0d2009f5f5382ec55524942c2c45c9786a163c0d9044a6abe1d3e3744e956302b716dfd6f495c02400abb3d9ebebf9e682d224c5004fb551bccd80492d0b558b8bc5b92a8d7932a70b5e5f29dc652375ac7f79eb5e959533c44450a054241424501019c96d586a03b98b95aabd6ea912ff6c344fedd728146c706598764a17987350c8c9b640ea8e3675d17dd12c895fd8098fcec87aa4f903afb796d8ad9f4741d883b58f8ad0f9b39e3aea8d8e90094700f2c35288cade446b221337ce525cb61f04ec2e8010ba72998abad0da3226bfdef4adc9440386892b61ac08a54410b012a689f5f4ee87a9188b6b58e2aeb4cef1f386830379df47b8643a68e9739427231bbfb634e080642414245b501016601000094b3221000000000908dbc0d304716feaf8abe115fd7471e351f7fe843594bb5fc98528eb3b72b38698c29634cb576c3bf9ba4172650e1a944b34341124cce6764314a21e891f10d1c87b42bef3e2e2735fc46ca2d9f3d3572829a4f6d2294cb1835f26b8f7ba406054241424501015219ad586f0121b75a09745f129fb8a2002a4fa6721bf42d3960ea5812c91225f0e2a57da6be8bef5ae008027fca45acc0963756ff835cde83d1d26625f2aa85ea1ecd44816766b0775439a6e4569061fa23829954edc2b7e5448e9b659d613252c2e801b9bdf25209b7476f033fbd0056665194a18296deadf8d7f288bf60a7fe8e6fdd308adb150bd6a6b302edb8fbcb57bf5a5716a4d9227229758bebb2423fb89568080642414245b501011e01000095b3221000000000b8c50e9656a0e3aa4dd28eeccd87377a7a413ae047eba09cf9d2622d4f3ee25c646a02810f3192988e36ad96c6604813bb6dbd822c249cb14ca02dde3c6ddf0ae2d5dc4da34fc01ce5bb33db503f23096109136d7f34fd88d69b8c3c0735d70a05424142450101b056ccb00d7b104391b52f25405488e0e663eed86ebde3bf8ff4a1a3881dc1521005819daa26b4f060db88008b8063c7c6efcb27f41216ed06b483a285c0c081ca8a88763ba9137a8a15f4dab197d3746e365853664a5dce2e536a08a6d6204356c2e801c6a13bee78155cdefde1b7b439096c707c0b65a42a9fcbcdfc0d354f127e6cd24805e82a317b424c4f7d0ff406d15add3b38e9b80ab8503154a5f0b1e406fb28080642414245b501015e00000096b3221000000000b22ab1871b8a0db17efb80a66c86a9b6f5d2fd194b0c5c2e720cdfaa279c4876d18295549225b56fe8d8f174482e58f52558bbcc8097ed6cef252bd85dbe96076c7db033c356d29694348ee5a304d70247000f365dbf9d4dd5c6be92c31d3b010542414245010132ace71e482993e46a8c660540389661f563cfa0cf22b7c5244ff8295d121d78a88b001437aa962d22a3a489a2205ffba87bf95e395270c9b52922e7359b888f297bddb06a384cb1199d62d214d0e1d5027f436286dac8148aa7d186efb4905c5ac2e801afac9d8687f3449100be9f821405a6d73f8b4ecadf251f894e6cdbdc9b70d951cf603e7b5db38a90f54da2774d5cbb081f0aad1950333d8538495d46d97adf60080642414245b501033d03000097b3221000000000a26b745c34db26c7a2a97ec1fb97bf92abdea5be35d65fead4e8a09435c5f517947f7a377419a880e1c22ab5dbe80ad590ff9ddf2645b36f62743867c2543f0aa28bafe74776dae8f8a411521db4a80c44cb22a88800cad59506b99799563c030542414245010122e1010ea439cd03758c31065ed2a96cc1a8277701803183012303edc2aeda1fe4444c1fd32e06440dc30ad187a63349231126615a355f65d6fe53733509b0884d8df76e2ccad920d2fb8d23d860d4f4df04afb9411e9e8fcc651d216af75cae5ec2e80147788dc9b1322310da1d9665ee699004fcfca520a7c7e31f43b1535fbe61612843d4019f2a864d5bd940fe1bf0eb359b14503e5b2554d07b6a322d9309a87b4c080642414245b501016a01000098b3221000000000d21d577982b826aa2fe808eb8622ad559da09ddc22beee063ad31714b2cc15425fcc44e99527d92eaa64ac6cec8aac40d516af7265fdde3ad52b717871a63e0a09046888ef054100204d8dec55116384c78f673ef16983197c17f3350e206d0905424142450101eebf74b9c03340b902b8c294b4d97bd257eadc859490e1a9b80c1067de282255be71abd3d406afc79620b49e713006cbb5ee62ae3213ae7909119bf3487bc08f4a565cf0aa69c6b2a0b39f88528ef0454e2adc08356b9ba87a07eec20ef3201e62c2e8011dabbfd0b2e02ec9189addc6dce16bac155abb85a902dec2683f56e609ec0e8da75bc7ac307ed343a26bbffe3879bb5de470dc9d6ec464a3be5ea93802987b8b080642414245b50101cf01000099b32210000000002ae35680b5852ab43e6da1f3b5de495b775329e717c87707e2a9b562e3307c6da9af88172378d37a9479318d8e5453d9d5d5db50c2a40e333624b8c813ec8007c03dc8277b2383da69049a0fcbeda86777370facf2ab6631d7894751b54c2e0105424142450101923a65415240ca7f14669f067f59021ac50c3ef9dd1968daa8a4833afd41d36d0f6a833ac2a3df8644e6ed615268a04a410e56731c31cf05e083f14445e3cf83eaecf859b17ecd3772fc0cd833fc81be850e37b3f37ec122a8673667f5717ce766c2e8017d8f1671d89a5a3bff16c1765a128132bb4f25a15257d945295ec87d5640f062fcf52a56c5a9ea9e486cba135038564bbd60f719068f062f8ce87eb48cd25746080642414245b50103110200009ab32210000000008a85646717444a9faab17e7bc29b03344afcdca6d417bd629a55467707f98452f67c5ac172bf2160fda91290b628329e1a138ec08a0e7fe76f28a6466a815d0632dc6a37996285d100ea9f08ad8d5584df677071e61912fb318787a5acdb330205424142450101d25741f456909a8b75edcc89653628d345a9089e2c336260f55efc48649831176ac2d728467d546a620035aeed4a1eacf07606d6996e9e9afa4bb0151df59b8c89c3b5da1dee9b26020b9e730afdfdfb070f986afa97b954037bb1a0d71664e96ac2e801dc994680cc0f075cc7fa29cca72f75f855b24b573d82de1a37f0639450d494cb122f550d4d9d86540708b1eb9107750666a1038627997883b336e4125fde7b41080642414245b50103450300009bb32210000000003079272cf2762f52bdbf29f2ab3110fb4d802347fd15e4a4a5420c88f093c07d8185598489110c1e9c0d742ca68529fe7efebafd22536f4f6b35257e96649b0d65bacdf20b481bf0b68e1a7491f9bb1089bc566c93bac0442514d41af6fd4e0b0542414245010138e1de26c4b7c0529def2add7c64314882241c4f32ee40c353e3f70b007b40479bbdd07a99d24b91966b3f856d9c35d63b47db11fd9085ce18a95fad5257348482b0847078895efdec39ad965417c9df6b71fa67fe16a97f48e36bc1bb88ddff6ec2e8011e9ff81d914b7abdc1cf91018e1f23010de9efd7f3210a7d74fb4c960d3f4f1e3623dc542e5f7919778f751c3cfd0a70d986b165e29f8fec56ee0bb8bd52fa5c080642414245b50101970100009cb3221000000000f0943f15c5198d5bcb459a89f8c756e130b4db489155ba73e1e5fd4fc46b2d5a607cba62010a1ddf5a2f9621a528f6ad977d5455881ecbb5b8857d0de3e88105feffb7e47272be2acddad60fda1c5ab0b59fa4aa7e2b3b588330fea35bb4560605424142450101fe9b293e7d7d1d7cbf09a8b83d1acd86a1a25af88c3f7c965c5dfe6ba23e786c6ec2f7840e0031c6706d166af2aa6a2be13ed0f6f3298b418302c59fe6a4d38f53c534c6c74a2f7c4295fc7f8ebb1b468cc1a88ab0742ca10524e0b1de1c0cd172c2e80101f0c814a9782f3dfa2d239a8ffda2c6cd83f6ae0d6250d719b7b72a1cd132861d94e133abf63c6047060485fe83a2db6e89a14f57a560eeb358919ca3e39e94080642414245b50103480100009db32210000000009802706ccc6c45e7b7d3117c55ad59a80203a8c0407ec2b830ad97cf6be0371a68f123494efbed3b64d57b473da40dfb92e259890b5b5f572bf3fdc8ea1c3c057b450d0614802913b783578a2c39c77b412431bd14a91317a63111f6e6df700305424142450101a450d926b8bb83116a7d838719c7fd09dac0695ca83a03fdfd69aa0273711b477d6565542f1f265d7f0d9b3eece289782e3569bcaa48ba09e96f930e62eed68ca4fecc67a8a9afb650ee4959cfe146d01ea091a7b30a73995c9a130caca800e476c2e801c4d56b83773f12a7bd76ab7326a042006fb4f28af16f8fac32440928e4a537eabf1b1ce199c8481bdf74cf5884018642dceb39e6764e754328ae9412ccc44f4f080642414245b50103950200009eb32210000000008c5c98747ec78a1d96addc7ba41aa9843f7a719f228f9feb81cafbc0ff1750704c5fe3310825db4055d81f7f125d735c1412e8ab12b9400e9f8ce0021bfc1c010313d6b84449e849e58fd86a78692a904b726792b95456321e13da99ab71b50405424142450101a0c1c733ecc09d06568c6a4e48b7ab72ce344bf0ed44959af9e1c3e5a761721c07b1934e77aa1866b86d113521ba808dbbd52b99c58a7b0cc45267e8fc4b468e5ebe6c2dbd76eb3bc256db74a315091142eb1856fb60621d77ca52666c49dbdb7ac2e801cc9057d2fa72e7ec32a6f1c601dd5c214465e1091422677623ef3665a7ffd65d61fc7e49c1df27867d8d20c83255725edbae905b5c46c1d8bd42ee355ae24f0d080642414245b50103fb0000009fb3221000000000ea7e094e3b65cc208b0dc161d5e1713090663c1ff8d8b26c75e84daa7d31ae2d8367ab269276188e8db581bcf8a62ff7512cd6ffa4c337405b43ccbefbad8b04a3b13f0b22a0b37fc710fd234cf053ea1d0f14e9589bea6ac6a82508075bbd0505424142450101e643baaa5c6b75422e9d1ecda2696c3db520ad37c503446a3f4debd30322bc368dd89dd94295219844c867f4e419055f3dbeedb60f5f320d042bb05f6f2c898d0beef0db6c0586cc44d7e382c62d87fc4890e8a3e2bfe41e18b4846f243c3e407ec2e8016f297e4044da15fa9980415b388b64167162dc12fe8dbf12a8d85a2b418e47a5312331dd552316364ef48208946e0316b0424a95bf0638ac1e7f530470ac6c79080642414245b5010342000000a0b322100000000000df1aee3c6d46fa2f8857a51c844486c11f33c58f897e7395300e1653fdf4276927f67b0254038e833680ff153c21b04a783273166a816a498d93592d03a90e508db5cf6005f02a16a94ea9cf06de1560fab232d8b27e7ae1364d893a7a42060542414245010180e5f69002b7b7edb79230e0adb1022e39ca380e859ce2ccd26b5373bf989f00715d637d99ad2dd6ad56b5c606fe0f664142c687ec6cc9e9bcc1400d382aad8194ccad3e4273ed427be76be68fbd7af245db5f1e47d6a8eda0f6b6d5510b4bd782c2e8017d90e01032651df23c1517624d0a8af3669c1b51c635af8962f64b436ab7f93b58042d956391ef56a2e4e343178a7228463f5272b29c1c7085d6b43d8b997350080642414245b5010366030000a1b3221000000000a2d068212cf620135d3c78043258e506af31480e4495f37e4ce2c6a4ccf6d41afd4e3c77b25b24fe4fe62ed3d3488887ae37106a91db2fec599982aac3aa750dbb2133665bfe73337caf78edff92cbca836bb37bb34eed6bef3495acd59d2d09054241424501011a50bfb33cbd781fc1795fd36f6f76a2a4acb6138334a0bc7e8e90ccee07967feee9ba193fbd0061231a5aae31aa0a10b608682954c4b07467a9b1c41d17b78c3e0ad7f488b9b89f41c279303106a15d93fa685a5d1ad2f3ddcc28bddc0a4ed186c2e8012f50ad8f9341d4ac2a4ebc43441fef0c0e657e96ef731cf2581010bc5b397864227c8ee685eff43e1f4d095973344941ae2009aa84832c4282c320a5a9c5814d080642414245b5010374020000a2b3221000000000cc1f8a4893f481176ab29d458bc853dd6ed6c132d214638abb499399dd013110bac7b5f311364a008acf7547bf24a767399bc535039a583eeecdc29c47572704f144d41c131bcf1cc41de9b463f6626d9e4885d491781902a0c4dfae58895f0b05424142450101361f8b38350c87f9c45d6badbaf46a14c90d3fbf95035d10b0abb0f0fac3d14717b53548d64c6e5fc7b487416c092b2837e887e0a89cd45e6183a42c9348338d29ab6c0e31318ec24db587c1225111beba4b3e1c5d426d068dcb2d1023d1bb138ac2e8014c661dda9cad949ab1ef54b56e8db9b589581946a4b87647a81cc231321a17e316df4b537adfef6c454dad2ecc7723aece3c354b0debc7de7a9dd94db666fd9d080642414245b5010195020000a3b32210000000007030c821e370db3ac741eb34a8117a588aec6300f355fbd7c511326fa558b67bebbe9b50d1997f0425a9abaaf21918d564d14effecbae7064fd212c8502b9c04cc6cc1bb905b32c44c77a1043aa7ef5fd71c15c3f2cbef05e22859a438f42b080542414245010154c304f115028f18adb18ad01c46877ccf824976ee98e0b09f7fd64650f5313872d4c32e59d6c8a45f0bb70892a5692f96c1d47f63e533af75dc6e95f6d60089dbf36e57b628267872c1f243a105bdbfb640fe340fef419b93045cba82f1e8de8ec2e801910a0b98e39b50f123bfbbbff838fc19724a2bcf6be85161fa580e762ab3e38732f11dcf3cff64e7327de3951c8464c0decc7974d01a6f3ff11bd243496163ef080642414245b5010136010000a4b3221000000000e2b457055614e683a5485031123fa5f6f13ad5facba539b92c2cfddc964ecb01bde86353812943ca0d9f796717ef28e09f1239751fa93c330ee9c43eb515170f83820b919e3cb04cd3669102bc3c89dead4ad598644b94b484b40276c5a8ae08054241424501013e0d8921028c2c08622b2804be58c32baffbfe2310504f369421d86f8782f66f92c7dd955326b631fe853a7b4c37c4ee386cd49c59b33ee0cf673314565f3b80a189c1aafceef7583699df7d131202306c9f4d361c509b409c5b951bbb72a9e292c2e8018e0df5ce84db5b9e413db26a0b1340e56cd07c808d3380e37d161ec98f38880eea267eda1e87848e521df7ac413a7b749ab1b855ad0dbb051ae693ae14161e6a080642414245b501011e020000a5b3221000000000147eebd30202b7a45ab9a95e72a940e4f353f7d57b6a1cd9dc0d0f635949956e34f54a3ec23da4f44a85031c3999b0a59db03eb5999e90400eb7b60230335a0175b715870fe41179fb360c703ec0d28c9b7791518cdd9ac94ce82a358de44a0b05424142450101900aa5f54e125e121576f5b8503715fa77f3dda7f4e30431291eafd7cc04a67cd5743f6b31d77ae545c98f772273a8a9634e1baa02f6ca2222fe5c1540c13381aaf2450bc864eed8f24be28125202e8ec25cb091ae497896724b61aae17797c096c2e801cd27f7373aeb2c29d9678a4a1935e6cc2042c2817e5a4c7a62b3e3139bd45b5bf586e951300673fe990e0d60ca8854be21daea32f09c7e6e4e038849f7095e74080642414245b50103db020000a6b3221000000000ca86576f14a8e926413230250748036071a0d41594cc26e6b29e338f0f21646b4d90de185dfa76c82fac06117603b9d370720f7947739d951ac48f436f6f89068ce7706b7a26963682ee9a4b3ab59ba2ae5df8debf163086fc22a805a3166c040542414245010174717c25469deffad45a523272048e68eebc2f6788df56546723ee3ef3c30355ac8b41c436afaa555f802ffee58b23c51175e9b22787795964661768952f5b826a5f46b894c6eada6e884789170a50fc295c25b1e96702b430d55859dce4316d9ac2e80187a11015029082926f13a42c41509dda0a2ae5eef169821332db8099e061256e26115823cbf42d12e42007d2adc73a6e895827f5a008a5f5599f783fdf1dfabb080642414245b5010302030000a7b322100000000040d564a0396b0892355ecbebca977353ecef8a2c99453619045ed2e0ea74524b33a97d48edfcd4277a91419d8f4a2321543cfcf2e7ab22e65056659b59d1e1004e7b4ad0a97e431183dbe561e8beda1f3c948242ddd8a0f576869199d1acd80605424142450101b495c889f22ecfa07ae1bbef3a50972fe92ad73b023c050024b3466d3a83247408890c8d98bc6437f68f4a815b3872082e14d5c4a02d40bd2b2c0e88fb00fe8386ba4fa7a208e760a6b840a92873935fb9a2f737087b58e1a3acaea127a64b7d9ec2e801f4033076bd16e2161649157fecab22f6ab153db44f471c0e7e94285bda3060cda49f8da79eb07a331d17c534f1367f5e57bd4c7600e7895d03a1dbf24345c668080642414245b5010385000000a8b3221000000000cecc2fcb9abb0b0b8a134ccb603cad4043051e99e5f554b3068cde2d49f4ec0e6e1637e6c618a39d3ac14502057a3666bacc87eb2e785dfe2c2df8c61053090a38052b0cf0eff7b86922b19fa4e7993612f32c37dd86c781bb510fca2f6f3e01054241424501015600e72934e5330bb43a6f1d242f09411e339e323a460e350d952d7122411f624eba15c6f9e6f3bf032bcdfc8c6577c34a60e259c9da043615d6d2e52e4460835491e5cede4c021f7724ffac1a6bd1c3899727b1d7f97c90116c3e7a03c42fc2a2c2e8015450866c0769ded8266f04e159c4e5f4b63a66ec030f33c604f576d2a876f0f3660c065f4bf74398b5fbf5686c8786e39323ac0b2073187f8e83c089299fd042080642414245b50103ee020000a9b32210000000001ac18eec5deaeec2f4f1924c27cd51595fea7f6b5a3016cd565db6817ebe2f38aa60ccf6aba0df8f042f177f9b36c502ac0845373e7f726b37279a554d9dbe0c7f016d201cc8446a53d4217dfe5e1856fb2db0c4801668b7ddf501528bc6870d05424142450101b6b084ba300686ec85bd1676e0fbddb2dd2f3f9c95dec41e31323e880e7d7e6a63e2d2754dffaf09f9a18e4ebd137c20e6e4ad4c8cc1858f981bac40b2c1c58af14a54eda3e6f1f2a892834f2a20db79138e6a04771b813096d4bb1ef741eb89a6c2e801ff4e52ed9383ede13accef504cde06a74807c94223c431f5f2daceaa13129f0da9cf6d1000edd1b79e5914a5a0c22991da4fc702489a16526d581a340cb1ec34080642414245b5010337000000aab32210000000005a748d1a722526b1250351ff1bc48b4896e1176fffdc1393ac61f6074fb1e9635d9d48067f66d76e6139d40eb75bb692fc91bb817b58647fcc9f57ce0afb27083fe742985d275b877fa7c6e3165bcc9a67f3c8ef59d0b7da9fa17d6788e60c0e0542414245010198025b3529d4e2f176937346be6875361aaab6a23aea7cd49680defe613fb169175c17c1bdee712f1de8f03484a69a035ace1c876df10a2b34111cafc24d4584816a81b6f36bb163ef5ac203bfa56d1407305e346554d00c5843d59f9f653f19aac2e801876c1caabb7206b757dfe68e90e5590bc8c9cd6e72e96a830a070f18aeefdb4053706640608c6ef1df16a2e4cf51b93ab9eaff16521bc7ec98ea7e2f0f884153080642414245b501032b010000abb322100000000042125338f53fd5cf26df32dc30e9362579cae9d653add0b7942bc709e82c562a5a38411875fb3bbb9cf9d6e87f14bca775c347a1f9ffd27404864aada9c04603d16e8a10c8a71afe0e9efb3dae72d1dad69cc3e8a45bd9b3d3a546bc29441e0c0542414245010146dde013505ad239bf30f1335dc8581dc1e972c2d15883361cb0b558f4b2c62a9ce26a724658e87c60e63bb603cf86c58803756a960410d04a6b0d6860931d8cd528086e06fbd22d4318cf102c2794296461dba103b0fb10c53b52ce32cd1aacaec2e801d02563adcf5c550d33a790baaed41ab28d556e115149805ce0ab8f36a75deae2213f97a0f0786e490756297b30f8908a56b1358b11b2f901aa711f99a7bfe3e7080642414245b5010367010000acb322100000000086f22080cf12dcd30f15bd1cde883a859249596e4c473d2e5dbd4a8e904af13de0b09e625b82b7ebcc89027d936054e4a278991a4709c78dce1e70c68f910d0fe2e429126265ddf8f6ad2e15c082a464e9315e814cf777f8a5edc286ed85170805424142450101321ecabef4e3ba49a808ff24b8136a083cebb3e400eb0558e085146cf8322212a657ef1e0bafa4e479ff5a25929e95c0c7bb18489e087b55233c57a9f8c54e8f2c1b7b03e996aaed1b13fb7aaef461ceef6d94db4ecf9eb49318cf8c8e749d64b2c2e801193df08cbb8cff32844a29bb9afcfe41ed984840a1f9caae6fc7435a55e7f2bc3a9e4453001e52844aeb512fd70f7f254edda482f29b5e2cef115a83fa4ece71080642414245b5010152010000adb3221000000000fea7c79a610de13dd0849bd60f286e74ac917202ae4e741487efa0a4124b6a3e73a7ccebd4ceb418b51aa8ed4e60b10fdf9b9ae889ba3eca78d48be939d18f09be7d1b3f6b37309db4a7cd7405fff69202be676eb535bb489335e50642e3eb0505424142450101825cd6da10626a2e45a0119304ac1b214dc1a1c601ecde50244843b59fe89e404ddc87ad343c54e254f4975bd0d64fb61fbbae2dd136004cf4dff89b7b892680a7b85c2d1a7197bc5dea65b15b5e27aa73b9434d0e6aff9fa8248d54560c2ee4b6c2e80147374592c07c8ec087b8bbb866fc5967fb221472343042eb9ca15910ef10cef6b33278c6065c2a18814121180ee81f6700f01a6469865c3ece1c872220e7fe62080642414245b5010303030000aeb322100000000026c036daf8895caafdf7dd55f384747a697d932e11b9704aacdbab70ef2993052fc388aebcdf70b458d739f80adda9faae7a901ce4cad2f117baf4d461131f07ff82f1fc9924f5c231430cd8125ee5c45b5e0edf4a0524bf121616edf9aab30e05424142450101ecfe06c5f3780dddbc2fb47139c8b52245bc5eab569232e414270273a4aa684eb51508a983ebd07d975b1f466796fe447f78635596b7ea7f042abbb107be3082120d5a7f7591532197ecff268a66cb6fc7f16acf640c3c37c08951275a55874fbac2e801139678716aa5b264c4122623faa91459351606c221e4ffa909ae955b8dc17dd6ef9cab2e6ac02a8f80e727fbe6c533089ad714ed2191d35085eca40d9c21940d080642414245b501031c020000afb3221000000000165086051e7ba76e5eca23dc5fed08d505533ee73c6c00a607b79e4020ebcf38f2d8c802bd7dec062b5adee97cfe113e7f0d3e8b73e5ddb0a54f61a031dd8d0489c8fc1deac186d940bb02568dbc4337a25ed078be508c88d00d330797279a0a05424142450101b6a3b98d36203fa25c852d8058e78c7cbe21821ff937464ac72c150978a36e7030971aae779c68c91e8551478fa0afaae49fd3b04c112b55af6b846c758d358f2f05a71786f297cd469eab5d1c8b33450fde075d2ebee5dbd3f1871b31fb828ebec2e801571e23581c134d76748a042b1a10c6862f18d7edc6dae1cc723f31432fb743b9b51dcce4f7ed20d34ad2c5778e8a80619a3da1dc55014ae5ff4855dc535b9d53080642414245b5010110030000b0b32210000000006cce5668e09e9c71849ffab9331736cb1327233e3a0ddd3e235bc5520b130c3434d32c488209d2f06a6aa8b8d59b3097f72a5323866c49a75e6be72d5e00a20d8f63d84f5ad68573d0abf9223dda96166b3a9e0f63342f089c6a8e6a5df09a0505424142450101a222db75b390debf24beff20fd303e5aa67dd691fbd33807ecc8ffe87122d94eb3cca78744563b1c59bda48eb32bbd3dec4946e61a285c77f815eac64f9d828dc7e0d627a5bb62e9f86c656f17cc981ab7f7518a7f079a87924f0c1ebb451cdcc2c2e8012f2a8734aa6b29aba8692c7bd6a2fcddd7c2786eb2c09e7e69b811eeb3e2963e4a8e25eb43482313e618abd74680fdad38fa2eb82a69b25f8efe6d50691cb6d9080642414245b5010379000000b1b3221000000000702ed7f69437a374c15625081a9f9479171d07dec6782d850800f0540214aa6684e813f5fe7762d32548766756a36ac9d94103d05825c3c52913490b4ee9f204f7adbe13173c07c421d07727335a63055561012cd879dc7dfb7bd34dd2e9c803054241424501010e598b9b1e6f53780fd203e7443d948d78310a1e07f04a9400d6b7556602a735d9718cb2ba2baff4a070a4b00bb291f2b35f692f56c8239ea45a45903ba2b189a7e1941d57a09bec2fdf5f0d5466a6dc5397b2cb8a203076b86030a00cffcb8ac6c2e80170e9d7b4452a0483b6a199d35212aabfebc7dc528b3d9a298c3c06e041383cd1df42e020dec05d9bdcc6d6e6e4fa1c4926d5203e279627173a566a67bdaf9dd8080642414245b5010350030000b2b32210000000000c8a1d841f21e1b19ac07a0286369bc07cc2b6d9a0d3c8c66858055a0e58970c049025d98d93c9c4adfd2360b6f4ea4f05606a1b891b6df0359ba76642dd4f007d4c00980af7e4dd55f7e038df41c80d980ab4ac582b12e4a134400abec3740b0542414245010102468ce383b1ad5a951f727f6ad3d2da457dc1b51e377aad6f2dc110b35c8619dc7ae9c79d3b699d932bf63dfa72f65c9f87b954c1cbed7482771e3c049baa84b7f56eefcce1bffc277e652cf85d929265a575633209ea01e6cd61490bfb2013cac2e801b808d3979237174a383996d041c2dbbf0acabd8cb7d36cda6c594075dd46c5e324733225f081dee3722b9c6d1325b8505c15b30aaffa2ba573d2c632131a3e6b080642414245b5010358030000b3b322100000000000b503bcdf2cf0eb49e20214d143d2c853922a5bfed3c60e5af92d28faa8cb40af0c343d51a093573d1fb7a891cb9a8614afa517a03e9388c68d817042eb3701e5b97fe9bcf852937a7ebcfedf657643c02ca6e3907ffb2c82ed6a6a21e92f0105424142450101947115bd7fba058b3d73488fc13a77783770a6061b615714e5968be48b8f245d30a7b8fa1cd7e2ef84c576ce015c9c04695485bed7f1e98c8be96cc3c621998e3e18825f829e2bb870baecd9e5d7a28a48a0ebd8ab42fcf10484946b34f37701cec2e80180ee4b51885b7c8f39f6b72c62e6b696a15c9872c0e3a59340f6f1b30f0ffd3050c43a7cce512988a9cb328d56d3d95c6ef5d3855c6a3d546332933f29acc31b080642414245b501031f010000b4b3221000000000cc05e01221f671411624ae617461b88e0a79a5b049ea81de7719827cdfdcd776d969dbc5e1edce4f9048d7272cdc5de25890ccda21fd7d0e4706a6f685d6a70f9867af3540aa3aeb3b9593ec036d37412ad448b9fe29e891641c4cc48f1a330305424142450101ec3ce8078126488677f84ee734ad6660ffbb44d582a06d5421883f01556ca0115686608a6351a1c393a7d6c2a89d069aa32dabeeca2a02dc9e8a9f6f1aa86b8fa7adafa9bf0b7e8a6b0e7ea0ac4c5105a2e1dc52c499e3966fb719d3b593de95d2c2e801762f2c5a8bcf4032013c30bf7587f58e4a573e53e4c1c860cf49e73db6d7fb497bc30b3eb35a69a97f2224b3cbdbb96052a0005771d51f8d3571fd6ddd61bde2080642414245b501032c010000b5b32210000000005cb58ef4fadde19d1127a64fe316b355191e7b74ce64723fb8f115390ffc774c7fc0d60ee5ab753fb1970fbbc00af53b57766b56a3c030ce0904836c7d030f0c64d620abe960983c52010bcd85b263cd7b06af3659de64d9be8ef7fb1dccf10e0542414245010144ef2c32849e909d724055e782aac140f61a23c2d6a2fa1d322ad2695e939b3af2a46e16583ee49c955b0fb7662f546ccd14b8410388848b8ec327fddb76d48e28bfd24e182c32432eb0a3e728c3468072d34ab94bf035668950e24df71a08e7d6c2e8010b5364f4187419d06df156a50d0d714bad7dad24041d037e0018ee5af19f3fc11934aa072cd896176aa6a8dbcc47b20e7bdb0b810f96065b18b80930701cece8080642414245b5010350000000b6b3221000000000de0bfe84666a54c3ee03a515320e8380c2d45ead37738aa8f28efdef91b2735e7538a495235fb3cf3d709b5023288364f5774617b117da21308e070998600b0280b15e54f3587903df8092cc8684d7ddc2bc585a781489b811f6c5473c01370a054241424501013c46039fc76ccb715dd05a65d223fdf3f05b3de39ffdba9208e496a8322800027047a23f0ae0453d38fd553703735ffa6cb84644d307b5fc198e6502110a5286e715195ce958fa3e6fde0bf71756e204204de4f39d916492d8a23811802f45d8dac2e801856a761d5449adf8040ea09795785a114920f36f0d6d8a4a154b19ddbf8d818a0a9d40880f9172729501b9126c7693d42f5e6fac45adc3f40e8258e14dbde413080642414245b5010338030000b7b32210000000000ce9d8099695a64c68d9c6d294e6720b26a8477f9b58833fc59c60572dd9e362a7d99fb930fa45faf825a1c7a0dfad0044a6a13d3edf4e9f5cdb392c65044b0fe43137e7382beff0f0cb4436e48346d524eada6a4089cadb20f129e43d421704054241424501018cc06e0326af116ae88150eaa803c2652541d39edca604357eacbd03168557466e8fbce39ab206a2c2bb155e2f037852ef959a11cd32e4eeee54841c19caa18804a7f9ec1ab6faf0ec8399db17170654aa3f0cf380bc60ae0c74e23644569808dec2e801fc846791cff041cfcfd76b9c04cfe65dc5ce5b8cf6707fbddc45028bc70f05258020452c9679502166dd05edd69ec7003f14c2e004f5a595cea7f426ab74df2a080642414245b50103a1010000b8b3221000000000466c9a042a0739fe9d33f2446ef4aef83b2ba750b87b24eff28fd6a00a37f0350a2134f0cf098fc032f3a94e5601070c3b6a4c46962c2f448afdc002f55d090ac012bca964eca1c274998278fc2b38ed30f1d02dd6da8b7ea92066a1acee040a0542414245010118ac92daa7c0b48c50eb2067bd39637b986171c12382f3e8ca20e95e44adde45f75f1a4e0f6f85492935e799890b4bd4827bc17e32bb66f8490cc2f8526d2d8f9587c626c19188057ab181aed5340a680883707e5b2810cea73182f10803ad76e2c2e80112340c8ed9718c4a07ea96006e97ecb475c50be1c565d37c9dac0ad7bc0ed53112bc8980f9d7bc0505ce57ff0a9f5d61c7c4e2264e41d4c0a758e8344c4ceb14080642414245b5010305030000b9b32210000000003852fe28d9df1db20f5f7314e5e65afa425d1db179d7583be792607d0b0f9d78f51c89f39cad2afec77922cbaeac8b956e5719477ec21b0b575252cda82e900f1bbc553fde9fd9aeda1bb8b57ed686c1f721bcccad8b8ae1eeae81a543bd90080542414245010116d6a6a914864eb311a6180bb46088f2278ba623b7bd0c64001c14bc51fc1c33a78e5767bb8399be3e154b6a79abf16f297ac3091e5dee4909132540a106768d267645a451342b0e9200408a55a9bd7fc9bc1883b707ca308bf3336b9a4d071ae6c2e801ecde2028b0c358b1c2b78612e7d8c619fec1e55185a38fc941f12c3b2b2e9c896877c30925073b6d183f4e5178c3565320e15ff4782ec21c5e077c7ba9be66af080642414245b501034b020000bab3221000000000d2b4b8b9a4cd3d8d53e26fa17025b5878d48c5e5bb62335636afc3c46d97cd5ef330974d8aa5c371dcdcac4107f69b058846aaad223bb0d1912ba7ad09337f06d5b115fe71ee2c555b0e9203e2c038767587e6f8bbc98a4647b3dafa1b0ee5020542414245010152c6525f7fa08512b328932453bdac4de8363a798fcbbdc9aa9212c9f9be5738beb2c898a2ea2c1d2a8430eed09d7d8aeb7ee80c1c3ba3a947d6612044de6c82592662550bceefb607cc489c2cd6da99f6eb31e7167ae43e62014daf93a16ce2eac2e801954024b66cf275c0523fa792387942a4ec8cdbaa7b1a96538a5425aefd2e552b4c8266042930236f0f8afaab75ee0e072bd634dc6faa77f02312b7d488cb6614080642414245b50103ea000000bbb3221000000000aedc397908ec9685ef3ff042faf14bdadbc3a339ae825413f67ae9dd5650c219ccb2a2a63a3cce04b47a32a7317428d55d50f41d44c3789fed77ffbd7a64c7041c7f04eebeda5d7069dc20c5814ac58f12793b4f444faf6fa04f33f39adbb40605424142450101c8d73cff8ac2a9ca2122d78653573fadbbca47da0e21089db503081689602f6cd1393a976a6ddb06b6ad05316627f75bb1609fb87672b60dda0f91fedf33ed890cce4e1e3e276b7a5751e6bc940872865fa760bd2950b46ad91b84cb80b33fe6eec2e80134bfffcf4ad42dfa2ba4e8763f556647dc2dd57dfea05e997cb756e5fc3d9f8e40adb2f56ba5fa79ee6383452506c22f5a44d1c3bc41871a77c8e1cea56fd37b080642414245b501035a020000bcb3221000000000129c8dfc21ca9b7cd151c10732fb8167d7e8b310e00d9e54c61e1c59efca31563b047b2932906ba772b4ac9e64260cd1919b53944f519b4fef2b5244d3deab0ddb50dc2c7cd550d7f7779f1501cecd6568081ecb3ad81fa96c14882e9120830f05424142450101086d7027d2d7a0c674a85d8ef04f7d954f727bd699e4f873bb8f19ff0fe80e474cff208151993923e0a929ad7acf60629b32418deabe64f98b382d7ba9dbc386d9df7fb72a3f4cf3d9af5796e370865055bcbbb904c4963ed4921be797052b0cf2c2e8016f2a78daa9ba7b2cf19da92574939626c80d33d1c38cb7fb9eb6ad2b51796057c3060bd82855ea5eacf7e60493b166d9d7a0ffaf21f752ef9ee86caecf414514080642414245b50103e2010000bdb32210000000007487432e8a82a690e06d94ceb9094cd44fd43760ec3eed36b7fe74185d4ab70097956b3b9be373f85bd8a426d39c40c4d464149bcf0d07a73c0b5f6dad9cb50aac4fe701604de29491f096efdc7ee223441a217be447ddfa0b73158af56e290405424142450101cafcfe7b548f7f0dcf74f19a9847c31409b7774c687308f23d38feaa7993ce0cdb0587b5c2306a12ff822802a34c7fe928a2a8250c29faac4948556c18d8068d435d8d517499089b8133d603e234daa3d934c347977fc9201ebbff50b73b1122f6c2e8012b74be8e571d584aa52c162cb186d2a8663b37f3115ba9097878bd6fce27600c2e5874b8c9d1f68eee0142aeb47f264c48b430f28a0da66aaa9acde520392703080642414245b50103fe020000beb3221000000000ca8f0c6e9750502eb2591c6675fa656187e02ca0394061996559892da4243c2278b4f813b7e3cbe4fe9410d815ef4b99e642e9591d72001fac2c667e330a670adde223c16f6bddda3f386a26ac0db5d3c83bc4014fcc89b1efe7ea28ec93b308054241424501011697c488b224bc212e07cc9c3812b677a534de0438e09c84ca950937e13c405bb00c8078b965ce1b69a35b7ccaaac301081baf66e72a65d9b13febf2ab686a8882964ab14eb3aff7554434a550658d5186e45bcf2fe936f3e7933878daf3de96fac2e8015b4693a05e4e404db3d51dd4eff4e4fba7571b2e2383662a0ae6ebef2903cd85e50a1ac126e74a2c5855ecb63596d8e4242a55beb841706fcf142504f7eecc8b080642414245b501030f020000bfb3221000000000544e216a0e4917f60f9bc28a31cd873e375a6eec3fe61c5ee1b9c3538bd2660038d66c7be1536109205eb83c3365971615dd63fce0f023148de4800341c91d06f2b4333e8245e8943a378590ff669702ec65de7e9eb85e2570f8030f6091b908054241424501013019dd3f8768ed73277b02d265e4b02d4f4bdf33d1aa02eb41f2003a3eb31274c4a205844748d61d30f7e32479fc2b7bcb4c5508a1dba58e6bd8e2fc5c7e32828fcfbedc3e32835628a9f6bb7d49d29ab5b2a71e90c566e31094c075adc1ff33fec2e801fd5a133b374fc58130c7458df4297c3caf04e3601caa2d7ee06520fdfb945c65dec4c5da6f43cc987f2ea922c6c8eb8aa1907983b1034800d3af3f4702c028b3080642414245b50103bd010000c0b322100000000012e891a3ab3ac197afa4fde3acd7109b6cbc1992e9eb594c92ba2db5dac8db77cf7aeb01287d8616dc37c5cd774d5ff0010caa1afc671a162db11ab42543860884d6d5232b80c9293329b3e32743d1194c87fcbf21e30930040d23c81d6cb10e05424142450101c0973ad9732836754383374835b33a528795a75c0e43b2ee07c7c472b630564aeca37c21800a8c2d8465333ba9d2c8ffa7389fc9e42eac4c69fb6ebc7356eb8dd52dbea48dbc9a50b0222d5537ecda15ecc1d177318ce5130ceb9e5796a9e26002c3e801b7b11306e9ca89af80099ae84a52988e9bb6b49f44b5dd9e90670e02a67903c186d7f3631e2769940c8309be3fe0f63487b343d057fc522ccaea28ef346f9cac080642414245b50103fb020000c1b32210000000001454e562cec56c386aa6c7561d8d791c399e1442e62609bddea3e98dc10f4f24af4dddf4bdd264dbcd2604c38b89f4ae901bc41a87356174ea7a278560a9a20b73040258f1cb59d246e9676030869999d4b14bb73ea4d703e857df79ab7d1108054241424501018e6319de2c68b71f3346cc7a4cf2c0f908eb4c5b6498e18565cd1f8f623770652016c2d81a00fbaa7a192fb3f1cf6124e9068fdf61f998984097ad3a3f221b835c18eca5a4537ce124713d6f21d1dd777d119d545c77dc63f20968e0cd103cc906c3e801014165008c6aafcf9de778f48cbb4f48087351e155bf104fc344c3410d816c7a943719e4fd4a81a9fea96b304c5db2b818df45161d052597f7fdfd6589e995b6080642414245b5010315020000c2b3221000000000a67993e3aad0c5d36f1ee86010706c11f7943b92ab8acd113d38d740d3d00569d631189301156b13a861e637acaeab0ffe592ed3eb1021d73fdcfe8a1083f208b84aea24a204a523f945e096a0fdde022d31f536919fbcbc9b6e36f85ed8530d05424142450101c694b0d04d0bfb537adc08e6a6912d3efa2d54716c21826baf397683726323719ac32b0f2fc8315c4b49ee3fdb1056d07eb96e9189fe3ede91dc39132c8bd4807c236e46ec56c002ac72e158908d97be151381ca2e8c3cedaab4d75e48bed2d60ac3e8011d95e07cac2fb3f646d80b08007a5cb81358013402a7e4ba8418d3b18c343a67fdc64b5298f1703ed088097a5056918e72ccca132a37c71d743f1aab6846566b080642414245b50101f1020000c3b3221000000000e0d14bb7461e7157e3437735ec7d035560fcd4adf594b719c2affb9dc6cd3b39dec5b5c1ee6188ff4a883b8f4fc3919a700bd9e12291d24046c123854c36bf069af3f164af3f629c8d9e475315e13deed77cb1750b35e3504c3d0a1a1efb1c0905424142450101401d20d9125542c1d6d60ffa6a0dd68d7865da6e66358ed00b8999877823c754c72baa3862d36624b8f19386ff50e1d30fa21a4aafae6e6bafce37f3d20eae8ff372c858893ae9c0b62e385e40293a1cb7f6574399549718a2c1ecc8e81a29fe0ec3e801a7ce801d3b1497e1e4d2d2f3d1eed0fc975e3a07750cae4d0d5aabb302a50244a19970e8a419301cd3311eda29b51535964ed0dd0dabfab2011fc2608c87ea29080642414245b50103d3020000c4b322100000000086c487ecfae534ce454dc1f8ee72831c6802f989df2a3f685452db629f2d4c2ddd4a6bcf431b7aaac8d7e3bad32b0d47cae2634a71887aa29be2b7d427be5b03a30d3d40bd88e373b057b2c4ed21163dafae241c8b9e3e28c8e7b71a228b71020542414245010158c3c151ea5eddcb3ce1c7c7cdfe08040e20249338a7f687ff1c65f8a70c18561f4507cd8b02c110841944527aaafb7498369062017c5df8cfb363c1b554638361942d689b4db3364ac9020549c4111f0524513a0a5d6d70f2b30148d7a4fb8f12c3e80131f98ade28a4953ebdb8d663bdfbd17e81087e4ba5327cc3e3ea042d8d719d82ab43bfdcebd63b62f1e14526a75889887d3348c65b5e4a6ccc36ebadc8311740080642414245b50103e9020000c5b32210000000003468d2acfb794c211c92332e7f6ca5d013573eb02746b00ba0c59db29d36b87dbe59e90d74ff6252e619d41962a645fcaeb86481cf715eb715f6714ff698ec060a5f5ca3e4c498a8623aa8e614d1053c211c3e8367c52934c71d2f8d639c090305424142450101ae3fa5745cfe484dd8c3c68a3f6cc26e53ebd5d6e420929d29ebd7c346d9fa55568515edf78e4756e31cc9cb3c82f2472de20c14197ab344b1b62a8f9bbc1888fc421c6388ffcdbe9ddba1f6c39e5b27eab446fc0b5882c55c433847bfc65f6716c3e80142fa95df49c97a41c544ea2613d0e107191c8569a98cbbc44838df7f10dbf10e5cb073c55ce23722c1fb98eb2cf5321fac7caf29e9ab3cc3bf86b1129621fb4c080642414245b501030e020000c6b32210000000009283eb38f3d9e246f89993fbed545f78b7fa24aca1b4afa3ea625e0e1580f63c03e4743ac542f083a0c546bf0ded18e0f233f0aeb4fdcf8263cb235f50f394024b2b5fbf0481cc7c633038e2a1bc78676a48491a649d912b3253d4a4d6d4780e05424142450101c88e2226c7275f8ab747ccc13ef6464c1eb6f716597747f0cac84e11e6a6b2251924ced347560b4b7a57ac434a9438d1a3cd3b80e8151b8f623224e059fd908da97525eb7f3fd641fb7cd6ce37f0612828d9387da474fa034c617077aa0b7b271ac3e8017a5dfc09053b29f512837c63a45dea571217877805dfd538122a2b19cf783e22f3471054184a2165493c91d8debb63b72c0f258480f7cdae448cd7de880237b7080642414245b5010319020000c7b32210000000000871c64910167623000fb85ba23c97ecd1213f079a7ebfbb82cb7ef60e37ec27eba0883440c431f6ba7511040ca125c9a65215b4411663c3db72081e3e11410afb0fc63914fcb13a7c8a66fa9d577c4388ee5e649b1e260bc102cb389be93a00054241424501012a00c9ce9730bf715e4b62032364960f7c05288d06121b5efd91b10af5189a3dd4544110a1c44face41fa0c439c8a9c5f144c05086e6b957d810d0eae5b9e28d24a75eb7748e0a6654ac46123245e339f620a953113d5f5e36a410f32ecfd0b11ec3e801a2d6852659c092de927b0ae9e3854bf47bb46350639dc08ebdc7f87b6e74133b31684befa202aea2f7376ca83df243898be1de296c80082c444e976d654fecdd080642414245b50103bc020000c8b3221000000000ee83fad17014246f8074b4d09e740a8265b5a43ed1f0730583065f6e84c8660ad51fdea0d0bcca28a9e9a16513765b04e4fca00f12e58e29333df2236493bb00fbdd42d1157f30923beb3bcc2cf138797f5bee948e550afa6fbe3844f93eb2090542414245010110e02616de0d5d27be13e7de76a8e67cb8621f8d80ef6c080b6eb05b8d9a0a4485e495a8a3458ab415e4cb8d81a134004b29322274705bd800f87110e28f75822ef972cac1f7037fdae454d13b6ca777ab343ad22cc1394e35dedfedf1cc211222c3e8016d81f373688432d6e99c4acf6a646b3475e587f8b53445a4d07693db3e03b4d5b0ee48085de341530fc65808071d01367ddbfd273e7adb736ee8012fa667be8a080642414245b501012f030000c9b32210000000007e80172a3843840fda6705ad5e7ccfeabd765c36c499025c20bbf1f649de916dabef409e0a2d5ca613a35cf9f0cc847b684084ec2130c27c56ba32a4e4e59b07ab7eefb720f95d203752803b529fde31f63bcc2d2b8b4c8aed53b1296bc90802054241424501019e2a144581a36634ce8389e3760f7de42ecb7c46716d560d65b577c2c2c8da2bdf0b94fd062885a9dbe7be7f77089de9cd81bca73e4f22c1208d7b37300d4a83aa8e7f214098a694efa13243aa77399b94f6515f6e41afac90bc5c81f279163226c3e8015fdf2a0f0bc66750bc74323f3f77c5c2810c0ee8de204d33acc6cd6126776b99ce41514d242c391331fa4deae67dc3cc3f2b760a24d411b2db8dcffb10b6587a080642414245b50101cf010000cab32210000000003cfcdda1a2ac1964d160dbe8d3d1f0b16ad6756ad69e451a9cb52ec5246895147f39f9601e7d3f1d4ab01326e388133e3a359f5f52965333c1ea972109340900fb26ca29bc6754768189d1e9371e17bebf9d50b6fd3b220bf7d1280efd75210105424142450101726613a3fb44769cc64348ced3bbd97b1496651e9a5c4638576bcdd066ab0f1eb73cbb6689619f75ac719c3be992aceb6af2b214c0e1321335b4a5f8d3e6b78dc6849c6ed51b4ac1c729a2f69c7d247331bfd79c0df38f9d03d8afd137a7b8d12ac3e8018f57a45cd12d95f808933cb3b31951e87568a2e425dd24f25ee0dc9d629ab7b531ffb53687aac8cb6086abef927fd8563b85aecc599e4f2337f011ef2c990a6a080642414245b5010381020000cbb3221000000000faba838c07d76fb2ce8b0ff64aa6550bef32e07dbaff8fb1f6577cce74938a74966d3640d46edc9d5f286572ac7ea9d7a4ac704ff2a75a1fd1096d92c49c8b02526d36ee01fae8a49073175bc288f19a8b74acc147db31193227d34e9703bf01054241424501011eb31b678096fcf6bbbf4fec5d0b3e0ba8e2cbba6550062e06b6b65eee922845716c834ad572cb2c4a7d1000a6f417bfdbc0bf99c98f15acfcf1fd210e67e58ec4582d72e8efa66fccef57509151c158ede59116117f98f02cc53c876df4ffc32ec3e8018f7e3d951e78306ab31d83794e41502f8a8c515437139ee62913f95dcea8aeb66ef2ff6cc9e88239fd8023e58e20340521437f1edd877b1d31262465cf95e4e9080642414245b50103e1010000ccb3221000000000506c849a2326539db3cf50ed3eae5b3e2d8fb8893b3a184acec9c782f114af1464d869a938ed65a1477eb0580a7d0a7b59f3a4dcda0fcf5c15a356f9fb771a08fa704069c2106022966849a18b49c53302d7474a5573e78644d16fd263428a0505424142450101943df2ddad108ff5a0df56d30275ec899e472d5a718460d7121f1601c6708e77ea32ae564b9cfccc207a1df4a4a033c6dda8063787bb7bd8dc4c16236868098062640bbe1ccc02860afdc62aa94af13e3d9e72fade5201aa861ac05819b3575732c3e801a88acf2330605477a270785cfc90befd519496b9d3b4e7794986108221911468254cf44dd05a262c8403015b2d5b3f2d4b96b89cee4aa2c58bdf50b3ef89b147080642414245b5010363020000cdb3221000000000ae07acfe065180b5d8b58d644ec296890d3c62a4593bf351eec21343682ed843de10bfea8ca1d448e19979e3c800c846b189fe796a37d5c8d69e9dd8e9f50604a7c27459a6ed6bd8b4f2890b00671a14033da35cdc942458a5f2d39444e1910205424142450101dedb21dfb79678fb99596b1fdb5d6abe7023375b22229ea5e789e69711796e0ba04f3cb16c39e46c92beea4fefe74b6c211edf6a6f784358cedd69b2102d65854ff1dd27f1bdd5dc42868940f1ecc5f2849fe2acf51f297016588087badd7bba36c3e801b5e979099b1365ddd73ad09999d4af528141485a949b40ab076d4a357bdeed9f10f270ec8179c4c1813a5adb7ef62d490e6a4fd7c10221298e2d091822bb40be080642414245b501014f000000ceb32210000000005a5b1355a81e3b9eb2e5029181d69088472ed7235a33410d5111b77e8fc49d6931e14e27b7b427f3d9deed3fb54b898b34e1da1ce993247d02d0c5f2cd04d10e4159563040ef1e28cc8b5758c5f3032e53fabd62f4ad87968293d11cc80b6302054241424501016aacf47c1febfa7275a9ae5f6af0a859102a82435f66d6ed880dd3ed3e4e3038aae42ab4fc270a683fc7e6abe893b85f59d8005196cc57b6c5ba031e2fbb5d88c76f074c14bb9ab5f66e8b49b0096260e5b51ac4b238e2d531e8eb7650683fa23ac3e8012489b239201f9ba90fc760aa52516b79dbf6ee87e7293df91717f662f62c322a0f12801c1bedbfacaeefb6082c65b6724544c767bee8ed4227e88aa356dc0a88080642414245b5010397020000cfb3221000000000801f29e3703b547fe2f8b4dbaa896f07986c675c7bc920a901eba84b8501de77da3064905e7f56d993430372d3eb626513cbe3fd18747dc377a44d707af1d50cd51998085a6383a28c7fabd3887fb78b49ccc7109ad0ad6e6ca271eb26acea0205424142450101ba170bc042ee40f4fe7005fb4e582a0d981db6bb33c14d1f0a6f2685d21a0e4d3a5c61dab0726fe35f8c73cc6542ebb05fdd1bfa438f201e5407b0039eab278d541b1b9f22c891e323743e82a41aa964fe3d8edd0de6278c152e7994da411e6a3ec3e801215dea85fd7d61d965b30c3d6b19498e1a3fb5a73ed6604cdfaa3ad2b642b0fd856eadd38bb1911205d41d2ad4d5f58045264a6eaeff4852243617ea6a6facae080642414245b5010355020000d0b3221000000000307a71224ff87f42112539b12e8adbbac7c0d0d448a390438642b0c2116f863a25fd575978f72fd9b25b9f36c3b8b99795c6c0abe30757f408f20d9ad1079e0fa6601ac201f29b4db807958e4c7b667922e5796907640c8eb9facc2ea1e41d0505424142450101dc057d3c8620c6f366661671a7711dbe7111ac0b823001309cf6c0f179abc86313d691d5d1d41af0b6b5c141aff3c24768c62a5d823fb133046f676b23003a88688a797557ce44f9eaee8d99a57e137bdc95d4840b195e8c24404224754ad1c842c3e80113e5a127aff08105a9d8201231eaf803ce70ce09879eddc6f2ff74354f1183820c1d614ef5fc579ad593d6a996d5dbc4e01b4937ec38a54bba3abd37e9871f11080642414245b50101f1010000d1b3221000000000361e8d2867caf7cc79fd52a109c71c19978749557a0d120489955b13b91d03133880287f713933ce8abdc410ea94fdd87f290f0b789b8b7bf639a21d0980e806c112a120d4898efb6b284ec1f014bc123860caceaa838352830055c522785d0505424142450101b02f081b2ea7b95d9ad7b431ba0df181f01484ea97a5b24b75af764e60f0d81c1450ea5f5fd84a31e0f37fd11b9d12687a3a6bbdb5cf586f522353a375e7c583edbd57fe19dd5ee2bfb1f2eba2080b59a6b7467ff6f4bbc2884788359cca8ec146c3e801e9dc4c4379bb220b1a70e638fcc8ac89d468e3bbd20e8a4c8a3290ef5ed2563d3cf12a45dea2dda747d90cdd3de517697fb456616b596a1d3fa76ef2525ef73d080642414245b5010139020000d2b32210000000002c70fdd366e2d937dd0f8f15bdbe709f4a422f12462ede206b0f3db99a93753e2252fc3a31822bca0f4a3790b668e602a91b16ee82b7826db4f2cf17a81e3602aaf0501747d818b4206ebf4fa5222f6de4801fb96a3496c3ea7b5cc0c87b600205424142450101886bb097960627ecad862e4cc5de158dd4d2f450711192176259ffcfeb5e4c3c1014a24fe19cbc415631fefe6f835edc332b92cacd4c09e3b5e730fe1bfbb58944d5e5cc7db43b109e312a3c88464edbe2f410227bc5819b13a08cd9cd6be2cd4ac3e801a111a2ba44c545ded9e3f47f3176fe0aaece37aaff9d66076b5ca5261ce30da7077d7628369b77990d34a1cc7777146d0d33f39045c525b7d467fb3c85379e70080642414245b50101bd000000d3b32210000000009267cc904567822350349468013549e8832bdc1f33b1e05012956b85def10d6abcf7c7a4f35ef7ec244547fc218661928017f04041adfb12c517bd45d1326007af9e43873a3f5ac497642658e68d8b0e2a4792c4799472edc14b9be0f7b05e0105424142450101ca0037c2cd85c1c2bbd7f8787b8381f1dad38c0455d464d535a549e433507e53df7960c005e68bf58aa23e963517b3e9782753820c4db0256402c6ce688ecd893a1a57615bd41f928d78919928a099b3b649510e9b9897b8bf4974e382afdb454ec3e801c04458710c3c7075cce2cc4d6780c85c033b4a0a52b4c3fa51cacb823870ee815a3fd15952f8b395445ad9b7e7561d395442e37be07eeb7f816408d45ebaf788080642414245b5010399000000d4b32210000000003839937cdf0ce4ee1afff8071357450fa22c06e8ed248e821b56f6b8a9df814172b015b1872228209e92d59c6d969d2ff2f24c49c74102326d7eac04da4eac0594391d321632515bc0418b5cc62cf2d27250a14975e0dedbc59bce0d6e7b210005424142450101f88a81780ae79747d4239817460e9a8f6a96186bab7c4295bcf32be25d257b4f0df8618e22c5bd01cf6f352d2590cb520b2618b3679658b9dede7405d3f956842cc572feb0be9d391dab27489a60ddc013b05017746006e3a54ab686d93cf4ad52c3e801ae95f629caa89fd1381320a564da18cf02f33aa61f2c59f8ce66a3b2caffbaeda451f47d533705ddd529e4ece3c2aab6afe0ea6cfd06e597a212b572996602d0080642414245b50101e6000000d5b3221000000000e6df3aa2e8ef24e1845e6bd7d82ab324b32b35e5e91fdb6bf2c3992817255f4e9b963a0d5dfd65145faac7e0a717b58a4d0305e60d7716127a4e9de1e78cfe00c0f20f25102d5a97af49076fd0805c18867fa24eca1d56ab2355a163b980400b054241424501012c80784c7d0f30818edf2111eda59aafa191685e7c690caeef0d8b4d9f69542a34614631dfd50f0a9d02e20c93825694416a31e3cdb944a4d95ce842428d4b8db0b978aa831625b791066d0ee66ab9f3ed144d7c181f014e0ac17993eaea4bdc56c3e8019717429ad3db56e57f3ba46521e553f4d09b73d97a481587e6cad570e10c3df6a56c32f8b81cd8dbf241f5834823accfcb4b2c2333a738b79bc409930609ffba080642414245b5010381030000d6b3221000000000bc7d6ca1f869d89a0933b42c2ccbd3120746eadc472579db53578ca863d80067fe88896115c6e570b872cd7f6d0e504166d3b2bb6de6bad345b6e2f702ce1d00a78a6b0111b856ab0ac2320b44958aaaed4a197f1b095689c1425f828059e30b0542414245010106c3826459a5c6c3f55a9bb754e1919c1933544ddfa6e51233c723cadd45782928171cef7b3cf6e69aff0f59252d9c0ffc1a04483a526b51106a694dbcbb0887d1f9d541d6888e66a0b26779e492f0381ed266c84d81bba3952ee8c877046ec35ac3e801792e78da3eaa0c6754761f86e90bbcacd04cc8df81610e9cfde497b4ff1703c5e424bdbd2c2a6cc6bb340163f0360cced75a732cab735d428be7eff401027d3c080642414245b50101a7010000d7b32210000000009433855f708e87a1dd858d69baa2ca1369b875a956e1c43767921f84115f887479e90a5beee4bb9b73e6112fb3996a4fa06c0714188fd698c34d8e754c23ad0814cc02d2086465b4413361d96d15125450c7b79ede054a712a9d09d98907800b05424142450101c6f705a45a3b3f2cd6cb054f523673abe0d594786e622d3a4191106cd7dde710ae11c7a75e2cf54ac485ea09d0993970ff6d4324ab59415438f4667ed8823d8298555bbd754f62af3c6978eab07e10964a43950502698cdb75632de265afd1695ec3e801c392c824fa9760c9fb8b328f778ea04a4d50f259b475f466ea35d6e6e470480a3f234a07d6037d8eac00f13f07f94376084fecfb0f3a85559c04da6e6f3d4f67080642414245b5010374030000d9b32210000000009c1f2b3efd63795f3e0decae86f64f7e898c8b275c7942444ba361d8daa1297c168c5e766db8d14276ab072cc3d27ecbbc305a4a852fecb6ac228672c732c20030c7ffa9f53fa38c31fdf57310e75c5c4a8f4979a4077bc236ed045a1f679a0d05424142450101aed017a5294c37b3073685241fc2b850ffbf73a0f1ee473d5b4426f11127be6db0b27b0a0c233c5e8fe1744d14c84856bcb14129e8520f191fa8017e8cf2bb8d3e3b383e81583dde1eccf53580eca9bdc702cdd600259062fce2609462fa76e762c3e80118a64d4d4304f948e687c199a53c98327fe49de1f372b39c96ea1576cfeb52f8e4a460ff15465a45ba53591461115d414cec9daae3f2e8e70804880cfa85489f080642414245b5010307020000dab322100000000040154d677b288b08841e0b4645ed5e613ea11576029e341533499addb003fc33a3d394132ce61c30c61658702d758aca8fdf869c62da0283ab26dadf0bc2a70dfddb79c6dc8cd74ca0748d5a2d36355efc61e42cb1211bc91ac8315024c19300054241424501012ad6aea3810fe7bb45d44235e0729ad04a27d77e847a15e5a99f311fd2bb351896b56e99acde1af54723f1965ed0bc7d33ece5d4dacfbdc8287659866da64d8b33960c54943088c43851bec0b51330fb132ba59e193b6a6509202c02d87fb40266c3e801592b4ab7477cb05f4798b4f5341d20764700a6531e99bd19ed4db3d5f406d35e59f3ec63bc6b34f521c4bdd7159c36dade59d7c40290a0d8396abee2178e6bff080642414245b50103f1000000dbb3221000000000ec4ce26d5fce4ff98de023d27d0ac060f7c4033e9d3fdc8f2c36d46f5e8ea070764d960c5a53a4c5427129a2c451506d2e558a9e9c5272f6ec13ce7abcc0890f45764536684bf9e0afbfcca4ec0bcb6f95bd121d61d5040edf9ec951f0138b0405424142450101b41520a9d40ed605afbf3f7373b0e6df8bf2907694128a391f6d46abb1e8375a89be3a7b17d7a16cf2cc4cbeff02f18c2b512283613d36e2b3774d1de9ad86847e2a077a217fa4c64328a8a8c2df51c73ea44fb3fcb26d92d35e156f4f556cd26ac3e801fdd55f87280cdbf8fa5eacedf3682a733e9d73c5f9c7b2bd2ef288f9b8da771bf0dc57b2086221b23d1d500286052e5b186de5a0f90bad1c64d57eada1a53b5c080642414245b50103b8000000dcb3221000000000d8f4bc79b1397694d563205ae3565c8c03dd32a57d9428a313ae6c44cd9241349284ea76b12702696d335f7fb5b925c7378d333b1120bb17838f06483abf5b02655259bebc01d4fe77434df55c55c056edac12c3ce8499d447b8233ffc74980105424142450101288a96b05a0c78c3d657065b731760ddf3c9f39577ebc9233067f7e40c74c22bb37ab98f2413a5b7ead2b138c57cc3fe6f75a3792f797889005b6a9ab9c3e18c869f4183ca2b04bea2ec69e75f1426520e0d062f6b6e0e27f150b3d34df48caa6ec3e8013fa3cdaf30353d51e98afe3ae1f5962e916d8ad5ace60753f6568d692d893ed8992dc350b180acd073bfb42391ef26155bd622b552b727b373ddff4f785e5f38080642414245b501039f010000ddb3221000000000a0f5c3853da4f13540590afc66d4f736d2cba4d52a2735aa827b99abeac0792225443809b324d7a4143cfc2ff5319acfd61792ebb2f989f1f136b6dfd203c70996fbf7d9c904d55b4232d307017b82c5f441c14da44a2afc3f52514360713b0b05424142450101d859133b01115394a6f0ef31e0113560a36aa0cfb6b80106d34d3874c3c7205904c7556775c58c11abea3db8fb2ba7e1043a82da4b744379afa56085872a51886b3f940affef7e999da1561333e63253663a0c9279c7a707cb69772e0f11b38e72c3e801d43c4794684f27a3b52052c4bb5a09de8a26fcfc09e44674ff8aee5e12f4085b21a77e18c2e78373d313d5ddf3574197baca0e4389a0b387a0d869471ca89f91080642414245b5010121030000deb32210000000005658dcd9cc182c0b348a125bc6fd9419e44c4cd6c04be7ffd568db832c99f16c1063515b14724a6708d8b9977bae346a003be93e1ee0ceb96b0d813babe3d101ddea5bd7107ac1f4364f8e07de2933ac123c7467374ff4ccb34eeb0a92250009054241424501015ea3318116801dd107a59c523b46745f20d115358b95aff40d61906250d0431dc861f7f70cf8079935af7cda322a4efbaa2a85cc03d62b1799aeb9881bc859847ab5803f2fdba8c81ddda6ec0828f5e1c2bca88e8a8e853b1fc033e912fd47b776c3e801384d5efe244b144fae58207ca8890b2588734a10811f0589c9740235b01dddfa596ec0b9cfa01612437fb98f4dd747deece421530ee71a8636a2e5b3fe3775a9080642414245b501033d000000dfb3221000000000f209e6e56cdfb85c40b0bb3d3d48a15130260a4e7a55243694004b1c0b5f5822bcdf24593db5f2254202c7ad1acff2a0a65fcf4b3ab07930c2c3415e505db8054367a8627f43ac3ae515251c3bd5edbc52eb762eec267b771db9cec03a83040d05424142450101a2ab8121c21008896beff905ed1f761c91782ca51e3c5a921f120b6e54fb62773d8f109cd3b8d8b2256b31906119a5c89f8172ca45f2ca065ede0a8a691c1083efe76f1707f7ca23fb4f9072309e6ac3979803c6a2409a2eac5d0c78ba344d0a7ac3e8014dbaf3a3da4bf952618e8993328648430dc00528ce763c74fbd0bde22ce9bc46ede76b98b9455feac890908fcd6e822f161f30708e7bf340d73918e3fee0279c080642414245b50103c5010000e0b32210000000003e92f270af641221aa45e37ac6115435ac01c82699453e53ec4f3880d43aa034abd63b0a5b382144a8c58c743c7d78575e2d42416007a1259901d09fa41a000a1a6d783477a56a55b1a69b4f5804b5f784a8f62dee6f600f3df74fd8a9b21a0f05424142450101501ceb963b0edff5ce93599068c074642f5efde6ada829ae9b920affbb56f072f174a5f7962c2836bd9aec4b98bcfa594412d3bd335bb2c4d64a0923ddf8d9820cf085411ea4dbdf72ec1f948c0c7c6f8a6e24c45a43c2e94cb00c35d95997097ec3e8010aa053633ffde27b339618fe9635282ef0b4784b56e4e1081df8830f337851d6828dc77b3b593d637b1b9be42a07c9327baf65034eaba34bb11e61ea3461ca6d080642414245b50101c1020000e1b32210000000006cea37c5733a9403b37fb8c448ecf6a889d5cf42c59a05e847f0ad440a36e224ce91b79cf7a1ca4b19c14da7baa45349fa96d1b37ec9a6325e4e93fd3e394b0b6d17d6a1a2ddad26dacdf7aa12e9cfc709c1707dc4bb1c9ed0ebd4baa8ebd205054241424501018aed4052b73518c2012f42dd2b0c24eb5760ff7e86b3d1016e2f6f27756fb8582b2fd8051a90d4afa1cfc2109c6a1ae82217bad598c51bceee7af88af6f9e583b08a33237be6dcc77f911806398a0565accf2c268e85f4c72462b72394a17cd382c3e801ffcd80a07299245dbbe9d87f6662b00f9042746956eae3cae3e7747bdad72aa3daefff0292c9474c9c507371c83f833dfdb12dc0133fb9e8b54705bb02119fde080642414245b5010113010000e2b32210000000006c288190b4ffd45a6ec1e3ebc4aa9f8d7369ce56def28b628c74e75f62fc570e31e2ce9ea9e8c5845a38723998d5f4a9f25a20d02373dcc5e2e4140df8fd830d9e134b2015018a3c584229fd6e7c52e324f93faabe5c36d2c25f65d8eeb5020905424142450101a461f52a300b4d9172250a7a0312ff00bfa5e1db479879b588e776fc2fc040423159ef0bc5fca72cc5405b1790929bcdbcf2ba317754bb84ba47fe2d3d07b98690332f489a9a54f50ef59b2f072e1327c4e6c06bddfb4d6b4dfbb73469afccc486c3e8019ad8925b507927e9f1e1c2f856eda02f442d83dd6944aeadd4061c60567ab16fde004aae0c8f26c5474951943eb256549437d6a3165950da9b2b68fde5c089b4080642414245b5010317020000e3b322100000000094b5cc7f89b7ea600d05fcda5a1a4279111660bbf0db2b2047b69071f5e6443aad3fd4ee9bf41f13ab725883407331eab8f00860cceffab563b1cb93779dc70ae8631d65491dbd75af19b872f5a861f1969fcff676ca56d768475a16912a94040542414245010190ee8524e095b4a56111df09a56a29795978a8ff4424f7882110eaad18c22038295c1c9cb9e69e0ff2cb042c29ee7fb541ee7bc885c3d61cd741c8fead39808ee2f05c4f8c660c3c139641e97d5713e3c259693788ffc6757d9e8ea6e48429428ac3e801bfb7aa887679d42e97c84d195252016f2327327e8ba58ed897496fb570f54f5d67bfef71c5cb386ea86f429701d146e961bef4ac2694cfc550e91d7ae73b4125080642414245b5010311010000e4b32210000000009c01427210f66ceab33c18763ad9dfbbb989ebfea92a559b1c1362e059e5241cfba7ffa21f46fac634f5b5d293b7cb85400322f5852213053b5e82abc7d5c601626e81a6f81125fe0c48e2100e915c0b35cec875f69cd4f3bfcca5d4651d2609054241424501018ec44b06ec5011d9fda32df1310af0db888d46bc06dc324ebca6c8799ea4ce2c3a7a2b57e2e062626670b8072da088e185413910ec5532a8c61e90772f38d384445baee9d9c59b99afca57b1fc6e9e4cdd7333af933995b10cf45e4ecc57187c8ec3e8010b92b8263ef456b9ebfd4633081c035ce52d7d5588a7a34322db590a43f53e1903201f9a18e661638124390e75ece2f928436518c0d574823ddf2df06addd5c2080642414245b50103ac020000e5b3221000000000e20ffd63c8bf35e1071a6f364daaeb750ea9dbd7b21b45b2e449c48e13512f675da62b5dbc8bdceb8e721d9fd55fc0e8978ff1cb9f39b092b29356fdf345630b1450832b1d6561b6fd0f71fe66c609783ff05374c33bdc31fb0dff7a1793f30b05424142450101ba3b4968de1954473c475df2c58e02bdf66dce8448417d69459ed85bc0e1a5455710fed264e55133193b367303cd4df718b4c6d80e42a91dfb93eddb965b8e8e13779b630d4c5df7d2aeeb6ce239140e12d02c473600ef2379eb28f09e96ca3492c3e8014c5ca48702fc416d759c57b45690b67f9d81177ab1bf3e4facb999f6a8d31b6f96cf149b95b3521d6a790fc42b0bf8f33c4fc7787463969e0df7af570435a9a4080642414245b50103ea020000e6b3221000000000725934c3ab0541fb1bd89dc88f6a4ef0d64b6eb687acb659f0897bda87c4d2512f4c1256fdfa4846cd0bd848208fd7fbd6124ff7d076f101c1b058b8855f060b78dffe5e71d3d4ae2204e2ec466054c5aa42d3f4b40427d846b0245a8ac164040542414245010112b1c6cc7e06f4e5a896615605c4357b27095b473bb357f851ca91c3a04556550d687fbdf8a5de164974d0598f9a7d6b78d6dae6db8b06edabfb94a56a1d6a8db30855a6bce55db300b89424d3a793455195e3e1210838b6ada56c4aafe52b3996c3e8015f6bdb37c92f92c051273606701d25615bf0f73ce62610fee8f4e655e4ff7a14efddf9df3a24188590023c5650674f8a9f00ff65d1b3d113047457533c142ee2080642414245b501034f010000e7b32210000000008a28cac2216a47602f2180cc1f9bf441355fe5304d19a3aeee36a48cf0184a0ed6f9ca1c16acdd49e2756f2d18d0f5e50f2da18fa01d7cc399780d14c6bb690b8ff13ab5473b88797fa8291197219bfbd873c3760d5a1463b9ffe8145dd20d0105424142450101241677729d37fa6b2cae1b7a7df93092ff7aff7a04d0f77ea42a3adb583cf564d42a90ca6307bba4abe4768f012551fa7dc199f7cb1838ae8d00483c16ab63889545b8ff48768666f5da76bc671d20cc7e90f20c74b99cdfd221ee5d8717f6d09ac3e801e4b8285768f317e359c92501df4af29c42201a2f79e5a4172e8f9442c4539b69a441c31bb89d13fdd1516353eec7b9f874aa3e780679dffc59bee4e18321b1f0080642414245b5010182000000e8b32210000000006047280d25241d5db45bdd880dd8b5f5927c3bf9211fd69b519b6ecba2b15d6b82e4b1fdafbefa8afee213013f2ed12de4a1118ebdc39dda6db74ee00bd7520b7b4669c57d962845af017a8eccc45c8029af7468411810b8b676c62c0e82cf0905424142450101aa86f1fc75f527100fbb013f8ee65bbb3c1e6fb26478e9b1216aad27e6fe4459109bd8d8aff3c4e067bc6123cebb116822add7f70c6d23381fd77e367083008f1da5ac7be860954ff8c75c66e11a17c96af421764052c4374a2369034db39ef69ec3e8013e726159f95156449cc3062cdc7ec9980d6cb0b353e92420f7b168d41134ead4fd3854cb1316d3b343a6842b1381eecbf54fa47c8ddb407fb089c28accec0820080642414245b501037f010000e9b32210000000002e5003adf94df43d0278f61f971e9f0ec82601dbfe9d7e4f725fb15c536598160b781ae350a9b6da90984b3b6c059d636dfb9e91a4f544065b55c5addd9700097cadaf7d0d9ad85b9573e13f677f7587adf4f4278b0d7bc75ee3f8162c23c702054241424501015e41e9f2e5f074eb2b250cacf6b8628ea7cd53a7d91d9a3b9f0c457d5000750191b0e9c276babdf7bb99842ae4cd5594c436ad9a4d5ee2539265f5df17203881d164ec457ef298282562b62cc2ea47d7531c4d44ca9e5d60e363f62d0067cefaa2c3e8011b22602638bec26cb2bf0f79f59a15135cb4d1c3393af3fe5a2c7f68ccc1ef10cf073d10eab794858fbb4095c4c60c2e3f44955f51a7ce3648a3a6b874be6416080642414245b50103b4000000eab32210000000001c1523848f2931bb54b4657c02ca187173512ac5caacf97ca19837856bdcce0a476704228268dd71e6ec7ff2a2711b00d53e79d622c567048c46be1e68acfb07ef651afe152214f16c98d0087a1e46ea3dcd214962427186a20d963abb169b0a054241424501017e08fa0a6604a89cce439d0bb60c2c917f1c306599295d65565cf4f256cefe3c06406a14efd51348060994fa8b2210a33014d857ebf5cacb7dec560eaa5a3f8e6119b44f815e0cab1cd4bf1797f33524f2094f04c6e12f453a3edef50b18533aa6c3e801405c3edd935395b20a1b084f138ef77d0557f3dc7006f7ebfa7f8a939d5314f62aa41c12a7914cbcc74b5a457b600a6f0ede648a1fdb80da3c5396fb157f3211080642414245b5010303000000ebb32210000000008884089cd1cd1648187965788b8ae636a8419315ff0cef69b94260115d01253b48bad9d64dac1067b38f6f039ce5b7b5e5cd348de53b499657172b07e8b3040af7392398fa8dfefa6f590a2e8db485fbda4640f0b44f4270a29624b8c3fdc50a054241424501010467f7277562d58873189618ebcffa70880bca8307642f8d22b2d8d19d41b13e8dd82f318f9105cc37ac4d971ca7628aa83d11d08ea9f8b101b54ff1c9cfea86b470dd5da96c37472ed8c35668aeeaaca422213bb8a9c6531df6176c1d8d6e83aac3e801423c15ae4a6ae176b4071d5b7593526e4ce5804a9663d05d69c92eb45c37d1bc3afa8fb66cfeab6d989357023dd2cd6974f61cae2a8c1d3b68c4ffe29c506c3b080642414245b501033e020000ecb32210000000003886237a0209742ab8d89c4519172e58882ccdd3cc1a1ab0776ad418bdd715192620fcd53d655b754093554581ae26e0ad602ca5c619871ff3ff84df1af29f0b11c1720d1fd77c82250b0bc72c5b3c647072880d936529064369d8186d8aba0b054241424501014a86ea4113a1e8602127106c6d4ed5827d6d93bb79b220ffcc5e7da15ba2307257e9151b65d2064983acfdc890561f89c3477008f67ba8d758b65a4c6225d4852cceb6c47865edcd8a27ab165a381a968c4a4fd6fb8e53dd9e5bf1ba922586cbaec3e801e8d44878a459397422a514e9f4c502807bae24a4ada4271553a6ef73105f1e7d4a8941905a47e923b6ead4f3435ea4b2ba001328c04fd84f541823647c6ea6c0080642414245b50103be020000edb3221000000000e43d53a2a6f8341fda3d68fe05580f686fbbb5f60c840d86cdd673f8c961ec506e85a8346225d410998336b9e2b42206618f17efd9c1209a3e9c59a2a7729c021823e6dfabe73ab5c0220b660f1770e07aad4e789cb9b9110b2d8f8175ea4004054241424501014ebfabff241157a10764dd35f583dbecd13088cb0c56a8bfe0ae7463308be12c2d2f5a656845456906a932d7bb6751ee828c82ad853f4ee8676c9537f7b8cd847f75b2b0b1e25e196f02a37b00ef5747408f1fc3a61fa3a4becad8d01ca1627ab2c3e801d4ca6aa038a3645c03482c3137a3365f5c70b4d564621e677e88a892544eadfcdfad8dd9db5f3e23e9321768947202ec6bb8f3d74bf8123c97488b198c0bd35c080642414245b5010307000000eeb3221000000000f4cad8e43b9ec0f83ae660861a0709c558600f20b8ce52b761adbfb0674dc610b760a93d749ce09004005d41361c1d07686e5a7d7e1d01412ecd658fe1af680f93645c1ea9be00d7dc2671e84b432c8c88d0db735e8f3429d3c83f136752b502054241424501014a2c52ba6f1dbc05ac20918a688aab36dcaa0d86e8d987f102be887dbb31a90a213c25fb91fbca613ab39218569b96184bed6f3411d88564d53e11c376f4b6802fba05a3e8050f87eafe14b145347b80d274964416b237b49f740708dedd602fb6c3e8015c643d0199e872e0dacdedfca9ed3600b134851a172607d96643d3968ab4d6885a32de67fc6cfe8d60496ae5ba6fc9b5b5708a5d586101742f126ce0f94c5cf6080642414245b50103d9010000efb3221000000000c4dbc163437c96f34ffbee6916a382a55488b5d62927fd631af7a33eaf986245d6a75bcf780fcf153f5ef35505f738d3d8749c44aa32f1b66289326cffde4f0471f5ac65f31884bc996c14654c06f05a21b2e700ee4dded20b620d69a4c6d20305424142450101fe1eb0fd3b2fe7a09da30dc739c589c6d62b8847e55ad5f824affc90dd6b552606bda8732e16a009c2286f8f2166534725d2e465446eda2ca87b6e04d146d98c5326112892d42a7525f438f4cbe85b13821e2a650aa981a0b6b025b842b107ccbac3e801055ad311e480ef82ea96e73dbc7b8c34ef8da3edb494aa1d7ad5d6e413afd00082397f89f984ffcb2494c35c64b68fd92f8a92dd7a8bbb57299d268a59a20e0c080642414245b501012e000000f0b3221000000000361db2c663a65c0c61ff8eeefc6da10e9309283066c5a94e667e9d33b77d9816c3c902099efe8f4a7b01635e0dd6ca41dc40f6053ec92762462453c429c4f80019068b9b28e8d8607aa31363fc07db82e74a9c2c072f85378fe62973b1c000030542414245010118a60f58d51d14c0d610ec45355a75abe5ebc4f5277dccc26f2252d89a8cf51142ce0d9c4a6f7ef7a60f632695bedb8c33c5f154fa0871a8f341d4563e532d89a55f97f39649d9d578f0f75467c6f8ae99a5b094a2b886d9c41d5bf4c620df9bbec3e80102311b9ddaef0e9b936872590fdafa72bbda8ac6a5b2629af79672dd2b710fc0401a6b9a77604db6f9f56e385929599dfe9582fb78c9542ab369dcc0c49db64d080642414245b50103f5000000f1b3221000000000e860ce88f9369c1b648cf264c7eee6f4b3009ae51d7c3909baf5655a5ca7fd6248a4452b7e294bc3c9c9a2961f2f368d0d4635525a7b2b1fbc08e53f403a040fe2985ae526067e729a165d1e652911e04477cef426c57919e9be9ee917a9850c05424142450101ee90421f7b5723f8740490e04cee4b7c3ef2d9d18fe0d99074a21ab43cec464c8f25a1e916a3cb7562ec6ce20c2dc77991c2b84316f90a00aa7adfac2a878b811f767f95948c48e8e9506d1d488bf715a7d1ab5ced670997b37567b468da6f5cc2c3e801ff2420607f8a361ad933989048b89c96989f3be2cfac8156ad8d33f4e0d01b92e15e2ecf2bb8e4b95da264836b20be228698d6162d907949f5792242227835de080642414245b5010345030000f2b322100000000080362eeb78ca23257dbab759c3c37281b12afb6a4c6c4fb60fb379a691842f195750e260ca69de29a03728e8970774fe04c2863cc567ea251e3e82e90c59270b54fdd962bf68df00e17eb76e6d69126d0b932ddfebc79518651489a279b4870d05424142450101f69604ce432c016055ff932c06f43494e00fb06d0dd44f5487763b1453055a48ef0503dbfb9c138db442a5c8d903305698d08c2e4f0c20a3f7d89871ddfa138642260e44a148efaec6ff6492b91ac4c51dc64beb8ca1e04c006fff1d0c5d73f1c6c3e8016dcb45789e83224bb5452bd1c120c1859d4176e8841ef2e2445d4ab6998b28a2ec99fddaf95f023ccec6332c6f3f493b600f3a38c55cf4465b8eb2250530593e080642414245b5010148000000f3b3221000000000161092ddb6389ad602b80579bfe7cac47b2ca43555110db7de33866982fc4f661548ebe982cc2deaa1381b758e0763f0a49f44e50eb34b248a1e271e79379e0486742281924b4dfda183cfcbbfa9f221587a4b2f5d88615e80f08c41b575e508054241424501016e72d279c993997f864b987ccc56256801da706276a4987ac69ac9f06899334524ee0c08c51b6e44f5248bc123173df2aa420ad4f451da8366495180ae8e768b76e14ce8d057d80cb5a63327b006d36f364a7db282500f661110c283463465c5cac3e8012256703a32aa17635db3ef4b0031487b9c496d75c0b3fd5c7915ae4d7314d357d3a7c3d4463625c9a08e948b6b0b02fec3fba4d96be228e4994aed020ba7d856080642414245b50103c0020000f4b3221000000000bee4b726cef1c0b07a422d3efe3f78300e2a01a3ad22c6eced041506ee2dd5447093c2db58ad58671c18392a7bc503c90eda31d961dc8e80dec97ae6692bb300f585737f3e81b746b0b64f58c8471f7bc47bac1242e3b8240a9314508be4a90105424142450101106bb59afbc31142eb62f8dcdd8f09393784621e5ec4c382a6ffd87ba27a813c6593710e8ae0f2e7d3b549c9e1da7dcf483c18da601c256a51caa1884ba3dc8788771c5793e52698be7ff535c381a26a6b9efcbfbc604663e2f71e332a77f2c6cec3e801acd81d7684bdf1fcaab9bb14cf5b7088c6e32ecfe657b162141f72a7e0ba38640219b8a44d01975a5c995d8f4ba8003e474d4a07c414d414428846ff27fc31e3080642414245b5010380020000f5b3221000000000325e274fe881aa16907e146fb1c833cab30ff41ba6867c4af056e35095d21d22e95bbbbc8d1963f1070e72609fcfb236200b36f42e9b905cbe7af0adbc563b0d5b90da46a19b4e22ed8168571c27382c853fce82fcd9d4fadac2fc5eff3d0f0c054241424501017ee230d6d942a1f4e57706b5a39b3b31b48c79e70546bb5354bdd6441f84f51d3433ba0452c43c3a551646af847974bbfe1c33d6c4a093d058ab6cb6a2ed0d8c74a1f91345e20e66b74857f8f2c0612bfbdece97ce2297184f27ad8d95333e38d2c3e801f1d45dc4546f5ebbf13ace7b51dc699de05e8d792f53d3652fee4c9ebb9c371b7feb6f59d36592f82f70d0107aa950aa0f9ca3f3f35a30b67d260ec75a71e232080642414245b501015c010000f6b322100000000054d3fb7a684b92b77fdc77435e31bc34ffbca1b89449963b0bcbb88cf0b29559b22ca26b724136b810263f0d4bbc907fc9318f09953c08c793608ddcfce46f03324be87b3463e329aa30c718d4a4366270c486f0b21949c261a4f1cedd1de704054241424501012447711917f5ccfd5a7be037bfd9278c9851b334244d11c53cd99d4d61a9530f44e43837aaa7c60c4945f3a98fb27e39c2ee357152aff8bd3f529ce4cbe8a788520483bde5c5589db0952b796be01b3503c79a808d6a6bba921ce10671360accd6c3e801bb6ec676eb065379c892965965c621c8a6b0721ac1e0a20831da4665847691f6215d292723b3a01e17840f716e51cb51d8438f47f063ba1c547b15d9766b2d2a080642414245b50101de010000f7b32210000000008a0a76a03abe5d281968e2a1783d561b1730365fbc467b5a1dc2145a9bcaea158245c8928dd768f64eabaaa5dd457ff17d4423ff4bb8c37e76f916bcaa89d90ca2c8674125c713dd63bd7652663735fff62877e0af214e78733fce200362d70505424142450101bafdc6f9c2469acb54259bd4917dc9efaf951d1b87a000f7042e0c4196647d092e713d87a1807723abceb950311c15a6f2ba93242b6254cba82b5b61eade9d8bff3d0d43d36e881bd1e23c445874d562d9a064f9c13a0920f179ae7456de0557dac3e801a6d174d7abd7bc637b146c02176284b1cb4445c3e9fecdba9fe1af4d7be949486b474dbc15bdee0998c53feac36007c51dae4ca3697a0108a07c4a6c9382f70d080642414245b501036b030000f8b32210000000007e2c8d8cfd35a470b999b3e15825cc035e0a459b5c82790aad8feac6a0d8a972bf596f5f83525c5379e9cb3c7968cbf19da9e54f62e4f1b9999e55fa8a50930c7c540800b273344fd777d9a8dadf3b25537982da18d40a37daa01ed4d477b906054241424501010463a1802da285a160d694b145598454c803503998701e16f6aa6fdd1dd51c3c677ff2a618e642b3cd802bc9433d512c734bc76d784be6912a1d7a24f417a68c3ae38824b98b953d668ad4037a43c9b06962cb97b13b436e734c65585892b0fedec3e80189827fde07f4660c27c2dd4f6e244e85022663ca0012c88af872bd14d269e3a44ddf114c7756cc660ee6f4d0c70cfb36bf8b1448540dff084e95d3dc53779a54080642414245b5010394010000f9b3221000000000e0ee16ddffd86b0a1b72cf5ac46b436466e26725809a5ebbd88a3e03718c22333151c5dd68d1bf1f1587f7df67a70829b1dca450df2bea4713f0fe0ec4d9fc0ef9ebb80f4f1034428fd0c33471bfa5b3ee6bf35a18425a934ac9a2fdcdabca000542414245010170aaf969feddfc785ba22cb3e68c923cc873951f2f369ead0b9284367f1ffd4be8fba9ecd997de3d07ff529a9d7663c104711845cf4a56cd1d58919193b3ae8d96b0499842de96144849a3fda839477787d3b85cb0c2267c82eb410e81eab27fe2c3e801f00120b9e1a43348dda436504d88a13252452c48c5bbfbea3bf8aa761bea0a1a1aac23bba5da94b5d7effe058d55e8d16648988546bd100c684678bb3f3f78d7080642414245b5010319030000fab3221000000000da7b3116f2199962fd865d34601e089be2dd6b9a86e0989430fd60d49ad19a2d31f9c08a77ff3ba955f486de2f0a550b9572a072cd1d6a2494a2453a74e54407048a621935b11e50621542b17588b24c37df835d4f5fc5302ba12acdfd00440305424142450101d04ee23d0d6e6364bdab90c97c48d21caf72ba7749405cf3194a66322dbf640dffa5d0b6397578a52bd7df20e4d564dc4cf979d1fdc3bc5eefff817da52ba08ddaba6be68ddb6807a9910304be4d494d07ec64cf77d0fd98e5f419d689472730e6c3e801f6d3f5e658e1db0980df53768181a9025aa20be303a49934363678f18c100869e63811ddd4f654b21f53f7578464aae220f3c7e1166d0068e0c485ec8eecbaf2080642414245b50103a0010000fbb3221000000000ecb7458ac5daf264480c3faeca6355d88ea32cdfd1474c7a44abe7647dac3354a6e4da4f2a567fd828a00133c66a3fbb785c98278a31dd2bcd5b73429daec8017e7c516af6d8f5391ba935af91beab32b70263b6adfa298a1137d95ba0ebde0a05424142450101a6e0574bd00e33196a953f3352e109094534ef6c469961d343f756e27eb3d86212b6be906f1141fe5f7ca4b578df8238c0ee233a0608df015a59c139233bca863933dfde74726097eede607e7a51c617ce4c4f969d9e3a4ec3d08bdc5656f659eac3e8013e309eb5b6971f692baff2d0290a561697100699dd0930dc16fe5b7d256c6edbbc5f7c7e1eee1e69b8f7642e1023b6d128864f9e3e1547549c3956f95e55c1f3080642414245b5010317010000fcb322100000000024da7f93ec885b5c548512d207c48a9b9f820ac332a8bc557ed9f3fc737a660aab67851b50d0c6840adfc1aa7f840feb75eee3e46145180b75c1068b5c1b610db4225a2cf58d812366d409895db42d23fe298f67037e19ca987d96604a0b9204054241424501016a9d0e9686783cd4220c416f9b889f2cf78add65ffba276a4443e3f3ecdbf3453a80ccfaee0379e51d6d562a775cbaf57f469e5b84492d39c848f4fd1ff09181eb4faf0bf940f2298376367c03d7a665c6ff0a4aa7e48fd8dd293bc856f125d8eec3e8016d276ced98982652d1f0375c3b9ee46f6900e61d12451fe60f962ae83599ea4740edb220a99ca10ca7cffe98908c861aa842852cb3d0d23157e60987943df0a5080642414245b5010174000000fdb3221000000000a21a5d7870d5a9e9a24f74d7cff6c28c5627ce84b531972d429271a76b37641a8ff5e4c819cc31c0c817f28267498e7d30168005cbecbac75a84af8a2e90b90a909add200e19bdabb9728076c5cf201a40132f24d092a7ca107915f4d2097a0305424142450101ea3535f8ba1a41825901922df21555d870721e2ab0667884ffea6c9691916f2cce9f51211417f368355cb47e001b6d8c65d50868497c826059bfe39abef048828f2331e7d6349576272e0011fb8af7bc83c7f051073d7c8182bbd621e07f740ff2c3e801c40f9ade1b9a4011f2a50d2730e6440f0d2a0e74d462db193391f29837ff425d3428e604d6771a8f6bcd0b654a505a015cf9af1df3a7cdf7a35ffe71b8b9ff94080642414245b50101e8000000feb3221000000000e6fbf3340edd1980922ab77675a74572ec440bf860d82b8290e20b4c770068614e58afd5d5d9e92d1da6e098554f1972fd62d1508c3d61a2349262a708b38804c24f2ec5a8119c3b8b843d04c87f8748623e75fda4288fccf836db4f77980607054241424501015aae1866550fb4e1e2fff343daa9aaab058cfa17ebbc94884abfd860fa1a3d6d926d050d048c04390e89475cea6107e74fac45c758f20fdd9d97aa2f6eaaaf85ebb3bcfc5fdc86ba4bba5fee998253b85f115fb9db8a0ce7ad0ce700a1989337f6c3e801954fd36808041d989dd74eb4654763412ed1e8f9d8a6c3a7036a232a83c12d20f27149cd1f290bf048ab573cd2b64b8405929d754bfd6dd1f74a9edfc56ed6b7080642414245b5010127000000ffb3221000000000c0fba7edbd095bbab1a4cab5aa2d962e66eb90100fae2e28b660c3dc66d01403f0c920a74126c7f4a3570e9115c92138afcaee203369589cc62bffc90fa7c80eb5be20594dd280ed3a43cdf57eadeae55363f314e987f7d51645428f817a790d05424142450101a845034aca21e342755efa9abd9afb45d64aa4a559bcd1de9111dc8fc9a67025566a7773e2dcceb04744f0738a3af7bd67caf5c997f7bd979fd5b9808e012b8419c5f23dcc1d3d437f3b49aaf11f6f37f742da1c766c1e8ff4aa1ed1b3b8bcedfac3e8012d1d6ae5fb131ac154eeef90ee284fa090788f88648bf424985f6612d39120ab73df7d185e9d0d8be45584c12afb6eaf484fc22723f34f780d324a7c13faa204080642414245b50103ff01000000b422100000000062a73b85d149cd306ecd13417de4e557c6b83c7ae6e437f55225bd0dcc4e9b63082bbd3af6f6baeae5273f18253ca04eeb94b082f60fe7a5d3341499ea139e081a6853be738778a7bc4754c6cb442c4e64a1621dd39157189829ca479a32b4080542414245010180ae00d3464f1ee85972e281d5c662e008e20ac151a89335497963144d53140835e6f31572ceacb979184b4551dc34dcf331006c349b1a456becf1784f75198fb55e0a1960efa9ae550efed66c685c7513b276bb1e6c03e6a1843f482243a480fec3e801f0d3633d3abbd5ecb548677591fdcdf3ae71d15982ed76558d610549a184812b06af1073491a372578f8a20fde3132fd90bab0e9573ef9d0565463de3c922c3b080642414245b50103df02000001b42210000000005a3855ac22f7192d09ac7a1b8d3e880d354f1ceee4d42181eea985da137f0d094e9925604e919d0954bd2a9fb16eeb0daca206f8b9a9f9516f1dc8dd9b5d870f5f3c2d0bb026873b4a4224386a727f53f6be0f6ee8b837491dbda8fe08b85a0b054241424501012cc0773603a030fbecef2c7ee04f4a3c34af0bd943b3a728da03666ae343ee3961241a36c7d6600f8c88a1525576ec9a674c2b20d4cfd73515751e29df15d687956fb42bd3617343f22d93b56e4dafc4ca3c5cc2fa52b11abbaad04f5bc5a17d02c4e8015d84e2855bb83743e3e74113df363d3187e742b8955e72276ef0e04d4f78ef7251ba990033b4459f11976b61403074e0e03ce99ab885a4ad6ff66307183c54ba080642414245b501018902000002b4221000000000c8ad8796e6f510ecf8191cab7d9adf8b59b064547a77e753ba7d416c50b0c3362771c6a4a981c7b2d1f5605689111098e9b552807732915832d14b43cb1d4601c0e32cb17ec70b675a88dd4175657b8973be42825f04b4c567a2605b385ce70f054241424501019adb0c456ac32ad368639039402752d9f9f805d6a629d33df452954ae7f0f73d4b1e3dd93e4d9829af39d388a19b2e959eac37e9c8b13fa26b55693972b7dc8555e6f0ea55e76d39a3fa518431d31e7fa3e9b79082526ad25760d0619367993706c4e80175420b8992b9d757d0e691ba9aa3b727c16817434a156230cca545971787cf219d4f9a91abb83b3a04bebed431a1cdbc1773b029c927aee261036fd9bb8fee99080642414245b50103c002000003b4221000000000cc3008be97fa3b6ffc860b716023b447985576a43590c69bcd09741805d12b7a01f8445250a7a83a6763c62ec46bf46c08deef0d6cd60f40faa757f8fac2d90a28bd86895a5872bb118053fa28e5a318fc132c4a39498daac23a97f5da2d610e05424142450101645ba08e945b1d2765be8573f95e745c8a7553f2618a14a8ee6e2d22ecb1a4339d5214dfe0ed59c5abf9323d6913cf81af38d144ff3f74b24954708dafd9b18680c0a26528298fbf8b4b980d162a9b7b984e355673130f7b1288834de55dc8e10ac4e80198eb3d5dec107060f8c00adfbecdf6339ee22ee6f976cf97d0b750a9d547cabce5106b4573d4b26e5e1f1bcf7ebf777fc2436707b0c5c6c3eb1666f4d6920b41080642414245b501036803000004b422100000000064de9f803da2eb7d95628e9333f66077ef3c0452c155536cee20c8fc87a6e57ba109ec036eaa9c3ecf4f809fce8e8fc7ca2ea6f8875a18aa4483037aff70dd0bcc14e5905f3a597472fd61af19f5715295a285c76b56a943aa0059ece4d1d80505424142450101627557a2eedde3d79a372a6bf5e37073026b3afe9eceb6bf4a1986c9acb0864a5779b59e274cee2dca22f72ec5d423ee8014eedc0f45b408990762888fb7e28ecfa6201a1acdd6dc6b75c7ee3cc9c0e26427023e0e21065413ac6108abc628570ec4e801c121a91677c0b6a7c2dc9026b6b8ea8705c41a32af21cbb58c1cfdaab89bc26b7213c8f2b1e1ec38b4355b927d90cf38d3afe303fd4f0dc13b5847560aa076bf080642414245b501033a03000005b422100000000010da2d97cdc98367c060d7841317589b211e769f5332c61413c0416f6630df7eac506a87cadf7869da840943d41851caf3cba2635c46f427ceb3f0c5b180bb0615fd0dd2fb14adf61c8fe65762ced2c0765ad11095fca05ba5fd78043697ea00054241424501019a7ccff24cc40cc32d0870636d1af6684a28ab0ec3c17a11f6853a04804cd80ecae88b7372eda358937b9fa9864920de24f337cc7961c310edf381298a084987ef452163d2b869ae6d5a316487a02cbc82d419430334a829131d2888531bb4d012c4e801799ad3f2a370d3886a617f7d0fb1fddedafaa66fa0f33001a2272ee0483d016b54c57562717bbeef431d53f5d01d9497333974635d82968c9b0b77a6f497a496080642414245b501037203000006b422100000000096461c2d51fda5a11cc4d0094672371adb7edbc0b8019330da26d187c59dc903c0c96f45a82b46102d28583760623f820b43f5cb0ffbf650c874a541b611300ef35201787d14c479a687c3c06967a90d72e4d6aa3a080fd08168ccd3f572cc0105424142450101febbfb55cff03758e25c716eca1edc586e02e942e7efdca1f6dcc1646e492f19ef9954e0165d86bad739f895ac73f56345f2f739f63ed856909e809d1f62678ce2af3d1c2d66c44163d847763b72b9e861f1d3fe61c006a92d747438f659e98416c4e801a70d86782f78b17f4b970e2dfb6ce347cd681499a83088690938dd7224a16570760d8f679dff9479085fc92c75ed00fab0ab426a9af2e4648298899ce8db93d3080642414245b50103c801000007b422100000000052432b105711136fbdad7d2c772e93747c865b377844eea5250f85dca67cb647acf8e48ee24d9b6dfb9f9671026f9f601bd1e4264035b127f4b1ec32731ce0003956a268db3a47c6b06229b362e60495a8b807c089aec94a7435946e2021cd00054241424501015635e75e7d17e30c24e54385af7fd0f20638254b4f26f41103cd5b673f0891679b3c0b63123c08d6db32b8e2bd206b982fcc983e32d865d30d36bbc6f7f8d386ccd5d9d70dd14f67bbf2da271f50ce213fc4eb5028283418ac330f9bd39b3b621ac4e80191e1470c9b776c55e292cbc326a5e8a4b839ec01a8670e04439c1797704eafe3824660ae9ba651b368316773bb6f1d90b10da0a601f43f0c26605b8c9f71e94e080642414245b501039e00000008b42210000000006ef3a9c5ce454647ddeb98980a90ef75d084fa5966c49689c880b930acc86c11358a0e0df63719e741bf823784654a9cbf4db7bd647f2283e4798fb511b7a602899a21d8ef5f3685c2b0de50c441c298f62463777837121fffe07a3122a37b0205424142450101fa227df653349d9eb58aa0a2827207a86eecea0d6a5c819ca19435a3d719197d419863650a662e09c5767509cf273847d80176e0cab640fd56168133194fd182c4ae3c3730d1bba25c04a76c756abd8e46da456b1220b6215aa339c4b18bf5331ec4e80124098a0a4d6803234933b4ddbf4614a28369c472de1acccaff845aaafab1d3aa249ec26a4f3e685d70ed80f3acd9f43a3a2295863c574b1bd5ddf37047c67892080642414245b50103f501000009b422100000000086fc452c08a5d5472a7cfd9ab7d92a81a9dc31bb80c940da2b314c0399f5367411c3b567c41a285507f9b8091ae7bab3cfd8cafb7a95a2eb0f2e1a031459690d3f5ddacb6718fdbe7bfe7837d645b30a8c2f60515a33fa3de40ef2350984640b0542414245010150f6521bce0124dbed566728977fd87d6700c8bbe30f497335513a500120826cdff6cbd99621c9d0aff950122ceb5be8582cce63f240a730c79c8276440e68833a69d9132030d624f1b3df85ce80411fb5dacdf9902d7fb76f6282317aa0267b22c4e80183deb4b9f6d3400ddff1e052ffab1175c5904a1ea42233129a89a3c58b00f016482c769576956a54ec9a8d720dee15e885479f3ea9c37ded066fac972e4b7626080642414245b50103540000000ab4221000000000e2498eab22a685e20cfaf5b1cecc8573b6947f77ee2ca5f754a2942a00c9243557ebdf46d1ccf95b96f33b3244a145c8e9c45b4c0ca4ca76be4fdcb124584d0c89671f7eb7bff169f107fd1d48a5d2054cb99891443b89c6a9073e61f2c8e20805424142450101d68c4a234daeea5fe8bef91ef794fba632878fd32c9e0c13eaa0b799bc14b72055d25ccfcceee3def7d346fdeeb5c9db484d8c4694f388bc575660b36c8c0981f7fc7936681ea2456f27bd51a51ea507478570713111ebad02f26a90ba21316926c4e80175ba60f17d229b53452b4c65c64766c83f1eebcca8c077405b04bb767a4e3f0523b86831fe118df9091255fa1551c4b77025dd752b34ecbf2be561d037d80926080642414245b501032e0000000bb4221000000000084bc319f6e6edebb635518d3274f924b40f99a1c2cf03aa41f4724a2afab572b646abec57ac736fcfbc469b2741ce3cd58a003e34e9a2f5f752d9f587acc40b377b96400bb11825d90b2f1be69e0d3322cbc0edabb00309fd3fa4f0dd44b10d05424142450101ba8b21cbbc1e231f364cd8a6f096d907f9ee5ea002f74b5b1149c7ed868c004cd773f8d70d6aeab8f1625a2b95932ee916b033830b3f61dbe5f971b44b0d978421fa5395b8dda4c21de940532fe830e4052a1a2509404b6a184569a357c8edea2ac4e8015f91fa5721906d8a79712c1a245a9f11546c92bbda3c5fd8e6034d95d43444217d89c710f979c1b36e00c235b2c02f176a0e7771aa5da8ef82876dbf04255f40080642414245b50103a10200000cb4221000000000be010cbba3b70fc1e30ef93cd175a806df47a1e5b9253daa0364ab1d24e000221a7dc3beb22b7b619f0a9d644bbbef625efe104a5d76882194fb00dd21bc5b0e82ab7cf6b8e36a223fe6ded616fd9d4079df7382b318605c4fe7b89571287103054241424501017a7eae824a9ae9e03ac8da9ba98c551ffeef70ddd9b7260fbfa8485d7f7ce3397bae7ca3d3ed18ac7cba9c6d56e31920ac493de58c769be246a9361d304bb2864a242b20c79c62a2f9112d2d25ff9186ca04cc34f64eaf4dccf737a066899b082ec4e801d7fec86369e636e585f8134a54059ead1ccac78f50fb0230328e796a660e1dd81a3916bcd5f1ac423b439e6e2504c6b7ae039025bdd0db65789db3af9b14da97080642414245b50103020200000db4221000000000eecc20362d5818d4dba0fd387a2892042951055e977bfdc4616ad41768ce673ea40eaa217dae913530fbfed0b741d6ece162ebad457ea87adec503ae9b8a030d58a2f43b4c3fecd6b676ab9fedb92847d8bf92444ab6af5cf875d9a1ce839b070542414245010184cfc32ed9a9638d55d4011aed54d52f834794d402fe51ebd69fdc77bb63ad58b9fe4f9d73f312563066e087cbc1ff805733356a0b5d2a013a4657cf553f9f80aa0aea017c00e5b590c3e19ec4b7e64fb85e538cd7179f7a4b966a6582bd4c9232c4e8019dc1620e5d679ec2f8061e7682e872b3d131d98f1a63192c2c0b517e184668db3ed111111b3958c283e02e415a9ece6c536eca9bfe868ba64289d1a84ed66dac080642414245b50103080100000eb42210000000004ce1cb18d159d78fca5060088fd7dfe2fa704f798080ff68291685618aec782f262fa58fdb9adca884f02cd1ef931d7b257ae60117f82ecc33648947a856e308cf2b7ae90b7ab76a05cea9f8be43f0d0c837aad3ad61e9bdb02a66a2b73f670705424142450101b4e3fc53fe18323df5f7bfa6211b77232bf2efda87dd9eba78cdda88269dc76feef46bedb8cab26a9deb5a8af21296084bf268cd314110d048c111997634b58ed8d5e80cc46c07a16dd347f7aac98e8ddc706d9611abb01b2ba405af681282c236c4e8011a3cb7d6ca17daad2f819ad5d6973ae2135e6be61389267352d0ed0ab47444ac1df4b00f83644ef88b9bb8d6599263d87f89368cdd0e32066e2fcf7f40349642080642414245b50103e00000000fb42210000000000c9c89ce2d416028151df52954acf72a5dfa49deea478d839281a5d9d472ca29dc4da959f1fbf6d9b9d98444084b91122f534d610ff2ee0d9e452b1b0cf820068525ec0f4c575df87969522da279ae2b8e1a8f19604154a972af734e524e4a0505424142450101c89ab4fae0d004d6120d2fc01637a5c56f427448422463080ef0afc47d12cf00cd1bf06e4c50336feab4ca77380486ce3ab7c5f64e18f1b6ab45bea1413cd98a4198fbf28b1ac0ad8975fefe12896b04a8bce995c0ade6d7cae3a248034775d43ac4e80115f498ec264d73a490205908591336e4d47ffd9c7ccf0a9f35db75f98e8df1aa33570a1981d9c575086f7b98bf1baa1d374afac05b75b7fa9d4b0ce79d6dc9b9080642414245b50101eb00000010b42210000000006a02e85ac08a3875c3f83b745dd546c98a66de6fd2c55d0133463b03fdcf4e6f6eca2a8c847a3a6e011bd862526fdf02269372ea342193cb793ecce343aefa05ee30728c7df97dd54423962cdd86028329bccfcfccc896ec7f471497e9e7a30205424142450101801793606b73a6b718a9767adfe6d468306a81cd4e8c6729c456d58ff3c3323bff78c0a3559c4e79b700ee541028fe18ce822e8e62075e72e6b948c70e1e6980496418d8dc02aade7350af55c3c256f645fa38b609ac1f785f0522cb43c7406e3ec4e8017375981e2ce8c84fadba124e8e9b39d9be8adaafeccdf82a632d281729c59c7e84156510af30180275d27c2e2cc4b57bb320be078bfa2b967ef9f1a9cda1c43d080642414245b501030802000011b4221000000000f2b2723e57635adf8ad0d584472809839e3a04806a8aee8f4eb0830b65844378529046b2146e163d7b882c1988e481a6e55b3cefb2b30c1e4c4b594df071b102ee95bae1e71c71b334db5253c3fbb0d8410f0c78d3d263efe0815dcbd9cbbe0505424142450101d621a02e5006ed5f05dbdde3029e41502becb0af2f2a15beeea053a62243676fca1de8839db58b06d1309e319ad49f132b228ef18e18bb25ad030469c623608824b62d1748d6502725eb9759d95a82625924112cf8c6312f1ccb552cba14050c42c4e801e9c4273a52f56faf1b3578f2823098c741a28006962609ccf67af23dc2c086e72c41e7b46917563c822c53b221e0750f0c82f8248ce2810186e9d4a208b1a4fe080642414245b501035e02000012b42210000000005a1a138b9439051d4c4bb0b9fb2a0a0196665aabff5b5ff857c468965ca5277a95924db9413169498780d61c6edb04b3ad546c436ef96bc8fc49b470fd5ea30f41ee6ed74ca518756b2638e2532c101de4c74e6c9ec5f62a4dc340f253810c090542414245010120ed6f493521d03b1c53a6011772c59b189d014372ad2733bb02b0028c23092680fbbde75fc3f6a07bb1223f81f2d91128d2517f2ca6d1baf821344c649f3c817fe0cfb8900a81034f9e9e01781d26e1f85f34de086b9d6d7e5b745d2f651ca446c4e80100e7f729390f3d7ba4d0c2e491213980ddc4534f905dd0555f535881419b208a493ee860b7e65942540cee0d8224582cba27bf5424a8c4236ad6e006ec5718d9080642414245b501030803000013b42210000000007833fa1a98f4f5d543c067ce5cabf887d4b188a7d130d4048bdee3b8eedac43195bdee41910fccdae41b24fabda6e25c9ab6a640270768f4e45780c15da4e30ac1b014d4df89588491472d872f57afddef0672ced1959d7475d0d1de3ac20f020542414245010158e19e6b8c24613f421aa8fb39104b0822d3d1eb2fbce1f2d7d2e9905e847331f9fa0a9f450bb2314c98ba837cd0042f33ee0478369b8e39dc390eb6be07e3890d447035d09feeaefa63c7bbb129f00aca453ccf90102fb1a0385776b53f27fd4ac4e80117275e3f38835ff1c3488122d27292f01b6d0770044823ac1666d2f2cf20f9abd32cebee9a10bebe37e310952f9baa7335adfcec76041e9612aabdbdc0640dce080642414245b501031603000014b4221000000000262b5458d5f16f0dd7fd31696e1c8693f8030052930940b99a31fc437441eb494d7537faeb5adf0b548a1b3b336582163ab53cf22b580c2fd1e349eb7f50bb04e383e8fcb5b4b3283fa379047bcdd25cad4155e51772c0b40b7878fb925e8d0a05424142450101f65c7a218ce3747daa5aeabe7ba8f319cb8e88a54b0cfbb6ee86ed32eac0484b4c68ba185941db320de2c82250c207265ca3bdf537cc03ebc140672a839daa81ee4bae21455afd803add1e6e349df81ae7fd9bfa5b396378dd326f1a9da69ca34ec4e80170840882969e412a9db32ca26f0b03e1aa2091a2f66bc48b20af30c8dd973d2da8a228df1f152a5b35d58908711f3b4f6162c2e22d203f88aebb625243468581080642414245b50103e301000015b4221000000000de2652b541b050923a7d75d376cd6275a41d8fdce574ee26e2506b4f1c608d0ede6a4e87fb0b2257957d57edc0f282be0a5f7263c197f670a2ee0697c09a760646c575349c27a7bd704a5c17738581cc56e9793bad753247fd0a4bcb85f14905054241424501019ccb36afd47e72d97ce29ffc54e1e4ab0ad244c93fb6ed0b601696085a493c4341dec4cff600c49ee9b1165441419d3568bdb322d715b34520b1c0eda9080f85b44f90b3a74fc2cc149a4f0215b6af37fc1a7ef308815f4d25598adcd92bd07552c4e801f2649f165bd47505e9c6da3d1becfef2dbc27b78574e776b85c51939c6d9126c520027533a03de9eb5657b4b759134db1bf50abb12865d08d9bbb2d050aced9e080642414245b501038f00000016b42210000000000a98c004bfb35603662d48e856bca23a676df41c6c3662f71a04d1617ce8cb40700fe09e7a09be5ff95c0f45d1c3c61dfdbebbd11cf74f2918244c1fc84f5305a0bb49452923f22c280de99ffdf167adb250340a67ec0cb1465d6a502ac27803054241424501019099da58e7e8ad65294bfce985909bfca3016cb974689fad5ccc95343c63597369cd304a74c482bd5bae31c3ea02e5d8b23883eda3d2949ba881bcaa8b8cac8f2f54908c6b2ef746c60022d03cb290bbdd691ba549e89239d02db8676c622e0056c4e80168a0754e8a50e3a1d62bebf4b97be9c28b4b6b66d43fbab52ea6fdcca09c4f40e0680788defd55978f0d201ad2b5990046eb2168325563cdcb51c05ab29ae960080642414245b501011000000017b42210000000001ac3e949953b90a4bfccec4f894c9333f81b5fa30e79686119289832d34d6859b0582be4cbbd2ea06cfc3ebaa8acdabccc068848a50d3fb23a8a1c468725fc00e345545739f6cd033c24f525819f20f601aa2444f3fd96d4f0ef932b82414a06054241424501010e0d192ab06225ba5591d3423593605509b78d173b3f477edee442ab7161b771ef97f6f1dd2883df5a3bba366aa303cfacfd538bbc4ad152cdc7416755932082122a99c9702bfe974ca6a7c2ee3d4358e23f79c9036b5c4c183d5c7c56d299dd5ac4e801a5a8697b2bfd2559924ca86019433e68c2d9dcfbd249dea52b9dfc303275e9988cb158c17e06ea4b57b0e24a419b06e422e3c1ba8cca815516a3b3152c9311e9080642414245b501030501000018b42210000000006e169f25836516b3567f05597960b439c71241be99ea95694cb65e49f940402c0ba37813c7563491c0e1a9e13bfdd2eff1cc00d92a3ee898005bd0d5b717af0514568e4bf8754f5b82b6ee212235df3ad9855fee445406044b48ce19bde823070542414245010136953300377ad870cdf6771726cd914493407730fba8a9ea47bb956d061f460da0952df2a42abf7404c8c47993d69a5f658ca2404857bd0a7f6e6362543ad58afe0bcb8bd6d9bb90883f3d35a1b4dae757b58d1f785127fe674fb16c250c89ef5ec4e801de9902148c50eb4e72ae541f8aa3a028d9e18c63a568e1f38437d347301961ebc7623ce7c230d1c8d370458348a0a63c6587ed4770476ef428797cfc83b9d307080642414245b501013903000019b4221000000000b22860b96777c54a760c1897bbe7233a9056ef78e2500666482f54b467b8156861d659b3390f2bf2dd169fe3cc8900c6fe4119111d803f0081b405acfbde7202c623c8f401a47b4b7fda5613886f7fb1c289cf9bb596b39a9f057f3c2c208d0c05424142450101da435077f8b54d3652a7f089bb57132336cfd98c20fc19ddf15cb59ffc685f22e0a570537d64004a19ca29a530b1bb15d4fed50e467fbfce816d78f8306a7385f1a30c92bb729031dccba64fcc5803db95c1424aeb76a791652a8ded275d356562c4e8010d1dca7dc5fb0f9721b10d0d4bb5efd73df637c32d5fdceec6250321e30df4b9c94bf60c635c2bef5d5b1dbf1ed6ae5a0c0bdaafe78536705226a7167d6f1499080642414245b501036a0000001ab4221000000000eac7596ae92739f0ac6d7af6d5bd3ddd2e44284d8c8bf70938fa68741119c909ce8c5b8f211af990c6165a9a45cc1b7e9a7da22ca92c50a1b32c9f9734c3dc09245b7a1cd2e2e2a9fa612c5b137c36d48005cf3c34b6aa719f975b31ed0c7c0a05424142450101b819a9723da20d82943070e1e6938d6c9e276864411f2dbc77f99891f159ae3ceef3f8ab7a3429883f3a77decc6520c9d4e6d61488b334eb7975b7ea726cb08cfeb6fcc984eda161e28dd0502b904705ab612879d4a6a044b004c4a30757279866c4e801cb88f131d51063e81b1dc98a5aa68614eb468553bb46037f21868209eb4161ac731b74f8b92026d22d9d9ce747e578932f68ab3b5f4d4105eeac247d824b457c080642414245b50103a80000001bb42210000000009689874b4b5a9b30c73b1e0cc15a0f7a1bbfc4b27ecabe2a4bb07a1fadd0de0996b36412b12e23930c99209927ff3db026e0159e5256c09409336a6bcaba71018e1da753e15a5b72a74113f66bb79fd27bc18e94287c1835f93e1ce7ebf5110805424142450101d43fa29a67d709d9d26c0bed78b3d97c841034c47609d66cbcdf92024db5673cd42d8531723df669dfe66bfe837bc06a758fb367dd1884c9a5e466e22b7ad38e6e16216bd51921e15e213f559487643e9a7f793635c85f0ad55f386f3b2552646ac4e8013e08e19f877273e48590f4885373587526079200782ad79d7fe1d2166cf004b36e58819a6ea5cc650c365c91d5b43e159bcabad296d1666e5b0b23a30469c285080642414245b50103190000001cb42210000000002e975b423317c3532260b9be6df8bd87e109ffcee4dc6c6e038c78ad9bf0df5269e948975bb31ddb9b77419ff8ed8df1c9a3f2729dbad1cf9c937113725d1d0acecf202e1e950884dd459a712ccd427e11d7248431e3350c74f513f380d58703054241424501018c6d33e94afa24c6d9c9759e4bccaf4610b96bd23025c43f4d57e0e0b12b0573c0c40a1db54b4b1d28167ec4fb478400939daff4d7d75eb368a63a9cbaeda68e426b0d7ecec67699692590a25789615fd530f21c4825af64c99acf3e940430ac6ec4e801ad9890eaeeb86d4e03a35880837c5aa256c7230142a4de4001474fd251b4a26260b62e0676f782f315f31cb9d1745977bb72ab287b79821c5353ed8fca730d71080642414245b50103570200001db4221000000000b2892699bdf6b625f5bc4a768562ed120ea0e0b8df50fa50e95fd50513b0e7554672bd1936263c1e49c6aa64e250c1b542c574b29df702fe7c74ff5711ef19051250128549236257bf13c601b94206516ca3096be5db032781cac74847e90b0c054241424501014a17a304f0303b458f9bfb8a2b3de15147abbe9b4dca48fc0f39c596edfff2573d6f7b2bd76a7aa90dcd5ae027b240c4e44588f58bbcdc0679fcdd0dfbf33283c812c92593cdad8b151587f198f2c03b487d1ac4dfd5a41a49373dae83ace86b72c4e8014f9b26f0662bddcbdeafeca730036c9ef18362a4c7d1cdfa6b82b5df780596d13a7f047932d973ed73575c91aa6fbc53c63c634eb3af5f3a6a17499a9eb72e8d080642414245b50103b10000001eb4221000000000c0d79be2b830a93bdd5a699a62397263311dfc59109e8802721a00f85765cf6f33e57eea2cb404ceddf8e5ea268d9021cfd8e356584a2ff4799d2138627f9d0fee20b2fc78770c312b1e10e8bf42c523c6c7831be619aaf9e65b0b02c187250905424142450101a092f694559233831dc78e737c5608243baa74feb577f0d71a0f4685ab1e094f4d732b81432e2c69a68a2c70809c46a6b27b21767e771569476912036270d888f65aa9773d6b8f0a6dfe7e198f91bf653ad87fab86e44f500bfc27272d0877d276c4e80110dfa9983bb668dbaf118f18e0feacaa32d0903bbcc9a770818add7901abb222fc66b2e32ebbc496c4d5089766160a971725ce70468858d170887764c984b564080642414245b501036e0300001fb42210000000005c54cd5f02251072d9e0e7b24f7e747ada716f8339ba4435ffffcc51c799bb1eb835c12fdafcebe8251eb9985929bedf9df5b2c6c424621362ad862e7d34ef0db297a977b514d54fbb43b80901823c514471eb188b96add8163ea3961fbf67060542414245010178148f8e6f9760501866f11747c2aadff76df9e1adc9d86cb9bba080b5832270cea465604751c3a1c4bb09810b0b6ae0960138d687bad6a063ad4eb662a79880f84de03a3db00ac28956fab0a65ee70e00112a3a09aa423557b371fe2560baaf7ac4e8016e2da64f611365ae1afa128a155dff122b7eaed0b09056ff50a4443a09af58097bf7f1f23f84db43df06b05ffcc4a010a8c8575c597249e94edee0f59c4638c2080642414245b50103ea01000020b4221000000000984aad288599a23883d471409dc35122e7830b421d30dd266c09601dd8e5022a7c5f8bee75682157a62ee423f7f6d0eb314261230d8666342458c7d68a6c3e0be608adcc91a18048678ca6bf18620a0a47d273cf9030b52ad337c759e6d8fa02054241424501019086118aae8de5a0fda5e7a820ef3ce4a2aea47ee004a91e3e432bb5c971144cfd669a7f01bbcbb769b7b338a1082709a37703b6a5b951e6d235243130119282b4cd9fda3c34ff879df2daca3c400747a159074bf5ab3e44b3fd8e94ce7091e17ec4e80135a915986d0d8b3b6340e3ab3336e94debea8a2c9744cfbed2e4bd6b98b065ce45e91ef8441cda8c2c2af0e47d5e999054982a3740c1378a22808e030e70f95f080642414245b501030101000021b4221000000000486472ea43f32ab9090e0eda9e1f2e91f9f08d016ec1888298d0579045577c519719f2fff9e20055113386873730eee7d66410688d5d5381ee1290029009360dbbd0da086c1d18156e09b7ffe38a10637a6e6379d2aaf4980ea4a8262a18bb04054241424501011c81b95f142de6a88b3979fc90b0223d5fb65529297122c2e74a1ae995947919108d969f45fce7f32527eab62d555218e7277a7ed9fcc03222614e454208b7856fc191e5e8bd1d106b8e141604e741ab83a2b0731ab450384266b2abf76a1bf282c4e8018ee321706eaddb8a5ce968db3e323fa9aab61f087313aa9e35458a74b75947f78e6867912e9e66825b01748be20c5e6b74ab91cdb9f1769398b14995228fac8b080642414245b501030b00000022b422100000000046c4d20f5e8f2742728442668e6a575bdfd7c3a70a2df5dee8ef6015eb4e933ecfb545072c60552645929796a76af34bfbe4b2487376f9651f127f5e06d9270d83228f31008cfa6e9b6b2fe6b4d446605ee07f4b52263440dbe39838a76ac10105424142450101aab515303a676fcb337f34095a04676e4c35d5b3786e0726ab174e58e4e3427beeba4f12a0fa93c7492adb20b8019169f5c522c293487d3aabc98f219c836a89df0127719412bac0ce89052c6d1b0857a877747d54a4d9e9707ae0fc7d43826286c4e8013732b6a7087da905eac26680979bf80f0ade48a11cac4c58dd161a1e9ac35253330c2c2f4dd93a9ef2d2c840d8dca9f77bc3b2f0124c9fd185b17d98bac1ebed080642414245b50103c302000023b4221000000000008689c8f825886c28c324882ec36936050640c22ea2b672d413129430acca0ce2727cc5547029feaf939f68acaf2c73b7d9180e044be3f5523a8ac9422a590b3439c33700f9ef10e35405c4e0aa22333a815fdda34705c87975457e4fcfcd0b054241424501014cffcf0eee11efdd02ea7ffdbfccb063e6b51b89c9a9e1dab3c3ad3e54d8615e6add279f106d5bbfba371fda15a01c613ca22e40714d1f4544b138fb52807e82f65d3b3d271d7101b1ae8bf95a000d736a2385cfdf6daa1af3bd222ab0df74758ac4e8016d2273779fcf5d105f413f2669fe7284e8468824152ddb7f406e82791967fb6b4ee35b4ea29a5ef0fedba0bcce16568b3af913f2f27867f2fa2ef6fe52e79c2c080642414245b501037601000024b422100000000084441c08d4fce6adb172fe74f028490659501abf474fe7c0aff42ca0e7ad8b5ac152e444794d8096a4a56d9fcfacd97b0a582cb274e960ea2fb0f8cf8a139c0af9b8149c6c3915f9870ed3ead76f230082288de2cd587e96ae2cd6a28c44600105424142450101d4d522623c75de8d601225883cf8c4aa717cec50041f18a5ecb90f9a0482245f3abd5ca686ef3893d73b69bd41118ce01b7859a1752714c285cb6f7804aead8e9860c8a55df8221f77585c7744b12b51612d03fd447fd268ab592000bc263a548ec4e80180e4124423d4b8cb54341c3e966a451933267e0e4dfd916376a5300c97b6abab4547fc3060b43e4d99f98715e90dfa7d3ced0eb31694dfb16fc11d51c5888d10080642414245b50101c501000025b422100000000084edb5397803ae24c36bf857fe269d98f319fee9e8e79c9bd6371ecf00114d33f16fc8c66370f83287254f6e0ad93af8022e036ac75074ab0a546e72a24b9d0773b3ac1eb30a1c0eae8e69db2e6532b670240ab13e5f17a62b5764254a3d6a0305424142450101b6f89474183c8d0c1b30d77fa9e4c22e3e03b980b25a15ed6c99c25accb96e25e72cc0970e87ab0a879f64615685058526045dca51df2c52d5c263f9c27bda866e8f0e3b0af0add727f042b67bcabd915fe7561624e859c53e3f0e08de2057ba92c4e8017d53e9c5c05e852461713e8fba58e6a6068394bc595554c17ee3a5a3c51b27411b44642908f3c3378fc0e813c339af974f2125b9d6130bab7e41cf151bcf04b1080642414245b501038202000026b422100000000052f225c5798be44852baec01364628ba93918d574bb8240fd0a8e462959d25186935b40c661023f955ebc8627f1cb3e411cd6c1d4b7c12e0e565b43097ede70327ed086df76d4bfb0f9ff02cb3d951e39d0b5ff9d0b0b1e6a9cf5e150858080705424142450101d0889b369c4a3bd14b85334528215713dc68b0dbde8b7926dc627905b203423bd25ccc8369c796af16953fd4e394ffae9e028b788fc51f5edc49268a9dc8378c91e425542e2d217be5bd4ba00a279fcfdc59421c9166b2702129c0dc3a30158796c4e801ca97cca2c77eebdec485762c8fd15735c7963a8bf6ba485fb2b78ae2749751606993867138f06f06733d8ba4df33ecec7b555a4a0e105a3e27ba145907055e20080642414245b50103fe01000027b4221000000000924dfac99b30399109f3661f4c550e56177abcc1f32d2564a10e37023e51946ce6bcd436868791995e12a452db7ef7f3f113367cbf3f44105898b157a7eb7d05be6a789f5957f7a98ad210c9a43c78088710e45f650e7efa491a630ddd80420405424142450101669a958f8bb03b50bc539126bec02e0ac742dc73f180f19921706055fc3762604a1e9c84203e491618006ea9a155d38c22b55489d73b66c0c8fea33da17cdc8c1e4bf7e0088a2c6b0e17b11c7866bb53075df5f8bd5b12381c8f79e4f2373e5b9ac4e8019d622ea500bf7716ff201335126188b69489081482f7eb8385d2ec270dec7ec1365a2db27cc8186ecf037035ecd6edb1f0a72e19347477569a9ddbecae2ae718080642414245b501033602000028b42210000000008c407afab21ddfb3d00f9cd0c82004efdfa93e5060aa326ca7645648029a82525b2e847fbdf19c3cc6d4283fcda75903297fb5a0aa2a4d2381416cc9c5f12008e94e52cbf410955a8838509a109f65b3fe71ab2cb04892a67a5b0ee852215c0c05424142450101467b4783367ecba5e825df930c2fc0800d71dce3e986457c3ac27c6a2b9458455ecaa6a7e4f9f4ab457240932d00d8a2d404732a47d3d485a23771403a40e18240f27065128ce04f2a4a88a8c91b1bfa474533fcddb26adbcb20656aa2eb44f29ec4e801c6cdd7fe11ec5314eb1e7e6b7966d7265ac2797a3facad1bd735828cb6d9aed89d36dbe6756bee39976f60336c383a8cf6fb784ba8eb43e231812a1f1226b353080642414245b50103e600000029b42210000000007a234f516347b8b10588d120386c748bff2315b26ab66aa8bd4d225775e16f243132424b02e6cbc8f9d607976f31646d0146112585072e2502e964f8ddd8c5011736eee7e9a6ba7423e44aaac108feccdd96b7ea1da5877248fd1e580cf5960805424142450101208ea4d38004537728c4c9373cb0ce0782c26d8ab1c210589560cd7916f000283d41626e6341fd02f0df090fbf14246c6c148350dcaa4aa7ea7ff5f4e22d09845899e02cf54eb245ab6beeda5ebf4a98da7db130993c8b4b68ea8c916e9866e3a2c4e8010b83411e5c98faa4118ae10924590499cebbd3fe134abfe78ec924c427d17b9ba506a8cf4490658cc09cda444d0dd6cf3f2bb8481e3e6f1f5dea934430145374080642414245b501032a0000002ab42210000000008887c6e7d7bb75c8e49672f8ea22e1bf55edc2c4bd9b67d293f6dd6fbf1f304af1d344098527864f6a51feacdb7e2414d705a563956a4ae116d1f565a7f8e90d660e5884e8abc3d23837afafa34b1ce8ae41ee6851298b2f87d3b0074742ca0705424142450101bc141f0a1cc77fa2f27af117dd9039e1acc0a1e6f78b0a18da88cb884a3518707412cbad6826b7a4e856a5fcf8b7c20e2c39eab8d7e73c45efcdc4303f1169881dc6ad1b7e7b27256e7fdc303b1434315432c361d042ebf77f83aae1c27179f1a6c4e801fbbd9404dcdf73adf2e74883ab5308b83d5dbaaeefbd323a8d59f0f356ef01971de62102dbdce16b8ebdcc6537b3ee3a5629e2ef493e39e28c1439837703a2d9080642414245b50103fb0100002bb42210000000001eb186d195fd5f7c312905f10aa6bbfd5f19cd1c797c9575246b346068049e444b597ac7532ff35ac445a8e7d056786bf9a16addb4c69a44f38b608ac3cd8d02fd3337e3baaea0f261277abb93669ca779865da4ea57d385fcd5fdab910ee00805424142450101005de0e8d87ac121c49bd143da341dc1a947521f04a876fee332a597a59889166835dc7db9423bf8ef5908c04110fa06fd54a28696bec353f98b3fabb93b5085bd6b4df8035a58c947a10a90dbdf1bc076948041586d3b4c411af55b108fadc7aac4e80102143f49f07bc5ef158ea80269536d135c987c00a6014386dce224413016b648725bb72af590afe299b3d9512a5f070063a7322f52e9017913bf192781c39517080642414245b501039a0000002cb4221000000000a6e539a51afaaa29c776c675298079bddb8929c36162d7f0ffe611cbe0cd3d2e44cbe2fda52e458f678c95c4bda8ab525a7a212c2029aa5432bf351da36eee08a0db3cccd6e6776439da224c4913b70b11069fe29c89462b37e0a96c03a3a30005424142450101d47bb0d9564b8e147d174a02414bbd539d808cd9bb55793328b09d0dc76d3b3a4dcdd2b7b52339c126c025406e409bc9040384ca780aa70cb77abb7ff0c7a381a73a874c2539131769549905b9126cfd74083a7fedf287185681166788826899aec4e80114122f3c984afab4e620b863db0246c98465a9647d66936f5870e070eee4a03d51e94471f058b560bb1b58f2358be12625c8d44f4aa3a26e35b9c0db06c7c186080642414245b50103830300002db4221000000000124a92c5c3ad983380d3833f963666ec70b1875ce751f86f339bee8a6ed89c7ec251db120c7bbb73dfd299f5f3db75711e9eea7513f9a16c6bb01b3c0af9960b5556a545b0f9576ade87536aea925f341e22fb772ebd885366bb7c487b0e7f0005424142450101baa3c26e5c9c62d551773638ab31cdf8fe97b6a33fed27462cd197aecb0c754e73de3a256374a2a81bc69a04f40838cc602a34fb9a0d40076420efbe073c9b882f73478c06d3b642c0dadf0ff1d1897dacc2dcf94c88ea35fc2ac53989e72539b2c4e80189f6c22fb592314c122c0055f695ea8885789d787a5d98a28f4189b08759204950e024a7547780d933f36224c333e96a7d875d9eaca132601af722c3557cb145080642414245b501031e0200002eb42210000000003ac0b6ce4fcfb1add11142b9a18d6f6fa802ffdb569ce824b10d890c76f2c12da861d4fba19b5fc13ae17b07c3ffe85e9d87778a56ee758362f48d00e3f810076e8cbdcd15d3707c2a648d72752b9abe6c4275bd0b11a0390e6cd29734da3900054241424501019c56d04924a891c0baf7db6d2ba2ceadee5ad043ed4df7c7d086942e600da6738e53136f1b06ce9ee15aee5e86bfc7c28696972a4924906eb356e16806536781e6245151c7317b9598437fa5b8aa671a3d22c36b880febffeb1c40e1813afd74b6c4e8017ec06c152779d9ab82bba94850566594c905e3c3019d1cded522c02b8c7b1c29198675212b05a9c1c17c8067054ba22e47e49fd647d93061faa461e7273df628080642414245b50103bb0000002fb4221000000000e4f413b68fd27da521da26f8d6a86891025da3f8480dae0672560f70c912936120ba9979e0cc14a8b42bc64c1c5ac941a9df11e7c4928ac09a2f633a08d0ab0fbb63e359ec6afd9603f7e45a2d2b66d0306c927ff6f4f6c28cb84e7a84f2630805424142450101a6505a3e2bc288232d122a51d55960d914b967d6a13beec26af8652294dd6e6330c2ddd6aeea7afee942c356b3e23eadea54dafe84de190ccdaaef139c0d318b9c4f2fa2b92cc18e6b499ce95bd8de1f5f38f17fe02d667fba8e9ed40d4ede60bac4e801717f9197eb2ae84eec8a758a6a977fcc4ae85b3e42fac203b1d2b650efe456d89aa8f79975b54fc04512bc033cc201f5855381f411f3d40fdc662a80614222e4080642414245b501038801000030b42210000000003cd58534dfa9fdebe03fe740339332316fc5ec0d17945133982ac83918c4c01b76adf1919e5d30b1bec4976af4acf4d6382376297a97d1531d17a73edb2e170db6e2b16d270ca902d139ce8bf924c95c3065c009b119ae9624c6e784c9102408054241424501019c3d3c38d111de8f2f6003c83852a08f4bdadf8d64b971ac2db774a49e3b561f51cbe52ee010568ce9b111bded8b4239c3a366b9065ecc7bf5af0d060a3401880291e02e823903f5e12d69d3261508d29b6eb514d6ee8b41f23d98931354cbc7bec4e801d9ee4efc5d9ec4aa187180b6026aa146e159bbaa645c22a42e30432d74d3958f81f627462c160e1766ad57d90a78ef5dffade9a87bc1afb9bc788403a2d13fc5080642414245b501031001000031b4221000000000eef35326f4bd76c0dc1cf95817792d30631857dd4c36b901a193131088dc1716078e536183c3455f65925a1fbd5d214521767765ffd06822a2b61691e1911e04138c9a08afcb89c00793730f92b1119319b318ca5cf12a085401625dd1356c07054241424501014aca709b9e63a36f39a60348eb6436ea48440ee89fde799783158cf04875085eb50af0461daab49de148365b3094d67f716d0bf9a5c12ae7cfbea1cf031b738a8d04a1d3a547b9d0259d179c15e12aa394ed5dcc3c60db247efba3827a457fe7c2c4e8010a2902b54249e307fb76bacb3741d7cf5ccedb32a69f4d20030c741d78e3c762da60ba85347c498a1ca4766769c2e4483de59d50a5ebb3c3e9aca4c820bc9118080642414245b50103c801000032b4221000000000809577b435e5b76a2607bc973a32b2205ac5e1cabc5e045963fb0b4274574f4b5723cf5beffe5f839c4bab25adbeb6c2548957146276a68885f04c18290e0b08bbcfe70c46bd6beabdf1ea44b63ad96a477d13761a94e66cd51521df649ffb05054241424501015469f122259a873d1d72d168669f86c72536170696a2fdafb6c235b26b78f9506a14d11c7688e5d18ea16104c75ca180c1d950c9a5b850acad6149a6b9eb2e8c86f212b9a047672d42b08bf6db1ef57f6a977850b9f8ea02edce73769d487bf7c6c4e801416a470a5c73fc2989dccc6cd1003c07d6ac4a631a9449031fd78c4560020260dd0d7ac94f2c06f13ab03b1a240e092b667570928245adae0787ba35ebc6ee4c080642414245b501015301000033b42210000000007aa631eeeb91cb4801a33dd34ee4156e40507b74f77274f9ff7525d204ace5099ab59f8e8b0922115ff2df625aaadd155857d8060a3f8128919c623db3a29b05ef65444ef9959e8b4dbaa743eea11db9941530c0950caef6ca00d87b2299f00e0542414245010136d202532af3794c72d5de164e108dc435ffadfae78888f389e63a9d25774531fd9941564889056d5b629db1404bf9556ff2d05de40029a5b570d14e202f3485199c719c44c19b91937dd7365489604a86287141221323428b203da5caac0aa1cac4e801f98a6aceebf15ae80a4f91a90a04337d43f5cd47ec4a86ed33b9e63fccd390c332be2dc63fbc44d8a91d228af9103e27b29dbd02d773816e97984f27404c59ff080642414245b50103da02000034b42210000000009aac7af18b7d9db9c9f8d99f9f524c5217adcc4df6fb7ed0caad571664fd041b2735e17f2e72547ec77fc7e11850abd46dfcba465f3d8b0817d669cf39d5f30cf857c7955c798563268066d071dc0de1c7ab48122c57b8955899b07f6c6f720f05424142450101de830db1cb9a8dce3e7403c930df796d1932e253114f79ae00a7d08de4929345487e75a6eafd3dba549a1a254fef26a144e9c6e825502a69a49ad3dc5ee2ca8c6f32a12e6a10b494aba7c8aef3e2e3f3f69aa2fbfbc4683542b673e4e068b9a5cec4e801bfafde5a50e7b516e0389cd0af4ffc059cd09334dc43c0354ef287d503e1c692932b5c4c38d465066af916e81f9558db81eda6d7bca9649c397fb277b47470da080642414245b501035c02000035b422100000000084f3d7615cc7971d41d3f7781065626fa5ebcf6fa26e22fbe081167da035d42b13e6ab2de6551d4be8c276d3a5e976499c22e809b703c29b53f97ea4ca37740e23932152950556cc6d85bfbcef43b12d9de757b354f62cf778bdc9cd45cb1b0405424142450101c6b66bf4cd9aa285202256d2d1db369646aa13adaac6da2898840ae8ab443359b656d2f2b1dd725b4fa3dbf405f84342999e5d60566e896661671985a53d518b86f2814a46f9912380430446341ef3eba7c174cf19a60d97881814e2804df304d2c4e8014fdc8d05d187b8a8b1327aa9614687125e9f76eb178fc30ca781b7aeecda03ba498dae440252c3537e741398b942826b02d6b92cb38fc924eb9de246d8923e74080642414245b501013202000036b4221000000000d8142bfeef0f030e7481eac89787c22af23106c2b320d849d28febd3104ffa45846b5b7537870a2c2c6a352716a9cfdad91ebefebd9ffff6cfa6adc8ee7bd408cfd81b9bf586b80f9e29fa9952ee331bd1b9bfff3c3b455cb39fa70234a8fd0d0542414245010110a34c4f6988125bede7dfeadb45387eecb42c1f40f48b55fbccf18f1a3c6521635ff8908724d9f27eecd9635dbbba3a0b287364f86251c16be07f3bddc66c8d9df08c8d461a0f25ba1aca0404d3a42d74215c8633733494cfad8550e4ed3c59d6c4e801bb6c1a9c0e2279bed56accff72d20d7710ea6ea5884eec70f64c5a9c3ab1a05f5b2533e20c3532b131c1f7d953c78dea8bdb387f1e0aa400b5fd80c25fad1e33080642414245b501034100000037b422100000000022cd58256c4ff8100633e8e11d5aae4df0a4cd0943540028b7d8afc579744867c2f3d1024818fce407cf77169020ce19729cdc7877a23a48d2f21751e0828f0f98f930a5bd9b0bacda6624e06fd82141f8103e64e550ab0cef29f5c1bec16c0b05424142450101d4b6ac9292ad459208637e4b6087febdd7df8fd2e76966a2c73d1aebb2b2f954b5e8e935bd6a22c2eafa03045bebf8d1212f161e180057de26abe95674d7ce87d2e08aec2fcf88272b6cce0b43a45b45fa5d6f24fb70a964215da117de89456fdac4e8010d125c656b1bfe852a83b29194c2f20923948f8fb0a30df5167bbb82619b4da70eed0ba558ae504a4b422a0eb927006d15d0ab7efaecfd7d175310dd43477783080642414245b501030e02000038b4221000000000ae00a227308b41bf3f05c69731f22283e1a5105816d0a9f33f11ae528504161eeaa1927560d1d1172ea5e88af5cb1fe28577df6f86b7b4f390c0aac2b2efdd0ee165b1de4237702813663518ef7d0fa68b95a14ebb713b93170bc7003f47bf060542414245010136c990a70172226c16c1192e2aeb3e04a95f221d1b52c06aca6edbad3210e07353de16b973e0d31d22dbe51884bcf8196d56f6817c0a12df5fadfd469456c987c55406db96b4f226d80e3db4b015d9b5ba8517ffb9c8e0a9d52f13ceac95cdb8dec4e8017dbe8e4186303452a381ca764df66d937c7879c84709f06290226c506c684c95bbebc934746fae4212069826ddc8632fed4904a77c7a64e7a520c8ce80d679b0080642414245b50103cb00000039b422100000000064a9ddffa53182eac29b8e54b0092e1705b673f9ba9eeeda5ec5a12805cc261df2843554e2e8c82b127ffa7941d3655ca9f1ec2ebd57d34011ae8c9267a56b08bd5149da2ea10b7538fd5bed0ae55b0d91f9540eb5e14ef02f5022431e98e50605424142450101e2854f4b9e6709b0aa9ba9b582e54d21e48f734666826b87322a282592ffc25a31a885470814bea1ddcac274750483dbd2061fff6d0c62b74d6cfb7ddaa3e687c9ef6a8c398e1aa98dedd356c0b70b65142bcf103c952261a382359c68530aa7e2c4e801f7da031da256edb6db0d8217987689056807a422b444e11810165a49e616dbcb96b09e77dd5356720b05f138381d077c397db93d5bf924833c2b68eb0d2c62bc080642414245b501011f0200003ab4221000000000c21f0cda111f4998ff200ccea4c4750dc103c5f7c4904fc5d7c505ea2102845a226d2085efd28173c3686d966bc06bdb678d00e8ae1405d4f39e50ef51d56f0b7fab44ffa4930124fcb1759ee28199df58c98bc79ae71295c97a6e598786910f054241424501018461b06a9bf267d1200656158e6a1a11847067f432b0701c49b350fce8c57949fda2f97d43282e1db11830924e07655bb36ac89c18d62c0bf1e7bafbaa16c084064b50ebe7e30bc2f96880c707be0ca8d163cbd517be994281f5123030d44abfe6c4e80190bb77577453699a2626dd8cc124b97027469f24b40b910fb3c806f2a33e8afd86d5fa15a309235d7e4c95feca9cdd99a077a1a94598a3beb9107df492081706080642414245b501031f0300003bb4221000000000888822c3755a95f0fa61529a27da40ac7cab5877c019c606d65c37b090ddba1cf0d37a1c11f3eee4726f8f10b89077da308897b7d6de4da190abd6495e2c150a7ed3a7c1f2e29941e2c71976d91a0339b5f0ac50820d722eaa111b72db216e0405424142450101724d9de02c208b71ed51be1afe1a6fb1026979ea0a07277d107d6d0931975255c96b694688d9820fca1e508e52f2feee5cfc69c4bb02d24586f3253b17a7608e636e4b30459a9d9f216ba5334d20970d645822f2b4e0deb54215f3980cd6a101eac4e8016b32aa1a801c6cce2f14e4abc45a3552f1733da51f203baab011659ada4b3b502a048ddd81c498bd6aece0515972f9b052f1e0c404116b87d11802873ba2739a080642414245b501030a0300003cb422100000000054fe073f455adc9f957b78b168fbb3a56726d162c52dd43d1b3bf9d3f733e97b3390b06edb5c90a224ad8295c949f71b128cb95d8727691cf57a7a7263663c0f4cc7467005def09b542b219cc76304d02fc0bfbebda685e47438102452ebbb0e054241424501010c5181e53425882e31aeb7057db015d60b8dd42073cbabd63a7ac88907c19a29ad0d2a8bd0321a380c4bb83e2ced9e7623c4c27c43d090cff8e64e75edbe688b189f7bdf36e47674cb4b34ad2d450ffcf7423d0a76098fa9918b2ff5702fde3eeec4e8012f911ba70c6bcd5baa89a0ea8b425d82cc18344d107db80ce28e4ce4acfbd1b5e65020776b11b111ba896b6ee5724d22603cba35ec878d3423bcc42450c291b1080642414245b501033e0300003db42210000000001034007e1d15f8c60315e56cab13149664509fb62f44042796c4abecdd7ef549a0cbad5560a721445f2b4ec95833f3c380d855bbb1015e0512a7e832d0f28c0b2330c7bee6d302074d389107bc50d870388fe3d0b767727e1b8be737c587ff0d05424142450101dafed1ece6d09370543bfb9182b9222a8e08ad08b68a89840b85e0895b861d0351dcdd9449886f98edc5f98af39611f5522e23e67418663c25387386c5f01a8d34feba54e5988a8328362a0da6d23207985f2d7646c5ab697329f9ac052ffda6f2c4e8013e4d8a1879bd7acc105646ee48e96294f558a02f4801138282aa1c0db7f669bad085e7890289b6c923562b69a9658c7cf2fc436dbf758d9fc6fd16978b6e8b53080642414245b50103e50200003eb4221000000000a46168b83472a337c5363c4a71c7a510e1d71781e322b9b053fe7b27bedec564ff2900ea1729b608e985029ee76325d5e51b45caebcf6b7c3a4d2e44c3a873070a68de8841c1d5fdfd56b9d383880eee087e576279806ac61fbefca0c8ef20080542414245010124d2c65ac71ca9943d88d121d40e110de8091604520ddb04e49babcbb6f3f75f635b5221659c8aaec68e862652daf816b643f0c399530136b7e978d4c74fc68bdd9682807c6ad292564cf53633b289cb9aa9ec0fbae6c6f4b93e934a3a16de11f6c4e8011db4e992fd1ee40e670a7f4c8c2bd3a8d288130c2fb899203be6d913f8e655210b7e048ad70c98b43c990b40fb4ffc184f47485e1e5e35ccadaee4ebc813af0b080642414245b501035d0200003fb42210000000009cc96d360a90c0bd42528a687f35b59360470c03709d8a5784c22e850cd66b02c073bbae8b4c39bcac6bac30fd70e1e1c0d1e512b7bd408ffcd0b42b9d6658053860958120da682f56b2fcdd4de26a560dbd842b6f9b01757057abcd805e900f054241424501015cf729eec53c422e390a25ec017d29b09254d586919ccc295506a0e6d83b6606b3c64f08d9ddde51f154be9c21a17dc03c75af95fcecf9526427fb0d9a22ce8ce1b34d45a48bb39f3ee07bfa362c8046c8bf77314b1e3984e8b9a3bb92072e97fac4e801ebe29cdeaaebc7cc6df5876ca95e56c33596699eab3d297af9ba6df3a8ac6533edbaf2a60aec5dbb9cf677189ec33194e191268e711a0ce0df4919039485cb30080642414245b501012e00000040b422100000000004c656fceba5b5df068f2d743620c4afb78f05f79f5c2f16434f0ed5e8c3814d87ebbc5753784c8d8ea3cec45be7305e43b3dbd3f28653927b7659b52af8340fb5a9531b10253bedf4158433b6784ed50a3cbc8377f4b4622f7d984f180ad30d0542414245010180daf35d9657d350a30946643f265131ee85a67b8a7eef5f0450c56c639d0f1d7877416b10b25b09bc04a5484c389e5d324d418e5ee4aaaa16792ab5948aec84f8381add1729fafd3f36e3f863aa23f34a9fe03496a41bcdfdffdb9dbbf62346fec4e801679e3de0db62f229d82c9eb14b567370e4a09a30dbc9d4d79a9b81eafaadab899ad9277b3d03e32d1fef724c1a5b79e9e42776a63b69b078c6d35d293ff76cc9080642414245b50103a802000041b42210000000008c8a9e7e9bd71bf66abcea5c26d16999e953b54f2ed77ee90c54057807224812d7563e80727c5169b50a9d5aefebfdaea0bff931bf8ca7d2cd853c6800a32f01fe986dc846e4fb3a489f2d7bfd6bd74485b3cd4f9b93764089e81aa880445106054241424501011a6cd220d798d43ea039ce7caf8b8a3b89d3e29f59a2f2f69cf1c29da9faf6039746afd86432dcab1aa643a9a0eb24a7f930c78a390c26ef340522b5e8c2bd8c56395e362e328b14353da31d56067eb7d1da2626bb390371f0ce8dfd165ff6e902c5e80105ff722b7d514bacc0882b83720547aa39c42160e91b18a082b14eb226796f3c587cf0ba4f85906d0d0c3e72e341f1be3439e786a618711d3fb5fdc1e93b536a080642414245b50103fc00000042b422100000000014780b94f9f0261ebbae6c53c4a5c880df0095d4dad5e5968d8ac9925fb2481a7abe4f9a0df849cff6a217931edcc9691a676058c6805befd4c7f2e74a933d05ac73b49c9b92d85421e12864c17d462a66d61e0ac9c1c42a3ea2d9ef986ad10905424142450101589c4cb16b11aacb15ec5142e6ace6ee6f420377ef9312ad6d95f80a6960f72762fe8b4a635bdb1e763f3eda1248339548c255bb5e2782b14ffa8dd9227ccc87086080ba46c2c2f7c97b81f2e3590033f9036feaa5bdd35c0dc78c1cb6b770ee06c5e801654741f7f65c6951ed26b24e0baf9deb4eddf462338a5de1f7459ce88cecd4504311cb515eb25b373b5e78733cd04ff84b0b34428558688b86b241db1bfa6744080642414245b50103d802000043b4221000000000f69cfa3989f3eb82ff3344429959b989e21140f56aaaf6b19febc5f1858903556634707602a13f8c01af831db2411cc8f8b40900cbc498bf1e868ca16a114d03499d2aae1b604d9b220636ce7179ba8ff36f1094a7e3275cf42d2363df50cc01054241424501019a03a59016e8c0588638287c79b4418ec7bf72e2b13baded49727b61db303837da4682f3ea96979d63adb3c948d126af8bb58ca474f07f17b041e0b730bc898bb79b92c9dcf1858aa3111414caf3924d2b5919e28cd932d3fc212055ab8c93cc0ac5e8011f729e39a3c63c41eb5e169e7810d850a06c317d4998216414b4df81935c8b3a32f6b834fc143ced3aec00c32d66a0b75ca1b36b0e0e84a97b3ed2c718760c9a080642414245b501019000000044b4221000000000fa4cc9f5032cbd372011ff8ab91d308d0217a70c085f9e8b6e3f0b2ac15e051743c364a6830c0ecb64e204219b5b072836305dc2fc81ad862b6cc7e1e8275904738bccbd306c248dfa98d413d9d71246ffce116ad86ed2827d92b5792b67f201054241424501014e6a9b43a74235d8b0957e38ac9b9d5524af54cfc6f95ae12d160d6248854e288d72870368150d1224132ca6fa184e87e990fba6b5ebc551f9ddbdf6ee76178c23ac63c1d475709a29b6fc159814d1c923cb0f9cf07604ef6179e36286de74e90ec5e801d33003579919e2397cd8f0aaa31d4acfd082001e087fa59323f85381d14f90b4ac6f538aeb950826f26ea7392d3b81578409883874a47c8be34a79f651464e0d080642414245b50103f201000045b4221000000000c0397cd6d91633affd64f64d4b4c4a821a608489f1e23ec1e704af7b67432408e56a9c85d53085f324c35854cd42f0dc1e8cfa15102afdd4b40d5c98ca737105f7a834786d3ac24a37df9109b38e52f275f4e4905107639fa84c9b7f52d34c0f05424142450101b0a563d03cd1f8dce0f4b6a1d1e7b1011b6b5d5f48c6e03c8544adc155fdb639d494d66461aca8a4fd013b2e8017e2b1c7e6486b0a1f953d6bc8cd36159f0e8e39fc45ad00dc744325cbc3c4ea72088ad69b766ce0c4c4760c160b2bfc0c805112c5e8018c5842b73f6be15992750529026c48839fe8a4335a8d529ef734d6b3b2c7df1171313e1e0bd9c42e0eeee5698f8eff28f7a5ca47e8e3c986133ceeecd84cb048080642414245b50103c901000046b4221000000000d60c8f6306ce36c023c50af8a30051924877f53d8b5560153a2cc2cb24afff15c3fd4d6c1d89a30764e75a96e2e924376956a08293a6346820dac9e17bdaf50ad4e71df1a1657debf89f8de2f9b2dd895e797f8ba1c936fdc1036b1196a2f80d0542414245010182de7bda54711a8b51712f3ba412d396f60870f26321f4cf00a14b06b563e968dccbfac9c2208a09e5ba6948d8eeb017c9ca6a4913a1bf9d3822aca8719ac48a6496430542b09be21c5791ad0dd24aca6cf5510a1aaab0190161eac12e1349cc16c5e801aea074aa089e0a3f48bce5f521fd3da98491271fa05527529a0791e311a42010958c312bbd7bc7744bdf6de8d0cf7d10c25d690d56c8db5284adfb9cd5346d99080642414245b501036c00000047b4221000000000184a708904a705636ee61a4c21e2aadb059b982837779b15ac1e2354d6435c5f10b5d394ea253c0b4f95e8af312e8c6cede8344a2aa29c4268d1ca29331e530f043d1ff1fe10a468bba4b4e157dfe22a218caa748510bee395397e160120f80205424142450101ba8c08b12e07d86a06fb0599431c3db5531797e3c09dd5bd1f920fc9609df45ce481a418114b0ea0f86380ebfbbe2d2bbf611546c3dd5b473a233cabb0e2c58bc5338a7ae7cf24818a2189a3e150df6fc0d77639863f1e992a093462d57651521ac5e801a07543640f8b89c2cfc2008870aafb550a9eac5823003096a6eab10c7d9ad368ee5c061698ad418dc7c811b6ab8d4720ebced80f8ecefaa72c420541697fbad3080642414245b501031200000048b422100000000036287461d46be9a6305b4400ac0150f09716af008ece503d2cb2a0447a1cdf7767ba20c00e26182332b76d51f397b800c53713f64bdc91a614136e1cfdd2a800589a5770db99d4ee811568a11b6c9ea82b440f10f4a44f289ec3a213b266e10f05424142450101dcf3e92ac48d3ebfc022971b713b62f9cf6d6955a7131b0e68ef24b121d221033eb80d385556942268500352c35d945eeab86b92c3d34e12b6f65f7932d62185ecb23dccdc98b42aa1601e61b4ad696fb961ce6523d52800a0953cbe07f511cb1ec5e8017c39365451841db2811795d5d734ea6416337426765aa49f52defe411d73671637546ebf61e896f9e54810a3555ab740d2d61133f17c4351a62f87b390fb69bd080642414245b501035703000049b422100000000006ef65605654049924e88c94394ad6d500cf57ff630f022f671af888084223678797ff6a40516969cb8f1d745df5b8fc622e786417bf572837e902dcac79450026fb397023c179342612b99c6fbb0fe5fc8caf230c79bf5a662367cccaaaeb0005424142450101941c5cb5b96f006ba50182aa6851dff507dbdf6da39688704c5fb614a950431ae7e9f90468081a169b4ce129e8fa76a74d076e7426253d6a3062c234a290d68da57fd89f9c8d773f7d2130ca4d8412db0a4efb0988c1bc874a4cbd660546604f22c5e80164d11f35440dbf9a1092ae509525c1d0512718c9d71af49c98ba0bfe40d039ace4bcbb794e1fd74c497a4f9408e33dafa734ffb532a5ff6dca6e0100c1338ecf080642414245b50103cf0100004ab4221000000000566d3d696e1e1f4f18427a7a7ecfde30b2d323df6525e8fbe49bd0f9b0994b2469fc27e722f4a4bd25fa01054307cf816c62136dbbddf34e16a846339ad1bc07f46742e4ed23e32863e5ad8da3b6559793791c1abe9d157597ff84215cc7240e05424142450101be60038cbb0736a3f04d8e3753e8693f63cb1df66f9ae663bd0f2a6edd7bba6781ac6168996c99470e5ea3810b8405772c27526e7661c6e001ebf8709c69d689aca5a2dc06b48f204482ab1522470f196c959380f49c3f9b4d3f64bdd8704fce26c5e801a8a3ae369b577fe9136a4c63a34d82a585440cea2186f11b889e0861070ab32089c627259fcfc702d083d3574ef7147816669a4bc82aa288b359b9d931865a63080642414245b50103de0000004bb4221000000000dede221c99500133f23f895a6647b12991c8434fa1f38e2b6a11f1e7a4fc3064650a4c5db23af4baf62ec222b42ecfff8ad2b3ec5cf98030cad8d86487238702aa5fe4e5906a0f4908324bf2617bcd16ba30e435557621d1ec05d9e92caca4020542414245010146713ff1ec8a60fe44371f86a68fbd0f85fd20b70878b48164b09e1026bef229d3570c4a4cfb41fc88fd3abf508b827f50493fd017b3f91cc4eac0c518464d847b10dc9e94f5b2e9c2e6247641dfce481440429f6447d8ab9a6b09b33bdae9d02ac5e8010834a54151aa6795d168bd812d5dd6127806cfe9599f60216b8c38abba9f1f155cff97bddd68ccc47eea6a5eba5a0d7ce05c842e4e9e17451e651861c741f156080642414245b50103440300004cb4221000000000aa4c040045a1ace0847e3247f301ad009ca73a5c6e2ceee09f08725923f4b72d8c15cca08c3180c20381dcec28ef12c3019cac8ed48832bdc9e0627b83acb30407ce3e8a01830b50380477cd62e1d7b4ec6fce6ec0d66ce940cc6d417b7ade0b054241424501011a7dde8b3811fb5c01ed7e2a1692218a9a8a8f7b906fad4bdbcd396ad70f3226a50af523166d8c1e993b421bc58c4cc22b82e2fb79f7e148e8fdf62d823d588c6dca87659dac85ba5e76e72b1c9c9a26f48cce73b6f08e1d2f13f03d9c0074c02ec5e8011212dc47cced61f2de72c7a1c6fa5dd8831bf6da037338776cc5cea00067b12ac487a2ef0c1010260e8d9de9efacb93e1c6478eb3b440360ce4e45be4349aa10080642414245b50101470100004db422100000000004e7c47d3a3a8fc27f46b146835498dda7659eee18bbf8b42e1546cb38a78a629b2a2a9b84f306024cb51afc74ad9956fe207d62cd7b845e2211b7099863910a2e4cbf49cff5288c2a6a6929ca40ce0d26218af683ef96590c7a4ea6cdbc190305424142450101b8b8350a08c292f5aa66687780caffe0c1c32947457ffe8cbf6a98e5cbc0453ea9c7b120cb6d6c226aa9f7f8a825e7d7abc405a8a4e6a6502d73695fb6eede8e0c59b66f82b70b4d2e31faac64c190e98c5415a8d46a4b9260bf49017c95844632c5e801a31add52c909117237a616be2140900f0744ce7abd3cb5fcaea1c356ca3dcdeeba77551d8dc6fcab4892c2cee8824df3d6c3507beabaee4bc7f6c86731c39439080642414245b50103900100004eb422100000000048cce5d64e9ba3a3c280b056f5fca669db3e09982cd2ac679e3804999775c36b700663822cd00ee83bca78747c6caea7ea42a4c0fa19eaaadd552918ecbce501985a7b6414536d74eddc5afd96c30cf0ddd48a785269b5a9c63209961c8db50d05424142450101f65df2ca8fa07617740352367f1d1356dbe683ff1d648a92471ac94815f703533c7840fbd93be4f6e0cebcceccd30013d104d54ebfc1de46de7d20995bd7a78b31614ba7f64228e0b90cd573e357e892a992fe39a609f5ab81723bed3486931336c5e801a9713ae779f1429ccc1f9e58df554bbc9f69046a571f4a8dccf455936147b4d0275e433a267c98bf520f8535654713ac133a70cdc49b6323622c5b881506bf42080642414245b50103de0200004fb422100000000040bd25f8c28dd9740c2f0a7689d9d2135a6008fb9788e72b6479436357a0797338c109f43a61d513ad585b1772e9b7e97dce4f140cc01c3421027f21fca64007db43703b540f63ad16cd75be9d975ad55717df662c72aca10e542c785db25f030542414245010160ff6039ecd87c5e7d32a4bde1068ec8bcfdd3cbba63cd6eebe21760c9d5173ccab358743ed09ceff1f4ecc6f7425a984775174f3df7c9182488b87153ac248bba3b2869c126091d426877ec04a3e88b0463f0b28ce5128fb5afda07d5eba2a23ac5e801d02f744f7776aa4f9cd75c4fc248b5b856326cebb6fa1d479714359c388f3aabf14e815dd864ed1bf4ec63752cfa0297a848de679c915422367e7e2741618f2d080642414245b501033601000050b4221000000000d0e19754991c55a2111885136b215bbc95b64fea09b8af455f22683c261f0b6d339302c922d2f8ad3eb0af758f65367a6b22c85dca3d344ada43a16818ad230d8e79437da32d484e2bac0a53a6ae5d7c68d540e56f24b351cca7291c7b8d510105424142450101c0a1adeb0c5460ab5126457be906b241b89e3c5c6468cf1b3909436ed84e8701dd3dfe612899612c51d9dd54a41fce6b573886b3fb3804fd5bf6e4cc764f4a85bbcd5065581f70918f85fed1f6a0a4b8a877f14b48279d60cae4c191395690f23ec5e801dee305088215e8cf0236608924cc2070523b29173759bb981cf913b09e271e0741466669eacf04f663638d96562fa3751d4e3d103fe4448f9dd0b0c656edb04f080642414245b50103dc01000051b422100000000040ed5f6a82871217faa8556a4aa7628a295e4d44a403ce7d0e4677ef89b41b7661084b1dfd1bd12001c9dc941b6323018837e8f79f07cce70331274810d93a09416fa42a567c814c820b67d700b0785eeeee173ab6e783a64dc684609c657b0c0542414245010162ea707b508fbbbcc1ab93108c5ea88ac812e80afef5e9b8592d7d1b7fb6895d44dac2afd420ca08e51eb0e34b61e8021e75c417522b1445b848d762278cd78c64bc9fc7fcc1c13f08f2d9648e3105ba3ec069ce0acbfcee5fae3da9f3f6c64042c5e8015f14c992ff94bb761d7fc229f656f7059217d4e28d0f456611952c54b0ecb56da622fb6abf10a0f59f9513b7ac3243f0c339ac7d534d7dc3632b4ef0ab90d250080642414245b501017501000052b4221000000000a8222715f52c4b48b92ea7ce081fc314708ca64004fe601bdb36f6095f4323528d88a5f04067199ee1f17e8b38d2c2e1ea8a785cd545b53ac233bbf522ebac004d62d885a586d547bbcfc2ea36913ee4df146a23ed2fb0fbde0be0feca521f02054241424501011ca45255991f8dbbab5db03465c0294af444d4b51dc4172fa516d980609a9569b43c92264939df395895167873ad18c86b749e5c2917e46671d6b093c319a580d40517ce1df6a9dbd5d192cecc690f6562d717b7fdc746106c8cc9a86b82349946c5e801963d288cc7a09bfdecda0eccc236c98e9e22795e490523018e4041c99dc4c82e9be617a194b4aadb2df39ed500e78b063a6972f40523bb5a3a42be25287980f7080642414245b501035a01000053b42210000000000c76dfd150001cbe26174bd301072c8e1a9a6da57358e9fe80a311fc03de583c3bf6aea7301dac9b2ccb306ee9f2a589b95abe6f212f18ae6846d1848a0ef701541e2dd2c0137f8d6067731f95a5f70a55dc01c865c85874593199761bccd60d05424142450101865e4f93e334f4a35a56fdabdea5e90b8917dba93e95f97d46e319f48ca7d6669461722105739d22e781891b17bc313a04570494612b88cd43415f4a0d08ce8745544a9730e5078e46000a88211437e9d210cc02a6825cdd42e8bf50ebf2b50b4ac5e8015e2e66bc5f81a05f827420e0c2f54372650e75cd0ab834a766f40e70d4cb7076fc2581d62ae3a3b6ef60af0389ec7bed551cc850257d9292033ca146e29bfdb8080642414245b501038800000054b4221000000000f4b9891f068053f985fe5fef6ef62e545312ddd93824de3de0916dad696a4741e984e4e0d54dfc803d8243588bdb574d5fa45ae2f24d8ca383166d6e5f8fce0f1128fc5dfa12e14f9aa393e46a72ecdffa3d9025199a775fa240543fa6356a0a05424142450101586b5a063676ddab2f6851b3fa8177ade8bc0ba647c7cb9308cebe493b9bbe0d2f3c0fc68b02a20819206979ba29b07b084debcd2d21568942589d95432dfd846fdc33e59318a34db3488c298b18238a87440477141981299ac1c81b3ddb80404ec5e801c5d6a7b9c4dd0824fab64011463b799edc01c91a714e7fddcf1c70a7c00775b60e66efc7f77525098215776a42a577cc42349b935bb57d1764167bfc911779d7080642414245b50103d600000055b42210000000003c1740f2bfa1a55de8018c73031cac65f6f0e749190ec728dce9ce9aa1ccc63ae06b7077bf5f7a7b63008b7bc76a83649f4c5cf80951f312334d3c4272e46d0d16469cc9b8d7b52c1a5c11edb9302018ac62fde2d86d2994694b4cd6702e550005424142450101743fac4e0ca6b02b7baad9479fbca0af8929ef4f82ffabf0c8416d4a030e864db642daf4e0def914dc457a210627ae4353f5137586ed0c75e0ab50b6c402108ad578c7997504e4d2f0678e422bc275958f4c5a6efad38048137fc7a2f6abef7652c5e801db3d64545ba2c92f2f8e7083c77f35a697e88e54005993986c59386a1e5f1371466002ef9579b61b7431c7997db3c04f19610883c298deec137e501f329972a0080642414245b501030602000056b4221000000000b41d1d1c4fe2f9570bcc6ae9cebc74455ca52f6dd933f02a3f57131eb51a6804f584fa3ef431c3d821b7431cb2c27e856971f7785f9dd00130a6b7fe0a5f2d09fabb623151a92b53baa11414ffa681658298b29844ef1b924bccaa63b730de0e05424142450101fa2e93dbf9460383dd57a61cb5161ab5bc19d53989e158cabc04a425134d5f2911649e73876e5eb1244e76d589eea420a73f2bb1ca1d77960e5e2c862f9688832876a58c8f6cd51e2d9bd5331c517505f9174d503660178207e74605725952ac56c5e8017caec32b736b5825e2a66b6b69e7ecb31c4a60a51d28ac8898d149486d34f39e00a87753a64c47b6aeeb369286aa23869707fddfd177b8c3b1c0e469dc3aec9e080642414245b50103f500000057b4221000000000bad1f25241a542fb254e738ea5a04c54360b22f98a06b0347edd24116a10fe059844090dddcc71390dabf25ee5195281d82c410941e7aade22e143275ff7980a874dfe809656b00c8b7d5003fd207a85ca1fcef8d149f7f87c137777b64bc30405424142450101b83078ad0fad1a5b99d2226dcfdb9ffa1f827fc07776df49b94a40403054e946e683b9fff758c4ab0f96921c3d868fb0d88f96bf0811fa976819e21088853780b40db3eeb4e90999cbee2bbeb12d45b368e88199da537adea1778b441557a4585ac5e801d336fb55dcb12519d23402b26a59d177d0f1fd1b6f601650a53d4bd029d67e96bca749dfde1bfde9186fadb53be0be1b7b239e763ce606a79ac88440f326f3fa080642414245b501013c02000058b4221000000000cac509b329c9d0b22b2239343a07eb0eefa47ed192a5dd8de3b6eea3938fe71a95af154b28faafb4fa393fdf9e3c8f43448af9a8869b5f6d05456c931e102306043e101cca61c05f7bcf6fed032db9694fa6903e623c7d0cb5340eebdaf0a30405424142450101582e595fa1a150f1f33f89f4dd9e217168e373e20c5b66e9a8494032905be66604e228efbac0eb95239f95f508e23cac509124dfb8044d7c7551cc0490c82a88494fbf87babf2e96011de0b9557273c881d7f9d7500a8410a91af352aacbd8e35ec5e8011b3e79fc465280fe35c6df266073a1500987ea9c8238f6aa53746f0c6c87c6c5d90874f3ffa9a67a73af93a4fc3511e746a46be0935ada1f33beaff7d2e8dabb080642414245b501016b02000059b42210000000000c10f44732be5a99a98bfb2e7ce81f5d1a4606d1363431d1a395069eef8c1137193058785e3e956093cbf1a4d4b852f8f9bc570f82c406168d0786eaf56a0c0ab56c9b21a8ced08e4c1f161ee58e11dbaf28fff5d838221716b34e6afe7be906054241424501015efc6a49e440fb60afc02cf7c1e7023f914945c9f2c9ade33f4a3714318510575ee3e19fd673146943a266171bde78967753f819a481f9f642476ae2ada62f8cbaae8b299a7b9946371ea4ed11cb8ab7ee56cd2933b342eed24eae362cf4c6bd62c5e8015b53ca6f8f6e0864f731c6a711c2d1569dc111a7343d963f05e110d181a02d608607d65b223780cec0ac396c18fac3217c7f00684c3ad32a4136830a3f9442cb080642414245b501037f0300005ab422100000000070893ae45cbd50437201464c8c7b8f438252a3fba594c678ce25f1da4fca1419b80d440b1cd1604cc267e5b04f2898fe4e8e337e09808e6e6ce5d1855e2f330f21e4961873cc96ef8d71b19d547d3cabc6683ea00b075e6a6815d6aa30b30e0905424142450101a887060e9e5ba49b64da2150dbe8999e4aa6939379a02fcbd42792b50811a14f1630d78a27eba11287891109a0a5106a51c9425d8ef66d1b3c24bf4ca1ff8e879d0c26a90b438f49521f0c9de866d65a616cc6a15b4f3cc19e07a02b9af219ff66c5e801128c5285fd6887aed207b704a606c9adef1c6bbb3f59ee6fe68f0b276b51c97d25644b3c778e73ee40fdfe33150f7b566cdd794ff3f5603c508b9306d040453c080642414245b50103000000005bb4221000000000f27377f73b571656e79c2129b6907c009a642a9e6e235603befbbada9a0c4d027fc3869a26fca316e03f0a737b36c04b23630fee6b2dba0b6e8cb39146eb7b04be3812009165d18a74d5f39f00c588d6507c8cadd28858c35bef5831690f0c04054241424501012807a47baec77138ae96281a713244261e5066ce1a5f306fbd25473db7780a5a1b8f1a28f5ab524fd054e9b6106dd61ff9a14a82f740eae5192456dd006627846b91e5b9472b362f00c7674b9c0a78adabdcc78e3c89f8c673c738859ed496846ac5e80105bd4a4e57971bbdc67d0c0b2d249cbf50cf51890665a5dcfd4a8d9454e062847264bd5fdb9c1483d436e569cb91b3619234a2089a7ba8f2c018c10e0237bbd0080642414245b50101890100005cb4221000000000c654dfd1833476bddc169e938ec15331969f6e3f1553546cc092c3593d3da830512aaa574b2f0bc8547733b8660724f7d8e5902d4f4a8deb303a915f6be9aa0f92e44d279660a2843162e83bb7fd75426f586b4e6b64d2344e7d093c2d66ab0f05424142450101ac9967c25bf51f42a1a2402a42f67c0228bb90b825b329953316b5f086ebc54bac56ef49245f71fb63e7c59ccb7a9414e70d1aea06df374bd86b4684229ef0886d2b938860d2081c8af5a9ee007f9a789ba8a49ab1eed9eec9d8c5b941bf6e0a6ec5e801c55c907a8034ab09ffd297edb39208807618cf77d6eb072caac0b4e6be11cdaf6b0e69afbf2c9bff18712d88b536337d6fbf5b022f4a7f94a82cda95bf990c0b080642414245b50103170000005db42210000000009477696ab1b63bf03f992937e56707d00debfdacf2601cb55e9f47814f1bd0744271faec993d2b8a0485c2111fca8610f94699cdea328d54d379137d19c8240d5954314297d75707729eafac4bd62bd34a691b52a66fdf56d7f93fcc0b4bca0005424142450101543eece0b4d3597677e54954bcf3c6b38187ca27767d8297182ab6570201c402754545c63f543bce7bc9b108c337df7139ef74354fdc35d17d19ffc6efc3c7821c8dd8111c0f73f34e137a135919e0798c2c6eee0a1884ad186baab2d6ab581272c5e8016e330eaa9b809f878fdbc381feb10e407a2bd23a8b1631c83c6057625c6c106ec8940f1986b78ae86fe3b307593642fc52a79a6ea2b2d1691887ce4b76d40c61080642414245b501010e0000005eb4221000000000dcf2d474b658577327b0d53cf1248eb9de21f8c14589b1862280ef6099ff1d2e1ac34424faecaee8c5d64de493d3c77ff9f52a95547ecec94b70426d1b2b400f48bca402573aee7e9467acfb17463e010a42889168a3e3c29722ce430a32240f0542414245010138c2499551b7c1dea7591bd524c9887881c5ab4993a5eafbcd97d3335ae2f978989159d2ca371dc02f14e8a7f0a65ca0bfd21e99323c5376d6cb14494501608797877f178e5e76b359fc0293db3b172b04c0de2cbe4218078eff0b723830766276c5e8018d3ee1b5f269d789a71cf03a1080ed87c4652495b737f2b4737f93e6c98c10475bb6c4181caa3fdec8e8a4a920cad5b69b74a4aca8615b184ea30cd459905ee5080642414245b50101850200005fb42210000000005c42cce4d7ab003bad0697509328df1c3d039637622f467c61c8bf637202246c5f93854ef869cc86368420f4904d0e20cde2103d3c0fcdcf5784324ea666c802f63f4cc4c164a074ca4b1616188ddee8e8103ea643502a5a638f6a3086454d0605424142450101c20d9404e16473f2264220355f4e0e7b61695e86f6493ed99b9ca2f145515c7c35340188553683910ff648edf223b36a305d4eefb3b1fd580b7be19d7fdcd08a115fb06d2069ae31ccba854a9c1fde71fde6080689a761176c6aaac4c38492b57ac5e801a8080c17f5f7ecc63536c3266369fdf998e9a6f5d8b1c8928b3bdd40a54d1681abe51bbe73d72bd031ad31069bc261f3a7939e76d6af6d886275e58cee98a7cb080642414245b501031a02000060b4221000000000ea4df95736035c46842c8fdd38a160bee8c69fdf565d356bf9da729ff9f804136ab67cdbf808be699771fbe4834494730516adcd9acea4a0b910f45acc92d900433629f80d85de2a567b414f7331e99346978445c2891bebfa04a539a65f7c0b0542414245010166ad7c62ba85e4dd7c79f4f1d848c6109c455d835c3b77cd34cd76dfb0e6f8104100b0e93ff24a0255488dbe9924cbaf41d44c740560b16b2ada4fb3f6c68288748ae83c31c470d2bb2671f89f1fb70d5ed17da6b3bae1e10ade65867c983e947ec5e80159b9a40deba7154c2dd2fc7fd689a50711a530383f31d94d32f08967c3cca7e3ecdd81a9a0c33af2214862ae88706d4592a5ee5ff0652c53c9a0513b907ba835080642414245b501037d01000061b422100000000022ae9d82c720b36de147494a2e98dcf948e3dbf6275e9f986030d4ff5ec37c09fb934f9c06ea131d05717365314121ac1ec48a3a6bd60139564cc2b89980d5032c0ffdce428e71a9d32a9b88cb94c755f4dbe0da8f053160cd7405ea5552b4030542414245010152de74b5135cceac390e14932ca06d089162d6f61d626a566b753552045213306ba7caebff51f3ae1cfb5f697a0fbc7988b6d6fea9896be798b4f91cd6ad64898d899b86ce9fb8aef05b40d13e9e42940012732ba3ba4d35378f8352c66a1a1082c5e8011ff0ba8341337b5a4f2eea7eb918ddefba7d05252582315d8dc743bf9abbd0e1d97e335d6f1d0c042f1eeba8fff123bdca348d485f9010c87a9d85700ea140ac080642414245b501039501000062b4221000000000ea7ddad058ae4bb291c22583df43bdf4debd4e758c1037d9183aa0e1f2507c24d5bbc4f8401b5930bc071e47fe9e37998f59b589c6078e19ef19f38f7e6daa0aba2e30f47b8c7af4d3875a25b3dd815e56227d1f0406551a4bfa71785bcf980405424142450101ded85ea00067baa628e2df0b120f74a99699d5f0d4a2a10fe128d7e321ae722b5c736ea088cd031815afe9b98e959b489fbf95f2069d78c4827fcb7fa701bd8c2c343e62119ca8134e4343d74e80cdd228e22a09e8e41673bfa5487b16b4c48086c5e8010c75af1625407607ac1a03ef85347e59bba3df9dbe1601e88021d08e8633741cfa48c90be7b7cf667309e5229036a2a080631a07f504fe3f67533acf491abccf080642414245b501032202000063b4221000000000624a49e67d98bbe4a3114e7716d78ddbc72ffd3d2157763bfbb7dbb831d67a09e0fbe918fb5e07000ed4959e4745dc2918591434a1b74e9b8b967c98875ef50e0bbd9cbd0c64a2782b81acee9b4556b8e1376aa0f4fbf515d6412dd085efad0c05424142450101dea5eb1c12e17103e8a49b272dd3864c4f241a0f280222d85006b5c293091e5e2f115bca55b0d9cc52ffca85257c12fa898c68ba3b5ad404ca92a8a85287a78c5b0263fd2c5eb21c6d308f7b00ad734dff7c1d306104886844f25055b94cdc688ac5e801560f057d103f8efcb8486c3a3f79d14c18c4feefec32399bf68c080ade57e44cfdd74def8d6f7c25ecf5ac12b7c62d3cb668b85b2efd2082dd158e56d287767d080642414245b501036f01000064b4221000000000ccde0ad54a933c7672d63969f679ee025d6c28f97a17f4984f181991ea74a323c223685286aa7e89cb25ada652286b81f5a6ddd6465012ad519ddf17a3b1c108fd6f87009b8ab5a9444a75068e0ad2199bacd4dcf32706ad651fc91dbfd83103054241424501018a2385af0b9112af73e3223514250fed3538468db79980571c54cd173383214f6d8ac07a14207d096b7c1922e8a6e6fccee63b0ae375a4af1129ad312951128d4f9531472b2276c7cdcc2d5317bdec27d865da3a58d9880eb24bbe3ab77ce44a8ec5e801d1c9e7b06c276e71d86937243484d750034750cf7698ca9300af494a12a370a335f0640052a2ac30fd6d9fdcf112a0aa18a0c106de72ae594f2bb555255adf7a080642414245b501036f02000065b4221000000000cc50515801cc13cf2c96eebb40eebfb4a25bbb03317b108cdb9b80ee8daac071b74ba733a4ee0716aa69c84e7644c84c5456b687ec8e240b64865a9e812116090877936ab720843d9153926fdf10d34fd4a878bb1c4d317a4ed262fe9ef0810e054241424501013c4d28dbe84ccf8739b42184e8907109313631922cfc916d3dcbd1c520df352b25957cc167e266a06a5cb1b3f7e06c38b9a23c729d0c677828fb864b32c7a582477f1919d110338cd2315b90ee2a74981a26a9bdc44022b9240263f6fff2673592c5e801d8eac80966e1501ad5da0c2e9e170f78483b34ec5457d714e059399729051ca2e1b3c17a4e05095d984c7d4190b27d5b5a24de3fd9c5d53129fe9f1d3ae0519d080642414245b501032800000066b4221000000000d8960fe0e39600085a51f7c98399f3c5fcdd332e34f63b52a277916f7c954604ecf69919e444c807c9cfd3d5df1f40f5d51fae6bd0de5b9eebbfe95d9a274600c82ba091c36d36c39d552df76b11f59e573a38cc191823b1164f742d66cd9d0f05424142450101ba8b66e4e778f5afc68391cd97d82761af804d857c51a040ba2e232b4e1ce304ec8dba1a29653c61e95bf784bf602d9bf3488fb23dda2d9f586ee679ca293f84cefc083ccb4252269ae0c53fd176b18128136e50de5cee6d0a08c58b6945123896c5e80166c393972b3ae4b813d75613345e974a8de21c03c5d7a7142d1fd2652b1b58d1867ead784dd11070990c6444b4e0582f754fbc495cff7c69519ee0982aff3654080642414245b501012100000067b422100000000038ddee3352ca55abbc8cdfeb83686a4f66e40e34114c94a0e1abcc94b30e94565c2b76491dfb5a9fabca5df179206e354ff498b65bb609ee1494bcfc626000079dda5e5a84d6d1c1a7e7946559c3d3b621ffd9116681765074915869af41460f0542414245010114309e64a481d9e3c0a18517872868143bdaf5b566654718898081e9abde9543cfed093feb30b953f5172e009b33414cfc50a1c4c6b0152d65599cb0b9b222813d0e1bf0a4acbfc8189c7d94cf6a632bafb32ddbef4cdeb18bf791f9c87ee64f9ac5e8011c909bbe1beabbf4fb9307eee1da3c6af48c34c44a47a2aaabc048fbb45575bd907fae830f7c4248160e1f6430491abf0fdf94cfae41ee004a91f4ac564c8297080642414245b501012900000068b42210000000006a81e73d8ab1ffa39cd99f3d1b027feff19b914ed42bfde1fa70b75a96b4bc077883497ea3574252acdd54de67d5d1c387141f8968cfc5dbe675f582b14ddd04966e1a66753adb7dbb98e6902560070eed08285092b5724a8402cfa15655430105424142450101ce82be320e2cd872dabcfda92f3826a43b0ea9dbcb42a2b54722359ddfa06047d6fb0c1b3d08cc05954843c1dfe0edf2ce2db2aaa20e51ca3e4551c88ef36f80eeb5ccba1c8a070f84ee19780b73f56e02cdc536986816317b3e12deb311742b9ec5e801add8a233ed854b020e166fa2fabb746707615b81be7dc20d3875d9af063324aa2af4665070c9b4f6102f687905b60e4c2a40efed9218e01c019691bd9c6b6ed6080642414245b501034f02000069b4221000000000223c692510dc76c900f13fdd9303afaa2fe641f51ba79252333d3407ad4dac416a5c97090cdb3b66d62611d801070b6e29a3f436d1efbc413e24431a702bd20a73a981429107125bbcdd802075c8f3bcb09ca045950e9b478fa65c6fd5566205054241424501013405ae10a17d3f3ee6684498bc50e64bea695b127289d56a0cd42775a91d7f22d36a17aad237fd0cdfcf76fe3734d424131ed6383f10fe767e9c507dd8f7b986879687a9d733cdda84665961c104783358d6a10dd4e660190ad944f95b3db689a2c5e801df05f3394e6a7d25af683346ef1529a7cba5526a217ed15577c5a2329eb2ce1c6c37500c825e260a5753914b56ba94d6fa089e34e1156f2055bff481db787412080642414245b501032e0100006ab4221000000000848d6df6d2c6dc2c38e954ee889d824ea9416499ffd2e9bc263517e560557103fb831997e26114e208f55c23af39f2d8fb468338699f452c4e5957f96bab1f0711a4ddb90fdf94a6dce1f50e5596a447c8b670465f9ee97178dc1bf21f185e0505424142450101b8da0c0eeb4b5a32d72ed9a92b65be855e341c0702d2c25103e8d9531b1f8245e6bc057289c12cb95fbbdde998c17de65258a289a3f450b5e8b5e0cc3575c88fdd61c36c63cb07578319c0707c3cc176756cf32371bd18e6d1a1660f2116b8d7a6c5e801715931b9fe7e1a5a8c8364c3b47540884a1ff535be0890ec0c89696646cf656dff4b981f4ccef43a8c5d6c6d96a69f57cd2b907cf804aa028c91fad912a7d6d7080642414245b50101130000006bb42210000000004e247cb2fe56264a90c30bc3d009f95c1782ff0e147fd690a7298005edb4be179ead810ae8d1d457ff49b5f4b79376c4d85e6e473249c42110da14836003200bb2093cc9f40e33b4f8cc9e68bbad17dd3a7737d647947f5540649b7f841729050542414245010118ab35511cdf1a3bbf9cfa274fa6fd18b12bc70b1dd6454282d1e8500b82c746480648b6ea8e948d006482466aaaee3660d8d46b2d987d43d0c68aceb66bd88fcf20c3bfcb52be9d14d16aa373138db0d6c4b5ac04ade321a27bf8be082e984eaac5e8011ff6c174b942efa91538a1c8f0d056debf6e8293f3161bd99dc4b0633e36e358e40eba137702c853ddcd7bc9095a43215db602c5cbc92323cbff7ed8374e9310080642414245b50103480000006cb42210000000000a5c9cf9fab9d1c087e4e8a3050e6e1dc9061ed05eda3345ed5dd203b744c4198b94822f6642df80e7078809d6c4206a79ee4c962046e8fe441a52b628647505f4929cd2789c1320b0320a6f015b250537511527298282fbe7df8d07a0a7790e05424142450101924f807ab614bcab7e0713a214418eff3b4ae1cac3f2b223e60fb91c1408c93f989feb88181a69cba0cc470ab151079e88f1759b246c1a6e8227da9acae52080a59b2626dbd9d95ba940225a95f4e3c5b2246e739b71884c26960b37f5a33b56aec5e8016e724096954184cbbe0036373849ad699b22503c67d4deaee9ad7b382faf29033815f55e8bd805a932c1ca096302bea7c729bb0c09977f155f139c8acc5439fa080642414245b50103fb0100006db422100000000080f699d4c27f7337481d1fadb887f8daff5da591c173929f028c27ddf20ac75980f50c8319f9f22833a366d6640bad974969258f3045a164ea35d19e10aa7c0d6fae904fa3011ff4c54310204b460eff63f82c754d45805f2c112ad6cc779a0c0542414245010112591dce383771f481745d8f3c19ed00a160c4f9a4349c9f5bfda11512cf06430adb6299cee6023c24588918094b68a1fbe1c09e65d659030ef606f8f6f51387c64bcc207bc06c30881d8997b358a1cdcccb92993fc5f3013145643f0cc6e5dfb2c5e80148e3586c50733761f411a1d28c9d8cafb855ec6419f7e1a7f01b27898253a49f429c6633b434534957df9a8c0522c3e4517cdd0d5e279479b4252d0720d50e79080642414245b50101c00100006eb42210000000005ccf1f1cd4237fc190ec6eaba68fad2389187bd72d2ff14f16557e5145eb04088117f60c7c9970b8e1d02f2ff8c454e4a01f0381cf6cd93c2ae077c59c41630d0cc39bb6ab9099cfd90f4437eefcd76ce1a59fc1a60e43eafb5b6f40c556670c0542414245010132aafb09b7aff8927c19e4893a80d26075c77e329c71e6440a20d915368e2644327768e7e51a2139d1c0e4a465162cde75bf4be2f4a3e31804a2574eddf3578193f8ba70e70a8c86ef9b5da4f42327cd3442fe87afa0b98c4a45f1ee2cc7b4a7b6c5e801dd84e9effb7d14a26ae239e99f9e15c4b9d47b25db7ae5530402f17ba2b4c8d2103e8832e636010dea9b0e2b71dde5d5f83020193f15151f898d983f6bc355e1080642414245b50103020100006fb42210000000003c470d6cc83203fd2412997147c40bc8a4dc8a2c7b9eb7506d79315b87355367c22522519addc110ec49015777313bdd4c5a505cf5fedff2e392dff8c0557d049b4470d8645904913ea2ba0faa1943222361021e75e039508fe29b6377aed00505424142450101dcb095e9e4251bfe8d022e9ed9528ee0933b050a7bde25b1f6cd84a99ad1b2613aca742a182fa35feed3007ee85c035e952378d65d0d735de4815cf291436a8c883f392b82887623f47b8e52ef09800d1310dc44639c9fc80de52e8469627d3fbac5e801f532cbfb60b48f4a1a0e28ecfadda397b80d05d939339708bbed499f5bd9aa2d6b19d2cf6184d40343c0f89b88a6c751471b532c908a7a8aaa420686b733d0f7080642414245b50103d802000070b4221000000000ea89db08d2e8a75488bd2428944223e4718f6aa96422b883c136267321a6441413890184502b5a1dd7dff43684470990bbd51f463afbd8e14e3baae93919250beed11bd308fd8b773269f867340463d989df05304b3dc7c77b4cf568dc57140e054241424501019ce56a8e56b48021b8af815c4afad56a5222706b5aef03c1ec296bfc111a933e73746f09958769222db72d25268a3e64499e890630ea26a6979dc56cdc8ef886feba297723f30ff54794c3932b3dd9ce4e5df7cef82df5b69e5ba86502593aa0bec5e80102ed8047c94401d5f0a5e15b1ed1e2a0ff4ab74ca97e36f6da79a546aa019ccb111bfa32ee8c2175298fabbd0af7880348a75610fdefecf0063b3aef7e573bde080642414245b50103dc01000071b4221000000000b692a5cd980ef3ba8129dfabebd05f5fee745e3c3c2a109cb47615277b0e8f1cbbbb7cb2fda80181c9244abe5f1b83d3b165574f59b7c7dc004baba92e5bfe080c92d2d64379daba6f99254bc9eb1ce210f27ec5cf195d824d51383bcd1d4702054241424501015e9035569fe225f1f74e2fda14f9dc91786d7ccf3f6a829f8b838bf1aef55213aad93b0057f7c705718e2759d4ebef76a33cf63b646a8d7a7c0e977b467eb3872519ca17457f22cbfad3203ab37ede2cd8fa8928a4e547ba8770e270fa0db8e0c2c5e8012b3733860939a8712ac098e8ba96acbdb5d27a728f9216d39347fd6075fd9df59f0376fe40974d7fef6e47bce1db10440b84a21614349f9af8f19e8b33b2f9fc080642414245b501010c00000072b4221000000000f4c29773e837bd4b56f1af4641d270eae5c92f2ca01cf5a2980f3940b0ed3900b8ac98db2ee05e547687776428e7cd3d0d273db9d46802c183c49b563655810eef48522ee88d3aad885416d2ec19a08eb664e8bd8d6f70b1f9bc104489ba830e05424142450101c419c8495c904f61994837aff1ed04fd7687957f44a6b7b72481bc436ca08a3eb2a2d4acf503353b3ea1d71f40d9783a12dbd9742b3c3b169fa868dd3a8b808cd4ef5d7d241e8dfcdb9126a74515f0b4782aeb4a5badbd1d9949b67431629347c6c5e801c4b7a89c099b0093e14c7ddfeb1057f59a5f40dc15859f77d1b2c5afef1bc33319c215485efc984a43c186cf539bd1f87e013294a42adaa987039694329e6a4b080642414245b501035502000073b4221000000000167f63426da06f3c757e0076315d524b48b4076546491998a1686eb11542c405afc922f1e27b58ca5c5ffc8012f4b004fb4f98f1599055e30d26c81226ef290f4fac77b189d78ad4702bbe86adff8ccdc87197647dbb2dc36c14015632159a08054241424501013099e0d5e0b720b6c3485febb0cee87f9721030514f765896bef676e69c30821b1ec7ee66486ba189398241229654a1ba57d5a85a57ff5a8cee7ede3b831468be2f959fe9305b428839926ae9d610e2968a23742714c853c001a4364a866979fcac5e801363d41e6c938585b0b5166da0114e01e5bfce564d476234674b37d5eb4191846ba4e5b44c168256a2ab00b9eda77fa7e0de08fe6aed367fe75518640c780b722080642414245b501036203000074b42210000000003c27e437a1bf6a7542b1218ea641257ba0cd267803d6fc8e25ba8c3c807c8f03df6e8a4d96d294d4ba52723fc412a9b79397810fc1dfd766d6643e771a230809b91aed08f69aec3467a0add399249025c3b0b44edf2c4dd12214c1dfdca58b0e0542414245010138984275aed1dc420af9526d9c8eeace5eaeed755a00a387c9ca85fbd04a960774fc680734d249c633eb2ced6e6393d81d20162e8f22077c6cfff0fcdb9bd98b4c6c2f8e630e5d82841af83df2e812bb5480e0619b9943b145e7e3d66dc95cd3cec5e80170b3fc9224607a46cad3a7fbfcac3ad834adc3afd066ddbd682d14703b4f2f998f7f975724131d8ed54f8cf3df63f628eeaca641fa89347189cd53f2005a9982080642414245b501033d03000075b42210000000002e7caa85f391bf3e96f80710fc727497ff3ac262314de7dcdd1d64648c3a2310df038edc8d7b686ea62468d62c2b193d2f3ac7856f8bf90220e17411dc3f8e03039d4546b84b3e96d4735932a7b1d438ffec6da031b8e8d173a40f492c2cce0d054241424501013c3e721f48e77e71a629b104e0e5e28a6f15f8b1a78c88c8b39330ccefabef6dcc3327cf535077949b9d80c2001df5f44c6b44b6aedc9819c82eedb768a6ed8cd9e5e53f092ec6f70688d81acb264d319fe81ef3047401e7f2d4bcde2d013842d2c5e80142d9ec775455240c40adf0a9a21e2e337dfb1e2314843cdb149e787e89a1dbd0d3ae268bd7dc900c883d1b26d8bd6356a99ea358115f3e187dd2ec29fc9560a9080642414245b501032f03000076b42210000000000835607900bfd41b5507bce8d9458332e37d17760cf23610691c9c03e9d27a457a0e10d2b2aee6877d855d40fb361c50f61454cb5e9222d0225b7dc0dc08170e6ec2be1e49f44750486511020e6585dba3a16f54c8c49e057e98d771b405a10605424142450101d43c6f28ac02ada508a04477aff32300c5b8851197740f41d07b760f5edcbf7fd2154f79bc73dc585dee6513a36a0a24d8814f12b76bc2df7b4695994a4808816d2f7f12e23b95a7289a916b45be512629c3ea6406afa3be78bda5e2cb090ec3d6c5e801dbf5ab02a0b31c8c082dea762af4ad89960b71fb7c986c3b8cd87f0a5ccbeb2aa26806e38615fe6d33bc91a89c89b09c3eec8b6d427d4c3c4af24f354a9a4ea2080642414245b501011c02000077b422100000000026538ea1212e6560f2af8bad7305eb80b35973c263b72c8978e977d7e35a7e0fea6a7e267111e3dfc1aac3dc89d7eae07769c51760382d481503346a51127a029c63b2f237e8f67f91c63b119e32cf7f1c2512fe9c5a55582fbc98d4ffa3050d05424142450101be267db1429089f88eb8b7e4faa7ed7986c40cf5b2b0ec5e25d45487d26bc745498c0c5afc52489862b419c9e7f7042b17c784739635313c7d62c5fbbcf3de8330b709955ca401f7869efdc2a578dc8a13c9e1f58b04b652f8764ce1d45fcf59dac5e80157f8f6e04fbd2c307423303dc16bb7f1f41a0febf38bd1f6c492fd84492ac057be7fb728da408f86f34d922a0ff651bb24bb4e8906008f1a4e109d57224f9417080642414245b50103b500000078b42210000000000cd6f3432fe84efb96dfe18e34ac72270cfa0cc89e420ca3916911a01d874b495ec679e09319dd8b06ebe2a60c2ad7be84ac38ea47e5add95f11af9c5a3d2a01b8e5dac32dafd5a48feb3058e8a29dce3249615ec94b41d4b713be1f836daa0905424142450101128efbae3c90666b4bc37e12359c6bdfc6fce03d1c2a5305e717309dc8b7f20a918f0a4dd8cf3b83e6da1df2d432cb27562e4ecc6926b1a1e67266ebe4ba878876cf8ed7077fb2d1027a352e6f5b12205a2c1e0c035b7c98e6e8418f50bba66adec5e801d31587a5ceb772ffdd73d3c47ab6f2191bc9c835719cd90c54c7197d411657020bec884e0e94e101580951f3410f6c61a7e206ef3b985ad8c30308918749d2b4080642414245b501033803000079b422100000000006e4441a12c3e4645d4d3b96a28f76d2f2a160ff468897f7f5a0c827045a3022bd55a42503c878153437cab1461c5d5b04cc163fb553edcb92680cf3e7b9320123746a7cee4b9d182924fb0755fbaddf8e481a4fed58ed954c34a58b2a3bda0e05424142450101f02fb8ce1f93e194b2a6489e29400015bb6077731030d873dd95c85c2b4a2f65b9039174d157511fd31fe4ecf508ed51592c840f27140fefceb144be7d1bd68228a870fb45172ab4ca7c7f35c585a6c53df41d3cbb37d8d077b4397ae94766c3e2c5e8010a9852cce0b6b5f34ffe98de511828271f5b071eb8618e3d38325bd79b0199ec86770ea82af06acf79a07a47819c05aa43cfc9e2655d82e31bfb1649340ed50e080642414245b501032f0300007ab42210000000007c60f9a41c708994b3ebebc18bc92b3903ce4031a13120648f64d98512b82d659b273cb89d981d249f5a3e90e1a55a1654fcf705bde338d8abebdee5cc307909a7d4d2b9615336c0fb76da2ae6d8daa782075b67ddbee60803ec57451ad19e0a0542414245010112f5e190238fffd4227043355dd671c1dd39dc852e223836dfcdbb8e7d1be13cde33e3b302536b1ea3ff4f56d20e96c757870ea1b17ae2e1f04ffe0ffcad9684f648b0db0ac753059116a5e537f5d2c6ca13a037a6af4ccabdcaeef97b7421a8e6c5e8013e1a5340068719d6c30e4a684fc745ae480f2ddb572714922a594fe577e6b77124a175e426ee150ec3f80fa646cbb0a419ccedcb0e47511eca165b36ac87f6b8080642414245b50103860000007bb42210000000003e5a63570cc7a0aa27ddefd644651007a54e78568e2848ad683dfb2c72b2cf37becb8cbb9d15f972bd771020144603a23302295bacfd75ab05590a7f69eb4901c0b1274a5036883748bcbeaea2365c96cd27540df9142dc7d5d2d7300804dc060542414245010194cfcecbd119ccc7991289826f2a62587c02bc0e43370bf1fd51424569d5a274b2ba33fab151fabc40907e5bec650dc01ecdea51d14f8f69c446ee67cee6e38ec0fe15d10d76b3e57b882398527db5fcb13a2b81ad798f2388135c97bd56a8a7eac5e8014e0237dd786c2f1d610d21cfd8ea4e700f168c398c305cfff90574cd97bf8643db0889335be642448f887841437a95e211700f6e5c2b3640240ab9c8ba427c64080642414245b50101700000007cb42210000000007c148675e8164f8814701513bee6b753667b377de62371e88630051e8b7edb71be6e13633f0ca25e286625f72cadbb7ce9a2cdcebc4fb255ab2432f5ea68130fa6cea32c7af13c18f42b3c26a8227541c509c78c9d96bdf5a37be4da2b1cbe0105424142450101aedc6ddf7090ef765ee2b847825a3bf6ad9a8a3e302d5dfc99cf5107840d5679162d0ffa4b5b41710502e3a89f0c44f9295424fd43f3030adc15e140455762826fb95e5b6ee2ebe407ed5f277832abb9424e676cb13e05f3669b53129a14d145eec5e80173b50dd1195f939db119c4bb0d267f5ab6a4f852e95148697125dc9f73c85e31c82a1b41fb49ecf89e4f981c34be23f3b4298412c7ada20ea8f36246d3f9119a080642414245b50101be0000007db42210000000006c5f00d76c41817d75267afdd0f50fb0cdfbef1bf8e744021b6307bb7dd346758c345c76d9e632f9059713913c0a0e61a70448f0dc1a5c52127f880d7d259a0effc1aed91962d54f547b38ac1a3ad9742a873078aeb9a7c64880385003b1c10a05424142450101e80000578a1c94db9ee4c8977b297c3ae03c0b38f75788519c457c1bf07ce461ba6c61294cc2a750f1a6a807dfa9276c27cc7ba771a54c8890687eab53fb4b8cefc7571da08d31919bc17aabff3dda96f5a4ba828227eb8dd3412303ff7c6b48f2c5e8011db1862c233b166af21ea349412de981797604522cfd6b634699e3b31d65383e49bcf5b27d0e7cdf723cd5fab117fc336b0135ef2d4b8dc5ba00463cceea7858080642414245b50101a60000007eb4221000000000ce91087a83b63ab20746df2f3827e77f6a75a6c8ebcb29e2cef29f451802b96d6b03884ad6cfe90abb27d2610c10ac0f52e739493338cfc091c5248134b90c06496726dd56a4d57c0fab84137daa402eb2f61affd640eac794b46cab2fb9300b05424142450101522ea5e61c06ebc6019631257a97a46741789338a2443c7d2f0b5bb8be74bf0e8eeb7683ce3aee8933c43c318997e2713b54daae61ef3516c39a97c23ec0fd82c106b0d40d6493fbf92c6390cf59f95bfec3de2516e462b42c603742e514b185f6c5e801ecb6600d182346e87d99a3f1c8d87cf46725bb5c3902b2b0080706ea155dea5c9478da350bb0a276024fe304ca4e91720c9779a62d1c3cf69a4033e715bf41a3080642414245b501015b0000007fb4221000000000e8650a3f0e9bc3c86b32bb32fec1411ff4df5f79d8650b0c4a7b14aa616e9b52ee9630b906bef1e0fb9627f0ca1dc21aa17097f79d6392e309e499017467d90954af07139d2262fa8f87f2f2734df02716c12464b14c6b4593c7a0dd87280102054241424501015ae11d98692bee7064bf099c2af48776c6610845c1e0c5ed124b06081ddc8f4ead9970d2584eebf4028878f7da61eff75597261d507f3b1a675a02816afbd58f0fdccad0dcdc34accbdb47e7c8cbbdded4c2ef20104c0547da6fc4a4c30f62d0fac5e801b29234ef04bff3bc18981b34fff3308f6bf1fa72c117f7622a0283c6ea7ca2fa4e7ac21757505013c65ef8c79f5e0094b38fbc626e875d8d7142630a6312cd3c080642414245b501036103000080b42210000000003a9a7463e0dac3024887f577653f25f715a37ad3a4eb92355f7ff7a645fd47134d3e31e4f1734ddacc7ddd74bebd09ccaedfa35193283f2bc96ec7e383879a0ae749729907e8e467c15bcae00a0220b95c3f34055b0f6e9832b4396c3f7d730e05424142450101e0699388ac5e9734113c3f33592ec68f26a42e39c571fd77d70d44d9f490831c860758ca10c5094db280c6702504ed5c449d5e4d0fd033247851f967dc776a83ba74e92be337c6b9a65c4401700a9f49b208c9ddc9598275b1aab778989964e5fec5e80117f228b78cc52fa547d36c1c44552df56752e12299013c59384cb7445ce3822b64e992e8603889c596772a48cdcdad22dc725cceb791e41074b7740f075de4b4080642414245b501032100000081b4221000000000f6fc1af867f7b4b68e2e8e9dc8230c38e21cb06161b9050ab015e5af245e29228830748b157a4ee163181a0182e5109720aa8e5f1079a6994b08f37e6252c502441aac382e51276e337af4f4b010b28c757928b8e575760385b414b563f8230905424142450101383c9aa0c4ac9bb2621bb72ec9fe119ffc15cef714e627a14b34c7d33fea6d6bf73f89f976f8bac9e7ba7d39dfeab41f74f0081c17265a15fdae6f8ae11076848c378c2687239a6f14f39f6f36ddc3ae01326daa74e6727def026a21e291a06002c6e801449dd70907ff0942bd2ec889910cf76c48696493acea8f3d814d9646ac5c5e04625557b4127de15e076071885ac4e344624c3f648f057522a0801ff56d02c5cf080642414245b501030e03000082b42210000000006e73f93cf3b14aab412fe649e4fa8497cf9171497d3aa7a50a39b437bc6ab942afe132e0f037df38efca4de5f1dbf8a6c362db18278c3b8e51272f562ef1ba0371f449d24e2b757e61ce882e5cf2ef8a656916df8f6fcfb86b3f9b728a90ad0905424142450101de400aeac9fa78a34c64bea7c475f7b6e7ae0e5e95a124a2c4b081e65cb83e380f1a405688499ee64f931d17fa49e50ec01fe2ad7070146591c938c6ff6a718b5f7c95f5a512524f1862959b162c9365eec837c03a72dfdff364aa22a3ab14fc06c6e801eaa99b835bdab5f075a87e518727b64fec3c613d63131331a0831c8b3915eccdbb71353ce6fe437c2bc7a7faef73e792fe45399cee27c7f31ffe856000cb0669080642414245b50101a002000083b4221000000000ac8c1bb229972547997f354b18751b661f10465cd0c84c55f531663220bf8041e0bf3e40a93a495c8a41a07ac0d2d05d7c69c17df3518f70e1db10e9b44bc8099087f8a6857a4c9503e9af4ae5c11b8f467a3caf759a43e6391c49c99039370f05424142450101e835f3630acc3364d5e4db840b23d15eeee3b1e972f69c849c2f23fbe0e4340138d714d27cf1e43c73da5a714a26e9dfbbfcd8d93fc72e3674ee76d7f242298fbfa021f0484f87269508f9627b186a852f43ae2ebbe60186f6f351cda0793b0a0ac6e801f15ce7e6b10a5a8d3d4e2b73761f101a0daf20e5c59fc4499a73efddac6cd774c665d6eddf0991e6bbd6922c34bee8808a4332f8dddab8e77818a0369a697133080642414245b501033e01000084b4221000000000388cbb082ad87de8184fc739136e98d298116a98b45bb9c1166e773f883a1049a930ad619b50a424e59af5d0ebf89600ccfa3b7b710854a964eaaed7f990440914a00bf7f7413a8fde1ffeefd4771d9fe3c3e8f4c2849b94daa4565f6beeca0505424142450101060f340373141da5a54eb67ce123dcbd47d386090e3a5807a8154cf6ef471f0b39ba21fa927d07fa02ecd0ac17bba14a011cf7abadee038ad2cc0f23a44c5d89d53b94acddab04dfa1caabaec7b2db8e31303b45d1c28663c799319bbaff85a80ec6e801847bd6f8ec904f16c1d7a93d8ea28b71b3498025264c8bf13b0d188f89d098a40e5d97c853039c64d2bc2b98bfe1fcb64ab6aadea7ce839fad1331d9d7d45dc7080642414245b501035d00000085b422100000000046d577973850c7285c205ad5a27f29e4f33862e6e0b8e1381697e238018c2a7e0ddabaf83a953026fe6493d25ad18cb1ef2626a1860398e5e56d9720cd64d60eec2e463644c4e3254da44e2a72bbade009754e9e214ccabaf4d9dd2b086a1f010542414245010178e90b8c29a46383c28d51106a69845f30f84f4233628bcbae5916167f97330dfefd5ec057c2f47fe8f1756afe3ff3c216a26b3a5ca934131e9d3c190ad37788339ae5d2e194ed681b77031ab745cf9072613549123f29a66437aed108d2ebcc12c6e801dd7facff3cc7ea31a183b213dfdd897d818eb68cc30d1608f21a082d8e3037e0455f2ff1947f5d1566d53a06539675b2394759503e580a76c71fda428f09114a080642414245b501039b00000086b4221000000000c62f9e97cfa2b1d6db97a4b826e420aae22c0f2e80a3b1366deddda3c1d2eb32d061a74c0aeb9634390552ecea7df58019f76c0e157005aeb171c74fba67340b45ff63f83663cf1e3a3519ec97c84ccfb9c65b3b083bffaffc735528de461f0f0542414245010158bfb31541b561a5b96ebe3cbb608b9d43eb747cac726432326966b732adba470f28f0b8369555b7c8a38f17a548acf133297e58c48849284fa4cd56751d0d806a01b7c70d907ebd6d3cd276a7743170e64fb3addf4cacddc0116d08ae6d0bc916c6e801936d8a842bfd17f66d7f15f364d35b28f09e1e5ab35d3b016b92cf32d8e14bbbbc015cc5688362a3402ffe120a7d636ffb963312629f4f97ecda461aa88d5dd0080642414245b501032e02000087b42210000000002ac8986c890c8795f1f9df2a95621efa22275cb465f54b927951f5fc2972174066cd46556574bc973512fee265794ea6f123754220ecbf1c69508b3736ada30816581ac65b4cf1d58f7b5fcc6c4f7e9746aff620649767c18c5d1797559dc50605424142450101bce6dcf194aeb76aa3fe20f8afa6a318a2dba85c1d9576b16c9c521081d45f72909fb30b8c280fd5127b2dbabeafafeb8771c76414f6d5c1f0df5a414e0b3d871510bff27b80282e9ae2bd64153a2743551b5b5f7c1f5d33cfdfcd8f8ad566321ac6e801e67892463e2286a0417e040eb06874a4112b17a80118359df95fb4f773d84f3dc00333ce5f9ed1a86566518e552cce8f4749d4c956a961f57613d36479ba67b9080642414245b501035301000088b42210000000007243403246b5ca360f2dc601be8311147a63e13558f8cbbf7e65895106c67a5ca24ade61fc06ee3839370b9672fafc489057f58b658ba91fe5b23a2a82d3ba0994301d68b7330fbe19bf46a294c7513c724a8a645279ffd867938e392160ee0705424142450101129cb80fb7797808dac96739dedad7da00259a5a7abfb630ed6c68accd79d97fbd84e4d7daf41c9055f8dab05cda9015d0bf3559e7692922f86286d28079ab84e6af0ac8fa402938ed5500979d074bebd0b26874eb59021e9cd9f751cdc4fe041ec6e801e576450febadcfaf35ee2b9f55bf948d680acab8af84dbe297825311b33ab7943ca631c827086df69f9e8b4eafc819db148f2aecbd3a192c87e286eb137fc7f3080642414245b501032902000089b422100000000006abb5d8df3d71c2024dced322c6442a386668786a60a0d5770c35eb42a6d3480a70ec2c335838c1d25c7a44d31e4e140be4167c7ebae5562dbb71fc562a800eb5ebf345132e3d7fdde50c989fdefeea3bfecd2c3d70c9738f14fa047a7ab70405424142450101c2a34a0fe1aa78ea6a23e5cf3d5c8983e49d99bd11ad632b8480d07feb5ae2323458949c5a07582bd54668d6649e0a057430f481f11c3ea4d4a8b5c2d280178e6095b9734ad6158f15a9a7b67b93a075518d1d4fccd34667057a86301020b69e22c6e801f51a54bc491adb10e1200fd6602d5b6dc4d658575f74a4d8f7c71955572c556a75fd3f5a49f7e1fb51dd0e7e89e188951fa7c115dd040818879cd7077f392a81080642414245b50103fd0100008ab4221000000000a6782451e283be97752f18951f1bebf4cd7e762e78c9bb83e35adc12667263001b742f3d81ccc0e5b9e4593b0ac3808eee2a765edd73a1016905ee4d1d00740591dda09d072490cce3f298d0b81a8ec249950de5ed260ad67625aaf5dc4ed30f054241424501019675e450628fe0a5534754112f694435de0d265c22772586d0b8f50584c73e16a45dd7bc2fa2dcdf70c319640512567327a99baa3d4b7c0fa89191e3e36909847855b3aa6f76ce2278ebbbc9f0a1240ce31af939ae6e6bbba38ce416260d4f8d26c6e8019d0aed72a8ec00d5dfe6130aeceae1ae32fa1d196c11c840566b0f9f309931b48e98b01284db1a155454e880757f41af808c8ac0af9f8c6065092e74c8437e60080642414245b501033f0000008bb42210000000004a56c0ab25810980105c52491195005371fa1f7eda91f184ced37dd89fcdde015d574f50f4387f92cb034a8a2774ed9170ebf6f0f4847f0c3e75417506aaf40986422c338ab8dbbcfdea6407b368576d834204ffda2e562a44b5fa855d6c0a080542414245010154554eda7823e0a306268a99f88b1d792a943856be27e0e1c082161e458a4a2eb3582413921940ef887c7eeb0661df5fd0154f7215501ad4d02e4d0e7ccb568efe41be12ff85d3fa7a20361ea5e8b11a4575995ce7cf8de8d0e21763784a2d6d2ac6e801a0b2ef9bd9148f701019d022f3e232841f6ff4fb419b76aa2221c2cee6252e1f1237d507c22fb5ef4e8bdd7242bcd62905623f7147b0974b348f1307fabc00b1080642414245b501034a0000008cb42210000000000c69c78412333e8d9c6dca19661759309b2b0df946d79c330c1ef0233e95fb39c747b5913e9c85e7dddc90bf345b327dc8b2e5c510d306c46fac96c9df899e0a1ecccc75481c872e3c4dbdcec179d4884939d44f3c00210644354719323e2904054241424501018ec0c794733e4fde30051fb5fd4c9d75187f686f594488cd22b079a163224f5011b7b678b5816f4fed15d8f4c285cb77b2c7e95c4ca8c4e697bc72fff768108ff026903872fef56d0be55e8e746fc366c46fbb7fe75f6f1abd3a5b9329055c542ec6e801312c7511684285e596282f26f9a8ce165a1e366d1f5773cb361ad162195e43ca2d1a5b2fdd1b767c4a71a00638419db6929cfbd68c940086ff75eb6bdc0e5d51080642414245b50101820200008db4221000000000de95654b4e6329bb67cbdde3f64f550a01d5cceae217718fd18bef9e064af62bb6583e09ba5ac5b09b4c319389624981e3f78b496149b9541ce0a5447a814e038f0dd3e00d889168a9b4c593ef7e8ce1daea350780b5d66d08b9c5ee97ce3a040542414245010108203d72489174ce46572916e2dfb3173d1cfd62c45ae08ee44bd1ab53ab144c127e9396687f20680ed4d13af2d204bbef83e082206972889200dda08620f88219958510fec6ccd0fa07952e3fffee6aba74da79db273abfd0c1f68b8e1b78a432c6e801607ced525dcd87c725e3543f625a5490e165e45c07fe7a27bbb3c594a0fa58ffed7039ec2162558afef4a2bb687a716371cf1173355c0ac71ca2b652c0568ab1080642414245b50103b60200008eb4221000000000cad5e78054078baccfdd3e7eb0ba2dce95fee518dde961d9024abc2ea1100904f42b31b9a046e4a50bc4bd97f41c668da83ead1b826a49ee3b591ca9dea00e061c8ac8e7ea47cd16238d5ee451d7570ff38aacc984ca7d5d92a35d4c2295140a05424142450101eaf24ed8a94d32eb137eb2d57430245d998761a4469733e7d61675457c8ef13390e670f943f8adf992ece09c94dce7feca5c1f87ecdf0ae2965d21ec6d05b282a99389c0df7985bcf78c57e3457e63115713a96ad1f17f219a26cbf504a504f036c6e80144c3b894d04bdca3555ff6ff0d25971c9be685675b71b2f9b314161fe6da84be62d88a56829456f37622c408b854bd80c1a435b320a93327edbeacb0daa6c24e080642414245b50101d90100008fb4221000000000da87edf97f19dcb0d2728cfb4a8ee1520aa2a3876d1524df7cb1d1fa13d27b2d33f96bc4ba9fafdffdb32129f7bb050d34ffe8b7bbfb093174f9bb733e43f60fa4f11ae0b488bad53eea5f9f9c7db576eade4ae19a0cfd3a7eb1c716469d790305424142450101263fe2d2bfee195c606d3b92d4bd44acdb830f317e345fc1624f278e44184a25187235da90c344d1082b33f82dad239f4e80e70134c64691381a63d966614d8422b92ea40c6ea5803078f9c1961ae00cbdc3a3d6e2b7f35352f25da242d274d13ac6e8017bea5df285364817e9c822fdfdb8ac90ce8851de81265cc72d33f8374a87030fc33aa15af38122d3ee643c17efb39103684365e64a2565d1ed94e6561badbbeb080642414245b501034301000090b422100000000088f2600a56c3ce3e83e2616ddd3bba9ad261d70a4e798c49f20afbc5a81a3929d572f24a85c5d7f2eabe877dfc47edf092ec46f1eb24dc23a3544bee1bc9d609ec965b72467eec56c503be103074a31d52158ca396d425b87fc4f68ec195f00b05424142450101f2dc87f9d09e2c0d93c403400aa3b1f439f6014989113a5d3ea2994f78d78e790f4d12d1ffd3eb201dd070dea4414a0991e331bf03485491dbf933dc562b3e8aef166883d106bce9ff9d56e61a1c1716eb0b27ba17a6586d1fac16dc93c357dd3ec6e801e3ef69b7ce8c0247936911650a5315dbbfd1760ead4f86a3be9c0a53a3fac420cf60e7c9e140eea6b72a66bff33df4ac98c4b429e4ffcacebd3f4d44ade70d33080642414245b50103af00000091b42210000000002c26aedc12e807d13e643480490aef02077f937046c2909bd9bd4cf44bb30051fbaa7ddc2503487fb00e0d9f72038af6045a83b4d51c0e08c06fb99da1a9520343b55ccf8691e7af36605fc22d0736a5e5292a97159dbbd4733dd71bb3a5630b0542414245010140f4049609e0036fc2c8f0a27a1544df95b13375ed4d96f89eae035cfe2040653a00f69621c4e3b7903744727912c4962f1b462f6a0542a4bcb0543874e3ef8a3fbaa5590a7b84dc703bbe3a217b60d588777ea91be972945a7e825d4427761942c6e80146216e766d1c90139b1fca2e36cfd3f8b01874472f37ac3611584fb0b934146ddd33f84c79ce3dda4eca46676d1f044c6690b80b561aa2d7a3504f79a0445568080642414245b501036001000092b422100000000070374ac61a9e2b1427ee37af2a312c730aa7573282b101685461dae7744a2a5eaaa552293491fe4d007b09e08c8a818b343ebfc3d4ee88aaca1b177b485a7b097a619399c71c6301b8394559cdcb77282c4edbee54e3a7810c0ea748069f1e0205424142450101981ad10f4d3ec1a4a4b4200631c5debe13baee5756ffddb222bdcac69ef1461f7fc2e8d0d6995a69fad2d34fd199b31107b7411f4b1395b88f345bd0a375f78cc119f1f6628775d98dd17e016b96aeab6bf6f338fe7ad2e54821b7f1e277930f46c6e801b163dcc1da71f93b66172cf1396a5b8f1f34c49025eb9c1bade55a30b0bbae763cc1014d41e70a14a7aee4bdfd56a080ec7fc07f50b06183a67db26501db6010080642414245b501015701000093b4221000000000803f89e01a1e9a3c6a7b139144f193f89e8ee507d3d61f0cb877a22fc50cbd6d8c90c4ace6c6b0b7d24efbb04a418422330fe25748a87f541c9d40e8d2299a0cdac666ad4e550d63e447d828a0f1222c0084cbf547131ee892223b7fbf32f30005424142450101f4a75ed2da8ca705e49fa2255bedcc9f78ca2f4378855246f386105042b22e24ae8c72919e431f97e6e8a5b1e3aab09e2b264f04f5b65b458850938b1f485f83982f016e1898fc2761a8baef71cbbaad94f3d371400ef7a9d906cb71c8ca764a4ac6e8014a78fb632d79da04233820196d02dfdb44ec802acd23dc0733a7afb076c5e434a3d3e18553a099c2f818a4328bf231c6fb2728663a0f983f2ac51cbe8c843ebe080642414245b501017d03000094b422100000000010b301e73973bf899ad17d36eed829525155e1dc3b13bfdadb281a92a050892bba0b99382f2039bdc11d27c964853b68697929c4f6536e829b5b26aaca553203631131085781bd6d5f6358eb2d97131cce928b3f84379ce8134aa4a16e023a08054241424501019c20b86caf5b9489261ea20062f1fb791b730197b6ad9a08a0be0c731722782215e90f207749f1c1f2fb8e9941fa000cfab35095ee5740c52a3487adb927a3879f64292706bd1ac4b367b30b89dfefe42cad8393b2856c23ba9155b311d3fc874ec6e801e346c4369ca8a0d890dbff528851aebffb8eb47a2f46ad5d33d350bb8eddc2a2a7625707f6185c37c2ae93f50162e49ed852957e2ccedd2888bbf8f83fabda2e080642414245b50103f302000095b4221000000000b0f1dff48e2f4821f8d08e22f2b3049cf002324a509d5385f19a9c153a5654368f04821d550bcfdff90e107153633445928c975aaf086a17af49aa1c801c8b0256b4cc91dd8ebd165a3a0d7d6c4070c663dd6e34f82d88a4852d67fdbe61600505424142450101aa62cc8a9c60feb1e04b60d4f10a02886e27ca8bd9935e7f1d36110e8109be4889a1a96f8ae576699f6e7460fb1e4c2d3ce190f68798ef69dad52a4955f7308864c7825e996f1ed410f96a7e9a1c58c88e164f342d84b1a7e8093764f566b49052c6e8017b5d45449b6b2ee88e2a4bc4006f189aa180c0b6ad46918687894948345a9ca2e248c819cb6e0bf21b6e4f788825bfa1ae00ed8de09db83efc1e8699168e42a1080642414245b501035a03000096b422100000000036e41901c417a706d0bf91aa87953fd647ddec0bb5590dbb3906599513caf926a74170082abaeb6bde5fbf0da10f5730d0585f9bd037a9e7d2629cda8867500bbed756bbb4cd6057fc602bf73137584c4e9d649b0fcda8ca90257bfcc190f40b054241424501013a80ba60ecfc0da8efb8661f97233ce349c57bb3b18f46e4775e9af0aa066d2a19982f7aa093625aa8ed8a53fda0ffdec7ad3f988a4693e49217facade1544884f907d980501b20259d4ed119f9241c76bb77f24a1d23dd0ff907c06c351835056c6e8015f9afe3691ad7398640aea6e014b73f75ffb994e1155339718f962e616dc277388aceb31e317b9c8f593506cd8876899e7611bb32f2bfb6e4f472fdd43cdb2f9080642414245b50101e801000097b4221000000000cc780833e8b14f97d0ce20557c454783d9f666da1c051b3cdb2d8df1fa4cc2724ceb64f83b9a0bad7245147275407050bc5d67bd7e9e90d1679ea2bca70ca50fcf36f78794b8c6ce0e063fd65eef71e3813a825487baeabce1a08b874859b3000542414245010108bc15d955d299d1f802b1b96a08de9ef855f2257a8279f0d5dc7b6168c68d7f90199c2ce6e5ee433b70820c77e7772d3c340760bee47c194bae4bc62c75208f7c7cf582eaf0a23a383945560a1d1a378cb5b3c26a764316b515535cc4f82a7d5ac6e8015a8c364f8ee751c884cc31a60f201f19d9af5feca7d42a127818550cb8bae15ada35b33d9fe3b4c8b9725ff67ee4eedf3569f21d0e730f13f502b0a8e75eec54080642414245b501017301000098b42210000000003e88ed3ae2bdb186efc021907cea04f633218d46d5c9cc005883f4c1fc08c033b496a2c6a97005db8739db77b54c91790e3307b47b3eccfd5dd258b7f8e3c30dc3a7e0c7ec890968acbd09f402e39fc3b5c2e78de8d45a4137f4d25835a5ec0005424142450101986ec19ff0fad03e483e4b1a882c9d1170fed597a3d9c81a65963568eabd704a576cfd807e91e8307fd3af7ab3d0f311ea75d9b58a577ce70e599124c206a48b8004d4d8b2caa6024c073691010229ec82f8c9e8255eb1f551cf591e0c4f58a35ec6e801afa9ab8087cae451afe97520eb665156c0e9e305c7e9c6d5264120e0be8d62d79a289b9636938bfbe8057b79eb315a1433abaa966e038de185382b0092b3fdc5080642414245b501035502000099b42210000000005eee500983e97e7441c07ad0edde1980865c03f34fc7fd992b823fd39277a504101c7e478953b2f21d584ffcd6c9017129c23a12ac9e9f1652aa34f0363bb2070182a8dc10cb9587b5bf4bb5f50888f30243b1173e10c363ffab9c6aee5d940a05424142450101045349078d46e6608e1f00fd9a49851a9af47b48b75e080188002ecf97c69c373f54ee80161edfc54ed85ff70c18f3bf99ee0f307d99f358255dea2cc15ee88eb6deea3215217ae5c767e9df6705327c292728b352a6cd01dd799ebe46abc42b62c6e8018e99eb3919efd0261d08f1bf95c606746dc2fbacb11f72b5d73a53ba966d0bdc1e46ac4f883b80601733dac2b513581b670a5eaccdc74fb4e4264519fb2acfb5080642414245b50103d90000009ab42210000000006addbd0aa18528341f1689a4cbedf47a4b47110a70d0f798b4f6e9a36168b47f78086ea7baf3ab9a8b44642531391a7f7755aa41a43444fa1d775ec175c611005f90cdd965879f66a914d799a22eb12f5d9bb230d62f256790cc50f7ce8585000542414245010100898cdf698b24b93d9fd9ffa33bd64c35673083a76ec4b75d0b00241d5f8f0dbc510231a6e0a9bc1c7bd6e78f0a78f0a6836ecc22197fca25b781dc5796708f720e7d9f697881dd381b07381d66747d8338b87aa93e112095cef9bb054ddd4866c6e8012eed55eb848a607711ed1436eeb42a0c1d6576f8302fe4e7544c06a0f67b5d361605c8d12da424ef49beddb8b3e8703cb0a17f2c86442e7b9ded4f6ec54eee83080642414245b50103910100009bb4221000000000a8151f5780bf20c63936e44d12457e7114ef51bf897131eea763b8318f5f2128984d556f97ebdd30cc499fe9f558b7130fc13c5cf4c35c9631f7f337c95bca00f9a51e9dab992baca7ac9cd0d08ed8fe20392a1d6c0157f9d0f3df4b5822730b05424142450101f4618a778213b76bb51087bd4c37b58c70b241759cc474d43fece14f53ceb2685cb17f069f8deca28d5a8e8d5e171ee1c96f6e836fbcbf2fb382ea6c19339f86ac74896fdfd86add040e4306b17e894bce757590ba5bfc306b8e64cf83ea463c6ac6e801a7f3700655cef0349568db00afd5cb1214e60dc3bfc61b9ac19cbe9bdc1b6824782ec758325a276059494c5c555ab53362f0b05b3870ba83d34e03d059a9ebe3080642414245b50103be0100009cb422100000000096bd43f16c608f2f06a6fa066dce0979ec084edac886292b38c5c945bcbf9c7b4123bd54c02ef89d214bdc171cc9ac940d76494eac8d0c75db575a2f2bf80e09fd3700a47d45b71adeab5add08de5f47bc2ea732de4ef002db722448585b4c0e05424142450101a2f63e04d2044375cc93d99ee1fe52839a284ddd76d4efb69334a033b9a9427e5e92612f2b663d28d86a52b33c05011b724a040cdc3339067d51eadfde37f084eac1472f6f046c1b01978c2d65369dcaecf44016314b2458f4efcfb980822fb26ec6e8011c544b06d5d7ace244d4a99ea6b078b3fd7a87b2f3efe9a8541f30e22828c2a5f9eda2f98e1570d4dbede3d37564060b245757aa4433c2e9185415a93d7a5594080642414245b50103920000009db422100000000094e2d0f5df5d9fbb08489d61370527215eba91f955d9d681cdc47a576b462e71081d060860f9c6bf475609a100fafa9e3f69ad9c0bbaadd5e70280d03e644204eb041a9b1eaa9709b30b9aba63c4df2d294938b0f83b0842a4d8b77e87e8560f0542414245010158374b59ea1a96ffbb5be01735770f5d982b7a7e4fd9ab135f5fdb2e220ed56bdab5df1694ad3e53ac3c7aab1260bbf8676bbcb508d237687b7539404271a88b0ae2ca5dd8e1f0a661879ddc4946f1677cdb411ccef1622d8bae12f6fd27aa3572c6e801dbcc5fb3398fcc0f73ec672fdbb40510fc2fdd6c6d6bc681a30799f08b304203fba2c249301a4ccd558a67e9e5e080fb8d67a5ac81c892563ec6383fb2ceeca3080642414245b501031b0100009eb4221000000000a4421f080043d763a6b258cc44ffc62c66b6df9d251508e9bb420e0abf5cbb31624d4672fa7ca603f2337e00881900158e5666cf34a7aae305d9e035da3aa704a0b485dd794ee9874c5223342cc93c4cf1cf0035a3151ba2a6faff29379f9c0205424142450101f4f41d16afa0a1073e57018289929514fc8e022811be362147630c6a0110b82be02d4a6e0b5adab688a73515082f36e58c5866a0ac200f6d1f07d3febf61fc8008480cbb5c0218d6ee38a60acd1d033bdf0a6e6d0577cb4247957b792748a51a76c6e801d4a91699264c3daed98b1140f6c710c3b3d449bd166c93aaabd5dc9f553d70829608b3062b564791a3bc43c8830e5c5dc9c08b8387ad78f6667be4b588a247de080642414245b50103950200009fb4221000000000a8e60536f8da46acfc53de004b15b5b3c7b1790139122fa9e622fe60951e6b3f9ac42c409761d8cc4e7bb8b0f12b0a7e44b07c494cbab05a7a6d1ba1de491c0135c64c7413d1478e58e96a0218ea7179e5cab92d61b952b94417d427d8ef79030542414245010186049641dca18110a735131f28b3fdee3f0f8ac00bee17ff4f3d5eecbbc91d721c5c8ccc8831fe5c34c631856c2aa8039c2311268d657344684431bc9f746b87b01c5f15d18f8856c3949601cb46e344e969408d14ab552207ab4e77049c4f277ac6e801945a276b2ab75d0c7c776a26d7b1eba43ddb6226d577b9aa73547bbeb333a73540905e16ad582af9935f7e6c0c12bd7283605f7efa1e35d9cd8ddbd9a3e5d066080642414245b5010325000000a0b4221000000000108b21d1841c07f49de22e464743c98a229d79e0488a7724834eff7a5d330d35ac5aedebfb2ce39aa3ef9ebec873fb47f15f553e456334bbbd77219f115c26058357519996e4cd36b67ae8df77ffc85aac7562429ff29e2dd0e8670ea911170805424142450101ca66d53abdda9b37ccee18499a5cffc70593c72131b3e9d793c7d10cf748e050fae0e87699fe07ab8df76e2e231c0dfd4dd656ac2bd3cf514484688588a6018d1bce31cad094b15251f569c17dd923e626413e5545349006d5354953aa9a55b97ec6e80192a5414dd2879d0796ea33ef4f5141b4ce4f2228b2bf3b4219393c12f137cc858aa23822c7ae1daa3892b02b8f8c5abb31a6988895116c7970489d107f9f23dc080642414245b50103d1000000a1b4221000000000f6e94a956c2d03cc279fdb685d2e967ddfdb58086814f8597475383163fef457e88932a19529bd91b4c3ec0633d9e94b9bc95e2d38a8270bcabf9804fa258e0ea487807e6e6b633d40602fd9d30ed9b173e71111caf22976d87fd3636ceeee0405424142450101e0c2ddde1c94be07e93fb1c2fd4b52f49e6672e94988f7df8a11343c1915ca789da930396e505359c33aa4a7cce3d966e4e95cb08a5b662351f03206fd57198aae4d2b7c54004d81382db65cb3e523b4fe83f2926f9a424733284d758ff2283e82c6e8016715542b86c002b52927d114cd2a22d370c061a3ef4b774c40badf9f5f3d8efa828c5819dee79e1b26d504d5cd9b66875ea3761791c1ad25f827b505d6001e7f080642414245b5010152010000a2b4221000000000b45f674409d5cb1be66319fcaf8c42c73981e6f1de46761db53df4baf5e66b26cd737e458f7349e7ad155ec94040dcac4e150c288e170c130b54894921cce00911d054fdc2c114b4494b8ad532f3849ec127d2a2f876ec80edb2ca2fd3c4260005424142450101503ee93ed5da24b000d80f90cd2dcb3f26fdfdd2633677aebc6166447642bf4be7637308767deccc0e2faf6be64840b95a2a4ac1e77eb382c42e0098cd04c588939af527b4e53fc7bcc0048f06a7699ec19ac0886106df10b791a0e586f566ee86c6e801ef875f05e2aaa477213731ac24d923cfd549a33d94e45ba6ddfab9a0331b60b18581f2949ffe1f2755ece7033eb75a689e41886270c08c7edefbbb9c833e1a72080642414245b50103a5000000a3b4221000000000ea2d0872afa7235fbde464f7e02e1f787ae19963e225171870f0c906010670693113c4d2cccc93b715152be9e802d30a8ce588e8efb01b23b1971b9761e4320d5c1dde36d21d8dc1baeeb9c8e44f6ae4bbacbcd4051a06c868155044086a650f054241424501013e48db846e74030a42e0aa8098442644ca53719480a83749707d4ac58129e93d45f10631c806ca81a3428bcbd28cb038a8c6096a0aded48b829309c1f7933781fa0a6700f5d85a14d0bd3eb4131d66195a56db12020917c24ad89368b8ce080b8ac6e8018c3e71dc477cea7710bd019f50685a9cfac40cc68966452ae87d6bb428d5244408e4829e66c97e5d6f5aca935ec5d20d11c0e8aa627cef6824a42d18acef5da4080642414245b5010370020000a4b4221000000000ac09e4b48ae25eae515c577c5677613ea5885e1902bba0a11fec4aa6bb0b4357b29b0785ce3c28213f71addd1fb5ab52198c08ee101ea846234bbcc6435603039da924f060378cc55f6a8d6d53972171c667de210d7d571c883ad978f4db3a090542414245010158b461cf253de91bc9a6292566b562beacf9d769bf99e227fdb20db9910ffe7265b79e37a48f14d95c4aff3cbf20107a4e773f3da12d8edfe52fd5668d79ce80ea16f0908ec824d46f0ff0d2497e63046e2d91e3b9cbb6b36e6b49923a8b88a68ec6e801ee2e81a178cf3880a4753a51e9fd98b441e9a7206f188064ec8818b130a5c0185db4b69fb688f5ce7bd868653850f85d4c4b599a534f32d00bd1f8f17b1be623080642414245b5010360030000a5b4221000000000209028a64b28a156c055bd8df193384739b33ad1c46c2078e241ab0e852d6c28d40638e03df5ae95f9cd7cd3e55716edf315152d460775ef082ff0def9d2ce0f2dfba4e7265e902b31f4fad3b608b6e8ddc8c753b11be7cfb570dc4b0ed6bb02054241424501015c13431a935039af06fbf7200eb11a744f9597ba69ca1496f92dfcdc5368525d6228001284057cbf64a376d85d52e30f45fe70577a106f0f8fb9e5dbac5be9890a1c26f6cdba323220a08fd7094264cfa3ebdc273968152310f3624c6e955ab392c6e8012d9020ad4c233b773374c2b236fb79ad84c0050b7459a9a27e57bdece1b0566db53ac5945b80ed6b7d001536328c92ff658513a555d615298efa58890380e73d080642414245b501039a010000a6b4221000000000a021b6345ac173d28e27c487d02c98eaeec52be7b5685ee3a61b1319b6bd675719417c7f35e44fb1f5ade00e541b476d12f86e031778458e4321bc6ec453eb07dbb05dc6ace35da86e2e0ed495197e14855d27c47560f1f395ad3cfa1cfab60e0542414245010110cd1e380105c4eac8a2c5ef9042ad9dbf1b4ae7245f3e2aaaee03bb07cf7a0d933b87d2c8bae6d7a5f378e75cb9bc41bc476ae145099d8a6062349008f75a8e215c1fc040f64902e84cc87b2855a797da5702e173bc6a0df0763b9e824427fd96c6e801d8382aaf1288fc326780e297417f7febf68653a22a682ad19f5c5b3b578cba4d21456fc24fd7f8b6b5fb891eb81d7db7a21df7659e935b9ebef6cacdd4b9242c080642414245b50103ed010000a7b4221000000000b8619023d3b4ea37f49926b418d062a061e20ed9d8e327d8ea118797512e5e6d6efeec751b5ddd674bd7a9241ce35e8f71eb2affe9fa38e30e7dccf74e28ad0763df205389ffb1ccfce87d7cd0187a35c7f894273345ea4f85e1deb46061ca0905424142450101aaf33b3fcfba8b5ad75c9042f7e4ef178675e4be77d3e0aa3a319f53fe0a12465cb7580f1dc44c62f17f0e5942ae67fad9bb94e62526f312749fae1c1fcdf1807274d33463b19190aa6e7fa4890511def89324d9fd37b24d208e391a2a66312a9ac6e8016f997822ff23a96c440a532150ef540bcba76d9486f1df1301235d3bfea0248ad1a7ac7ac6dbccdcac87cfbb2ec1595f14e7d152fac6bcf1607798acfcfaf46b080642414245b50103cc020000a8b4221000000000c0d145e4c2ec09e60d292f3e55eea4213afd751a2ac6fa012ec1639de82a4a45bc6ab924558c5069b4980eebd5525e413f3db839abae5966e4efd740cd46530b169806675a16ef54fcb0c61fcd2849972670e144a6b76d1b9b478e3372da8502054241424501010c0daa7027fdd1237f9e1c5383f1acd6989ea1376174e9e72da716b2693b137d31e7047f86f6f7bf774aa950df944acbb6da56af7feb6847fcb7f4b57deb388b26bb14ed50daccdabb9c0013ecfd4230b0cc35df18620b50c973f10489cc4a5a9ec6e801b13fe969a8486eebcb99c3ff9d8c84b8327b5ae7b585c96ddcf599d4344b50f0381cdabb09a776a1e74ef2992f24416512a10001010df11749cc300184ac09e1080642414245b5010391000000a9b4221000000000c42d0be31520abf57604bbc6d36d84d7aebcd8d7c4a43f6467a6d02408c5b81347fb2626850193d487b261fc7161c2864a75df09bab5e7ee647eb5bc3df4df0eb40797a23ca3d8aeb5d40803ea3441475bb2d072362b0832bedde2c0e5679202054241424501014c96b3309bf041879e0e497055274ac8f2ce81fe0192336d5f5564db38fc292ad781b24ac567ab4c8caf4f6bef7a0d57f663b2d960b47946649cb5b187eca38b3542a6d75b4a317c7a0d079cf2195fe019f2e5e93177446bacdcfcdc20712c18a2c6e801d4fb7087d376f7ff82ccf15242d34a64bbea6dad71119356ea29787f6fb1c97a514859ae3f6acc4aa82f9e563ab906e9e75e18cf1a5bc08287821af219b2f253080642414245b501019e010000aab42210000000006858603df350d1f6719181c4280d1f8e9125184ce162ff707e42eaabee43775b938b0e59424612c30e181e01bc2b6da979933b1be38cd0651f6466d144070a022a50f1b9740afde0139c2ca99e913f931058a079cdf3205ad3cb2b22ace4260d05424142450101ac6a372b124aa163cde94bc18535217ba1c6c8817285181680d4df0acf22bb463d15fc70b59f3e55686df1bf53aaf10be42dab8643d95cf59759e83c1c615980aba60145a591ac7986006e88a8175c0ac4f0708cffbb82af2f3d9a99fe3a14d0a6c6e8011426b177d3ae61c79a6bc76182cb823e95f0794e7610f3e266d5da87af41242f6d24902b174b50f14e49dfb00a31808321de061047bda35f48bf787f95ec7229080642414245b50103b5020000abb42210000000004470e8be0d9c9529e31f9dff9b937735668302cdad49d3a26338503b9a4868326c0c19462b38c0c12a47bb95307d8ff36b1f973f18839859a3b0df4a0cdf0a0438424f150205b9a3d2a20cc41fa63a5369e555cc92b9efc6132dec3dd8efcb06054241424501019e634a37cf9dcfe71eb7445868a2340a98ac8d62bfb46e35c1d511a6c9ed8949241bfbc6baedcba9ebb6bf1ea9de8150647d42ea9f083d5260fb67ba22f05d8b8fb7d494d72a0db2c831f0e9d7d059f0957d06b9cb774dfedfc8a6bbc1afee86aac6e80122d2e6c88da718d698d2badea51836456c95bfcdfacfca941333a51e39ed85d8fc0e7af0ea064bb71876e3850118cd6c3c6d5f546c04062af3e6f3cc85b0ff84080642414245b50101f0000000acb4221000000000467e540d2e0ad396ccfdea57fb4e3a7dc1b4f1023cdf7587e4b37dfca7ea173cd9524e9090f4feb75472e42c0fc039f154d0b9f79c0465a27badcbd7e37f870c89e4aee4915e8cd28526165960eb8941c44d0091042e3d690f95cab6a790ce0105424142450101541bdddc52bf77e32b3276ffcba6ccfae9a7493860faa7e4db3cd4c37eb24f365cdad2016c842abe8b760b00e17974b1f87a1a074b93280b6f01f20b4386e78804403a4dcb11d4fad464a2bd209ca44f104d38b938eb778a23c8135065459516aec6e801f2e07951883f37fa74e885f9737f2ca2538e6c50c3405b4a1772544bf0455e46669196f1449f71dafe7fe8565896855c30d83daf3d84feb37af278ddea72bb41080642414245b5010335010000adb4221000000000e40fa95e2976f329d3002e440e35a62b3c7cc74db790ff7d7e9ff7718c8009151c0720f6c029c22f7020f9298add2a862715fe2710326e477f3d802efb475c0693eebceb760578d38400b959c15b1f62353e0222927a60537403ce27a17e3f0a054241424501010288f75b0b68ecedcbe4e4141784ffde3b86052aaf8e68124f2d88fd1e89a97426ecdfad62009ab3360d68e82eb40000481b17a70126efcbd05bfc3982d1118bf5a5f0acd708753d9f4db8fe3240bfe83ee67b473276fb1005202ef1302a0b79b2c6e801ca8726e81f3646f1c29f9f4a5130aff92c4c015e2f94ef77cf1392302a1b90968a701b64041314ee4821acc332fba0b21826b04de02cadd09025e5c95fd88499080642414245b5010365030000aeb4221000000000c2a9c25027b76443d5e3736d02204ce504839ac00b25402b1b6218b321446663a407e652e7565390c010d50aeb31e82977786584686158bde49e45707f997d0b3e942531307b2076fbc8c9db5f3e007cf4ec67f2a427a28743ddaad602781d0105424142450101e6f15d5baa25cc8827b65a431e76afb015826a080182d3721ad2c34906e4eb2842c71fe16c45724bdb72e42e1d64664d14d58de1038d5d8cfb7ffc9b695b25854b71916ced77eebf5bf5a722395a73cf972830fba7ad2f6b854e34ff0b9404d4b6c6e801917f4d9ebf094ff35af8a0419e593997db513dd8268af1f8656df7e8ff8ae307210f187b15f0449b377ae636a47437d0dff3f06ef9112d4c2d8db1a519ff3087080642414245b501038c000000afb4221000000000aad70ce88e8d2bc8bc307ff44554581e65c282a3be80d52f127cb72422563c57aff0b85951c54774aa7b0dbf81158598694c4faa4e07616739e6df832386f9062b3be345d6c92656faabe51cb6ac3f8039adfae6eb6a8044c058849083c324070542414245010156b10a00eb96d9af01526c4456e1d9d8d1cae4f5b13f48c8edeb8bcb70f8dd5b57843f2f5d0cf780acbff29e3e1ee3b0f775103ee870c4e018675dc127e4568eab2db833a1ba40fa0ef722f5018564cf4f6406338d579dfe765b95fbea6a707dbac6e80147a1acd3842f64b62a17c977373e31e24a4130033894dd9a72ec9b890738a6cdf322a438c79e71fb787a1d8eaad3b958851d8c0a7aedd610d066c36b0dee7d7d080642414245b501013d020000b0b422100000000034a9e0c8746f240eeb9baba6f3e9aea04930931d9e5e29dc304fb094acc2825386547ddea4cc8ee0c7709c0acb1b47bcc259a1d073769771f75d7c0064fe550017a463982118354a3c7c09ed3fdc6ee4752d578018098815734c49d44095f606054241424501017c52ed5d318a0a70aa3db503d21bbaad456d343cd18639f3f675c5eb88b8ad10313b429417d099d4f65a515082add58ade6ff585386f86e0127e8fc28baf0c8c2d88477c659b2b6c82244ab4da702b708a6fb07c6c50e43473d2659f7a8fd34abec6e801d00693912ee983612595da0344636abf0b0127c4e2eca0e921a372e97d32ac53a1cad8b7f6a0845b33d8163ee198a05e4b7dc99dc5823327ad2bfd69a45ac586080642414245b5010318010000b1b4221000000000c6939b4a6add9199f4840fa0b491403f9931b3b190d8ce37f6fc2c623538047f872a8c4ca0f1b189238b9f214af5d8f30df11f820ff2c8ae2c28f5a386975d0e77ffe381a9f8f0cfcbe6c2680376f211e83c72d5dc1f1d506ab45bea3180000905424142450101b23b653739f6f6216d69aa871e0229c031c25a783a7c030df76c4f14fb50767052e44cf2645e5ef53494ffedcaa80ea28c39ceff7d466919f6507defb68f348524dc6536e1bb39c70df9ec52a5fef4225eeebbe70645de8b3ad85e3c7e887249c2c6e801b3294e166459fb1f106427402362ec55f0855a41f64e13870fc07c3fb3d17e82d20d6970445cc07df06d4445f02f6bcd59e2eb0d4cb716f8dc2caea37a169265080642414245b501031f010000b2b42210000000003865a5c8d5aa4be207de30eb7036ae1b58c89d23fdd1d0116c0118f673b6df77ac3c3bd1c87eb7d3598402c67f2ecf166961a58e9146b721879c5857b24c8c00c0b11c0a493ab2c6d00ad5feebc3d466c4273bd338974719ebc0e598b201cf010542414245010172fc3b05155590a560e1743c820ae2f71dc3d9898e213db7d17de3544c92460a5b39a011cce7252f1de6fc584cddebc46e65f9f9ace96024f55ca55094e9498ce523a1d9283d4235c7aaf0f754fdbcddd42400b2991bd0e45671e3e2130b24cec6c6e8016c31a8efbacc913af2c152d3648edae43a1c92bb42e42b95dfa1fb4831f53b580c13777b1d596332a7217364105e10e681ba4aa72de661f336a855a1f88224bc080642414245b5010393000000b3b4221000000000a268383750723bccb77e7791442bb668c2a0290dd394c5f263f503fcba56685cfcbb1141a00121b07d425b39ef99996f5c948d5b1ad2cdeac1d54c5db31f5f09c706d0d8bc52203b67522d38e4134c351c3fe934d46cb48ac686e4a57450f10b05424142450101da34d1b46cbba8b94a72207744cd80c85e0773eb3bcee07183b4fdc4bd7abe49792cf46c0ba6c43e29f321870317baecf355a9656eb4f7f77bddf50245a74d8c483f8e630c739001bddfede9fb3d50943ef15a7a4392c1aa390c7ad3569e4225cac6e80147544ec1fdfaf0172f6eaad52e934b3e491b3071906c3342769cb6556c29937fc06c2db35faa3e63246835026a030b69dd74ce9ae26af4160dc6b8674b6f9531080642414245b501037c030000b4b4221000000000b0955ed5a4f97c52cc477f0ebbd6e2de251a8bf5d7074c896ff78d65c8bdc45de2de3d924ca75fb27f07dc913e69b6d41f3fd54a6789ef41166ec603e9136509a7262fb5f05fd236b2c57ba563a8d11145c47c9c6e217cbf806f3304b06b5e070542414245010164ac5c387b74a0ead08a215d41920da59947d0cd5ec64a568a1dc42fcb3a701c8c18af3cb207a2dd99d980fc374a374dcab5fcb791f7e1ac8c557487a8e92d857ae8f303912e700adb748df50259d1580c3ac3a46401cc181d460fc869ee88c7cec6e8015063805a8a79dea75ce6fcd5fa8488ef1d2a28e80168701532c4daf8c249ccf6a15adadc568ab026110c4554d8d09bcea277d8389dd06066ff86718b87e91f31080642414245b501033e010000b5b422100000000032c6d40d55957e7f2ff8a3223f76abe7d94e383f94f8f9f2c61a12d532768371f5a4c1fd6b461e0b88cc14ac4cfc5fe7705ff8d66fa61a228fa621cf47b1700c045f5c586f105b6b64523c52ba91f561809c2f34d7b0e9f4af4715b130af0c0805424142450101e2f8e7bed53a68d7e1758a42872dafb994476d12cbc495ee9411b8c2527e7554a4f3dc8abf954d7c603c98ac244121f472cfa076ddec47ddca06e6dbe322cf87e3e6caa3b705ce43ab03a63d8db04e211ec4d8c0a9c4bc76e7d11f07a315c7f9d2c6e801d51d929e83ddc90153e45915287393ca24a09f088e1f2cae8e933c1e82ccb50c54fed7f1eaddd3e560a6874034befba6bd5a5b8de940f6ac4858e62a8b1d8d3a080642414245b50103d8010000b6b42210000000006c10ad359275c5d1a809456ef742f9286b51cb7f2db910bb848cbf9188ce4830d54f5cde3586b013389cf25945abf59f42339048a15875e8f644a5c61d90bd00f91acd208e0b6f2ae0e2c787f4b03960cea69f0740507ada962264ca0766220f05424142450101446f6d8a1da3bff58ebb1e54560431a5239cbe1e4cb9e9350a9ffe8b726e0153b6304f66afb99b68908580ac765132b26162cc9e15c235ef24e30484692b068dc3b5621b87f920e5865c672e9eb128159a029108154f9f47aed497970e785821d6c6e801df07bd884c8f6c7df1dddd27b2eb19ca6e2e77bdbc9cfce75555a79d91c0513ef784ac94cc484614282e105d1fa3fdfe14b9ae0ec2291f81ad0c462200f397a4080642414245b501038f020000b7b4221000000000c2153df10333d8a6588a46e825d52a30fa3195049a400ab39d95d601ff0bc229d5e676c761318289b1340db4667c908c0e180218ca51217df6712ec1dfb4a30dadcae2c2663cf28ef61ba46b9f0044d027289e8a7d0ae3935a1b3894900dbc080542414245010122772dffbc5d42a4dbf040d010bba55857305332d2da10e65412f0f54afa7124448d47a9dd8b54c3c6b07afef0864bfb8f0e9e16cae9a7b8d0427562eafeb48278578fca705edf7749d90bdefee417160ab3e99bc836ce810f1e50730a52a2b0dac6e801ef5025a944145104b672595dab1f34d941d6aa83cde154ac96d86003885ddb3a4eba62eb28936c0e9ef14b881ac7e59d30843927bdc5131fa2f6fa7f4d867788080642414245b5010348020000b8b4221000000000da1fd54c1871bcb08c65fab78a8a9fb1f1b7deee3617edbfe01b9cc739b1363689c329a388ed7648d8dfe918573f802608821de5d2311e6a87a6197f2bb0590d4caa8b1bdf4e8c300b6a4d437f9fed697c4d6a98fa0101697f630ac8559ed4070542414245010146c8fc1fe6a035fe5c594eeacfa9c26be6b57388d8dd3506409e412438a4b64c35aadaca794433f5b921206b2e757b16c81920f85daf2777193f55b201ce158adbffe4e9069309dd404166e2eec917a37643d532e0c5d2e4b4fbe3cdcbf01874dec6e801e17c69e840fa55c93012c0570e081d5d5b6bd75fba3f223638f5abdde35bc0afb768ec089e83487db6b77c3c18f6dabd8265a7faa5ad74a8490e508ea2222ea7080642414245b50103dd010000b9b4221000000000aa0ef557bc908ff40951bcb1664229598216bbdef7f6e1587e52e8fe88a36e72a90e909b8f19a2f6dac36b2ab5dab8edfdcf33213c1f445d5081209cb4a5d2014ec4a185ee163d23c9d64e09fcdae861cfd524a537737fa6bdf37c0936e26d0105424142450101d207770f5a7286172ed0803ec197e8ff9756831ad2c8c2d0b62d76c11eac612ff66500094ae5e3cbe67e3cc60dd68b02760609d70404b1987cdaa47da98c178d5f382df326277b44f0fde848b00fe1fb3b774452a5f94de9ddaa51742307413ce2c6e801875035632cf4deb9a02b930ca0dc9bd58175813f94208be89e0c4f296c9fb8b333b9a6fbb02a2f91335afb64b24d971264df792a7f5925a2a5ead64bedcc4360080642414245b50101a4010000bab4221000000000aeac08891c524e18c59c4da5ac0f344d31361ab83806f2d61cef4e6997f89b25a8b1af05159f07dcbc2df71bf3f6209f8122b71621463c3aefcdd76d80a185039e8711ae9eaab1c0234d771e76c6712edc567cb644e00d595f06d77b0e5cca0c05424142450101f4e63da5348b9ab69ce9497f2213a23d53ed335dd965a5729f12c8b701a6c82448d65b6b7f5f72d6c693d7c2f3ce879a331c3f8cc41b1c616c36832fe6dfc984d4b2cea33be8d375311b7203b27a598a1a40cd93c47ce0835315d241a0dd8999e6c6e801e708684bd80097ec73f99dba465171bba60b762f53b8910947a90ddcceecddab214194116da16d2b2081607dbf3a4109bfb71d27276eb93cad5b648d2f952885080642414245b50103dd000000bbb4221000000000f69f775a21aa7e0805e1fa1f67980787e68f14c09dfb7f98ac2686a955ce9741bd6db088657986bb1716007dd06992233ecb5bda63c738a188c254ffc37adf0bc3ac18089f28d9fc32a62229c0dddf0b6133fb472770855022742293f3780c0105424142450101800745b7ac2f5951a697592ec73ecc44269c11c006c570f4bc0d3875df8a6f7ce84c7c74ca823f1b56173199ab4f0ae8abeb4adb76551b40a752f5be36188d8074cc08553933a9bca8413bdf8b521d19e18f84e7271f068dda3b09744519b781eac6e8018828712624759180375f6f140b83806d043bd8cee624c5a117e358e3c6984d4b22f454e27c5429e6e3e062ac89b1550c68b10b75b1f61b4a030850057b012d46080642414245b5010399020000bcb42210000000001e92d3f84543518bd082e0dedd8097d5fe0097afab6dee16d99e128bff3bda0f67a3663f39fcee3b2012b563d89b38f72b1a3f1346cf291ad37c10644f8859054ef3bb042a52cdfc5166037164808b6d82c0a23cc1e5f4f76fbbcfdffce33d0705424142450101f414515f3ad23575b0ca8b2602c66b01f77da0eecaf2545e5c0d0bf4f108863808985558d253e724446e74a180dd6fd81d8b8145cf36722da65ca1040e812b8ab2dcb118a175b0c86ac658d008a32a5d03d18a0e0b8d160fa2b0b09e90f37aa8eec6e801a36d8e92ee796e9a18c031243f781881fc2d4cb80fcffdd6b0f6efe267eb45360ef22d7cace8ba182b3c5f714c636e16a9c961120b5471c84d9a186ec9dc6b79080642414245b50101fb020000bdb4221000000000acb755896aa7a1951d240985a45e4f9739436d0a9d9cc4ff93eaec9151c4a47f9c1fcac43fe9ca7edcac985041b70b1d17e9bf6d07e269e06385dc509008a2092ba55c89c53ab68f4744934bbf7e97081a0b17267799ff07913f572cd8e16a010542414245010198eda5443372c8f9ca176cb4fa99b142d8c76de2f176d429726210d1002be441c083d0c3f691589391632ec0e078f2736f4cf30858c931f476fe6d890a155d84a6eba3e0a699ef30bfe8807a8474aa1dd06cc55cd124c2085d10b00ae4d3e606f2c6e8019c58633a26aa1f2b874ab78e6c6af067b77f22f7857828d598c33f7f47e3b6471b9ed14c9ed9a05774acc37f3f68a381135fb0c4bb64fb57c627de3d515ca6ad080642414245b5010389000000beb4221000000000dacd06431cb846c4542b2168e427b64265bbf220dcc03d9717030b39ae56062f277703170d9b052ae9baae3e8dfca6d455988062fd1ac6604f357929305d4500651a00735da53d0d7a8d5bb4b10d0c7c4330cd47f44eadafc3e9b79152af6e0505424142450101ec082f384176ceaf48fdb757fe1a4d8311265c0e5af15c9c5f5e209fa946ba0e7161aa172bbcfa559a6beccc273c12a2d0f807813cc2716e4a1ebb94e97fb483bd4172d17246f089100782d98b76173539c7a24966d94a8cd72f795d098eb8def6c6e8019ea7273ee3bfe37908211c68f0042558e17c93b7dc96a04d7b79939c9a6dc62b3012799bd244af53d9683bfc9bc0dcd92aff11a0a55d546c71e72f8879af81ed080642414245b5010322000000bfb4221000000000603f7f7f4cadffa964b841c74776fdc8d3151bf1eabb690010fde554e5bf6961720f4ea1ab9a0bf9d72a04324ca86d0706d5330b1d8162cd39467b7ce10d7c0c98d452d2dab760b6fee745b7fb65ea28f9105bc7ced93235f6308c4c0187020305424142450101e894880de5b44a8d0e9a588b48106cab93bc4392a7224719faec9d4251deb660d888d902b3aa754b60ad8b76485116463446470d34b6b9463a727c637e6dde8ccfb858fe7a12f684c85a7076e7a33607bf611dd265e588f949cb3d3a331e0461fac6e8012c5a10958ba1d4acb907f82ebb943b7ed51683d5e626235726856483c4b11f6f9a45484e221213d8a9e1aa6ebb481cb0e0b5f5e19f1c9a22cc3c962c4f0e2f14080642414245b5010123010000c0b42210000000004ef05fe23268960b84a40252fa53e02f22a571c4009f8269c259f33251115e1d699ce1ded379ac1ba9cfb58953190b3ec4989d128c26fb4400b8e1e3e3b27002785001d6a29fab1e211b670e2b2b8278b1ee7a26990352a98297a616c975d80a054241424501014a24c3cda5c56414b9ba695dc6a4db43fff568c54631b6e5240a807e90ab302fa9d2c742954d2702e2bd679d36f79e9f6d433cc6506f543fa2f2b1961a9b4c81d5862ec674e1885293d08437ba393e5eb7e9cfc029d1b58070d3f4f5eaad9f16fec6e801b17222ab9320cadd8b5ef40e162e66c80f6a610c3131ad2f32081c4969fc0a967fb6ac4a1df4843f3d5351e43bef4060c13fe76c603579b2e3d0b28c90f2b9c2080642414245b501038c010000c1b42210000000003cc4f7c38f58f92e92e2c18ffdbcfc387a14f4b10e6a35f05f398b13e8f3001fae3760b907dd878404bc3b9c8dc3f0a84e00db6ec74cef1ca429e47d6d579405d2750e040e49c21571937872c092958fe1f9e31d5f84e9edf135f6bacf499c0a05424142450101905337e134fc9d5881af27b95da13f61e021f70f4779096ffe9c21bbe594c55721c968d174f4f07074b8feec76a846434e6e1f6d628f61ed146395542329128c6a308d19700426f33c3744e6ed874e47631e910e24c8b9ec46dab60f37bdf33c02c7e801d327f7fc98a9d7d0afa1ab2dc086d9845a65209591119f980c5e736275c313c3b3a8af141f99c83f69bdd9bdc2c31d5c0ca52fd792b7a793b7a4b62ed5fa9d8b080642414245b50103a8000000c2b42210000000007ab38c4dcdb52a7b0f5f15a34414c63425ba2aadc1bec1bfccd3e1b9c67f4a140d1bcaa7fe0b935dd466e3ef2b5a8e302cb51f8ed58d0c076f101ed2febd0c0893771f8dd57c92c2fec1c43db9ae88b6ede7240dd23633f095dc8ef859b45e0305424142450101dedece21ae8125cb8ad11c6717b5a0da7ba43a5e3c1d45e58aa43c1cfce8dc272df488fe62b46ae8c1f74aa61b94ffa94266fea4e248c7eaef886d8c4e9cbc8c0a9aaecf4a2d318d78632c05869a3e55bb1318259838d0e6fd1b8b09d6bb2f7706c7e8015c4a0b3870988d809b2fe9d2c4711db537019cba209f2c1cebb463f3096662fe85648e6e96e3db2c2b33dc7e9fe391d0065331cd8b89483224013b98a2b5c6e2080642414245b501031c030000c3b42210000000004ebb835060fde46fc298d31bc79bae031d5e2070d94b5d535d2e2182c12baa6e74ac73b5d64faeb0e712c9b869ea9864eb43bdc5150329b3accf948c97ea62046bcb0ada636bc3130ef7f8a9c65448eb6514feead29fdb6a0d9025f8e9c6a20905424142450101363c3e1f93c2fd5081426fe6f1ae6c6b90440cb2a7c380b1464d9dd6a697d90b535d8679b03e1ec29ab366a829e656c5db70a6aad0f062ae8670891b4ff33589e29163e416d1ed75c06a9126ab46c455eca6a334cf249b9cbd6f054a88a82b4c0ac7e80174f8b9a8ce26f0682a16ac1bef37f51e556914709808575c0caf3e38fe491de79a27168f062d7c4d22790e70bc4aa92cca8bb5564ebede3908f2e0df4f4ce846080642414245b50101c4000000c4b4221000000000481923db0fae4f51940f18edfbbf3d42755a8e8a4f5975bd6cd0f8a6b8165909a131d06afa73bbfe23042c31b523ef5acd4ae40a089b1931935f90593b40a4036be8497b9a65593e2cc29e1dcd75328aa00fe3eea43bac7fc6d7504123fbcd0405424142450101f2afba5ead706fcf5f43b792e4723a9ac9aa02909ea769c47a589fa5f90fa17ff45eb6c2ecc64626c64f2e173cb6f79e97ddd81d841aaa55c4c60296ff3fbe8b30d78e4892b82e3bff4cffd2fcf63d629ee2a93eb3edd88c6f79ac8bb1232c7b0ec7e8011e00edeae94af20c1f7960b03585540f87c75ef5d717060112db8f8bfd7317f1a9b1313ba7b54792119eb868d8bc217660c371f20b5b5194ad9ab9ef1fdfd23c080642414245b50101fc010000c5b422100000000056f6be2f733120aa4b57289deca584a002a508e7a67102391a16269768dc407c50893022f806dd0e7dee057821274c783d31b2af5d249b2c7b74e4c45543e10e701d32c237041223e9a9338aba8962173cf6c560f51ce59036be2da2fcd2c409054241424501010e82a23a35263912e9ac3a524d8e51f86e5350f8a9fc60bfa34560378b7b666046d74b026c1953f6e22b9843c718c797e98b1310b2d87585200bed53a952118eb1f3df36f45331894532d01175c32bb10984f1482bb36da178f351637ddf5be512c7e80141173766f0270c17d30027c7101c0330b6ad74f3035e8f5442f619dc59cc26445d2652ddb73d100dc098eea44dbc62445a9909455014d7b639466f881a89a329080642414245b50101b9020000c6b4221000000000b8bd8fe2ef66c5dd4354b14606f8224e48c9c42ee6d8d597edea79ff978b396c67e73099313602c25536dd1a1d30478e8b430447bcb6b09ab17bbaf9da9ece014f6e2ee8dfdf7056bea311c6ea27d7a2fe03cfd369d1f584bb0b4992a4759c0205424142450101f089001c3008d2cfb7b57f382335fc07722dea9f18ac0c89ed921f4ffc8d4d2b8062f10e51f0b8d9c05d41d59c3fd80325d9eab2ad23ff5eac6b7c073be26d8e8c7ed8244df66c5462b85ac1f88ead94eceac3dcf33ff40745a9ebec6d9b4b6f16c7e801d6dfaf1b9aa98255552909456b35c6f7e58b3935810e74b8b31c3713563b7c07ddd9569c67fe876bbd755c7a2bdf71181fbc9a53d4f320e12d2644ef14b85fc1080642414245b50103df000000c7b4221000000000a6d2c826780775ee5338b58c5d5e55f5b0b1a631c63e43229e5eedc57a807b7f7f3eaf58aeb36967c5f7c04ac2e1a8f682f173729a0bfe53195ad316b791280a1d7abb573b0ffb489170dfc61961724be60a21d8ed52fc81a8ded2be08f39e050542414245010100488fc8abd3e20b701595da6cad84f7eee9f15cdc8f1acd9bb28d91632e150e5694c0f4754ccb41749979849292051dbfe1c0257ef59109dda9470de7bfeb8319bddac165259369f1bb6c567a12704335443fe7703398811dca95057bac5f741ac7e80188573e2308a89a3ef73681b4862252e3dfdbf5febff8b71c1af60468078c0f2335889f867767162efed2f4b20c88056eaa96aa1283de8617e02656bb48dc17ea080642414245b50103eb010000c8b4221000000000e68a992f94bffc12d017a46cc27a06f41db525f09334959ddc57b157cad88c52ce2e9b9564f6e899404014d2c6b8afa538608c142aa7ad0e999eef5f391d2e0f9a24c8499acb880a8fb1fdf564f4ac92774159e18e22d9071f7bce8dd802430705424142450101f056f22c2f5b5509e1e4eaf677622f247ba05f4b835ddbadc9da355659a6cf6ec96da08292b61078dfbc50cd798bf235a64b8e988bd3672b8e4d4bdc4269e68ac96b18ae57bbf9f6bc7191b7a88aed8b5ce896514ffd021b16d74321c134d03f1ec7e8019593ae56bf757933b3afeca8855f67c649f4d1567a52646d7c6a0c8ae3459d3ca564cb1da3dd55a533a805667071064d864578ddc0b39c467b78948dfc1f466a080642414245b5010167030000c9b422100000000062857a83512fda50ba77c633594b72cd4ad942beb60469c874449957ca5d140461312bf890857b430f1a8cb0b874ff6623cda0ea393975c94f33e97d17374206c406febc8e088fee18ae99e9294aa5a4ab8fbb9fc8b6d229543eaac38132d1030542414245010172595dbdbff1392d18f8b045f67ad129e64352fca431f7c3e154f55f4c0f6468ce2b7d33328dc3b812d60863c955e22ec6767ca9f2b9ed8dfda9b91126b3a08a670b00fddf8e6046623d71efc061a841c9381dd412f50db99e705505e1ecec3322c7e8011771e8cf399a2b091d0dd4222f18200d630541890e1d876ef8b4f84edbccf7768f7d8aa0d2ebc0c33e2e8d4d479dbc549836a913516d872ca7605867cfcf2d99080642414245b501033d000000cab42210000000002027f5a72dc1b8fac687ec9dac1b7d09e64625cff688319d04cdc267f1e11e5c9bced7b3ec507a70bc1b96a936dd76bef48d36ffd81c49ce3fcc27fe5d28450584f659fc31ab65d16114e3813f572323a9a312529207aa222fd5a230d1734b0b05424142450101e4a2916b30296180d5f79d89d317d583b2d48cab452ed4f7c334cd2dc736c428aef758c036533c10dccccdbc516c51b52a6d2f34a214455f955be8535a31d08eb2df763f0d55385d9ed24ea8186c811759db745f38d13b71c7549d6178b88c3826c7e8012efefa8bcaf64b862c9dcc78895f5a325f82da0c41199c1226930e3ee396b38c7e76602600bb06cc84bdbc0371da8e11c6a561a2b6667ef269d60629d06497f8080642414245b5010304030000cbb42210000000000e861ff42df78344709d5cab5d576d6c6e424b1edad69b67832e8ccec3bc9227bca1bac1b2de38c9fcafa77daf8e72d74e25838ba09665c38840cdcc137ee409b991eb4c5b819b2c950a91775cf993ba03f834a29ffd0b8a47d5339ae6407400054241424501013c71936af0954bacbeb82f1700e3d956e65f6a61d4f665d2157493e7f854615989da9bacb371398e41708c95fd830de32835570507e87b7d9fbdbace03294d8f781bc68407ea83b93b3853e447952c6bafacf77d2f84abd607ffab87527a3e9f2ac7e801d830567f7185e28b9283372b2202c74bbd0ef48e77ac594de70055901c778099b8eb58df47f5e27f7a2e56fcc8958dd721fbc07ff5061c7c47036de0892de104080642414245b50103d3010000ccb4221000000000b4036e7a02cfe2aca663c792e3b308acad887f8185f8303dabb74fc0de4b230242b515f75e6bb2d7b79e90184c47547e0c9897478123f9b4c58ff443a292820a288fb0d54d8851b993588478f8a93ebab7d6cd35bd0c07b1ecc880c69c63d507054241424501013e7b46ebd7ae209fd3221749bda84967e8c73e629236e567c70e3e69094c055da59cf31455c02dbc92bdf24c52abf515a4ac14bfeb009ab71cfb1ef1e92e5585cfb6f02f0b5da1492a197dfce4f4f4a549919cec0d11716a02a5c8a97a6e399e2ec7e801db426859a639a5d07d52b4ed2959978a6af71de91fb9d0ddf8156228b0d943a22643fd747f1a95766d22ef2d7411ca9bee16e7b756f46701657807d92199cde5080642414245b501010b030000cdb4221000000000329fbf3de0408452d552edf75a406b37ad9ab3de6f9e41aaa9198a13a13bbd7ab3527f127c8409dd275ae06734762d49254567c33e1fd2b81de882399224d20201df488b4b548fe629994d09d2579ea6fde3b6ca1acbaeae5262214be6b22d02054241424501017a3bfb9cf533043d756ee87833954a9be51c6f0a2b00b8bdb8fb8f5ad1498c39cc4ad9df2bb877fe0d4ab248723b65afc8ea6d6eef98e75cb1f7766a38ab0b8772dec3140efb53ed2909067a875f1f799cc761d43bb5985b6537fdf892e1c8eb32c7e80138b79662a7e893c2e95c1073107a50f7913d7c51e84c0cd5ce6a5919574e7a2b5f47b0bec14e1e52bec85c3f7c226751828876e5464714c3bc1b991fb9212ff3080642414245b5010307010000ceb422100000000072e4a4f5c599288cc9a6ef2a55c92536e838623578a0cbef48e2ddec28142c2556c33a5c3ce4ca2b17d6a584178657f8801c2fece9c6ae66eac812ab7ef0f00ad7d6b32965967042661e2be00b97a4f55cd98170d9c5c89579417f473fb6fe010542414245010152e73ccb6417348c10ae9d02ba7a6d688272535a8fec0b004d0be61fe7dbc36f7d2c17cef7578a09216db5a6f4483b71a7498924a7f1c58c0b02223f6875ba82faa019072fd90ebd345e7480ed4af20175d1ffb7a840f550265e76fe10c302d436c7e8017d182981b31c5e38a37fcc7219f6b6e8a3f8c82c9840e091e7a99f9f8eef18af043dbe2c6b7d161e2f2786e4e29618d8dcc54e246778d29451ea6fbfb34f4347080642414245b5010126030000cfb4221000000000b8f403fc0dea0598cfedd0edcc09def337b5f675840bb4578eef41a8a7389a249fa9155f620a8743fb4431aa0b033ba8a2e2f6fff945ae537b1426f50070ce0e6d8996b57f2ad65d49ea48449ededeed6619a345d92470ed217f56a8cf634708054241424501016cf0f75bfd895a921e5dd59c461dba5a7914db19a0d08cbe22137ce7651d4e26e529595bdbfb5c4c0ea8a943627c6ee81690f11fb25cf69d6ef5676cdb2c878744703bf3d11a2de225f9f84f24091061f91fc887967fd9878d6dd4b70f87f4a63ac7e8013f444065d97d24e97d86ddcfa0c90141d2acfe75f373ad9b7bfd52d1da12feffe7ff2f489e6cd05b4202b5855894a4822c96ae13042d20a56f46ab56c70fded0080642414245b5010125000000d0b42210000000009c625bf2742ecbe0ff5c71d767f92223b35d9a476130567e1ceb91d6a2a9211eaa93c774e37509f9126d6ac423d1ee6514f023e215ab21045f6f3de4ad58580bfd0f216f5f938fb16c21e5fc633425476dc8b7ca2ffd8842b2f5b8b54d8cd10e054241424501016497a3c0c2c1fbeee11a1c13f48ea0a623c50a479df188bd353273521920b857f22b968a76b5ff1ea0d4748ea4270e03ec2e2a661b0573c61600869c0841b78b23fa02224c580ce6b1a2ffcad6eac2018ad92a81aa18d1de6b87e0fcb10f9e063ec7e801b43e7626e52491ebec2f8159df28f3a1482e4fb6f6d321703c7817a7002adf1c84b0c3f0fef3f6ab6ea395b8f3c12ebbf5f210baec526cd029729e309286c4a8080642414245b5010167000000d1b4221000000000a6e99a0f9768013d75bbc333df64fb4718cc8d62f4d21fcd455fb2c47146686e186cfd1e05cd4656fcaf56a1343a5ebb311483b6b08ade191769a692346101020e4b0f0eec8a9479b0539f068385942b89ba856e6130252676766b9c6be2e00705424142450101727265f0a6bb327af57a279c0b8701eb8c86cbac4eb4317ad266cd852552c72536c74728ddeec604417ccb669daf0970920916cef50a674323bf4885c9ffe08bae2c4e0f0ce3e4b9dd029ba636b82bbe0c39c190691f00cd755c54bffb486f6242c7e8018b328cb07c43343667fc39db68a2fc3d7bdaba8c607354bd096545e48d7f9592cbcd789f7f971c86d560477797a194d2d7b5de92fcb5ed7e5a482243cd89ef9d080642414245b501032c000000d2b422100000000000028692193867d58d06830d05e7bae48ce98617acfd2353a95d837e0fc05e797fd9f2aa1a9bba15bbb32a26182c6625cdbbe3a31043b810badd4dfeade6770a53b511f3bd005b1e4e1065145b1d92f08ecb364504f5513a6341d18dfd09df00054241424501017c749a710b9da3f07db06c514034adb2ff224db938135bcc8d76335428836010cca47213190f17e77bd6931934246f19ec6e508796e12919659f9db5e635e38891a6743077772b8ca5f7c9fa326f45cf1d961b6c6207801d5bd53e36d944dad046c7e801ca1fcb8ed148d95879992cba3bc9d2cb5f0510872f89126d27e3e182252dfdc82130e5257d0167cc4b8d2fa1a82c1b239a138e6de03e889d5dd43a26b3076a49080642414245b5010354030000d3b422100000000076b3c1c01393c203db1be962eda0d7b42b78d6713dcf57c0bc375150a8fbb578d993482d769105caad650b324632d45302edde5986acb2519f0e58bc406ca4083d779b0dbe1dd8451ff51ffb20e62343c8ee4c7ec1b55640c79cbf030ccecc0305424142450101062577c7c39b59cd26ea1a559dddad95d520d77cd9db4b27f225b1aaff41e2418b58eab1f8058499f0344b1994d3cefe81393d8543f47f26b5fa954da7526e82fc9d1869a12dba14983bbb850b6be29e8ac19552f4104a39a7506f30374ae55a4ac7e8012f3f66b54562ce9475d3f3e1ab9eff9c0ab48986de10b1a2e0ca97374917055515f464ef94adc21247032e780a3bce84599835dc152f50fe72795709fbd0935d080642414245b5010377010000d4b4221000000000160c4d2272049d11425a7f2a24ac010ac7aa809df108366a6500b8f6616a9f3b316c31ff1771d51df2fef52e62535141617c1b857f7a47f89e7ba31f7e21c7091aef0caf4dc84a68c6b6dba7f3e285e70a066c36aac5ca65666e4bd5af40c80c0542414245010182b6ddcdaa2f718bfd7c134f90dc2fe3b9edad8cde61fc9570bd1f9e4060605c1a48ff3483e2e28d5350d4007952841990b5e04b9acc82ab3bac263d2fddf78b16b684caab51785f3c5f16b62a071aafa13b4ce6151dac4c6a8234ddcaa00d134ec7e80149e9a86c4fb3dca35185a086ead48d2cb26124cc9b4e894b663969147c9ee581f45f3b48c22fb31f920b25c1047cbf7389f246144756caeff674ead3bc3754de080642414245b50103bd020000d5b422100000000090ad2102054841af1104f83b632ef8b6eb731f51e673ba682402c0651439632b00f58244befed9333aa4bf7a3ed7e2755991bf29b300a58489001667f4fc9e04766a470f60b729f9b7539c9b3f67c270a6c92ebc6be7919192f2d27cb6cba20b05424142450101dcef328ce9ca44df3880b7aa702c1aaae049ff4f27019122a481fddde731b5774a8174f07e6ca81fe6210cac1404b5b71ad8cc25eb93ee3e6bba516e6672a88c1e073beeddc62cd70c31471bbe3a47492b7970c7fab49105d6fe8bf834653e6352c7e8010a9c61e6a000e9f9d852130fb92819d1bb4d345b2c7b64979418f0598e9c92e10653b11b8bcde8dd6b5d855b7dfa8ccd84f160a226878f40fd25fc86f3c4b171080642414245b5010357030000d6b422100000000064d595287261b51791969c4da5f8596af44df4cb22ba238ea4bfcf4e84d83852679e01088491c72466bd5d6ead7a4775495482458389d7183986688b9343550f13ea2593eb7d39497184565184e4bca8890319acffb54eae2f37f7be207a0f06054241424501010cd1ad594868ffb589a2026ccc051f2519b4e97111c117e1e617f201ac5142246435bda5f2f9abd4b3a0bae8088cb07f287cea07b28eda4c36193886aa590b806fa7cee616f2082812a4f7ebbefd6383e1b6f5676815c0d10f8ef88bba31636d56c7e8014ae3516b47adfd330c40ae573b300d839c390d3ce0c1f0d0b99c5aed95595de766371c068aaed5ceab1744cd31bc5e35a4f4fa8786ce24127331cbacbb48e955080642414245b50103ca000000d7b4221000000000a266cf13e0c8c96d3f3561246f976d9bbb46c20deb7273cf6920cf22d5f64d450f06b9398db88f175672d9cd0ff10e37b71f2ec9c9015422fcf2af42df56ba093deeafdf39e42c56b4fa762647e18803c98e948cc70c64535340c30e007f580605424142450101b4dcd11a41eaa89dc5e35436f6dfa3162acbde1ba71c82792300db1feb7017518b1f1c3c61661571a4c933bc1b4978514dcabfc2c35d6d7c9ddc9eda7933408963bcd7b9a9d9e837d4574401c341466e62740c47ee5b431845e87c58913d52b95ac7e8011a3adc62e694168bc21ddf558c8e7978c0820b664dc4460bfcfef561b3e27afd1364bd055e4ffd6978559951ee8278d7d201aeaccd8bfcd25c80834d8106bc79080642414245b5010317030000d8b42210000000006e0e8e4c48738dcfc9e446dc2059cdcf68eb6e4817b4d38d8f73c4c56fbb5c68935002dc49a6321e4215a2871f76bee282a940c9a50dbe97f2a968bcb1ecc90af456b04f97530254f11737303e1fa8ecf62fd0c797634b727e196bcae846c90405424142450101b6ed77619db5669818b6db5a65f3695b0537967d44962eb3b8c4371ae4d3b634307c4fc72513c76b494c20f2c7b85d3d0264323bb529ad1da22a914cd55dbb8bfc1fbbfea637d172a56a06a6a30751c0235651928b633aa7b02f8b74d69d52015ec7e8015697b242d112f696644d07baad7ab51f7a17d11c486f4fa46eb0b282a5b5056583311844e49e4c0d9abf286222694cd97794630ad38f465d77c0cc7a4d4930d6080642414245b501031b030000d9b422100000000056b17d9479377387f1cc7f0fbc0dc1e21aae838480d4646a6f335b69e3c59219afaa07afc1bc6addd7cb6ee798a4c068f8bc673af739a052dfa2ed635757020b056a33a39932ad0d85b85dcf9159a5a89e90337d6c63a2dc40e7ebff1064830a0542414245010174d3d0e86181c07642fcdd26c8d65b3019d34f4a4c6bf397f01951c5082d5c4b0cb724f08ae197e394f4c4bb38586befbda03b4c3dbb04d7a062625fe1cf398be1420a3404c51b7781d65e2504b2a63cd3249cabbcbcf3a56ab6af430e18cc5562c7e801ad7c34077f401ee9daea4af5fb0f9bdc9d351939f70461511c7df02cb463e9664e1196015abb6e4818c870902d6db63ef8a3df18ee02b4a89e6b65db3c0ce6d0080642414245b501011c020000dab4221000000000f45350df8ba945b7e4c6a3b6d12b732650d9a3003a620c2c635508e531403511794d30bc481cc0a60ebac26b0ebff77d8f697cb7efcd58419295d11601af080600aa4cbef7040d0e8fd43f06f2d9e70372c56de04b0c3dd019bcfa178327850b05424142450101ba54fa1e73a634a723ddac9ce98884cf79da7b06ba52898d7da6e3b2f8418e3680b9fb61ad50f702d7e12adf25e4255caea5ce7583fcaa08d5b7dcbb6274c883b9cf32cfcfc548165b72bf1c901aeaa8c6338afff9aa9d575d8b4e3fed596b2366c7e801fc5520643156e169cab465398ae28d7d173c7ad6e1103a51ec95010b87d11628e5cd3ef2356a6b825154a7f45e60c676b439d31a03e25376c91c4bd3ff50975c080642414245b501017f010000dbb4221000000000823d6678fa34da1e130576bb4dadd13fcbbaceb519d6979056b83a4a501d997c6e63c9b7b54f770ba0dd2101f1182d33ce45c44c933a0d28b98076e05c584d06805e1643b412f7a16b21f19ea8fc276612fbb1681ce0308bbd04e02482abc30105424142450101d4d5046f53d6cb3c4f8ccc785d29bf9454650b285cbe5a8d9a6f2ac428e9ee1a9f98037fddf8e76373876efbf44b596f795d4f7ce2c6f6b0712ec1c72e591a8fcbc7a9971796308de328473d301b7122cceda55779333e2caa9997909d7583e56ac7e80169bc073a4824de2f59d8ca6ac3514054d0ec4629e440f786975e2dbbf303a6e59037a097282d457b6a6535c3a330603a43bad0deb0a36dbc9e5f725e532b7495080642414245b5010180010000dcb42210000000002ab23bfa6ad94369f0ef30e59b20649d376742805478183095b2f5c54c2375308495f8e30d6285daa4beecfbae5a98592753491084b6e06defe48d80c536be071ff717fc1c166563aad048eea25fc31ee05c27d1f560d96641e653d43b4951060542414245010164c48babfca1a5c08d9222a4aa2f2caf19b1ad7edf00e2a94e8a0ae54d22d057815e67ab477dc34a11cd4d0facbcb841b8533e4e3cb27b78da7ec47050b0e682385157e1daaa0c4ab9f6d6208035f1ffff03f00bafe0c78461cd9fa37c3b917f6ec7e8018f494864513aa57cc1a0aeb191b706cf7d7b98f43b99d169b7ed85433ca98a62a115e7e7c5c1919cf84d0d6f2bd756d992303ce77c7144e89e3053db904207f5080642414245b50103d7010000ddb4221000000000ee3319e0a09e62d8af9a09cc5638c53d908fcca73c5e0e8401727cdc40269171eaf2db9877095393c0fbe15896417c2920d2294495f84d58348d956961d7f7091991368c420640221d967949a58d2e5d44e407105385a9dc9595894bcf946d000542414245010130a877939188a1349e37b6ff3ec80ed963134549a967ebd87a3853805ded0313445a6be0dea4990fdd696872448fc34fd64becc100e69193f63d2350b89f4f88496e2fec9bfbe45d16226ba82e5eca0ab24aecc68f1a88d6f330104a84ad861172c7e801bbd09317b238c09709205d6f3269c7af362f6a983faa94cc69b829e861fb5d901108d21b135c58a52f78e083a02fa917a4804ae0b39f051896648e0901bed9ce080642414245b5010359000000deb42210000000007e0ea20575282df468e694d9017fa55104fbb30b84e9f401d9cdef0091e2e900e337f463638b4a83f54cacb92b937536147f2e38997aa1a357b055531980dd051487d86d0fa0653836b361dc891acbe928510b4284cd0ef0eb611fc13bd6ed0f05424142450101b4c31ead779d31934e62eb4edcb2f6339bf5e35d7b5b9fcf0ef3c436b7d52a5af1c8d7ac01f0dc0605ee55b760aab8552552415fc414b69c32f2c3db52deb78587df64efb4212cd75c4340a5aa002b8f48492dbb055a839f34577061ea8f412b76c7e8018f3867ea2daeb91ecf9149c0baeefc26f8eeadf675fedc38ebda4be019a0b1b14108935d8066fcc91d59c0b56552cc66ab5e51db64db0e70dd1b8fa81260bd9f080642414245b5010314030000dfb4221000000000bab30eb83f7d2f5915b177fe2cfb09d4680b5aa213b3aac82c90838e5e7f990f9950e68bfbd0b87755e9f89aecfeab3b2547fd9e74240618dcf59c9e9b32d305ee3018d402e3c8a170b8b1f2465ea251c14ca3c8a3783cfcdbae55a3a5f6400d0542414245010176c5894cb0b8fa0ab870f9a26a92a7dfde75d763ba26052eb458ce637af29877488302338680b823d90eb9f99e6b383c5941f96c19693b179285aa327d18628c4308ec56b37fd166482cd4228d51bf6302b5725c716923a24c412346c841aaa17ac7e801f2541f159fe0f4bbd7de55fb0f41c80d67f23cf00dac05eac3edec93e6cf4fa39aebd607205bf2486a93c51891b762a21fb82e5702c6a84f727a50b2d670084c080642414245b5010375010000e0b4221000000000cc1bdcea17fb898c7bd86cd397f63ccc139889560965e8807eeddaeb550e221fa8f1ce92438a4ce7941181ec564ad75732bbe66064c3591f4431d24491506c0e60245236e89570c0919aaa901446c3fbac877e966517d902d312633b14dfba0405424142450101ea663c7af6fc338dde5c4536f3e22e026fc84507f6f23611e16042d11adfcb67621448d84c0ad1ffb36ade553e52042478b76c4874ddc33cad64bb2c92fde187aa35df4f38564f9f7cc095e0e8dcf5db7bcdce3515745091edc8ec97bfedcf3c7ec7e801945d9c014bc3e4580a8971b82ec47b0b9f61206e7e9c898ff5fccbfb76aeff2576945641a34216d70458f829cc565bd9a4442ba14137b7c56a99a10350e6e0c4080642414245b5010320020000e1b4221000000000d084a16f82d696c5dec3c3d106a69da9b58b738d9e53dd59766e07c8594d761588991973245dcc63e7345a8c3ecf8a333e4963e6828b4627e63cc27679c1d3069c1697490c3fbb954f82c3cf749939a5c34251413b58799d20783219234bec0b0542414245010174a594809e6239a58b2487a3fde20d7c5f97b7e490bc6a9113c0091739f4071ca66a307206c3144b179bbf779d1132d7c146968a7725f7af577183ca3ac16781dd8594de969acac9d9a11991d056c723b749f74dd209d3fa45008b3cc1dd3e6482c7e80114e0d736220dd657e5b0cdf6ea03ece39b69b25e146499074889a322e38cd9d4802068f2e80535f73a542b8c5a321369992c459003384ff2ba58e5b0afba9b15080642414245b50103e8020000e2b4221000000000626ac55d4dac4114794bd6ef755bf45c9121800c80c10c7eb8df99d9374e0d103aad8f6bac18a25cdefb0baa8967df03b6f1106e93dd7d151d47a26e652de40b85ef76311dc8b988653bbf40e11a7a456342d1cf40de274ad763d64caa81fa07054241424501014e8cf152bdac392bbbfed10615cfce8f8e4e2d230960b9b438177cfe2a3f547fb490beaecc81fc80383a4a3aebba3fe9ceafc8c9e6a9254e71cdee694f30a68e95a4809a3205a4836c480c9629a5678c834563d8af35d7a5d26b87911dcebb0686c7e80136227de4aafd6b718cc489208c4c5124adfbc2d9d0ce7e98779d576a39cd3f19c63070544c25a4e42c4e7ce90ad601c73768d31e5c979449cad6ec352f18ef35080642414245b5010365010000e3b422100000000094a69519ea6b41c70552fa983e420a6c3cc65cbb85ec0a79be611ae91e38d01c6eef5c5b78b0339aeb9193ded9f8a95aa514e77af9d7adeb7dbac6318a6b790b1ef24fc6aebb040debbb10b824fb05bdc1af5db83cadd623d12a4b34ae9ee40d05424142450101a01fa8364ebd7f1ac1a73c07d4aadcd6eec9e383dfccd5f748d0e1e0f55867133d6b32debf70a135043499ae5c12d66faef5f36feb30e67c74d615e575f43d806bbd82a480784d0b2ae727ea9876544fcbac7373bef301cb4d44e45c946474738ac7e8019ba6c9e56995582e71cebc19e00bed62e18381b13b62cf5d9bf42211e3318389067cb35fa02dbe439913d8bf9bd50022882293820a18a52e9cff457cb46b60d2080642414245b50103f0010000e4b42210000000005ed23269bfb9e39dd443474a12ae89f217f4b8a22d1d6bb085d8da624fe4283ae4f6d16ed6ef9e1f2a906d2743521ea3dc03c2273bd65098508abb8fd654af0d1f60e6efaba8128d1cc4ce60ab11e6e1cbe47f551753e0756aa21c661222a906054241424501019052ade32422f93d07a71b70e8a96e542a974dd9fa704013fece02898c552759b223968aedee81b8b1e28acd01f5e57f902cdedcb0a43bd47a7a5c19a92bf885ca845c63e64f865b573632097df857c73b8fdaea47b0b67bf8081e21cd304b8f8ec7e801826ea376f4c8f791230c1c79265486a78b163564d142a732601af3b83c4f46654d8557de7c5b0397ad096f724b5b10e056084b9bc5ce16b40d961820d107ae82080642414245b5010355000000e5b42210000000002aac43092d0370d18b931313dad463e5a7d034006d64bb7b1d7a67d5b6a2d83269dfa001ba72d5ff592b394ab8f151365df441d5e52945a559e7f47c58b248032c0f4e2a39dbb415bd99728d29b479c52d5967316241d57131ec5f207aa1d202054241424501016c299db7d1fbe77a37b7c3ff593f44adfbbc55d0aec7ada656796b62f34705145c95b534119f8ca5b4e5e3f69e1538cc3e2bddf665e9c584cf862ade345f0f83c47b2b4198997ca1b1e8ace9728b1c28c7ffad850c5a521fc5a78fd7e420219392c7e80181d71bd79d5ede214bb81d1a49e8c2e783a623f96748b39199b52caaa8b4642f26744f919958450e79c660f0c233da308bf3a823b93f9c18306f3b09d7583cfa080642414245b50103e1010000e6b4221000000000981d92e9510ab4f277b8721320e4103691745fc39f3a05f1c8cd261fc7a49a32c0d3a310064fac64253cec26889a613ee78097aa897654f1af312b574d989005bbc18417e1d1f1072f23ad7d2c8dc376c7c80a593ebaa59585c5c7d1345c8e0e054241424501013a8b2ecc0595bb76676902b515b3f60a6f36e2033e85877a80dd843d6973ee01df01e03bcc5a7fd89ebb3d5dbdecbd26d1ab3c9e1fdbcd163dfc311855451380c30c4db60f39a88a6737b6bfce5bf20121659019e9bce65c89c938959a0c462c96c7e80186da05ca1a60242d8fc6297f1a9b2796ebd2cc8c0494a8f9f0f2981b238138073725030949269d031f6e1f0557d96d7ca626cc712ee0a5b9700ca3bce029c53a080642414245b5010306030000e7b42210000000005e14a4c8b4e6edfec81f9d03c701c5d735adb6a27401c12965cdb51d75065d364d02d2fba99caa0cd558a58853b3a983465e5eb78dd081006cbdcab56ae7d008d018f56095acf57ea49e48663d9a822614e89fd802f137f4e90aeb3304bde10105424142450101b873f73132a7b7f9b1180060c0fbba119aa56085251de5c63c83fb6b7d34b956f66b4f0557dd6b08b858810f0bc3bdd2836b80eb3624ef06f8d5c0ccab73078b14a7ec511e27b6e7671dc5d2087341a8cf6874f39ff5f3338d18b12c533d6a0a9ac7e801183fbd20d2fba0ec5376d55816ee05cb52383c81408d289fcb5c2675c2c35aa189764c6e2e58e43a14a7a0bf512c58005249da2414ba833d802dff1339063353080642414245b5010148000000e8b4221000000000c86344abe3c15f4b37922cf7392a07e136faea9adfdfb9b5507470101ae0615d25140d371f8b541b28ba007c6f9f2c8601601e5857ecafa47490947844b5850d053141388d543daa8fd71ae034c984aa174c82e77e255cfbd2201fe04330610805424142450101462d19587b6489a4406ee968786ad13661970cf508e99084ee57c88b3cf59b5c44d37ac8debaf51b20459f26cc7a3552e514f4b833664ebfa503a6f480578285dff4d07de18fbb98ad379cb0ed41f89a63fedd363602376d495a30ad923ae14f9ec7e8019dc87231819a50cae707105f9f62c6733612dce5a7826eeb145d8dad068169b332282c33970d830adede87b4af29baf77960a191952031553b95a2fc46eb6d2a080642414245b5010180010000e9b4221000000000a6de337578e70aad82ded01832c1d9007422c049ac3b46c3c4e56081bf43a901c95c94c00b766b7d8d01f517517eaa5dc347d280ed0e95eae198564624da81073910c447199499fdfaefa62076148b9bf7b74e90a2c239fde9c573b0c62ddc0f054241424501019098fb4eff04a4c4966b13b6d07a0e68a9634a27993deaf2242269a7e60109230f1e7c88a889b2aac3a41d12e7f80ca18069481fd2fcf59f2971c93aa582de84b690207f30a17b8f55c0a26b9c8acde1a311f5d7fb5d3df2b5c88167d0a5d362a2c7e8018d30e99faf4e8261b623c2cb49868ac1b3015f46747bfd9d4fa8335d06eae73168401f085f40fa58876cb67f08bd7a7fe9dbe381a68df737b388be337d91d7e8080642414245b50101da010000eab42210000000007e046818ba56b114347e8d4d5ccc06f40c178b4bcbd7dcff4407401e3e095e7617d72bb4a847502e70211f01ae75ae3874f7dbe5e6324d4040b132f42c39d90ae134345d9d4dd0fde58ae8fdb614643dca649fa38f2cbb95a0bd05e58f5d43050542414245010188bc51825a473671cf896a757ab5e47cf484ef1b342ab5408137e497e5b2fc3b15e36896ded7a13fa39563be482aa8908a906afff0edc2a24646df78e75654896a39b0ab1482d462cea7930073e28ac925b737d933d104fdd67d7111207f7dcba6c7e801eb295a938982c8a9310252fcdd4ff0a3970808d2939463b9f07ebb455dfde9fa778c2b5ff4352d8798848e61d9ba35863c384bc927072e8838adb403ec23afd6080642414245b501010a010000ebb4221000000000dc6cffaf62480687b706880896e94999b0ad64ddd907f23f77706573bba2c07bab5e69e39a518f91fd176c109467d9f455ec6b58e20d80ac4313c870e806ad0a1a2b16e4eb057f3e797e946bbf4233b1b573806f7a9d24762242b7fc26a85001054241424501017c38caf864a315c2903107ae658b7cb80bbae9e99bd8f121b86c45639de5e30de4fa7cdf69fea0dff0d543ae1b92d6a4ee7a6432d49961d2d4d8fd0dbbdbaa843bc0d95d23d522183e7863aec73c3321fc88f5c681f31a075f897a557d166ffdaac7e801944141a98ba590a049cf615cf8fb71aa8bf0a90ef1bf9c6943774c28d7230befc8f7914e7aa806ff86fd2ccb5e7f788eba8e076f943ce5fcf01bb2230429ceb0080642414245b501032f020000ecb422100000000004ba44e652aebe28ca14dc19411b0f0d92e02a9dfb5b9bf1ee738505ab999659959cc168e403416c57dcd3fe3061ed93d744c28f6c0f96d4349c78eaf9879205f5c4ad92c19fd86a6283a2ed33ed6761bad1032b6163e734dd748a4bf29756010542414245010112102873c6802dc4bbbcb10aed46fe7eef161a0404fff1c8acc79906299130684006f8db4b6eb831ab4543142e6a2d7328d8b304cbaaab148375ec79422ba38f8bb3485c23ae5dac02406e9edb3f5bdd1bdf07fad2fe5985819219e951a2c9e8aec7e801136167683b35bad9656d0b2c052a01316be7fe06cb42ba8acf81f99ca7e4efdd6aa320bbd1c7e53eaea70beada022c0ba73880085cfcb262db96aaf17833d96d080642414245b50103db000000edb42210000000001a7614af4b47f322830ecf0af86cb9a686294b1485b6a73a4dcec4b592682c05902d43a1976a46dd12efb4ae0fc95e247e0f1e18ec7e286287d19659d88da402e955aa3a25c4dfdbf034f062fb90b619728b338cdf07e7ac7144b88ebc3e620705424142450101d6e02824bcca376f15400bf33f25f7b7c06262b28fa83f80e09758531e99292c213ecc2f584a966fa3620a68e5d82b9f85474f4801727a8759585ecc8045ba8bffbc07d03943799298fd37a0ec54eda9615effe7d76d18c1e3180bddfbd8fedcb2c7e801ce19f0e604fca488cdb8eccd3abb4a91d95929d8f8815b2989e37a672de0f45998857e436eb1877889f737e49d867b723ef449a619dcc578e3311114ad0fed9c080642414245b5010335020000eeb4221000000000c62e034562ec02ff709e4fa089e81c39b4f32b748dfb6087b546f86ea8008354b7c8b9e46aeaa073c907aa425b7f79d887ef0bda718f9061a6f97312ec1ee30e24aa489c5fb0387e0ae058fbabc51d8a88d1ab000bb9a47e6efacb2ac5bfb90c0542414245010130284d6f544326aea964309d4d8d708c2cd74fe3632adc1b4532e92b4f3f99743ace80fdfc34d661a6652c8428ce1f0d8a2a13c254283424bbb21370bef6dc81bfe60d61afd86e205613e8d30e31516e70308c5ec04ddac30c70f13faf772e3ab6c7e8018c87217803b0f847bbbc0b0e88e58ec9fb80b3883b0dd7415adb3ed48c0047edbd17263c6e5a939078fb93488b03e5432f1ae9d607bfb92c708783929c48702d080642414245b5010391000000efb4221000000000dcf2853d6ccfd3d31817d8d2b3f443e726ec174b5051ad67ff1797cabf46b91c61d22da80ea655874bd9a8e148ef8064e20756ff80424278c68b65de187f03076fc38d972df2f0e81990c1566bba77916caa7949ac651d851ddc81db9f997f03054241424501018eb12f2471b43557bbb28acf0734440d26a41da7e5ec3cac23c8750e38f49f210f66749e9ee92227cb17e367b728488a20f23f5b17b9b09f9485b14d13f2f68f377e2dacc552cff52a207cd504afdcd0c00aa857fd364685564dfc5486408c9ebac7e801d01f87f90b8b3a28e95704f1396f1294bd328a1e0862df9d959747f494adebba4ae75a7aec401eebc2153892d3520bb55db38673489a43741c42b1b88fd07b30080642414245b501033c030000f0b422100000000076cd58d18bb35ecc220e1ce2e6a012dcb5c0ec6bbf3dd9adceda5d7cf4dff41bf429fec0e247114ef7865de75466a83d59a7165fcdeba8a7637dfb70f4b4240fdca37f27c91129a73f5989c55ba43eed706648047026d151fdd60eb22996400f054241424501013c0b2abab5c6d9775fa4e7ff47c56932739324cc987e8a7bcd1c04901ad375716517e28ff6c39239f99257368bbfbd5f07a4a1896fd7d24bf4045991b4ac22823008a484dcbfa683448fef58757fab606e4526c9c019b39d28585f6f4de88b2dbec7e8015c6742762ec861f121b16cb6c1369ae8a6c250076f6e0efc2d274428aa21a684522a28a85c4e0b3deb568c6e26f078209169056d2c97721ccdcd61402cefc597080642414245b5010137000000f1b4221000000000628b6b17c6402a3d6e5aadc2c0f5d1fce1ab1f35a11f2e082313311233b9175f7a0dcae5d8e61128bc35d0f19d79260fedfa6f69e98f32a3e6f197be61664c06840fd0aedd18f6212d2b68e91c284515143af7a0466651eef66772714b71930c0542414245010178aafbbec385bccfc77443bd335ba3df8b01c854c8e1d8075b44dacc15e5d469b25cf7987768d715c005febcb849d9ad7deab0e0c0d2eb1381e43997760b978e922bd4222e958a15d0a339dd587d352ffde7988bf1d82dd4a9be8ffd893fdb00c2c7e80186bb61263b7d7206fb00e6c9ed5b09c3c27e96f435da2aa47bb854f926088dca69f75f878c9eb3605f663255b78b0a041e44c59e095548301f54fcbb0dbfe238080642414245b50101ef000000f2b42210000000009ca756c6fe4d3c2814278d8b8db3c37f037fcd14b907e0a4dff5f5c896d382369f739111bd9bc1c9900947db39c7a9191ff6473702190e84c85a51a48ab98602f8038e93f1c1611c0dbace3582157a470825b519d784c9845e619bf5a27a600d054241424501013c6f0cb03586572ba30f528395694b2ca9f617b80c0b3e2fb4c11a11772ca43d0527d17e34c9c2b9b7e10dbfdcb56f5bbf7e19ffa45376ef2fc49e03f2ff7e8e296b633557c8e623f75e8b374d06fce3c4e0f1ed8467f143ee430c2c691ea2bec6c7e801920194078f3180fcffdf9d9d5c5df3d9d0d128f81b6b3a67b2a3eaed98774d2f53333a8ff1a49f94e731a480fef348c805d87465844ab15f3bc1fbe33cb24b72080642414245b50103aa010000f3b4221000000000e832a51324f3773f1cf8e76322902cf415e6d981725b47608defce8fa1277433e678e4971d936e900c9c3276900682a3b711a3b0cb300433493b6c4f4c0530020b5678baf045b12c2033fcb2dd629b1397cae84ac9632dae3480ed553d639703054241424501015c2e8ce778fda3a2fc0b1d060f86f35af19a995a1bb7a713054f2f0c5027c30d0e32e3f8229dbeca4a0e948358b85f7890178e8653d131ba86c2b787bd112e8e6abd61c89051b7fec2f89b6a0e55ce3ad03a18a006bbd0d34b271e17039e6320cac7e801b7064c1e8c92999ccbe365e1f88b33f00e29146ecb4ba734d2e02906645c49749797ab071aa656cddd05e0facf3856e465c2133c71e5f23bfa768e3120caf496080642414245b50101eb020000f4b4221000000000c4483a3341574954d83d0c3fbbef523bdccc80c18df5981e6c4944e3f8dba52dc740508bdcf117c62540253ad8ca5b1b01050e11a96dedda21a7209a45bd7206606dc62cf7eb6af2c8619c793303e855328f180d34e58b8334bfd923366fda0b0542414245010174ce8d57b18ce162659ad7cf9b1a811370969c8c3405dc82472459aeebe11a74a063ab49f30389aea277abde5fbe8a830d7ecbf1735564e7baeb2bfe9756b388bce4affe093c07001fd66158690bc38559bbcd0855ac9bd6f600e8a2976fe223cec7e801adda94f4aead09eac58639016c0c178254820462d28a0cd8aa798c466912a2c9aaf922998c6a4625797d97eddccae39eb75d151107e69d545717759c2fea3830080642414245b50103a1020000f5b42210000000006c6cc745fb5190098bad7d0fe2cd3ed433551f3bd3d3b0688f1485c220c26b00d01ef6cfd4588865f180b5f55705ceb41012f6048058c1c8d6cdf5734daa250cc2d989c7bcaee4f843002e57fb304562870127122ef9b577faa6f99d9f48870905424142450101aab4077370fc9c26888a5476349a8d8d68366a163e8ea72e2a31f0805f783751df2b7f5cbdec64d8306f6477a1bf77ffb9ca1d3a85535d33049d18b993af5c87beef38a4b61dd2448a1f74d6ad1847b44f4ba1de7dbcd9c9bf9fc0598d6bc4f6d2c7e80159c199e0d4ccf341f55f42d7924475151b19b94b0a7927104bacc2332f81d34a9d87bc7bde82140aa59cba6b8ffea6aee5a9a7949f611d6ad90f069598863852080642414245b50103b8020000f6b4221000000000b836ffb1cf9ca2221747e173fbff71838c267aed492e42da2a8a677a06cb8e2247714591ef731ceae6152b6e0e2cf7a8980be5dc770f12dfb13fdb2ace7144037779a626a18c5ffc063fe28a9ff63224003c9b8a7e1362ebf1848eadbfd7fa02054241424501015e8bf1b78afcc92375b99921ae4f2908299f6f5aec6b9caafb208e14253e8358cb0c126dd9e254059b57fc4b969080ce4800d3552bfa4cb5b053fa16c70aad875ad1633080f83a6279e0242900e6cd8250880ad6341087788a2a20117783822ad6c7e801746cbe21f92d2156ade12d82d1b7e12cc9992bede2d3483da667346458a6e8749af7e3c062350315488ca012ffa3e692a82ef9d2f5925a1822166a0a2baa1404080642414245b5010369030000f7b4221000000000ba55c3752a4496df1c7f80aa1982a0a5a7bf4416ffd4a25f58494243d16d0a6ca082b4be55fd1a41d1d5500666f315aa9b0c6f398d784b71e9e57d5bcb93630254b91c9e44d8111d5ae7e30a5bde4df01444a3bd7d6fcf4edc0418bd7ef939050542414245010150ec9b9ddde459fcede7450dcf1ae6d1bea042eab1e6986ab8520c444ac1f421129aefe97e5ad8d748ca5f04b6fc53b265178b1a50ebae280fcdfeeda4768a88435ef2e494d0756016150b51145a882a552c4cd227cc0327a1941af1713cb625dac7e8018a2f8dec572e7f040d7eba5b716a9d00f3111c7047c82141835e6abe01697b874ad7086d04676c16ef4a946183e25d79de36c3ee750c03878e791f8461f8d0f1080642414245b501037a010000f8b42210000000005aa99c30183110621c41706774ea3f65dce0b5cfaa468f51146d0b9ba9067326d8053c1bb47df40cd7c8925a69f14a05388d0e2ea3eb39ccf57bf64d919ee00c7aef703a8d9a44e3043cdd31ad1a1f3b257e40183b38c92f898ae11b502ed20e054241424501013ebef154802e474ff248fe239d1eb6b5d5dab1790120f26e536809e81ef8ba2eba4505b3e7286c470d602a655230238f676a3fd2676b2dc4f4ebccfde1d40184b8bdcc9aba524f47b076b704136d9fdd54c26422d66d1954f316e28a2f6ebadfdec7e801e21be55fbd1de1e02184c0cbfb4c6207da4c5add3371075f4138bf4ce2987b4ec82e8c8aa7ba061d2db5ed8e6eca8de3de10b7bd85f3d70ddd7238fe80467c79080642414245b50103cb000000f9b4221000000000b498d965b26608fbd62cef263c6fb2060c4f80ba3a57c66cbfdc9c34c4a4240ec6cf667859b21f650446e507beae142c5a06bc843d853acf369e62f55bf3de0239deb276a95cb9f7bdd29ccbeb00c5539d1671416225712e9830faee33478e010542414245010172ef70d19bf6e9cf10ba9320579a7fa1472d5d7665aa1010ddee8fa3eb45e7109342a162915ec0c27dfdcf96ac1546025005b70246a7d01a49efe73413bc578080f2524eea5e140b37c890b16815d27b956b5c4f233f97ee0fead6cbea879f5fe2c7e8017bae27fa14c8b72302b66f2cdef598be5b47d165e5107109841b3e07826233969580355e48a9c0aee9950cc8c1966fbccddb964ffe56eaa1caf24987dcff8aaa080642414245b5010182010000fab4221000000000de12aa24abc0e84ca02725fd28b617a894ccd7cbebae5aca9026dcd3d51dc027169a174bd5b6518cafafcb449648b94f7fb50f2984f8403b7a16b7879b0d6807ffe2600615ba8a875f43c80e25eac65cdc1c6787e3c73f70ba578bab0cef23040542414245010142dbf15ebab8a33c5abf457616a3c45e5ba356f48a025679657fa7a39f931870d2e23fa91c48f13de8f6896a14f9fdbc9424dd1138eaed61dc409610299bf882e57e69fe813421d03bd85a1f8270546678010288b1e7345354e95f765a57a3a6e6c7e801a34e6acf31cfeeed55234d167d1cf3e07616f6a193a9116ed64a751d7e087a1163edfa1888c0332a5f4999a3db35709e7c6bc681c8e3ad5d2d8365c565fd9b51080642414245b50103ae000000fbb4221000000000304b384658ac48b8ef18645110130f83e0f0b17f8b184fba1a450f3ad9c0fd2bfb623ce19f6e9c0361de7677f34ee569b1f6cf94b5d7e587f2422a38ad471201beaa4d0dd3ed8e62d57df41fd2dad5fd04c37df48d8e6362f01e208427afaa050542414245010164cddb01f056a59e651ffcd893a4c56f7d67359e03905b7548b04fbfa8958e52b3301820911f56aefb265693e89e4d9f54659a02f8ac33087f3e8323ece4668f11ad11787613c03edcb309be8c4a0834dbdf7cb223b6bcd249c7fb45d2bceb75eac7e801cd8ee78b1dd5058366642d816089119f07988ce6e4f84f4ab0fde30c1928617942913b1f23ebb60edb5fd9590d799375d1a54d38f17d8ffbd945858a89680425080642414245b501033c000000fcb4221000000000f697ff34ea67fdc4c0f2288c315f0c5159b2fa2c31a93f705a1b2e8e0632c2067e4ac144b88104031341e9eb1606fd1b58ccd5175b71487be92f0796a6de1f0cb360043c09f719c40c7748483a7d36773759c06588afe77b39d7f9705a3f630f054241424501010811b6399082e58ec846237790b6bde4c8b1828fd170250df1fa6fdccbd9b71a25699b594b13a409963ada2280b2aac09a7f259844384f1b17e0601c2e7cdf8895553f416ae9925d5391fa43c7f895795431f2c3b9f5a3995f86a20e82de5d59eec7e8016059739dbb0b120876aea88c0295ae576df98976c0588f5bece6cfbd5c2f830754b26e9cca5848d9b65e8cf2adeccb2ecd358aaea0e0b850b96ae933f3fb665e080642414245b5010329020000fdb422100000000052771f3ae477d3ed9542d0683a0f46a5ff3fc2a07989de6ff6524ddc34158c54ebf5928ac9ba315d2bd33795a69ecf1fd22ac6224f2b2a6b68aa2327fb3ad50f0553afb70a56389237a12a72255901d403752acb8264fabbb970780045acf60a0542414245010110386b6e432536829bef0c5104aa0a8c51a9a02bd435a8c0a6ff371abda61f446e727a32b5acfe37e011740c9edc19c30f913609fd7dcc0a30d9868215a0a58baefa7449224701a1be139a28b79d2ab6a563c4c67e1ee2b853d0f5d976808ad0f2c7e8016b9fab31f289dc64f0bc780939e56520e74be28c4f24669153d32f74fad3d8902f6c0c3e8fe4cb2e0c7fdb098813f0d655a9e9af76eacc1b5c73fb00ac5bff3b080642414245b5010164000000feb4221000000000903b4f57c09336e13084742d717e13551b6e5c953f644ae02b05dd107a7f0e5072a47d296ee233aca93902b480d4651745a3b591e0cc5df4c1cbeb43a4fd4c0944399d13599ade059d49532f6e4e582a0ba949a6724b5583203524e5e2716b00054241424501015c1899f5da9649faee9c76e2428b799743b47eebd3fae24b23254a17b60059645403469e3d322275798410f94740afe401029e18003e3d19ad56ddaaee81ac86bf437fd9fc5d8a6d6e47c7c7ff5f3cb4dd08ff8307fa257467fd8c2e1fda2842f6c7e8012262bd4695bc62ff32149f6aa7b1cfc77d43359497aebf9cc254e148b4db4af2748bebb24570b5b93f09fde6b51797868aa6584573013667ce2f2c182cf4d263080642414245b501018b010000ffb4221000000000043d60e69eeefefb65f9c4beb8548c8f265d2657aa6c79f11df1f4b7100bad461c7cbcda36b8959b3049924d2ada7dfe544587e66a868804071ea4f81302690b071d23c4a2f8994dbb8fe9d01c27e3260771c6931182a69a5920b6825c8e790f054241424501019c688dd117f436051d26ffcfa45729edc2e41009a4198ce3fac6bb46ad05c470add04cd590b62db2f24aef483e7d9db91ca7fe842b550cbaeae2d61d0ef1588439d5470c6c09c78cf3e44144bff1a172648e5f36463dbbdd00e0ed15f4f9b779fac7e8018953f840ce641f0211256a595c77f04f3fe5d89c7266edc98aa5313f7255e2ed98711d241c08a35816fe2fafa9f488eb64bee910bb2905dde7ae2acd7739bce5080642414245b501036301000000b52210000000000cd4c0f863db4fba536920cc704c8ee592915704e2b4c96af9d954f1192d7d35ccd1ac0cb37937b96d361b2c7a891ebfb494cc58ee0a55958a055cb2216a980d6c3f032ee22df34cef489eb037dd131621e242e8267b4b38048a4e6db13040050542414245010114dc0a1374674ca48f06a51e507306f7e5de98bc0abb4d7890e2ab0fd1ee4c10f681a64293275e555c8125f24ea095d6be93a919bd4ea73c66bcd47b05437b811d1302305a413d1d04e71d83d018f3b6ca74666229f79bb341e850021c6868f2fec7e801de12a7e48340cb9085bdc02ffdae8d587580d1bf0b4e163d66a9f50f685466fc146de2e556723de1233541148e28b9e9aad2bc904983218c2ab93c83616d69ed080642414245b501016002000001b522100000000072439b38efdd06224326cc344d3ebf36a1447cc337fd6dcce4865aa85837cf51e5291bef3a22c3dcf86cac4edf926f10f34aeaa43d383602d98e3bd174e6a00a2dd208ce28e5c8f2e38e9eb4590cc654e594e6359d189b146f0a6bb1a5c1970505424142450101e672bb16619c49aeef3c99b831751a90a63e116633ff6715d65d3cbfa0b27122ce7d0b5faba9d649c12de8a8555f4c834e98d3eab6cd08d503c5b66cee6517865bf91263e4beb89ad0f92f3d26469b0c59b68fb1635ea0f4aa668e2ae00a9c5402c8e8012fad1ab4d9cac1eaacc9eabbfc67f1e8573d25f4457f3a886168ac6c929c6e6a6dfbd0ff1026a401770324f41ad8219d4d03f8a48938add92b5a0d892ee119aa080642414245b501033300000002b522100000000058725d928ade79d8a2960aee3d35fe9903a3888c3acb5b7695499bdced2de40da986a305b6e148bf6b9c3e62427953f2fc4284096f377f33845449c01be81003565185d8d117d30c489619c4fffaa2bc114451e571714c56c65fd8d73f50cf060542414245010168e166255e743a0b643ba275093535917ec4887ae4b2e6aa2fd45cf02670956d4723cf87d776fc8d4783642ff1c9d5bf462dc15f01f5c8213a7a4c82b9608e82bc4b2297d006b23aa2fd87e8ffdd42c653e55b4007e7f863d1be9efc69ea860906c8e801e91541d555fb5dff9eaf3a35ac94f7c866489ad2dec08f80a3dec307352eb73781d5505905c0514570868aec4431b89c087e32dd7266c6a81d253f1e6bf313ad080642414245b501015100000003b5221000000000aae9c7b381b6b024bd9c25a8fe2ae623d2e499283977d2e4b2d1f0addc0ce81ee8243e87476846223a683991d0ef5dec7b06968e794bd53190263c5f1a2b430d78fa1e998df25812bf18989a7de3b9e01382234f26075423c3393b9329cb710805424142450101c811645d5c7a75b2702d7bbcdabc36bdfc7c64606d6852fb29456aa73c42b6256264ba1cf147d175c24622581b103f52be2f81fb1220cfbeafb8dd96d23a668fb03ee5dc5e664ee5ea30854a2f7cb1152d64ae901170f5c49699157669a14b760ac8e80190e8cbb2982a583c0690e18fb967bc367f6430c7871e27d4441ce0cb4e7d2810463f7cd3123a92adc60c737560aed7c2fbf27deadc9a1e670b160926e3ceb4cc080642414245b50101c801000004b52210000000005001ebda58d40cabec73b09d5e4f3bc5e88deaac4600cd22c8355b0760fea921d03e1de6a2b8066f05d915183a90f0532e75e6eaeb11ef836742de531ee22e0563c47c3b0eb93d386574f1df7c120061c0ceebc7488f2a1074dda4911c6de80805424142450101321e4a59140112fbda3840ac6f151b19b69dace4ccced4ea9d5bd6bdaa8b816347b98192b8e15b6881da1a1984f9163f598eee3c62b32a39889f752677b1cb8541cb371a5be848cb339f99654c90b0433564f9ac9576a91e99f47fe53c80c2e50ec8e801e6761f8fddd307c5066c00820925649139d4a321566856102fa6bc3d09d8fe4f803f7fb4e6b1739432c22448d1e89a71ff20ae0c0118f34f4c7be5bf7ad92e47080642414245b501032901000005b52210000000001214c7ac1877ba0b4543e76a0892a88ca5380e2ea4169ee266a43c761c705d2d3d1f81136077b378f1df65bcd9b35af7d2d67378f74dc43f9da513cd615eca049ebecbdbe760ba00302a21d79fd231ce024e57c6c4a88aa1f7cead8686de290f0542414245010130956e6dd11aa64a8ed407b378e936c6415feb97189ca53d86ab20184810e62a4cbdb69e2d577291d1431d099c8ed24b1517ab57e93923168e63b8b092aa2e8b9f270f0c7b2535ebd5a32f40678d36051e641d73450779f94fd1d4c1e7007f6312c8e8010a373bca387091b2e1992371d42c65c89601f783ce024c669abccdd10eb594f17edb4d8bc8170bed7f89fdf4f1247fa43bec78229f3a0a4cd1f5ae06aaa6a0b1080642414245b50103af01000006b522100000000048cda136cb8c5d88234bdba41721bf9e64921aa3429c0af664e02318a265d033f52b3cf13da2a65d8589490350a74f44b2898be721d1c8bec5a1aa0ab899730bde6f17692ef87f71fde1ab78c19ea19d66d1b322b73b946cc705fcb64285920c05424142450101948b9dafa18b6d917e2d1bdb563db25a47767c2389569fc7e0dad3a28f452c6ab134177198a5300bfd0b3b7ec058dc52217c06c564e5da003a2d6991a0b3e284123702e8707a351599c2877e19ba45e12a68b17c467e18c95cd4a6ac1217edea16c8e801ac9d9456a0e46d3a3fb331b5c3adbb08afa345eb80b3ed2353102e5a29647ce91a63f81ebdc86d1aadffc42e4c2d4468dd844d52e8d372e6406e990ac5aeb21e080642414245b501036502000007b5221000000000242c733df4dfc402fccde738ebc5c828d92db6a433309075c540e63f19e971274629e9621f03c2da96076bdb60eae2594495c3c691173b3b46a2a247706f900e8a019ebb4aa6582caab0aef72c4f2e17b27743dbea081dbd8eb5cfc25689880805424142450101a6fcdb0545bf6076bae0c9f28bd7e868fb4cf61810ced48060d9837d324ae72758996951c4b179c1181e56bb2022ab5409e0e9847b332787606c1bf947aeb4880782d2db6e5fd5a76120956caf0bcc97bfe8a5fa5c217f288760ad3df9bf1fde1ac8e801b21d4e0688958e3f5569f34abe7bb7164547ed3b22b4235e3e4a17ad0a9d3ac5f0c36dcd86462beb55442797bbdf973e9263c7dcb85f6e36fc416c866575a875080642414245b501037501000008b52210000000007ae930fa193ad1c1bab06d73382600f5dfbde96da5332576c4e6d9a94bbb7e4ef8b365623617a630bc5ba7f99a2f3b24aa80618de1a2059e8bcbc8d14e502b076004b4537b3e308d5ec9c0841eb0af36080f9b491532942b1ecb2fdbbbbadb02054241424501014e2f108ef70b1f5670867153bb2703c581896014270c32091467216767e44a77a18d454561b37abd75eaf7eab43ab4aa5e958688910d94989d1d15994945858dfd10d7583a004280e15e0c0818c703d3bcfe9e2cd998017ec5933f54577de5761ec8e801dc25033f2b8f093dc85927dffcaa6233082445ef7fab7bbc1120900a1857b874383b1f3e0ec9b5a3c0f5a0acbcfe4e3603eb7b3b849ad9dbf61a183a0619dfa1080642414245b501036d02000009b5221000000000ee39bfa70d0e5d12cc1bb9744494d4c1de1ecce2b75a68555692093de18f4b64c10dede82c31d7b3581de585e8e439d3cf968ab8a007f064681a767353b01a02425df5cbadd048b108dc5fccc2112dfc360f88f62328a1dfae32dc593a97ff0205424142450101f642d2ad90cfb219845631233443fa059a86fd0a727fb6bd9e23c02085a28835dc65d5295bc565c18b805c94f4ca944fde15e1042034ffab7fc045c76d9ec18b0e66ef60cf8a95d1a17c823d48ab6906ce672d292217b116071971e1aeb2699822c8e8014d24b83c4b642bc3f75a2ab20cb2e922adb8e707620715a1408456f64282387149c4ba080d102fea28e29c5e70eec85a59701b1a14c71b94ad8b5ce42ddf3f2a080642414245b50103060300000ab522100000000098063628de3de6f5da0aedfde3647aed7e2b7d42529e843c55f3bbff3187b63878dcade85fc09bb1efa6184f97d022fee5cc528b715363c0cfb7a259dd1b5309fc2a94afe74534379324339956a0c2f67c0a2081dfefc300a0beee202fec550e05424142450101a4337a10447fd0fdaec24be0138356387ed0f8a76aae53a479e4db39bff0dc68174c2c16356b5cc4ecda3c713d856f3082aa0465b26bba2f55eca8bf92f12e86c91fec3e701d3f43967b27c48f858ef09b5a7a63bb4654d16504c1310db1af7b26c8e801ac12b6f10cb950a2c58eeece316e02f38664f41d2a3165402d4b69df826e44b75c107f466639ec56cd192555bccac4302dd1871b62d4e7f1dee91ec298690fd8080642414245b50103fa0100000bb52210000000001069c528ab1801f417152158aa0d51bf72bdd2073c8d4ca998622e7f9c75ba106bcdf047ffcf76035f1c26a585b100be4be1bbddc9b11646325604eeedca75099bbe308c07c92c3f92f2a4e28d77d0a32c901a5326367212ad6d0544bd14620c05424142450101920edbea5574e7db2e0588670c582f5f30ff32951b7b630e872925cf750d827d3d76aa1b8dd4a4a0741dec9aaac93f912e4d838520aa4f1580cba34636a0be88edb9e11b9f555597d3f2c85f610d1bb1d816bbe42b77fbae0a19420f8bd968f32ac8e801bea13d5e304e43a373fd9ac1688daffd4aede6c292cf5e50107e450f224f3a51b6dad5ee4080c518c59adfbdd41ad52ef69ab89f72c05d7597eb57611263bc5a080642414245b50103370200000cb52210000000007662b4b3d10246306556898ec34285c4d82ab273026f1a1fe5eebdc507964e6675c24f05a0ff5faecf142178562441fcc1dc696799d3e8a6883f067f8e34a30a1dc2ed472ece5f11430510f71c17e940694eaaee733b32b7e99cb4543b2d2f0d0542414245010186f478aec29a969168edfc2534fd4c7688f247cdfbbf9551e9c601d46dfc6c0b700594787ed2312755657f6cb8332fc5a935c6dea40743303b381a4667100f884ec15011789145988430d1789d645e5737a5c7ad54771d82b726122d5549c3952ec8e80154bc1187025ff80599634bf64cb1d1fc0322fc417c98d2c83393fe25c207c6fb0fdbf59b89039792e8c834c2ed473a525cb61d562c669551b09dea8f20e4d7c3080642414245b501036f0000000db5221000000000969365de5c97486e0fbf9d77859e7a99e3572df12a7af6c1a9fbc722ba7f1f6b8ed29eeeb17517c8c9b29552787c1efbd18a10d2df5f4e850424d8b20cd24e07c8fec284aedf643a27542f737d7edecb0b9d2068ca69551c73028e260a3ae20005424142450101ba9a4b450cbf9fe1dbd8216db69f8444589ebaa277737628a71d4ea8e949580458be881767cff50e7f547bbb97895d2a6a3a9196a4121a6ed2d228b7f8da738042036aa1950a0a8187f529cf03c0b7eabf5786a7b3daf2cf9d6f75e2b0613b0d32c8e8017155e2422e6c36fc8dd5d396bc2c275006d6629d18e73603681018c0ed7aeec430af3cd53ec5f01ae53e9c2279724fc9acb759dac421024a3ff255fbc5d35abe080642414245b50101280300000eb522100000000048b5ea1048285342318512b56bb13587ff46758162b118558ef717c5c53d8a48b28d95b0bf4bb2fa5d7cd1ca2cece89fea15ace1781398f07b5048a7eaeeb90c13286e4f512f057b01113af29af42b21527d857f6300f242a12021889f76620605424142450101723a7a0d7f60cfcd7cd22c0e33302760a5ba1c89a65eb5075a46ae3ca7361c70aa345a46ac44ecb7bfd8f3bd690ac4e9fe28b93a9a85501980385ed74626fd8a3ec536fa1346e6162f4ae7ba5837eb6e98ae02a961e38601228359e844a84dc336c8e801a9f74b7127be155f02e1ded9b2d40316e04c5e63d29b2339f5d08d1fc933257b2ba5344164828e4ff1c51ea551bcc229027ed136a776a6b91400e1a4793e1853080642414245b50103230200000fb52210000000000222f440298e90b045e8c78ada69dce4693cd9993e0f7551f9c7f1c0cc7f0314df90938ce0cdc2101556532559bcdcb421ac8a0f0871c12eb32d6fc3d09256035d7abffbee21e0d748786e87d64732a68cd9fc2914c53075cc84a5aaea7ca8050542414245010174e4752a7fdcae670bf96db8cba3a0ef5788216193bba44fbbc8fb0bc3c8a45da3b70f228c507929e9cc5123052e57547479653790dc9d3e85dcddf32f6f108abb1cc0cc48c278516500a828dc55a55da00d98987447a67300fb0e6cd15f8b3c3ac8e801308affa93927f05af85f06e6f9594ce16e23424911fe28c95f0ebac2cb00b9ce29ad6d63878110d5c7250725bd7d658da823d39658a0728bc6a25dc5310c4648080642414245b50101b102000010b5221000000000ee626ac06b59d9e4257337d4aa3ac3b61d335008c6857be4fccfebb5317b4b3db4387471dc7173a71f59ea3a1a37070c53fade6ac18a1d39a7e9bede3f3021097286be5ba9e6da5d423deb54c9deaef1be9bac6cab455bba05def8cd2908310b054241424501011ed3720df0864e63d4b3a5544a6dadb5f48d021a3d32fb0faf1a8fca8c29d7313f49f1d1640867ca11b55dd694c644a99fe4cada7c10a6dc0b059753cb02ba87f9a326f7e03c2a7a520af4ad35e69b609db3b91acc0fada1babc6b738c1d3cbc3ec8e801daec46fda0b1241730842af2355f17a9fd400ae2c1c255b3a2901092433c8c6a8478e8760f47ee4e354c1ae5838cef89b31854b44548465d3e17d9c308114ce5080642414245b501030b03000011b5221000000000d2f9edc7a3e2dafcec60f4a5b62848079b1537af275bfa03acdc686a8473376cb298231f452db8feeb988b676ae77d7c844b179bd80f0db2b98fcc3068dbec0f66a1dac4f654436c286855806060aa12bc121b754dc6637cbc9e2b86a4c6800d0542414245010148e8c5988dbe0e1ac01d589b95043ed14fe991267cbb5d2d0a7192a185e8f83f829e47215512d69677d3a633813ae6d487ed4babed57898de14314f84fe5558b8ed422a03470c0b82183c88882ca6e6bc6140da25a62c9cac58fb7980df94b8a42c8e80126ffdbe60b47d41a5626343bc6f40551d7b69b86ec27a3b76e60791a08d1b9ab9c9896e24d605c2b55e7d5590ede56fe322b147d19c4ef36bfa55da31829448f080642414245b501036801000012b5221000000000b4593ff364c36eb6856e90ee83ddedba08179f39d1b3f1a7755637f64f4b1913b188752e057309e5cc33aff27ae62f305f42f9eeac6f75eedba39aba30a3480fe1566d0d39847f2fb59c468fabb62eb1d5f4fabf28a28468d98506c951abc10305424142450101da9281e326c758c647308ecca62ffe85d3c62b311751c02384692e9bf4d34a52ec66c5defe9c139acafde023612cea728465e76ea343acbbafce5a3d2219db8544c9747874a949df7bd166ae6ac16be276646de88421d8b6c4499b63e2c2c78546c8e801ca5b985a904eb7e006ff8bdfc53d978c2ff168ffe7a9abc2d248412f3d62c0baae07f7851d00bee3d05024f7d9b073949b79c576317103d6d7b03bc91f3c2564080642414245b501038201000013b522100000000046f6a99195b71f4a3541876750d24a532e25a531145cb7e23cad7fa0999e361807d19b9bcd0ad034e754449b8fecf64d9046333a8aaa138df18be34bc6a9880a981888dd6bf1ae078a34b6a0724111554fb7c416fd4d20dbd8713e0f62b6910905424142450101dc80b8701a279351b7d03484a8a4a1af10bd35cbca0b9ed987aa6e05754def654a8ffcbfd145be5daac501065d5abe69e23e808e09bd03517459dd777c469c89adb42f6081377192c02a099d53f26b74381deeb3fb05b79fd28893f6a650adba4ac8e8010f9d0ff7d13d147a160f17f33ed11c97585c2498db9e11ca5b1bc0e793862e4c0dd9f5d5a33e48192a553999b376e413afa76e62155dd9fd8c1cf185018ad2c2080642414245b501011b01000014b52210000000004ec40d3a72fdef211e387dfda9f89e9322efa6ac4df9222721f6d6cbb68fb4552db1c78dc60cc929e5b1edd966654048807f533549641c9758aa4bf7abc1460a3de6253a161d54a0ac34e01c6a43d75afa1864af415fe0bfee4057e8593d030c054241424501012ae464344b69d2f4138c43c37a285539382189fdb4d079c85a2e496d265b11577e8738c8da1961d1a638a143c1fd133ea3ace71753c85a676d850a5078126b884c642c5b27ba0293947cd50c843344eb27ba6f6d3939442148d4f5bc7621612e4ec8e8015a0e8cc0a69b111b93d413d2368e94cfbb85ae4bd3ed2c1e5067da4fdafda72c7d2e4dfa80039a2bea20466ef1f9a0c840cf45061254197841a9b224a14e914a080642414245b501035e03000015b52210000000009e4fc8b1d652055c98b259df629ef3295b668de97f885e39345c0962a5aba20b64b42a2c5cdf638d74505140aef01f6d36920be33e8ebe400cad4911d39fb300bf25dbb80fda667bd26ed3e6fc2a257844f1e6a2888eaa1068893c2faf40e90d05424142450101a4d31c3578ede7b07be052745b8e20d87242cf8c19b1be9b6d125439ae4fe2484711693ced773a4392b243ba4f6792ad5a008d0a49f10994a3bf63b98598db8b84930ad55a252b14e6519cdfe89ae2207201a40abe4e7843b91f897c5086ed2252c8e801102a59c24e1dc5275b3bef2ae0c34e595e95fdc770229c171a9ff3c6fa5c1789426215b0b52708dbf4e9d46fe3f8714b9d15eeedd5d4f0f6258c79771debe0c9080642414245b50103eb00000016b5221000000000ccbd1f43d4c35ce87c740d52565a2ac0b7c95008ff032eb3acdc3fc2eebd076a9de629b8978f66372ac73f7e7dff1768116da61e71555982d9c31d51f779d00738386cf5e75877b6b0290f219328f15953285b6c60d9913d9eaadf788752a2020542414245010128ed5d064e2834c7efcd70f0d77ddf6caba2787e1943d9cfcade5bbaaed5e758ea34657ca6dd793c32c28f3f79a9a8d258f8d4c1a5b408320bc129346b3da381e68a9183ca7ad308ce4d6b47a6e68e5c1de8aa3b94a9140cefcb44569489905056c8e8010ebe8362e0ee2a2c1aa83c5f1c9287d529938ebccd74bbb0bc4dafc6fcf90200dbb6b6fb4c78cd1f3128997f44ea8dc87ab6c5ab0acafbbb236bbce405a2d023080642414245b501038502000017b5221000000000b2f16319008675b78273efd79b8b82bd161b8e81721bd6f85702b82830ff1c10a54d12feb3c93016c99d007c1b5319fb6cab5b71b1194b8f7ce693da12c5fe06d18ddcf01c8ed02dcf5e9ac7d23a14384dcc9e4cf447fbfbe5742efd1b5d7208054241424501012096261a584948774ad9bbebbf294058c51613269bcfd9e958998547b5867454cfa5068be154504f4738d531d29a25c83b1d25a105abfddcdb50b00ab452b88c524650f8d785e094d882fd100e677b5a45f2350a1eb0701f30a99ef270f92a8d5ac8e801d26fbb6ad8cc88c852b588a1281eba897b4f7a4ff7d62e96b4a45385af13ecd3a3854f97099b476f9ae10b0117d0def8051713e6e57b197010c826e06a5f6d9c080642414245b501036f01000018b5221000000000a0c780d2d8ed374c6da243d84e6204af445209c8b71fd652a4b88c94e6a256196a6bf5e13d7e27cf77dd74320c1e7ef0b12d18b856132cb4e97b7ddaa360a208ee7f918d189b2acbbcf16ddb151ff2770fbeef94dc3fd3315e0073c8e67b810f05424142450101745d1e5a6ec6136a9e72403a57a3b10d34b1ab5d5e9876eea6de6175825bcd4e936f3abc856748554e83c49255da352faf89fd15671f922dddbdfff2c1411b86d94c1359e5efac3a00994dd88339b4279f68d4220925a09c7c8b66b36ddc024f5ec8e8012f6f17c6e737ffa4a923156cb057432967cf9a9d8f78729f54f85efd1835c363164fe591bcd821999e6f5e85fc9c8a75631db242a075e4df0f45d570e8f4e26e080642414245b50103ef02000019b5221000000000c69757a557448acdf69060f2ce90d8dab582a622242a0636d302a4804ff2aa681c06b5fb98bbafab19585c434b98703e8c500a965f1a405eca7552e78ac61f0314373071271d7ce3da82282eb860f4d9803b0f77e5414b8f078d3a2787d0340505424142450101a23eaccb5cc7279a0eb80c1e29da6740a0e1857b31eb5df8ab4cb9a98c0192747ac7aa165c0a24f7f07e37cb090d04643051adda093b4dd41378f13469ef7e894104403ae859852bfcda5d7a481a0b37d090e01fa7770c2e28f7579d34b8cc3662c8e80193f1fad0666417644bd7ee6ed689e27f173928ebcad688ec0e15dc7e2b5375b0aa5bfba2b199b7b526013bf88f159e8baf8ad2e085bb8370c7e5ccb59b0c891f080642414245b50101430300001ab5221000000000cabe567bfd63866dcb8105d764ea773cad7330653ef5d9cffdcd0a216453a90f2f473506374d30fec3f28767d35ac91a0962dec2756dd24797470a6a58f1780326b65ea3a941ffc9f27254349e72acb0b1b853c8d04979cad9381f6cbe44b90f054241424501014e37caea365d418b1ea5742a14690842acdfa64125a439de3ef9577f3933510decb4e8b1678e5510f8c96be67bb1070e1c117b593d53448feafe94a826cc828228b1e85e4ee6b07cae63a9deec72379b3c01ba986cf0db7f8be5bb1df9d6604566c8e801830c4a10263e8f5e576b81b404db07914465f6d4fd83384a9e8c6e263772278132eed38b6151cd979befec53c2aeb775d3e99f5e6a122f7acb539349ec2221d9080642414245b501037c0000001bb52210000000009a027fe49cd5a5cbb6bb37bf0c891dab74b122c4873ced790c7ead2067e3f5678ef43d814b7aaf101d427134b848a8cb63cd353ae97f545f544a13a19021350df41355888fbb3d15b3efcd25152b53d4948dfdc406c695deb8dd49d7f97a470c054241424501018275ef16df1806a1a95976796d77b8accba645312844554d5905519c3803075696735f3ab6266f65895df460703615d537b893f52ba7b5f148086b030b3df480e31709197918420c610d8baed993adaca1d5cc1f04d5d98de16dee47570390cb6ac8e801c1824fe3b81a722db2e314fad3d10d97615a3551a877fa4d16a3e5bef4d44b114c72c2cc25019acde768876f1b81577f4bd22169bc8e776804a6eed77dacb29f080642414245b50103500100001cb5221000000000c8695fade91bfa6241023d3402890f54902a913e537f441f8884a08f1d510256a36fb018071f163e19f6a8eb0fadddd0202685870db0f9c4378fee5eefd84a04b47716d98174e288d955f2e86a83baaa2a7051656a9962648814fe10bd5124060542414245010168be45f407b642e1a34960195302cf9604a28ce1b6643c07e0737f8151382c6504796325c250adeb8d08ea68351dfc58a8e081cb8af3d653e00d29bc7d981c84f511ed0c4b74584148b73dd0b225c13215f3199596a9978fdd7e932f3e4de4546ec8e80138fadca6c2d57c6e11a972c03093279146686997286453e68ed27fbfa63851e455d0497742925829eeaf653b31f4f9916b6d7d82aaa7688f14c4195c109de8a4080642414245b50103cc0200001db5221000000000c03e594fc0e05b92b6fb4562b4219e04494d9f8af8384079ea6f3802f7a14633c9b65054a674a260694d696db67fef565dac9c4ed5ad28228a32cc2752a9d3036d151d18dd2c707fabc17b1d85173fc3337c5858e6b6eae93afa632e7617490a05424142450101640942cc47bf93b442c82a56f3f50740a5f53c765292a004dffbc51a369ca90f46bc0cd4611ea6a49682207c3280fb16f6f3eb5a3c81e73262fea90bebd2fb81e15061bbc464b0556ad2b0c9c91368877a740fac404623ddd9aaea25838a2a1a72c8e801f75d3489162e2e9547337141de6618ce354acfb2f15bc3bdf61505459270744b2bcccf92c80849c164fff0434fd275196487e62f578bc22fc8f4407029007f41080642414245b50103530300001eb5221000000000246b88f6cc387c77d13bde47661e978e232a2737bb402d496b577b5369b1804f84c67b48f437105e38a303a5d3aa4ce2626cec5d119661683d09fb3d508435068c8b59e59728a84390a16789308e0c890923efab0e2735d35e8043293529a00805424142450101fe28767cde0cdfa93bd35e476b8fedd73e7d0789745392999b2d12e14b46496329616c0bf8b98acea3191be4cacb2479ea677aa57239ee8973b0b58da4bb59828dba1a8aa0de306e3e27d01c47d7cd469b7ace5d76a5397cf396ae5c32ca8eea76c8e8013d13518ce62249c5664d0b924c83f9fa452a93ec8c88db375d9f7f7a5c4d0dc484a62638a78dc9371aa1081b76baa1ddffba389287f3f264c3ab327ea08559d6080642414245b50103d90000001fb522100000000012bcb7a72adfaf1a14429dddca6667d302e983e19aeca0029a20e78281e4cf1dbede22251127b4aa84f7514f4f73bd0eabd4c05183f8e6d7a798d5e17fb31d02b6a199f0884b768512b4775536c6100de033ca8c90ff99e6969bb34a46124a07054241424501015e9ac7d81fb188c670080ebe865aa8c2eb5e7672ca303320f289ed36282c772db5cd80792716c86aa9dcb9be1d032172a0e05dc1c0a7791adc909889985b7085989e759e7de35dc85f1446e168f76854839fbac6ed769eb0a754cdb41cc7b44a7ac8e801c239cbb10f5b365c65d712d63436c9e320d52052ae6a318fced63c128f1c4937e6d4959c9dcc42332e1ee1d32198020cf629f1b5a393bb9d8ddbfb1ac0b491c2080642414245b501016c02000020b5221000000000829bcbf451be4f6274c8eab894a806e82e474a6d068021d966ce6abc658c6c244a11d6f18e4f5efbe2e217c04cb94ae0e7a5e36cf97c423dd8f67e3e9bad5f009c220c92f35332a7eb39e1f958843cf0a3ec134ce5ba5c4143511694b669570d054241424501011caf29213496c5b821457d0309f45ae86ce95279611fc35999ceeddf7c3f3524e82ec837c0518365be258bf05c163bc1d070b94253888c7437483790d49ab085eadff87f1912f6c622e8b1f10b710333dd398242c1f1a8ef8c2d31066b713c5a7ec8e801b23259a12f9087859c55e97eaf664459b3d5719ec3ef5c5912b83880deea2edbbb78d3ece5489920c256046cf994a96a4c0ed76fa985c1e2c25dc837139b0ad5080642414245b501032902000021b522100000000018f8261f1128a6fa8c05aa95e3d43d74937173e82235140d6653c5dfec6d9f17aae45dff5a5d5ea5cffc2645d67f7b9dff53a39b1a17872776d485ff258a400ed1745a7b412c054e47d7c7fa4d93595935ba42831a4dfb9cd51f25320ea16607054241424501018849433b7bf0118a73a4f543e3a92574eb80937e96d04bec3aea1ab368a8cf754274fbac1bff2a91ead2d70615e8b8d37ac54c9ecf1c2282f10aed826229e588fd85c78e4e1814211bd9a4f1abcf704e9b091e5e9eb71b6eb4219ce419108d4982c8e801fe95dfe620b2639ea7a241c9a6744b18245c002fcc2c93a4a28af7e035d03d36b7b940bf54cd6fff7bd2e82f76c95e1fd2291e9bdbdd3537a4bd4aa01a5de951080642414245b501017202000022b5221000000000227ace1c2a7b10a702483542ae25bfe9fbe4bd99a19be0618a29bebddd89cb1b636ed5699c24750e91721832c65629b55d6837ccdf9d0a912f8f40d8d387460bbe94afba61d60160fc92d91ff5bc602d75ccb02fa53f373e4de06cc8e069100f05424142450101ce1dfb1c91b7194c6cbfa0ac65d91bac4c91e344cb8000158998ee9896939625b174666c50a72c6369d81b7fa41d6a162af4c2f899257fef3454052734096f837425af5a0905288631b29a8926cf111e02c6a36585041e02f418ec4054ead23e86c8e801c1ec965ef76d3e2233fe25fdb59f57d5aabea806548cb52bd15b192bb232bd7c485630e09b6bbf79f4a5f36e94de5ced03ddc3722a5a727457e082941cf646f4080642414245b501036601000023b5221000000000ee617aceee0924065298d086730b5bc1f1f4d41050d96dd6135cbc32c43b49108bd75e55849b6709cbc8a19b34344f2d3dcc3a3fa9cfd1735664da58984df9079fb329469206b520b3dec053d5efe01b7a5f7f551ad30625f1bab6ce8585ed0f05424142450101e0a1cf19ef154a290605fa023d6ed589d12fdac56938a6e9c03296b8f0600827bb7f5b97d897a62700ca8d9109e4afd3557537f3a4a78a3dc967b1662a465c83e41a11b2d187e50348cc9015d2f5013393db840dcf5cfad9bcb3c1193e1aa4f98ac8e801003b884589927ee89cec2c17470728085cf8c283a81eafc123e0700f6d25e4f58ef32b7ab329c34d870cd8204423cdaf579c6723c1453fbc164261742c072fe4080642414245b501035302000024b52210000000003c230670d44e433c6b08c0955f28ecdb4445d91adfcf3642d9c4a78daf51a70811a60a94a13137f474eb73d2835b1e4d8d2d0ae96e8a3c1bca74d98292fc5e01a9e55e80e5969f1a42134f3c7cdf95ec1fea03eb1bdbf81054930eea0af3250a0542414245010142c0c4c679303349cab63af9217276f45608534636710d2237e99ef380ff90382724136b8b3037ab17cd82b6b08fff3717a7356c72aaa56e11718cb0b3dd2686d647f466a4dffc45e4b5f05089295adbe754dd0ded2d8b9ca29e20ee70cd46c78ec8e801a30e40b53b87270187ffa2d0f7fe2331b1a01ce2ddbcecc8e0c2f9bb2e8ea1972b4d6441218f4a837debe354a7204d73d3b949ede7a4fd1de2dc4f4184e51ac0080642414245b501017303000025b5221000000000844320f5561de46b5b997f4ccbffe4461409c1db45a8f22c9de13d10568c961a11caa43a0921c80ccef834ed5e1ebdbb1dab3e1f37fdc6a0d4e414bd54d0b10a5faea06fe96c3a0deef5471ab6b49957e048b955d17310686ad3f3f08bc3700305424142450101e2bbf5df877226858d40970ca9a2a8e00fbeefcff168dfbbd5ec0e6ca832ef43497636b37f360ce826de4252d522a349a469287a762fdba3b934d5828c93d085ec98c1849f4b86b69b41600154e77f0a6712a74cee77176dc60eaadfdc4db1e292c8e801e5880277f3c4d2cdb6e62f34c7258d1c83450243ab5ec562ef92c3c4ab447c37951eb42e4e2967d0f55786a1223630b216451687ae26ee2bf9ad93ebe5e97dfd080642414245b501031e01000026b52210000000001ef0774f1bcaffd952e471c19df207b52502494852ea763afbf3a269908d360046c34cc30bbca88b111d6077126025bc2a2f6ebc3d3ea8384925f307e8420e02233f4038d8dad843dee135e8d6e06a0f83b31a61c5e0853fba188a02be85d807054241424501011c5f47a75d555e016ff9e34b4fad98319133f1c95145f5515bc752f2ec9ad720a0234761ece289e9152e85a47579313fb43f0070d07ca09fff389cd308e5aa85a8b78c75a53bcb9fdbeb7f59b987fae58249f2ecf0e3f2a3bbf1c4e9a6edd14496c8e801d4f0ee0299378e01b1f8b4b088a58814e89ed72999dcba2831e5557644be668942482abe709272ebb0f28bc55fbedac65baa4839fb8a757fca8e65062bd7b60c080642414245b50103d302000027b5221000000000a883f4c45837bc50275202897943e1a8284a4008930981c2713620c8cca7b46cf708eea37e986e9cee51dbc019b3439bc715d509120eba542accc9a99927e400eea68adab313326ee923f1be759ae13e559484166ceafc52c5790203309ad40005424142450101e6bd2118028bc9617668e3f81f939b7e480702bdc959dbf61b08cc53dbe37853f2cd237b42c04fa34f71b322a832c1fe59413511805531a7e2f8a135099f5885a163ef5867f3584c21cb6fb2735b323e70550847a7c2417c5e55243b3911180e9ac8e80142a6aed799c85a2d0c68763b87add651404d0d27f98938144c3600a07112c3b86819de54360ff17c72152a25a79ec88b0f38cc7d9fbd2f0ca9ab139cf01a4d62080642414245b501032e00000028b52210000000007acbfe3c18b32a4e2bce321a8ab6d91b26497dcdef5188d27f2cfd40ab7651658644b7e4226cebd3bdb396b6c1aa3638d5e9c43f8e6d203552b4e9159bb675065685d2fa3c46ed0ce10db185cfed49e878af0b39f1c829d6957ce6986121ec0c05424142450101a08055993ca328c1abe2cd5c279a4dfbed9170542a9699a82da6c5d68e525f38439583b578e0612f3db3c9fd55d303746d75f9f1ff478c8f84ff6d6a0418b98dd18dfcfc857013b3310b9b3d721616668e0055159288a73c34dc6342ef3ab3569ec8e8011695aaac8ba8ab7a7cc7ca4c130518dc3c3b155ac7af48f92c475b27805591b63aa162c060005d7c4f21ce06ceaa5a3a30151268ba1ab812d0d8a83c21b6bf6c080642414245b501034b02000029b5221000000000cc856ddc4e07bc53f813310c835b68352cdb019876d603da0da3c7af830d02027de635d962b32583128db60dae03e29cfbfb6266c8bdba58cd96e0bf820bf90d2af281b8dbb7ad67a85a81b755428a3cc8afbefda42868167069d8325807a10a054241424501015a42dc28a3cc663775515bfce7938dc2e93ce75efec7d47b059ed2500f51ae11b9a8ff35fcb39b398675bee36629830ee67997f34d9bf3e2ecc4af53105ef38eb0e0623d9e10e394de8fcf3a593f56a1fc48d268f9308b004ea6ed3e32f87371a2c8e801bd4c3a9232152fee5678853970b19bb83b6431f0a033707107f2d9eb7c565e1e1e9ad2ec171a10e361786c53e618dd995a12385d45924135870b53ffbc051941080642414245b50103c30100002ab52210000000005cc83a8677207561d2b05d5c6b83da02aa4236aef6a30df3f1c2c0bc600bcf230305dcf2482ce50be451c4111a1c5c13a8ea7adf4c7b1200fcc2d047efae16082a3e1e0df03c07e978b439db8c78261f392ff2b45a8688f989dc75c68a338d0905424142450101babdac5304331d650846bc0a1cfcc9046a758a44a44df6e6f7c073ad35109306f33ff672a04161335f9d3a54f91ee76434215e52b49f318575aa958084607086f2f81ad011917441999207a25c2b458e533134a0ca67dac5b55be7fc14fdc8c0a6c8e801cc7e8133749f613249df2a72abeec6ffb4a2678727668f53bd895201900540823fb54ecd155f42ce511ade8380c870d994b0742eb2aa5ee3412ec9848042454e080642414245b501031b0300002cb5221000000000427754e48226f73ca83a484b7c2a8508c0bc47a5c7f50db5f0b6e2c8f0497c37b3f72f99a5182caf06f7a0c5eaa0e762adba634ace977b91d86252049e2ba405f9629d2f122d3d3bd8b7f42dc0cfcea040f80e6a73576c1d0bb7ab5e31a03e0e0542414245010192a2d6cba12376c03810df5d09d2e16b9e01e1a14aed7204b1c378e55bca426976800e0922d277771e1b6d23a7c91c2c63ecc6dac294b49dfccd89ec2ff3548b231fa27c4261847cdd8a3d31f7b02f50d996bd82a31c1df928351a094826b46baac8e80101c72c39ccd8ebf793d4ec338e05ff4fd48110e7d0df466c2e8cb21a30880440d937bb44c40a59edd3ed6921acf54a6822e21a6cc6ebcf591326c06d252f4265080642414245b50101390300002db5221000000000dc7f3a3d5bf353417d07469c324790970e48cf956572b3cb63597cc80d5e027d38c09e9cd99e445a690348225f960967f50574472bf0fbb98312d57418218801e868e23f084af136de90d6b4a5d09bd56e9c9eb9185d177ef5cf9fe8dc09cf020542414245010150b0de635d3df71bbe43a4a78101cef765648b8e7a3884e1db560528978f560c8c026b17e26cba5fe6da66ddb48fd22bee11d4cfeff104581c8ff1e0a725f68a7da0ae0ace30ebaddcbcb1370bf4810e22d16e03ab5ad4ddde228001d3405a2caec8e8017d6b6e3bccb5d1841bfbe49b63506e8b8ceb5ffb1e4b22e13067713f087d209d22367948582a46604fcff9335fc222a9e59deb355f379e20c4fb27914d49003d080642414245b50103d40200002eb522100000000028000d572a0f45c1b8615c69faa12ab449bf4afcbe59b0a825da9bb69cdc392dd59f2922e2e1cf95353911a631064b88b28abdd659a16e39c973538903a70e09a1fe5223915ffa5088be74c0654f56ad2fe8296e9805f96e6c2195dc2c49f30405424142450101ecfe4d5a6802646fcdf2227ef2ccd48f9bfa0dc3196c395e2b461e9106018b50562575dd50df9f7f2632f0a1bef88f1001437cbf735d115fea0628868e2f5e8fcefa2a0c39ef0b8d4ea166f91e08f25782c79ef38d5b21f012e6db3f9e371be1b2c8e801c2320cb217ae88211d892eb133e5c6e0fc2f20440e6d0290dad9ba8fd3f930b7dd6fc057ff8242b6b60fcb537192e4e216d8879b7099226c8ef423efcbc1ff8b080642414245b50101350200002fb5221000000000c61ddab2d05e06d7b80ea65fb7b263eede7ce828b7fe1f63e8e743b110ff85726212253e5bc9466264eb024da3030f610269411ed353e15eb00abad2bb42930ca4ef9353ad1c3dd4549ee941f8811ae3f27f8496fa60468b1a3475c3d5b96d0f05424142450101ce2d96cfb0a657ad6be14f0c14dd41432f5a835e4225031d9890ef04a86a7e6ac93e3a570884ff6297254d2cc95395ec1bde0e3639bbf030a19728838ea79d83afd01e99ba8b5786fa896dab24cc246004512fe20518124174accdeb7c43a42bb6c8e8010a78a7d8d0096b498aa2530fc9851317dcec0754c03bfae455b819f7c75c13b946fadd76973ce43a5332af8b1bfe58bfa2ef00dcec2b8159a567fcb9b65d40c9080642414245b50101ed02000030b5221000000000a2aa968ee2f9910a569c88f497161b3ecd154ab88658b507f9f0040b0e129c2ee339ed1430659025758784f2d66380ac376c18c0ca07c09af2e5cf54a8c4800e95697d1f59bc496f720b97b9d9a77fcf0057891d668cae81b89e8f828edaa00d0542414245010102f02ab5a2592387a494a1c4c5baf30085b7170bfd51ff66c444a18f4df6a7587b23b766238eed5b8a518b1fe2dde20acb2cc678a3babbc14761138f6a6bdf81e7c628862192b1d2b401feef8630feafd12a5664bc4c1c0da2a1ea425b1af486bac8e801959c6c5b404776e4c96c280c15ed3e07db6bdfe30fa9f9c60fb128cbde4f623403537335bfff7819a4dc896537a5db1ef4a44fbe0e35206eadc18d9164e4a854080642414245b50103b601000031b5221000000000a491d40fd957b2d040dc8b160e779926746949f669f6dadf6a3ec3b33999cd13f73ef69f2b7687f2d69840b63f82a2b26e1e8786fded0e65f07778f545c53c09e835a0dec082b8e313290c168724ccc969b11b36a9d9ab879ce0a8d04e76980905424142450101225b81102be9b5fdc5864a81581af4d70d11869dc5616458b194325ebba4ba3d0c4daaf7b80432cbf51e3537456a00d81a68098647218ab3ca794ec9aeb5688c2b31836d1ebbd9cc1bb50091a827e0c3e732897d74d177154cdf49b761b17bb2bec8e801a9f69137f076d1b9ceb559123aeb6cd9706dc9699649a95235ed9803aaafabad84d90394298031aa689636134f6494dda7626d7073855455086fc973c425b3a5080642414245b50103b002000032b5221000000000a096239ac7e893c59d7ea9542dd5f2187bd9d2a8964b6121c43334c3444cd26aad002e03c3582fe591b808084080c97ce00017e5cd82d4602e50ec7924283201b5091b0b1178058f40835c76510a10f470b0ab9aecc0012cadc2440650c112000542414245010124d83509e693c2b6dba200d2c11ea7fe0a389a5d082e6da7bb1b26ce9b2e560e770438dcbdad4de02fe1132a40c6abe248592d4dbbc34e9209bb127b22377784a90d760a864cf11e0395c1de5cd115efab835ad2195f2f4379cc2ba1cf903c4ac2c8e8017fb9cd87a9d0a4a1df38b7a0c868a2f75b9b0ce0b433f469c7ff87e4169af03b47d6eaaae3260f9470f50a6f2fa2284a5e8017b3977a51f8a28ab777511b25e1080642414245b501030003000033b522100000000086bc873306934bfed726a2216b786ac9336e19f1ce4dd0b59e11ea3984d26c439c9ac7b4fdc13e5f90963b7c0c67dfa4db9d9f170b761fd84d506eb0450a6b03c5b90909ff7fdaee92c41cfcceb97dd3a22e94a706c8fc951f0ac19f7d6d8f0c05424142450101769cb340968bf62499b13ba618175ef6dea76276756ef1155d3c74873069d40d4f237cddea6e4db0a5c2d9d40b9743882c75a2208cb18e37ecf6ad120c05bc811633be33b6f41395757a96055164d375158bc89ffb22bc2546d7975316a80656c6c8e80140013ca8c4969859abc365f1bb043a0f0929d2b78aa8aa9f161c675182f73c0fafb21e4c82f85669d84f0c2dd9a18450302a4debcd1c7ce638476fa4892ad50b080642414245b501033000000034b52210000000001c842d8cae1e5e9be9922caf5d13fd2d183ed4a977fcef3f2467c813b979186ab2186b716f298595b3dbc7016c2e8e1462b88f2c34c5b8684ae918e9d6d3180b063f4a512cf0d2ecdad9b558ac6ebb23f07c6d5bf84c23046377eb5ad0b8290f05424142450101baa623b6842e764727f109f33ba4c311f37f7bdcc80f5e6da8497b55066a5052a1b7f09c434c3867b69f696b6804d4f7519a11cb84a0818b2c18888501a24082204443e95bd911543512337444ba7647cf5211c58b2e3f918b2f70c24b251a88cac8e8013b7a4048970d9386af743166c3cffc2650dbd0c66c2f65da08e8a46a5e896baca79729d43c8e8d6c8cbc7a0dcf90bc02a64de2ead8f480fd8c4b0fe7de1e58d8080642414245b50103f302000035b52210000000009c12572370b8ce9788d435955e94c16aa7ffdc7e6b9e54b20a0c7ab6a4001d69e19c563596becf5e0830764cf41985eb9998ed03f175fb8ba135b561c1666b0cba6a5937bce01a1296f3edaa7e755a14e031a27a1f4081ad66260b33767183080542414245010144d7d5dea78449b307a0e0758dae39071fc491823e92cef118e5ef3cbd9c8c03eebe38db682a7dc8f148b79047b2dad290218e7678a030d96e08d8a24346008d5ccbc2a6e356d7a1962bd90b8f6e9bd525f4522e37d5b2e77ab1ad9613bee72acec8e801d76a061d7dace5241b33cb4cdbaae0557d97603ab54a6021f7faf2af375bff6237bc1e76a6d2167a643e88eff672e6f17e2e2b404eea71635b300ed45a1cc625080642414245b501039c00000036b5221000000000ac0a776b0e8cf3ebb14d188e793b7f7fbe246d57ed8980dbb8e65ea71d85de1f9c9c0c4b2b0b995eb51451c0b847097fce6cc31da6cf694f490cb421cd7b630569f2ce6cd2f6878f607f212e55b0845450efeb8577bc2e7692cec868d345170705424142450101185bb2cf2830afb52c49d367c359b04ecd4f9476a37d3b676c5ecd4a55c86a6430ba0486e6a8b72c760da62d5c0dcadb8808d44ea555db765e4ee86f423c2e8a02b0d2eba483b7cbf98b01650a2653938b542722263593907b9687a3a6eb4407d2c8e80173b6f8eaef085cb36f9ae8ed2808b207b35d8da04ea8c91e1ada57dc8d65ff47c963874a1de201adfbb321a48092648ddb9951fd271ce4c800fca87566426dc9080642414245b501034e03000037b5221000000000cad8c42c49092048f29b42c6761ad823af0a02f14961c5637aa95a3a59f4fb2bd8bd351bf72b71d8363b6eed3305acdde2dc759341e8f558ddef6de3dccb0e05f25ff18d534d5b0f31f94625eb980511aca47f980e3e6c15e001f42060f9f80d05424142450101e0f277a944a80264b1197c454310c593b8a6c7f94d0587c8304a215e26112d729a9657a42a783a19480b74cdd0e5cdf0540dc40b1f3d6092a4bab0a5df29c881a48e4eab53bfdff4a0b7a1180da28b28d5774341711ed1a809c30ce5086dc383d6c8e8013ea8dea0bd39519cc8f00791cb2332e8ffe591c6b9534780335db2012cac7dd462de00df5b8af30761e1060a5106113dbeb9cb960c7a57a95bd6984c982095e4080642414245b501031802000038b522100000000078898f860ac48da230c5021b072eb4235b14e253d55b4d0219db2e92b3e4d17fd60534c67734b84244ee3b2d081a1f049b812e2607d06d09c75e890d59b8ee079eee88ff611b635f87529e7d30c89dbb8532db5c101b72a2d9de7c9e32bbf5040542414245010138130d55b8ca4ae8f75ee8365f22c74145d7faaf6a27f7de7b8f61699dfb2d35b718fcdfc68012429cbb0743dfdfe488f6573ea55ed536c5ecaad95876d99f8a8abd9f2c178df62832366a65280860c1ac6f020a9956a91138245ece6ccd7019dac8e801adf2e7229f6782f55d99d6f07fc84f87c902cec961f321a180688a06f26d81f4f933a9f21b9785a697fc424324e4cc8ceec944f61d3ccdb68cb8bc85be830927080642414245b50103d601000039b52210000000005ed5dcb4e2599b1d6fd362491e72de87e07a5343e558ac641baacfc1e56d974dd3cf6dad1d2c3aaee10a8f82bad96214d45bcb6716a6496c3c4403807d6f9b08edab1b254bc25f6768c16ee4633dfcd6ec8bd83a8e55c9c108e20c64615c05070542414245010194e21b31a543387fab267f1ed30fc033eec33bf57eed26a8caf1e7def26c3c21c10653761aa5e76eb014fe9426c7c98fb229b252a1727a4ed9150add31b7c08b2a460273c0a0378fe223a4ad7c906080e816bfaec8f7eba95534cf87331f3369dec8e8016b27ec906675a75d89609e0a3a21b0a8ec09371a1e09a1da5da515b3e592075cb97c232a484c569c497f90f472ec84341d1fcbefc5d0593212ef1df759d8760c080642414245b50103ce0200003ab5221000000000864614990418e239ac020a513b23a9a310e07c384f9da69d5a24548d1a70ab4cbb4a7b959e1b4af52f09de77b2c046c6bb4b79affd52b7137c28059e28e8e0094fe313d4c6f28ae16cb344f9627f8068fc991c774c033de3940482feb46bd10105424142450101fe2ded413686b706cabf1333e1de872ab5b224b69ad71e996214a42ef06c2179fdfa9fde0e4cc08e6379cb4e27c86feb54822db9283aa750ec011eb8fa9e058862f0d518ba43c36133b58b33b1e61706e47c8952829f8f7632c4fb7ad90d00f7e2c8e801d832edbbd47a47d2e9f01a25b78b70c061c12564ce29d77a3199c057ba0fed772cc0519e15ba474c3be3de67f208e56b4c67e81e6bed213d01126157aed82bfe080642414245b501032f0100003bb5221000000000f0eeee887eeb70824c2db704c5df85c0721799ae5f29e425392e626740d5760576fdd1178f0247d1d9569fa5050a66a121e0c31a08ea3408cc8d564953fd1b01951eacc475314d4f99e9daa556f373838aacacda5641a97068bea1c163a52c04054241424501011c5a52e57c05ecc13377d55ff3b0fcb4c11963e5886f742d7c999514d0c7ab22927be5237479a35a4c3ac8a089ffbbba0a5ec23b0bfd659cf8c8834ee57ed88be4f890ccbbde306bc96dae311b1a62d24e4cb7a5354c0630eabeed5465b14101e6c8e8010ad55a428b081780730521055b1f53e99afd4775ac802ad37c0055804f3693579932dd3107d061e0c3d5d1caad66fe3c7f938cdf7b958527113f76dd3d954c54080642414245b50103950100003cb522100000000072f50c9517bfc4ae356ce91ef1239b16340f2557d7fe9d2d0b6b1ff198a5b25c924ceaba36939a8916cfc922062f9b830815bded9497233c473281c636e68b0dc5b3f327cc30264d77d0514bf229f2fa68cbdb98ea9b116618b7c5085aa2c00605424142450101d60f8173def1daf89ea51a4e8fcd14b3e49dcd23f16c6540059badfcdad3cd6edf1e941c5541172bd53288fb8097b2746a378ffa3ab37cf0cfbc9cbaae03338f352ef52ae9b2be1003b3d40f2ea2226b017a6c09627c8062c127cf1212571325eac8e801aa7455cec40cb72ed0c97ad2433dad3ebef26a12dc30bde78a0287c1ca032d7f12b4fb0967a72b66659f055125a6c03ccd6629dd8d7b0f609636b6896c54a383080642414245b501032c0000003db5221000000000820faca3a061a62aba6678f8e58bc0cf718c0641f05d35a800045c1ffd1e1a78d6f0d2e2824a816089e79507e7b17c3db9185da1be70066ae86903058e274b0fb2d831bbdb795ee648956198acaaba1019100cea6ff36d9b95c77baa8004ea0205424142450101cc1bd22062730a9c03786433375fb3314ffa0b40839c6903ad064b592b1c620197c010c5031d2dbba6b8e12ef7db7a2194dba607f7cb0517f42d3a0496be38865e6d0f4d30bdc5a39eb44867db19f0eee2a26f6623f7b81824c53111ef9ab524eec8e801a0c9b4f8473c06ed31b79e5507d2b37c67f339b87f702ef936f3ca6d3530763f058195f7bc42a849113592496cee74d1fb1331b9215795bf776379d8273b6559080642414245b501030e0200003eb522100000000016bc69330232db33a9164346617f709fdd4ed8ad1533292418f99a32b8b2d7754468bb55cf91434c7aa19f5962064ce144f7eb03f5c9311a9c3f072b40548b08fbfc8fc63d344602d77350a579aaa3b11f058840e66a3f8fbc046baaeef8ad0505424142450101526aaacee3e15e8b59345cd6e9239d7aab0d57142996d325e06839e93efd5c782473b5f48979118645e85fcea83dba30505eb9da447ca9dbc78d9a6f6f60618a932de738cee05c44d5ff3027fda11ca9c81a3a6da5089d12513e7b85ae296a0ff2c8e801ab579d60c8326789407a5d96e3821576ee7e46a0233a2ec3926d62b000c1be4acc8495de1af86738c558e7bf63febcde0cd0fe35eac4312e071d9e585c63ef5e0c0642414245b501037e0200003fb52210000000007695db064cca4db51b9fa64e9bd990d7c935ed6ac54d8b66bed18839b024ae5b261df8c2e99d7ffb78bf230704bde4073a292fc8e26a52742643d9a3003add0c958d4027d90dc2de9901079e9ba273e24c3ea3684430e7da759e2ba3f13e670c04424142450e33020001110ec434cc142de3e83f6c5d32526fdb84efeb826c7441298ae84621479a717ddd2601000000000000009a4419c9fa76e2a85014f2604f3acbe29d052d7f8ebe31373f213cfeb3633f3901000000000000005004ada038dbeeb636a58c61857a2b2f64a467b929128d118889a390a81739400100000000000000b681933791fbbedbb24bbcb2eaa48411c7c27711875b942537fa2d247c51122e0100000000000000240a8579b9c6cfce8e9ffa06e49a0df2767744abed4a3b3458b01fe92d44b4540100000000000000ae8ef962e1fb879eeeae9f78e8d57fbe6c0fc2a5b404c93a65f5dd8c982b51230100000000000000863a39f3310812dcfd9725d1ab3cece6dc763e7a43eb5ca7c54f7ed5ba1ec3190100000000000000f44df689a3d5ace5b6a109e35ac63d1e62b52e3a1a582ce0fa0e220fc8b671570100000000000000124f6cb9120882340f4289c1e4ee09fcad3ed8930ec634feda536628bcb4fd55010000000000000080e41ac5dc4ac06088d9c560d193b6f7b7ac4d86e0e9bb517c21df9453efcf4f0100000000000000487888f977bf72f61ddfb9ca54897c22fdd73acea696eb009b5401dd7101130f01000000000000002ccd992d7a9f1650e3fcd531b307d9651c104df6d5facd2cbab688d3fc334c440100000000000000d043cceb4e7a4daaff5be2f8fa11cd425af051654571a146f254afc04ffde42a0100000000000000ca204674c4aa968ae4cbe115db05e478b6ee03533038eaa9cfb95ba1b265653f0100000000000000361fab81129e1e3ac7e7c9b4aea10a218991b824c6dddc55414a8770e84f7e6101000000000000003652dc7e781bf25e8bb4b1d25de1bd9e9a7fd67a8ec75b479ae370ed9bfb2c5a01000000000000001873d87e3d21ad568f5cdb255f9870015002606921c6a3a905ce146abd77a2340100000000000000fe585a6b3636068a9009b18083cd6242c78da6f5f57dc0a7571b65a36d354c2901000000000000004ca1067f6f4f8fc7c9432818d0225242e98796be516fcb04ee272799a66dd23a0100000000000000c441ccc085cca761a998a310a1f074495af3e839f8c71de12615f22a7ea1073501000000000000008e747ea3ea05bfccecc9595e9cefe7fdeab4a39fc7e847f56b04135fc4a012710100000000000000fa5557d061b27db6b1bce99a95c830029fc60ac3430d55af30e94edb3a77a25101000000000000001ad6ddc62c61923290e127bfc7708c944c8d6a012805b14c70156109d83986400100000000000000285c8ef3c7445f4ff0ce912cffb2e521044aebb62620963df624201c5e4dc7700100000000000000947ff774965431ec727966c6dd539cd70d15e9181b192ad47b74b1c85745b8480100000000000000e479230ab2921c9ede58fe6211d8fc8e8a3ed3ac8065a7dc6ac50ed63763607a01000000000000000ed017fc562bf08519533de0279f2f7588ec4ea382d48a63edf1df2c807fbd1f0100000000000000d8d8296101fc3a99c56bd85c4078c2e389c071f731e18cf6be7c00aa9f4e2e6b0100000000000000845f63a15c8afdf9737eae1b91666268894eaa6996079ac7297490544137e80101000000000000007c3c62b725f20653adaa44243e4b3d8485c66c310e57178a3d3940f8f780043a010000000000000070e65e0e42d42db5509f35fd338047df8c7a6879ddaf62465c3947b929eda6210100000000000000e415160504f666dadd30265cfcf02b9d10136ddcd69958d9174da69329f5f8490100000000000000da8f660f8f4b90fff45ff6fbaa4df167a0f94baa1daac048346c50698fd285690100000000000000de6317d82c9feea35a44cc73086d6e73d0c7287728bd16c63933f913b7f27a11010000000000000028db15599a7fec3c93c26f62014fd30c214358c4443dc6bd515ef09a113070760100000000000000141f7c5f1baab0cdc3e2b10fa7380084c25f20252d96578d81700414f9eb7b7f010000000000000020b32e50928e8148a1b3d137e6cacc80f2a1ee5a3730e8880bdb0cd5d6b07f630100000000000000a406fa3390513c131099c9ea276472163db611174ca44e3b5c3e52aa3b47f94e010000000000000094c8b55eb88a7e3764116702ef768950c3e59336623aca8260ff44291ff9f66101000000000000003e7c3c81fac56951ff05ff47ffb49285750f6f55619ef8d0e11a388d92020f550100000000000000f840d40d3f38ea72cc1598f7e2012808584c0b90ce7546cdb82250f6b049972a0100000000000000962c3b7413c205f80b072ba3d8b9a828b10e42be559fbc07e9c597ea72cbe56d0100000000000000f2a70fe0286602a06633c7aed149fa7a7bd542c97bd6ae90cb1a941e684518760100000000000000b6cc496a16c32cca3993d385b995f38a6ba1df00baa71632dbe8f750ecb9b2300100000000000000d625a53dae5c0f3fa7af221c68c96fdaa1a498f941e10a1751a93378c4f4d04a0100000000000000d236b78d518725dc6332642273c5f6375be4494ae1e8e6e8b42bd1a19af14c4501000000000000006464efe71ac4e34c699a088b19ea08801d9e394ec50d56c7cddcc3dcec103d4101000000000000009c6f977636e4989f241a12728b5d2635effc4fcacddb1d2cbb45a739504c827601000000000000000652cc6a94406c8d2bd9c9f9f09768125cad75244c0f495373af649b0c82295301000000000000009455588e090f0f98a3a77dbe74d336e08abe7a1b6e30c1ea6ba7d73687ce721201000000000000004ad8792f5556e979d62743cb45b3098c23c003000131eff8b41de0dceb8e053a0100000000000000e0888da4a5a14218fc2c38b8d18d21a4acbd0a912f39cbe8276be83305f1797a01000000000000000678705af1f738381114b82ee5b8a14757515185f69afa15b02f40ba9adfe4530100000000000000ee4343f61d67fe213365a0495087acbea7b000429f2fbb80af14d32d3244bd4201000000000000006844faca3ef4bb5a7ba212d2a2914ab99eba84f93bcf37abf62db3a9f19fe0050100000000000000145ec81be8208098bcefaafbd43e887c772685225a803c19730d25580814bf640100000000000000d0146ca7b1e2653d0127dbc6b46398f6421cedbfe73fcc796af22317c6aca71d0100000000000000aabd30681e440dd836c894b44d5af8e890cd99599899bc0195125e742fbe184b0100000000000000ee768be474ba6200704b6bb75d6a0526263047da6fbdcb9344ccb35268361603010000000000000082f72bb1cc00b5f96c95de38d2698ba9c64a7ccb4d58a077538ce1ccb55efb570100000000000000a0674ddb6f130eae680532c6ff0b05a317cb3840c8906c3a326f26ce8544f82e0100000000000000ac2bf96630b117699e7fd6c6c717890510e00fb6db3f8a0a5c461a9309ccb26801000000000000005afb98d27d68e67f6e8eb81c6d3375425a5dc323bfac122963eeb580cbde5f430100000000000000f614bac5b188d32a7041ae91c9decb77f68d21c8ca76e06ad9205eb838056714010000000000000068bf63365fd8ca42dde5e1adbfb98817f88f205e17c1a38e07f8620fa19eb909010000000000000098dda42a2ae6079331599ec904ddc18cf94fee85ff96e1fd3e7b6953db8fc70901000000000000005869c7315c16568345efb6241e1c0e9ca9d279c8c7b398d32ae7c7323a93092a01000000000000007a043f552a7cdc9de8641b15b0369b677badda49d8082fb71c7eea3344159e390100000000000000a2b759bd4c7dea8909c86f2f570eff3a3b56e71d50ec8893eb66abc945a7212b01000000000000004e9fb9675233d8de1b78adc3f27ec87e2cb1bb838354172d1fd9c93b318e582301000000000000004a7a434d9bc1105ae7df912b7d64c32ff70ed5e531f464447b7d3188c06736700100000000000000b04c669e6deec9b1f47db449905e1bfdfea3bbbff5d4b4fb9c85dbfcc5a0546d0100000000000000baa5986e9b5b50de5febb4ac70500028e71046fd84c8083c0f62ff6fe31d1e0e01000000000000009ccba303b66ee95ad28150a987a672273809107589fe2f7d29665fc943ca62660100000000000000c28524b5af98c5c4f13ea78f14ea81e91df1afdd4500333f9423a80ff331de0a0100000000000000e83c8269c658801c3ac9014fc5a3c8309d1769a8dfdfa272ebedecb1a2a1e4290100000000000000ae05ab8b2f6db5296f2766087549efc6169b3499dde3d6374f7c772085f65d2e01000000000000002083ee72d69233ce625e5f1c3629c331bd6c83385c73722e846752018c64e4580100000000000000586418c4ad73c32f19381e4c32d8f2d082671af513b0a2b528dcb24a4230281d0100000000000000863f88d41e8128af4882262a1d55cfac41539436f280b567385da2cfdda040260100000000000000e077c9be8e48279abeb236ba07e011b082f2bd09528aae59180c7eab0619e60e01000000000000002ad69fcc5342b7b7e11595eba5a00d20ba35d6f081cfe395bfa1dcce4a290a0a0100000000000000900733f798d51d2dc54005ba53eeb9c3d63909d4f7e7ed32dce66b7259552f6e01000000000000002c605db889f693e3f6f832512bf4d6ddd8570ad3c71db7a9b57bd5689b44a73601000000000000009aa7c8266d57e70714e5757cd2e187e287c63afe88ca3a4dd42b3f403737f6760100000000000000705af57ca737e78669fc625f5fc98b16d120ba871b488ec79a9d12a1ed72f2390100000000000000183be67e3f80e946e21d9a53db35eb2d9d53b227de1d371e61ecf0f7b5e6b87201000000000000000e2162fc65a7a09a88b48781078143933638f7f40c98048b0a9b87694e34d7130100000000000000ac72ebbb90dcc166bfba1257e42b8b58574704ceae439f2c19ac085b61d40a6201000000000000003a04567e101ebb28329de2600817729f6f3c17c5e5789f036a52ad49b99ddb23010000000000000088dc2749fd6af775f8d9f3a1dcfe548d60037cdb8ec1db422165cacf9c96970501000000000000004af471b98c45055a8487a042c44567060000e421e7908d4ee86169d4f36d4e0601000000000000004221e0d01a623d31f3f7afb312e0ae6158db5edf68319a8b6ff98d623b0b1b03010000000000000068bc9e681587d349c5ac904a296ab9a4481c7de5d6b585621c5ed2670bfebb5f010000000000000016d1f436ee6064b05ea10ff906a7b6baf9cae49431489a8afbefae088bdfca1d01000000000000009231b7f5d1f17d7dd8791204288f265060d3469292edee45533267a7a3976b780100000000000000aabeff206862500ce4d4d54491712448e3823ab909919cd759937759a086a10801000000000000007e4ea4d47bf58ce719af34f30ac79ad8515ff9cfd2574abe5309a21ef0b17d5c01000000000000004c230bfd3b60ba5597597b154fd95829385acf1bd740607b9ee07262c994106a0100000000000000f6b74d3139ee7543b6821c342b061af75b609c2728c9eef724066fd35abdfe5f010000000000000018aa6738aae287470270a2213f8644d31150682adff4cf3b7b2da19c473ac8390100000000000000fc81c16f4d0dc4b27d1e0dc381ef15781f8e0ee41fbe0586bd9afe15ed13173c0100000000000000249f2c086a0c22a7cd3189282875e2398b6dc28a2b9a8a0ac9eb3a53de73b43b0100000000000000b266ee40cfa61107e9ec12f162cd72c6518af1df74002c2004c5a98b476947130100000000000000aa42949770f022aa18616f18f02917b25ea0182b6aa062ab117f9268183d9d3d0100000000000000886edfab02c7ab5f4f62f26edbf5dda9868c8a413a5c379b1b63d72a2121cd36010000000000000076cc698d58e5e4938fde51916c8c320e0c3f71822b4664254fbfce6cd4dfc03c0100000000000000ee4b50b0921a9e1f4e5f22cefe6bb00d6a4357c8570636a39b26e7344b3f66240100000000000000e844261de8b691e753a7ac7132c821469d83f25ea8e323ec1cec4dd478a7e6530100000000000000925864a0fb6058ba04b0d1425df158e708ba37cfd877f126796e2bd7970c1d4f0100000000000000ceb4d80222ff1340bbf953176e38bf2a8d40a4c6bca020beacbcac1f1ebe7702010000000000000060606066af08f9fb9f9afa87fb07ad0baab01233516511c5e6947ab8f3524c7a0100000000000000ce3ece9acf75ed3be5d3558b5cbe3b5703ec326647668458e82e42cdb5a948010100000000000000661e5553fb5d358cbddb07dbe15fcf7826a27e2c30847b9548d0d8f8f8048d7d0100000000000000248769c8acc11f3a3dede5b15035433729ded2ad8af740f2e7d3340d9d40ef0d01000000000000001e97263763dc6b55363fa51306709f7e0243af1910630253381a8592de97f00401000000000000006c5a12a06624fc5084d48b6eeea4b0ba00f87cf57986fc6110124c44e7e9542901000000000000003a1a122ffc962253073ab844867308704f6d92ab8f6e318e734f3b18b7e3ba3301000000000000006040a8414acddd0b30dcb1fbe083cd72889055353bb9f64c6a4875b861a4295e0100000000000000e06380c0e44c490844857db7ce6269e52272daf3db8c9c98f38b24c79c1eaf560100000000000000667ae5fddf7c7a58681fd75da15d319dda30921d6c659db6d0bf982830cfa06c010000000000000036b1387f3674856f0fb22a876ef12bdf2ec21fdcc849f200acc609e5f33279470100000000000000ec2f04aa8bd2ce4717e5747c506be32db5db1cda05e3875fba658505ad2e373501000000000000008e9fbb58a96118053914ff5c36348f8bc82d7e0051e8183315a4746d77d5af000100000000000000f419d56e18e14258c7eb351d1951a0c091194aae5c55ab1ac4cbaf795ecff17f010000000000000092dcfaa7fc0a16bb4a2e197db3179d207663971a4ec18c5a78594d579d02fd7301000000000000002ab8ea26eb951dfa831de87f69aa6608b4de76df2914cbcddb163beb5ce21b7b01000000000000002c2a0dd67d2550d4231d79efee8891dcc1cc103c007935aa49e6a2157c11fd2a0100000000000000f8106e64f3900bdcefc9dee82dcb471571bc421ca0b0b60ae2d9fc0f482328630100000000000000d88f551dbc4416e3ee46cd89c683eb51054fc9242037ff19a52af87b30402d2c010000000000000056091588f3ee5b9fe15d20fc336d68541a08e823e20d691a41613ce2c154196201000000000000006e2fc806b2626507a907566aebc2a6fb1039a533df8e7a8c9d9a287f3e7bfc3d0100000000000000e45c8c0d6a5aded1d938d8efdc41e5ab6b289c605cc6c19c3d7a12b384ffc81a0100000000000000a0fe04881e8cb6865fda23d69a35ce7f28574bdad4fe3b85a9925f7aebc6620e01000000000000002a11c0724e4fb460dc5513aa71488c58d8cbe2b5af25a62a4363e51d23fbb60701000000000000004e263edd08343be113e4234765496cf31e3bb0068d66e4782209171df67b2c3f0100000000000000265680af1a1392be7d96ba0fd2e32b5fcc11f2c394bae83dd49a29e4bba585000100000000000000c284219802cb0e56397b42e6434eae1cc49b1f67b0d05b2f13ef175c8e0aee5a0100000000000000cec30d319a93c00b7044b2f11165f3a5679d82ff4db6cb2783fefa42b236b8220100000000000000a88f2dfe51d3fa0370ca543711ee20e76ac172768abc40759ee95d0f99bf364c0100000000000000a88a466b1f2b4b7b6c497f85d1d1c3e8c13517217f2f2cd00c8ce300406d311a010000000000000064326ea393fc2fb9fd9102b08971b19ddbc878d77d0f53978be7f3ab87ed826c0100000000000000b4e7de600ce05dff305c9e1ead59c7dcd2b27f05b71c71387583e886504fc453010000000000000086899c0ce665034c2d701b5429944f115e3c74ae858ff9e7355dbddf27a4e70e01000000000000000e71bed03fe04edd6d1e28e7bcf8cbbff881734408b49f7c3e87e0aec6489f4401000000000000009a64638ddd657bedde38872091a93d700c6a29facdabec86f916c51266375f570100000000000000187d6e92ae950ca3e83ea4b5def1553313c9f732e865113c24a5dd1075ce2f7e01000000000000006432baca82539fea5029b23f67c912ccd9a652fe58c95bde1646d21e2b02fc6201000000000000004607caa83c3a777629226b92ecfd37cfbf53fd266e6cea36042b5366cb7676270100000000000000a4cf6a4ed9ce95da9df37128939a4bd909771b726e5901be83669fd30ceb9f1c0100000000000000c6003d828b9b0c61a99e86053843c22cbd9fdd734219f449e6200f3602c04a7701000000000000009ee91399c765031a73adf234280853022bdafa455fc539ca3920838855b11659010000000000000070f27022b5b79b964b7084d664d1fb56899b3c00295962bd8e7f330b55ecbd3901000000000000000062420d348b1b6663b61c0078995d5c0ce88659271b202b9d15c6597cbf760d0100000000000000f61ff7a1acb727894f81bb4e328a7f64ddbca4fe5477b60a72fe491536f97b550100000000000000341699a2305af27ee4e032aae91b4779218a2be3173500502d6644dddd1e653801000000000000001c0927ab80ed3e549a848e77c86fd30b448d4d188e0de7bd66370c05569d19790100000000000000444488d1b3e45c7aa8e35cf71dc80b41639412dcaed88f71743811c40abadd61010000000000000040639e72e9c4806d47f4bb7519ee7ff613ac108b17d3be775d72a173f0fc691601000000000000004c8ae5be1fefb1169156bdad8365d33f6bbd91b77a3e3897cbe0d5c4d7f7e3620100000000000000284b3a6ef1ead33f5fac01a077dddc179190daec59fe99586b99c0e35340ca72010000000000000092ef51762ec663f4b099fe6a72a74107d3844913df78f0b80d626fc4e20b1e0c010000000000000008ff6a7d89b6e1936b595c141ffbda304e6d40e36ac264d411615faf8049ee110100000000000000ecab81c08817d3365ffb7e7f3a7d847a701947a1db288bd2177868251da5a56b010000000000000038ddd57c995be34d71d938d2d1cb188a0e8b827da6d23ccd08ecd936850e9e520100000000000000149dd20740e21748698d0d48fcfe3e97e3ec1de3fef8730d5ce5f2fc110590220100000000000000542b657db2973a25f1f076f7b15ad6cdccff6e9687e7a4b5c0129bc77c847c26010000000000000046659edf4ece89d3ba4e4539a2a4466334f4107f099c1a9fbd8bfba22de09d7a0100000000000000ec9afce67bf970d2eec61a3f309f52c860732b69b156205775af8eef041872720100000000000000045fe14e3b9bfef6b174bb0624f9ca0f897e4cf6168ea5952124c15b5b2597500100000000000000a8c43807924e0b4efdf713a557ac34056545de254fa3745fa6cab97df913c46e01000000000000001afa773e53d051ca10aaa2e7d76b01beaa90c8c2d6d4a9c36d6dace0fcbb88230100000000000000f40475fa28c0b67b56058df22178c5baab25a28d153dc3277e0dbac6c094b25401000000000000002ef10a06d765808d2d44b2034edee5654fb27218398f0e79731b318678ac07110100000000000000500b779c0cf43579b8927d8ab50ea09f3d540ef36911b7a7ae99e2fbf4e137570100000000000000ca1f7eb42dabd6595813c3b7404cab648a2467fa9e31752949e07090b286a36b01000000000000007e80e157841d2bd8452224800a02e3c4b561a27f1731f0125f1291c359b4d4130100000000000000b0d74c7b304195803ba0b25627cba2c79293acff064d41b010a32b6d3ce1060501000000000000008cce5eb3755f181c945b8de1c6a3bcb7454557ab4f2d8d3cb9f7d137c2e50b570100000000000000fc320d8d5e575097cfffdab4641c3feba3bc195d7ec01121e2729ade54fbdd0801000000000000002a8cb7211931f3477b5731b23c2d973280b64b509ff16096da574f0648c9275201000000000000001a5d6fa531d4845ce8e8b33fe5e37f7f6c25593f92edba3ed34689d15bd8db0d0100000000000000ec4b66140f558efc29047fa4cadc629c72756a4d70727bc00b7461b74baad96d0100000000000000c4a4b433dd005332b2ff4af2534cae3ebc0a696340d8add1a7399e95bb0ffd7301000000000000009c1233cf95166c9255e9a6c5e0e927b8663b1b8a5a179b6bca90aaa1608705520100000000000000100f8e7d113d41416cd80214599a75f85b3b19e96cd2da7039c471de2006a64101000000000000009c1b9343afd827544ee40e6f45af9f75d1d4488dd9f479510ee8cf47c188f92e01000000000000006cdaa7dad71fe0ac92388b99fe1dbdc5425b39eeaf06a022ea8d8976720d914c0100000000000000c870afa85f2324fb5d56a923fb5a481e606bf27a3660a674ecac23d81be6cd2a0100000000000000e293330fcbd554eee59e0561b517231bde9f1bd1c64cf8530525f997eae2690901000000000000008a40b5186b72111670500b8cc2794555e4c67408a703f89984e957e9dd9d532b0100000000000000e2898e69209f2ba56c07e0c3f037fb20a97e5980ab3bf5f19c840b8f5d14641c0100000000000000a44074d35bfb2169d9f8bdae3caf80bcbe66f8bbb20dac30c29393f2eaf4967d0100000000000000062b28feb8046eff2268a58ade1d5c30db94ac566c33ff5a99a60541703431570100000000000000a62c0a8c58b06086dbd00a76e81ce32688c54f510a13dc5a287bff4303f41b4001000000000000002aefd1ba05d4f7c13ead2b20b4d868e8d787b8fda35b182040a2aa9a8a163c2a0100000000000000beaacb072ff2347efd4169fc7993ae758ceb56ea6f6897cf9a25ff71cd41cb02010000000000000012541487fb284062dc65881d5a326f3c963924b68ff36efb87efa1520c816d060100000000000000a82f7ab0942f27c701a45054359b3658662c80c0c8b1ccafe8271c5d3489ee0d010000000000000034a21abcc93598500f0d1304dbb4df783cd4faa9ab063e4caae4dfdc09899c570100000000000000a47830f3a4a794fba6e4ab624dada5b5630a82f1d61eb6dc406e01319c1a2c2501000000000000001840550329e05c67e05813a375fbba9a2ec82d1934388ad0292bfac2b45bc1030100000000000000ae2f6e59604ff1c1492face76604bddc0c48209485eca50f8d82cf67c727e37201000000000000005af885b0d311bd032ff14efbbf42adfae2a8ff2088f1e2259394bb03409dc4330100000000000000d6d45addae162f1bb235d16821fcf1136568b644721367bd3dad6a9ba9dba6660100000000000000c248107abc3ab176f7952a645a89b3cbe1303b3af53b184a32360cdaca6c400701000000000000009e51e504c5eb7a27f36c064c1eb1373d66a4e11397ed215dc8909db7fe523d5e0100000000000000da5f327512e71306ffe1905a70e12a259d0f0ef611dc7f56be8f0ca30940033e01000000000000000e6bdcaea0c05306fe727b5593d281bfa2eb7ff3aeb4bbd965af01396807d6100100000000000000f69d3157e535952e2155d65b90f2059bcadeb8a7b898e0443dc2f719e5f0841a0100000000000000d678254fe840948c178fb1f90f58fe093d1aaeb45b4919089f52c7faf222f5000100000000000000eede1bef0c43d1266c31905705140c0aaf136887ae4446bbc94cd5440d1970660100000000000000868dcd842b9e0304ac20a855f4374fedddad8781be75eabdf4f1f3d638593d1b010000000000000066c9290ae33d9153a0635c8b00685f11f9a89dbb3e9e907f8bf56c7b1d797e670100000000000000bc7567b86703c8aaf553fbb6add4de6c65ace7cf1816e31db07c6b4056ee7a580100000000000000e0c2d0e86ba6e4484856ec5a68dcce77175b15ed87d436bfc5abb5a01f38c01801000000000000008e7d9341619a28dd6a2733adef62b13f8cee0733de46494549fc38ae52f02d6d0100000000000000c43d0c516a2377bff6712585e03422e226f99014c9565da7d61bfddcc60ba2230100000000000000c6b1190ae760998a1286b3e6b9dda502a335c56f868f442b971fb891f361f75501000000000000003c611bcf0d259f8b57865f3250a1a09ddf562089bc322eee54be703ad422fb340100000000000000dc0e6a184863bce89bfee06c3c046a7cccf040d0d235865c6f0c56a75790244e0100000000000000500f6ef080819e095e751b2d13e5980446df95cc3997438f3b5ffdfa6ea0ec5a0100000000000000ba94bfa261dfe0b32d2697c88f97b9b1a3e603d69f916023dd3afa9d8f3715260100000000000000d4d43a66ffc7bb3c0a543a75fa1ba8c1b25eb338c2165bd0ad9aab3ef367fc17010000000000000060a92cd57e6ccdb5667307b0a0c9daa782758ece051899c00a7cf0578c1f131d0100000000000000d064fcab2c3f2b380af1b6ad48f31c17df5ef9ff46ffb7bdbc1908c7af5ad26a01000000000000000244515309e58b54210661c190fc86d5b4568eb4e85c578318729b50cbdbbe190100000000000000c4d7221426175dcdca951d8e377fe9c314972a9667550d2980d184dc7713244601000000000000002e2473679e557a5ab4b5f53f3775458dca1ecb9780a69a12d491e06e7b9c4d7d0100000000000000666758dc7096f360265acd0446e0e68d4d2c6787ac5d2a5d5ca76ded97805f2f0100000000000000ea4a297a0d3ee9491ac96d774dff2da3c675b3d7e4dbb9fa4e76c047b093880101000000000000005aecc7d9d96f48094caee4d395c8891fdc90529b74d835dae0d52123e5d2386b01000000000000005e092b5458ab949391f3323be3b09b90f02c9f2cf57c99e61bf5111a293c245f0100000000000000285f4d091e0564aded8101aa98e417e47008261ff729ef856003c798fe62725201000000000000000abdcf937b0f368b81ed8e41a7060a9d7917e6339c48b6abc6d8517b00620f7e0100000000000000f25c49e92258e740a9a79f938d3ba8249438b07884ee48a4973bd15e6fc52e1901000000000000006233f94f49a8fde5ca1b30460fabf5f1537bf4eb800caef619fe1c5a0572353401000000000000001845051fe44100fab50a4621b65b7c189083c3701b8366e0d853e139f60a45470100000000000000a41bbd69c636b6c12fc0512d2b016808851770a22c97337acc5fc8240bb82c0b01000000000000006ac55a703d9f7b20d49bd2387ee9fd6f1c6447a3401f793ac7be2064f3b6f464010000000000000092061d3be62cbed8f429d95dc1eac9d9bea8e23c12499426704ca64ecc28675201000000000000002c44ca2a77214e408a8ebbc6ea0e31cc4c01f1e9477afc11d9903a47777b345801000000000000000c34bc418f0ce9002c060ddf2f2b4b6942b1163f4dacd5608c0bb6d2159af135010000000000000092a908b3b5f108df9401ff0c6213ebdf5eac412295be6fbcae76a9320f12231401000000000000001cbf8a564c0beda112e06ce28226c1775b350a506fcc9133f1c87594a655e02e010000000000000030ad59b9fc56fe3d392dfcab57fc9b874707c1fb3d59ee78463f8d74042ac3040100000000000000361601767f088b9ffd46588d7049d46d33f1acaa4ea4561219c4709510c1cf10010000000000000074111e0c15602d7fe0c6eeb12065b07c83d890b94029c8815db2443a01b0eb2701000000000000005ceb546da5983b4c431d196e5bd32aa4803dfb513b792066f83546f63dc7534d01000000000000004a16b874d8591d53c896cff1d280d5c4e5197ebd894804efd727e4bb37bde7060100000000000000141bd9a074f86b6b6851e89bab648df0e45972c23acc7ccd9d5bec4adbea3a190100000000000000365850afeaa8058c89017ceea5cc3dd218f4955d2a4567f8a964ef389078c54d0100000000000000608e3ec03a52c67c8b0f637cfac7345fa1c2b4b61d09e50bc7b79a2644ad053001000000000000005a7d2d2e52a9a7f01b1ef3b252202b4c96af120e867141675c0820fbb67738700100000000000000baaf82c13c70e24dd09199d2fabe38d3712470ad7c8235bb9c47df20870d301c01000000000000002c64e7353c81c2b4ee434edc4cb8ee6d7a484371540875c546f6c9b42f6dc2300100000000000000c2a1109372991310a96d78b8da314163f9385399aeea368ed7e7c37f201893340100000000000000040b8138245dea143b0e2cb70875ea60ee74ad3df50b916db931db5e583d5b0f0100000000000000f6d9021798d518b38d24a74035499871973f7e0e67a3bc5ba37c5d4893e1d9480100000000000000ce87603414deb87155f1df0a5bce17ab2fcf11302407b96b7b73ea33169a69670100000000000000f045461d722b94b6c0928af47a0d8ed3b78d646b2f4f754b1c90196465d7f92b01000000000000005ea61dfeccf577b54216c24d8580984b5a948fc2e0cb15cf75d3ec89ef00470f01000000000000004eefad32d3c3dd9b713031d2d3412b110a5997283e12e1573f9523bc479e77220100000000000000949ab1dab46139ac9df94d74a3a91b263a98da335b9127a2fc7369476bca3e41010000000000000080cf2f4b8c6f71647d087c97a83df3f3eb230bbaa5bf86299a2d3c8ff2f2664701000000000000004a85979f5a5af886d6808c63ca7f266f7a55bbf652e039b72f1fdaa9c90deb4501000000000000005878e7553def1229480d89260e485b7f3a5280d16b9c1f1163151dfe472e247c01000000000000000869d1174ea5bfe5b56385c981fc3d8b58424426b8a826f44bc7fb6e2aeead7d0100000000000000d876abe03feb4a8bcfdefda30228ed6820b1a1fcc5954148b0ab36c88fd1ef2f0100000000000000167fcf03f83b30dbddcdfb8e4854b03645d77a3be1268d674e65ff2085615e0701000000000000004e3ab53a4117874a7fc193a4e51a4b50efb808cc0f26a3c3616cecf4974079660100000000000000502537a2d009d0bc18752420e46bcf3ca17091bc04458ab50188240361ef633a01000000000000004e3ab9c976dd36a1fde5ba6c85e91f24b0e0f1a4101b2abd92f8eb6953593243010000000000000072febf0d8454db0a0c9287d7b22f739ba0154b6319111cb5104130557b6fc3670100000000000000e036c3ff765103769a69a0015caee3b83fdccadce2a269a3df376d0dd4001d4e0100000000000000e64945c884e8e1f126c36899bd7540a6866ddc58aae678cd0415e240dd3610220100000000000000744488e84dbbc73174838a2fe49f231ee82448487e5e52c5268f9e659be8e95f0100000000000000e6dfae0c3ed3754930c79a74e58d09583dae0bec35363be7c1bab1c3f25bcd480100000000000000408fc6ba61cad770bfd6555973b079db6ad49880fe3335c4561eba5ee649372a01000000000000001031987c29702b10623bcc65912a080416d2101ac6e204a2075d9a66dea5752601000000000000001c85862b279bcf4a725b66e8fdbf4150e253a52affacd77f2b52400a2209fb430100000000000000c648a1f7aef735b686df2634b86d91c94428cab340393aea3a5494d02cb57a4f0100000000000000181ad1bad2d424abe7f915a584f39a6953a87fafc1ac0b9942ea2c64406d4d510100000000000000a0668c4c43e71d99ad863e6f10e887b2a1f2bf32778d1d4d6eab57f28ba5425d01000000000000006a3f450101fe066e048ef87e288d88e0635b3677ea222f17ebbe87d4fb92575301000000000000003847ea56b040d92b53331bd3db1cd353b1001bb6678bf29cb12414eb5ed03178010000000000000092049218a50b3a59059599fa127bc3719546531c80ecb68d8ca8f5a0671c915a01000000000000002e1c9f1141ac38310b771a3b1477ec4456a02000abf09c4d0fe505cbd8cc0e2f0100000000000000acb79942c9051fe2047ba6a2f0c73ac71bfa033ff1b76d84332361573dbe8a1a0100000000000000848667087ecc48b5e6ce7a424eb24090c6b597cf121591e3d648bbde53393e79010000000000000048f1908f2c135d1c47c7dc79f63d6c4154a56ebbb46b039db014d8b444cfc1380100000000000000f01fba66537030d59b1e78a3246fed5a25c5322734ff422a9609e78eb0b6bb230100000000000000000ea8eddba18d4da471cc88fa5ebf59aa7e0cdbbcc7ab13643e2f9d2a1e120701000000000000004ce0e117bccbd3903add7cfecf3546010867b1a5fe6ba3ebd189e4cf9586a224010000000000000042f67b46c75b06dbebb389f8cf054f7c82187a85ed234f3920b919a161046e2401000000000000000a5aa5bb5d80bbf7ffad004fe82a73d66db8edad93ff6ce0d8df78d3e0e6fe1c0100000000000000acef4f49f13788a7356755512b74656cdf11fa80d7fe7223e250a7478d2a8d3a0100000000000000b4acebdb8857e0261b2229a1c6848cbf75001ab181e0504826d6ef04ca04e92101000000000000008cf03fcba2760c34855ce798becfa293b37a78b79820144da5ecb526f633cb60010000000000000082c55569a2c6861ffa4f116b34d3c6e12b5fb075f048e7d6ce35ceacfa40a93c0100000000000000da124054c707782430358583f3fd98aa1d4dd90968e8b4f3d8683ea3f27aeb1f0100000000000000f2bafbef8d77c9facf5a04d9c079d847bf793bbb1b659b4ea4c7679557007d7d010000000000000092ba571f52063897a993dc9818bf50cf039213409b550794ec32151fa089d31b0100000000000000da088ee3658a5e9587f0e2e7327bce5f20d5cc98cd986a5bb817d9b9a587567f0100000000000000dea6ca7823dcb0837f8533d8da27fd1a1411b1b48df4a8706b2f1201baf23b330100000000000000366e377084ded70d1065bcfa6e0629d9df8b01def4ff4620432dbd1ce70a6a55010000000000000098755d5f5fe724fa2f0116ddb92c0d9cfac4da3eeeb51e3ecf0356cb2966a71401000000000000007642f0c1c4d20c54338f0e3fce9b66576b392cf5ad165368c9924e1773c8f0280100000000000000ac198b700e0e627e2642654d791a9102e19bded7085332a1b76f543f0d94a1700100000000000000380ecd43a50457d037678d57a799cd411c1f56265ef38e0f777790e7ade53e7301000000000000000a42164a9749a911c34ff4a8ebdfc5d004de3fd7f06891fe5c882d2b56c2e6340100000000000000c2ebf281e96ead69e96276de04407f5d89a6626b5cf25bc97eab4d35b1b50a580100000000000000b64acae010e2e85df0d36ce79d919a14c43f0dbaba232ee96ed1e5708ceda94b01000000000000005ab857faaed9e6c7aed2d200aa81404e546160140fd0e5f314df885f6cb2c31c01000000000000000c7f753c4216d22f5e9a56f145f2dce74230a875c77c15ebb57bbc672a84bc5a010000000000000076f35f08d6050caca1bc42f1bd55d6d300580caf671f4a3b3bfa14b23b8019400100000000000000e8a3d82b64794602d009b8aa44a922a6c4aeb9c19c36720026b9b010e4880a420100000000000000442b8aea3303213b7f5e5b592ee0ad9a2043c34b62b8bbeb61958eddfb70912e0100000000000000c6daea6e872ae2760484c874d425703ce31f238748821e5fa7689e529d8931380100000000000000a2d5d4f254b43ef338ae46aeb6c96293c9e80b6508a4f9e3534ebc3786c3137b0100000000000000d00d343b5b85e47a6494a75fe67e3524841029026739ff98f7614b269c7e10440100000000000000e434239ced7867561ec1c186d12742bcacc9e69a19f6677540ab902f0e450a66010000000000000050a9d209313d30fc0decd461fffd3f46248364ab3a7e34932ec3b1e4ed720334010000000000000058ef7a28606a9d21571e962f3ef8b8adbf9d3acc41517bc9313985759ff1933b010000000000000016e5bb9cd97e2054f662f70325ed8440637c299e77736d3c31795e1225746c5e0100000000000000e03b6d54dd7017cd4ebcc6642ab9c23c62e2d846e610c7ee2e2d3cd71337b55e0100000000000000e6801825bed5f4cce30bde6abb75623a845bb633ff2e14a6d696a85e92690e490100000000000000f6c65ba1b18a790cc704f8e3a1bf3177ff51e7eb0081df2cb69173bf44a4f71c0100000000000000fe84d61179f6f25c0f3b84b4867b3dd0989aa22a2d9bb709ef67a8095008414201000000000000003231e6a8c5c5e47509eeb593347ec53cad6f05a0b30938af3861075488f2b03f01000000000000000e78888c098cf42dfe016c9ee1cfeab499eb765ad64cac48b28892a0a41ac20f01000000000000004c8d2e09ef8bb646de84fc38205dd2055f7ae3a56ec41b9d8d3b45a15f8b69200100000000000000d896c042b016100c440efe8a5ada9d0fe30f4eec18ba67992bbd1d5e513d147f01000000000000005a2e0cace9c852e709ab431b7a2ce9bfab90fddb0676af571452b8373756f63e0100000000000000de32e1c268482c89301b141116a85c438b6ce208513c71a12fd81777f144033701000000000000006211919c6d1b11584fcd6f3f3fef54d0d72f23ea821685ba235675bce10504290100000000000000e21d26c4a79725351bdc30e3825afaf4ec037fc82890c9eff8eab6c5dbe42f0b01000000000000009e25cf7dc9db334d01aa6215a0a9e27468a5bd09ad742bfe68b302c3975c31510100000000000000eeae3f5142c5fb5b16632f97c3885fce76a7969df551a3daa26c0690c610716701000000000000005adb6c8cced26392d5bf6151b5118a54c8c83d4a6b3bc26c2a623eeb3b2ad3190100000000000000ac91d2d8a78bb7d759742bd920741ed92c02115c7218d39642dea17086de3d3a0100000000000000d263e15b1310b82d6ab02f9f77b307fa234ea4a139b1135739602ec3c97e3a6f0100000000000000c20ac319e9cc300bed0ac95df0fd200751ce194400a66775ba8b68523b3f895e01000000000000008890f167e5966fb28d63bf02377a8b064b2466930fd947e54adaf5bbe6019c540100000000000000f6e92ca7e76a3ead20727d91199658a84ac784f496f078a71da0ab09e16a6c1a0100000000000000f65d2e46fa483be5f4d052b2f03563fdf7621ddcdedec753a4ef9508782e106f01000000000000004ca7ddc4814c4452f6d1c98371c6d2ba26ccf94841249edb7fd120fed311262b0100000000000000380a3f04ad89f508b389352994ac011d1845187c479281320ff77f4352ec0f200100000000000000a2c51949aef85bd6d86dc1d699c453fd89f19e171c580a43244b37ac32db775801000000000000008cfe7abfb01580f1d3a9633b11b9229b37f979e440073d885ecccdb32eb64e4d0100000000000000cae3883e0f4806a85ebdf81669fb03f002d699c95f5cf6299d678ec16edb5f6301000000000000001e2d439fee3ab0966bcfe0569144e399d8c1e431528b04a9a103a0d5d76bcd5b01000000000000008cef7f6ca0d96d48c03771e33900c28541bc7d5ee15689e697cd160aff14797001000000000000008c63ab0ca01ed8b358bb5eaa2582a0866fbbb68d170a03f2ec98e9eeffccf85a0100000000000000dc8fc9b4d3b7bb540e9f12c606e4b0d784dbf8542db625f56e686a2d2fcfcc25010000000000000040fbfdbe5c13e3a46a731b8191f92cf01734f42cf7293fd56d664a732c14ec5601000000000000000ef24b61e18983aa285f70ff8e361397b82a15a41c6742541ce7b92da9f8931101000000000000007854ee17d89a8b19b9756fbb60653d788f6d261fad13a56933e0640c674a3b3e0100000000000000e4a3a09d9e9b63295de7b3cb2284d87af269ee20b02de0b66fcddaddededd0070100000000000000e2b114eb3572e899237c85d50df9e64d4f29901220e9de1d7a8e04d96d2ef24b0100000000000000a42e70c4530076b877035b43c2e41672dbb02b95bf1755e1f808e3cbe23369630100000000000000d8df9dac3ccd81b28470ca51de56a4ce7c637a0ffe4b73a826cf7fd33b7820040100000000000000fcff2cb776b86e8a3aeea98f908c3195e4ce5db676d36bc3a7af3608698ef0690100000000000000b684476641cb59f5820a10f788f088080a953b907f990df22cc7943dc3a46d3f0100000000000000c0a1d3aa25d5150e5c38220dde8f6049268270e41242d623989357654078362c01000000000000001431ee16d3a18ea78d6d22801c6a25fe304172dc5febbe550026a1c0929864500100000000000000fafbb4dff5111e01f4ec36533c57a92f691edc68cf3e86a2a51ff16113728b290100000000000000a01d73a031bb8f4aba71d271be16a28896c85cfb9e2dda4fa3b44936207a2b290100000000000000eab3d83726de7218ba112c82a45c033830114b31870c16d0a49d9070621e012a01000000000000006c1eed8b9700d423e00bf57ddb3af32d21d8f0a104dc650e871ff044fd754708010000000000000004ad9d2a4b586292d19f935a31d89b90df9c99c74592adf2d29e64a5e79ab0120100000000000000e6dd828a602eb81c7522b98e53ceee9e22d0cb5a6f7cb0e3b00a7006f0e0770201000000000000006c7642774bd973c6114e6f0c3558c8f166cc9c8e980ec1d7ec3cc0276426c77b01000000000000001c3d79dc60c89a9873f2c5379ee71f4f298165e07109d4d47bef6d993a084e1a01000000000000001287be7a45554529c73b9243a51a201100a37a2c2509a9fa3acaa4dc04b5093c0100000000000000325f3840c5a9642addc7b6648fdcc21495999e5c00b368420b23e8173f5b9d570100000000000000820409942da35bb5ab23f20f62db3f47c5f87492b13f030734b7e7fbcb05312f010000000000000088ae601d5eca42f99686b6d19b3889fa79e32551d065f1cd650badb2fdc9450a01000000000000002eafead8f602b2ebe286db17142e60a41962e071346b4f1064fcc70f2e19552e0100000000000000de53a86b00a4690bb292c096b97dd222fa82e28a833bb5969aa9ad4a273d31630100000000000000f0efe1c1c20dcc2667571aa3375be67362e7f28a49bbd9195f4b5d71e99fc1650100000000000000545b0afc9dd9b56d8da46d35f40723548fc4c13033912da4fa1da2e41265047c0100000000000000904d79ac4481c639afe07fa078bacba2d3a890b3f8e133cbbf744e7fc5297558010000000000000036e5fcdf6e878fee49aedc20f21dd56bb41ffc148d32c34ade94e6fe40a947250100000000000000bc615a1317f7bcb567f078dcf0fb173bc02efaaaab4751c1d50c45b9d1db891f01000000000000001c808827ef3bf3e9de1e02128c8dfad7c13bca0a657b416dfb2352b48b02db3b0100000000000000ccdf3a11292657bec36bca11a4d5be787cefd82cac4440d0682b5cbe52af6c5e0100000000000000904c675598403fcaa16f8f3da9ffd6fa995046782c87590a7c715f2cd590543101000000000000008cb90a65a771000542944e389890ad0abbd2e2abae26dff9c389dbdb20f525440100000000000000b6ebe42548778f3fbb2fb24dab7aef418391da6fb9a07dbf8b889e338ca825010100000000000000fab692e1a2e6397aaaff6fc06302935eb2dedc68917fab400c049df1ce790e2d010000000000000022aff5aa2b5c2f1b1ae4ae3855f3ead541c2f0c9093a946178e127dbec92a04801000000000000008056a44b805cb78adca43e77724ea222b5cf6c25d6b314d38ba7c169576b0e1b010000000000000096169e83fd02bc9ed28c6c832a3240e0e01bffd7f8e5db4f2b85521077212a620100000000000000c848b06bb3577ba4fbf042fde0779bb9a7eeb2978fcde090dc5733ed36043e2c0100000000000000faa30132a968c6ad5db9938a74cfc8f2a878349fdce5776577bdeb3db7a96a13010000000000000026b3a3fb30b16dd2a07b9b6887998c789f2e0b2b50a4e26b792d16d9410215790100000000000000e68f1824cfe5dcc175d3796ab3ab85442bdb76f9210ecc09e01db47291f7e5110100000000000000b84d61ad8579f5930af27feb2c48e63831bd2929ef86c3145e1bb065da6b1c4f0100000000000000f4ca3b31c69d7a198e30c876e1bbb904ca8c5b038e5a811667d13e391e13207f0100000000000000fe922a6bcbb95646bd14a97d96c31cf7a4bc599b1ca7a53f39eb603db40d5e6901000000000000008ec82c3594fa26941692a378722e80fe77a45b83bd1c6adaf06ad48bff228423010000000000000032143326782c6df4957093bbd84260679d1373bb0a62bf12db89e2a7c6969d710100000000000000a0cd6c7a7119abdf54904eb287334be70f7d9ed0b14eb82e4b453efdb7899519010000000000000092ea0b49900eb565e46e7fe6c88d93858a5908a48e13c5e073183df24054bf44010000000000000044479354ecf5fd25d70f527143ac61b7824743dd51426d1eb89964e05489af02010000000000000016a58215c2c1c4a19db6a79ed12df45d1bd036ea02ca7fb81798fc28e80d054501000000000000007a9b8439f7407ffcb77f97f7bd878edefcef5c6c780bf4a49a5b4ecd9fe850190100000000000000e238e399e8aa6247525fe6e10f77ffe34e93465f6e2febe695ea74f90e3ec32d0100000000000000b45bbe08cc25b9b0e98a490833440b7932e4086201c82cc950376eb77a6ad94e010000000000000050b8e2a374c29f6a14cd27f72c3467663915b006599f220c40672bd080a0217401000000000000008ab52131785bc4db73448c72fd6bb6f55f6ef370e9293489e124d0eaa3215230010000000000000092aa82fc527b300d0365a62edab188ed0816aec9a68071f88d7d9fd78efbfd52010000000000000028bcc25f41f0efe2db27838a212f5f7abad9542345b3502ceb91226ef95d0e080100000000000000b2370f9da2faebcebb91db0f4acf2171e4667e1e88f6a011c1248df5e19d85310100000000000000e25536d123bb812892da8dca4cbe099a8b1c86ba9aa702cb5b95743935ab9d1601000000000000005467c16e538a310001c285235bf0ac5fe3a9caf9d82738451a9dc188c892cd5d0100000000000000f6b6e74ef97f7d14f4493ff14c9f0f6a76cec7a4f713d48fec5d008aaa830d4c010000000000000004e09dba366325cea9b0b8df63e0a00a917b53ce34aff64a8d1c3e76a15fe34b01000000000000005ec8bc4e1b32dd9e65c029e27494ea67fde94ff891b068f6f01a0ae33b17312c0100000000000000569605f7c34797a11605cec3b4950f8f5c690db51b24bbe366a1259105fbd1120100000000000000e80c0983f6e5a56461e1312af164e23f0bce9da93e1339df16b99e45a335434a01000000000000002afa459c5fb5a2e43b8c6162674b968cfede7f551acbffd9de8ce753643290400100000000000000c6ba3592ef9d1e79b36a3cb707372fd1e0c0994887600205f2265d370341c71b0100000000000000501a9f8a14e0f544bfa2dfa5793cd7f1549d45d213ac99a09f1dafd821cee6330100000000000000a4af5fed768cd4433ee90c534f86cc4a458795fa7bcc54b0f3ed8cab36cda46f0100000000000000048f25ba12ff8795ec8dc6a7ebda399cc18453accfdba1e58b111bd3c3db322701000000000000001628bdd8cc64a774215982d34698cc24cbf79a71c526e1253259c75c3ab8273e0100000000000000f86a955e179e71bf054104adf25dcfe05d6b4844c14a70b71818c6e0b1dfd81c010000000000000012a54ebefa1cbff0e621b59449bc24d94881d988c2439178c91ce7ffa374a2430100000000000000b2d01a3feedc72f78e575c8e77306f2f337f33a7d8a5a7d28d27dc58849a631e0100000000000000466e7c8220cbdbfbd1acf470b839b2c41f428ae619698a8bb640f10f0d9ebb6f010000000000000008427554054a0e5a7ce541a69a3b63c4734661eb39e303e3fed502435e5a73730100000000000000fc6000725023c95d259e5e405737c39a2965d6289843d3448fbe06a5165ac7390100000000000000789c23ffdcaa9fead87226e570e0ccacd11ef3659a0c7b5d960517d86797bf0b01000000000000005cdc38b8b8fcecdd85ed9c6ae897c0c6212d8fc55fc06ed61f503c157688831601000000000000000a790d1f76d6fafe4cf76f5086e16b4c0102e1f05bd92844034b342a69c2303801000000000000005404349feb5559d19fab0a2f924c4cc70dfb9cb28fc954322f184bee5026e10e010000000000000064ef00a9076e78d72baaa85d81238c5c02efafbfd43a0c07d8e57c19ef10b34401000000000000003e39bd391328e498ddc44d51f5ec01017790c2a0334210fce8239ee47487dc1a01000000000000004cc73eb33d62c0ec3e7f886195bcded7857aedb9ac161226bef22b0317582e63010000000000000004b263ecddcc9db4186f90e8b65bdceedcc5f786fb72698779f4d523a7d28b430100000000000000fe8fe762d7b321d00ca277b8c4aa433bbc8001c7d1d7a2846929c42e0dd6483b01000000000000003e6f47144b40eed6ebcca435d33d00d4609bdf3e7af96270c65e858094b309150100000000000000a0fdd556e5d6c1f8ced14627bdf01fdb4234118e85703d24274a265c45f92b4b0100000000000000522263467c287a18428c4a5a92f37d273699c9ebb0bdc21f5e9a49eee434dd360100000000000000c8073eef80dc2bbcea972a83302442249e5d2a5213e65297c60d56f4e9be86330100000000000000fc04c8e12ae94b0e3889ca36e2b4a09c32f9c6b7bfac999f56e5f2dc72badd1d010000000000000096c09ca3191f61bad04d1bfe86d042857d3df82ad457ea844caec645758c4d220100000000000000a04f6b14597d61c7600128326a1d75205fd239ade0c3b25cf359ee731c80c85201000000000000004e0a418c4c371c6e99b76781644eb38a63789b5d7c5263f949751e9714fa7c35010000000000000044f2675a02133f10999d3f5542ee2a6de9f29a95bbd0ffce8dd47adb1f87c7260100000000000000f4071d8ef295841a0bc13deb8799e23dea815e376eaa23c86f65a5083d9adb650100000000000000fa4086b525cf61d35736e736110859250992c443420778a90cadd8e8c88d8205010000000000000016dd18d2e8f3a507321ce87ca52145f9526c0fe35f7c868d95fba2b986f84169010000000000000040eec19a099c9ee6ff24e3eabd0a7bcf36e0e0130b4718d50106344044c0d6270100000000000000de220179c5aae1e95b3ccf3a989ca36ac76061bde0cf710670a6910d26564902010000000000000090c3500722dd6123a02fc74476a10b866118e9930e9c73d6e1d48cdb6d57e43201000000000000004246cd1eddf8b9255734a8503025d3d0a30995d71fb5645b1b16be777c78e901010000000000000070c83248401cd7b8780895793b6a0e568802f297a3bce0af7d51eb653d23304f01000000000000000854a35b9b536c8f18af98c90d6e8e639f855eb29bd76cf5105414c21a1ceb6f0100000000000000ae7c51f4c49c174b2400d057d61a1f43a8e13a6850abcfb079a909d136fe374d0100000000000000ca5b9515daf1925b3e200d73957dbc4aafe71160d33b5635ea57641eef8bfd7901000000000000001a43d2bff1c184ae4e83348c8b373df9dba72d90741f3e3c664ba6a2cc96e9460100000000000000225ddc65b656709973da9546280bb4b9921bda577344d3078f29a2a8d463131101000000000000001645c1277cd42f9d98b76f5b54b9c8e33550d31727f24e5c5c50acb1089f68600100000000000000b6e834b1c53a32fb3f8713bbc96bf812c6e208232c2d6f094805e8fdeb0c876d0100000000000000b60af9ed41b27a8e5667ed9c45cbe12bdccece1dbdcc096c0763c0f5046b7e1a0100000000000000f2118bc805ab00459fbbbf94668a8793c8fc142956a91a538b55797633728b4e0100000000000000e2d0e3cf521829f443aae687d4533647fbace9e641f5fe677a2c4bc3d7d3b142010000000000000034dcf88929109b300a7d0cb74fcf0ab261b1a273e09f3d9cde025e8a2b10d62001000000000000005eae7ec92e82f6fd8bf2b1f720a696d0256b35724406cc1c16fe4c071448311701000000000000000e28b34ec63d7e1e4dcf7a5136b63bced930bc9e02d416fd5aee66d248eaa2020100000000000000ce49383c47b78ad330a3e5899850092ed6c99012b59d57307de5168d0d75b423010000000000000098280d11d9c4bcdf35884c994da018af07701df1a3ac3da0fb8bfcc95b7aa05701000000000000005eab887073ebf35904cfc25021f4f15442d6f334488dee6a02b9117698f0d538010000000000000078dcc07cc7a2f8cce0384b4e14f9518d3f771960abea998292bb6a52f6bb88560100000000000000c8387a2ccdb4ec7443fd3cee75e8e559990acf2db6c54a88cd99f45fac1a66660100000000000000327e66f794b3379ce2ac64024813e987661fd4567c9f067d32438be273911f620100000000000000d25555083cf20fedd9b5aca0f26447b57bbc2788ccdcbf0af2efb0b010f0ef26010000000000000082693a174b02d3e8b8d6cd1574b5c782dc98edd92134854adc083ecb36c829250100000000000000fc18fad14af9aa7bdbc62e1fff9ed7078d63d1137a3c1e670bd3a3cf5a93d3720100000000000000b4765627822d1702513ff5648aa320cda597f54505757d630e96d66aa1f3c73401000000000000008a2c2b3a835311965eae34810e418fbd1bb858fb8840a850bf036fb7eeafdf7b01000000000000007abd57972d56585451d75240342fe7195758d6022e0120fb2cdbc9792bd2ec270100000000000000a2d8db3a4f3fb0687052b930bcefffc672c01a65538ee7b33ce9dab822bca1780100000000000000ba73d9161f8469269ece11dcfb9081eedb02df28643417622e258bf2a424a6460100000000000000084b56e7c7fbdd6b45d2db9e3e922a969b28318b35ff26692d88beca3a749b260100000000000000ecbd30a0be3dc9d3f8f4b29af83968bd76d0c62bea36505a114cff593e0b8b4d0100000000000000bec516da47d1cb28351ecaf868365c664aab9076816f1c536a24be5892426a150100000000000000688a95e2be5dd66a92c6b401a64dead0b35f90f517fab9673dcec192e1fa207001000000000000007cb3bd528db15070308159636ad9ca12c1378fa7f8adb5206ebee98c0986065b0100000000000000d8da588746cdbf28af1a52046bad4d5374c656a7111c4969094929548945df71010000000000000074c38c9e1867a292166ced2f98282cde342789a3b5d13c5f76f15998eba3e8510100000000000000485f591ac71f970ef4610ab2fd5f034fcddd9584d0213e46f3161611d141512f01000000000000003020c45aa46e4f75fd5fc42d966f1b99bdf1104a667722d91568f063bb2fd511010000000000000028c912c4d4ec34a97f6416c64424bf80494332248e6a7bb65784a6221f3fdc2b01000000000000000a3c03895ec10aa735b1e2eccb283242afe8ea6f17a675815112ce842ebfba0c0100000000000000e45e549d0f5e96b9c5b320e37828c80629fe7130e5ce695dccd3438dea5ae650010000000000000072f45f59c55d6206a31a1bc2361e096731b34a8745d0882ca2e77694b650134a01000000000000005405d5e7f3457fff778bad5558ace408e563e388edd901f3af12990afa7e8a0c01000000000000004e717a4d2c84dd7b159a91bfa677ff9dc57bc7fd3cd661523c8140a7680a27290100000000000000b280b7f37e7cc9a50d4a52e0b7fedcbcd3cae22ec16ed4b4b75ad460efc227160100000000000000282509d0ebf9a77532c60bbf28ba1509de4a0ccee5ee2f58ff267963b4e11c600100000000000000b85015643739ee03dba7b523f32db23cd192333ca1c2dc134be5b67c306f104d01000000000000004e8cf6fd4c64d23457b08b38b3a72a24279593b2aec4aec3294c67c92f4d801a01000000000000005c3a3673e42f0560adb44ee5b7be7948f8964eae967f80ec506c0147cc11ab020100000000000000d6451fc5d7809e480c3dd53853789b86678b890f4bea62024194b14a745370090100000000000000c0a1f13309be3124c2357c00f6a5545b843c70749fa8ece832dc77bbb7fb630601000000000000004c3b4d7a82e669e759b0cd7a6d6dfef7b94a00283be725f25b263d1b4d9a7f5301000000000000000c714d39ea91dc8f07a7b925a03895a148022b8f117d19fa2f29479189fe423a0100000000000000f697d527f96e9f3839e3502704ed69b500b765f6a64ce6dde88af24db12f14120100000000000000de63bd0d9626943917c561a484e93462de4c05d233bc4c1fd7c8b6833d29353a01000000000000001a4e01ffeb9f145ee8a17dcff9bef42b02b5c9b047e05742ac85de33639cb06c01000000000000002ad227e1e2b705e448702ad1d8744ba85d7d5756952b19976d3abe60d60fc70e0100000000000000907b44da2b726af53076263cde755c2723774fdc25ea52f46769d8f357ca9f01010000000000000078d57cef73882a2d361a28b7fd2ca671b3f726ddb22627d4b915217834b7f2390100000000000000082f296935a2a606bec1f195e6b8456f07cbf7b27e1e557b3887e1c46e4bc37d0100000000000000f8549151ec9c6dabfb6d844ba74728b04a8ff079d00e55f5ee55bc3b70604e0401000000000000001a12a4f27227ba6a04b5804f339b0ac61510b6c15abc85e3bd479d6536fffa7c0100000000000000baede105d1ba83fd42159a80f1c0969bd4cd6ee77700ebfa1aa3283a9d274a47010000000000000032e2a6ae50a3524184557db89a681b1de81e490b4066b4fd67364984da49931f01000000000000008ea9246c435f67a71e9708e1481478b762011e0965cb3ecc405155b4d2543078010000000000000048baca83f5efb426424d78dde70340fd86117f01108c84f4595deaddb22593420100000000000000d0d353277195e01a4236de8fdfe59b442f17af9028dd3f44967dcb17c2e01c5d010000000000000006978ce2bdeb4b02ac7d76a3086ef1bd09e3b9857e950083b50389a48871f3690100000000000000a6d6c761b15a3aa636f5254f6a838ab668421ba56e31430282b33a118ac9ff1e0100000000000000745f2d27e596483e2ebe6ee5750b0479f3c0d355af3498c570549a29b8ac730601000000000000007260ab80632d40fc1d7eeafeeeb99dd747a0f4800de34c2e82578786008c5248010000000000000064ba39c577fc90f2d2976ae979a7205a4918f4c8db532081f06bda711ffc3721010000000000000080ae71b506da12f8a73992372864dc19b64df51f1437fcb247c215a47a759220010000000000000032df009616a2b4d4f6bc9111f2ac419abf4890c99d6def4ce575f82fe7dd163501000000000000005a378e104d9df02d1dce1f9a344f3d4b6f9f088bd8904d0eefe08734a98b42710100000000000000b64c6d920385362820b9f7f9abe817a4bf64224c857fb65825376471d8c95e62010000000000000082b8b2c123aaccd04d02db43724f51bdb437265e59832714a2c2748ac83f2049010000000000000026a2889142114c9b4a07fc913e87a0e5283209088b6ba8cc0148a84f94f2cf4c010000000000000084ec16701285c3c5edc5ee9ae8172215873ec71af3abd53ed9d407c2bc029b3f01000000000000009c2328407d05c9c294c6f207f7c8829f1de10c13a77ae0707dba0dd9f40f8f6001000000000000005c450084e0a0ae90f805bb2e856bb5b46a43cbeda05a7c1e2cc09265e1791a500100000000000000e21ecb8f6b42f003d0d9d3496bb5dc1d57dc74e496acf2c4a3735940cd54231b01000000000000006aa3cfb637cdd3b960d2578823da2b6dac1083495e421c6c32c9e17ec0c905250100000000000000a25f7e57318e03aa832486e574815234404b22d1548f35cf77ffe26573da1f0d0100000000000000227bd022f3a23819559cefc5a323fe8347a3b75f74de5c7930db82cb7fecc8400100000000000000b4af11fd00750c08b33c22eb7445aa0d0fdd877bf701f0755c436d7907c1c27401000000000000002aee8f940e7da1699a6deb601858ec4aca5d597c50ecad1c3402dc97da339a0e0100000000000000d4738db14d68af6357fe1d33fe05635287c20b35a55a6df8018141ae26af9308010000000000000026ab3e54f192d9f8ddf020571780d10d92a0460284fd7b9ae94e74c926c2112b01000000000000008eb2e37eee463c14fe7855c6133aec6244ea3457ffb9b8e0f3a6ef5784548036010000000000000094d6f27dde80e42a7f95295a0f62492eec7be0b70478fe5f30f4c29e280cf92d0100000000000000d27d11dd2c3ab8a3e15a00507904b9fa81c0182176208f622543309788c68f050100000000000000f64491339e6b78127580800529b6ce624217adc82ce10f56d5c6911962e3135f01000000000000002808e5a217f5aa78b344d154002fefdfaf686ebfdee6b7deddfd1411b6a06d3d0100000000000000602f60398fba25dc09db2b4d8843ef9d96e0c60a01ab42ad976fceaca42195700100000000000000a0fc4378807ecdc482fe8d93dceebf78cc5d627c03f9412ce1c1d87d7b44090b01000000000000007008909e788793c04e57158251d1485884f3bb98741d889ebffca8ed9644141b010000000000000072dbf42dd0d9ec7a221912ba34b585d7fc6a0fa240d719332bc372180b3b636d01000000000000008a5716f1c004e49a930e1f0de54439f0a32145cf2565727dfafc11a024503659010000000000000030139a5482c8907486506234bbe2eed7359b6b7c0630468d46dbf2118fc5bd340100000000000000165d26b408a69bf67c63a945a49058f53250882485046f71375f9e3128aa9e52010000000000000060cf7d800df60f707be3b735805e13cd4a7eede0ac2ecbd7033a872b46403f2c01000000000000001872a4fa55daefa13b0b4e4f218cad38d08d7d39d42e47f7df5eaaaae02f0a370100000000000000bc800344f0bf1a71721fca1cbd7ccc47dc009987bd3524a883ebc1e925071f610100000000000000741b61fb1574f9989b1fcbfe4bdf72134ed5699983228a2cf27ca11d76c990160100000000000000ca2e3f3b6f3cf94b81b3bd4c55c730e41d8df97e97bd8084cf1cd24e68719401010000000000000060b1145a4451816b6d3ccc173fbc25b6c7b287fd270758152b92b30ae62eb7520100000000000000483e62e86514eee828c7144e945b7364598a01df7867f7f637cca09e702eb02201000000000000006aee8ec61df9edad0c5903feaf88f6297ea64efd82a52c692c5e937e744faa010100000000000000040d3137e8d771c19de798bd681da04262e8185db315bfe393cdb5cfaa79fe170100000000000000864b7adae776289119fd4da00567efe67846ede7f576955aa4ae889981276a680100000000000000ec52884a19e99c0df416f81fb211ef85aff4b0cbc220d9ff6cd34afe35bfc8790100000000000000b42d7f267a4e469af84c38da01808f135bd6853a7bd92f6cb44e7fcb952cba3d0100000000000000ac220a7798ad88df760c16cf8d18a42683c98528e854daefc7ee32bae85d646101000000000000003404a58d5e89fc3bbe715dd5921f5f0180059e74d9ae6196fabead7746cbc55a0100000000000000380702aac808412a0b886294725efbf4fc033b7bbb85d3b24c2876cc9e05d212010000000000000082af68c2e6079cea3474b2e9d76796b70b2acb3a0ce9fcc98503080f25e629230100000000000000345d63333cd7a9a17653f94374476eecf5eba6b1e4cc1c6e9ed215ba55e5cd000100000000000000960789a11e8dc46636aa94789c0985f9b8fe7fcea34abd0e56f92180c0ec773e0100000000000000a65039da8396aa3e559367c50a309f12e8747e4d16245ec6000d19a0af26f9470100000000000000680e8993ada64e3423fe61be3e70aa728ee73725fae78fa5bf8c57e8e1c54e44010000000000000052019b0e030fac4f642c31526c6308bf9501a297c9b6193a82d6915fb708ff7c010000000000000036b72e06ad058a7708948b66346a0350f8e61fa8c58b0e925600aa9bf1476025010000000000000068b06ac165f434922b8bcf48deb490e43b0d5497a2caa64654a1c673da36b83f01000000000000002654b04f0eeeb6c03a28fb4647305162a4e1112f631b66293ba16b563b971d42010000000000000052283740793ef867c77327902030e4f6c0deec494b5f4db2d680452880ba11040100000000000000785545f9357cc0a5e100f2ee283dbf8f5f9d4967b07aad4bca6ee866312430390100000000000000d60eeac98c4a07a3f08e3ed451c61174b367a787f720a6b7478178209e28334e0100000000000000843d6ad78e2665ef2b86f659fc0f12af29b11bc7f138e45f013e9afdc728624d01000000000000002e9d04dc6cc5425a6ba5e0f6d8b88623849a669542d42435c7c843aae0bdb7250100000000000000e6a422c3c7eb6cd9165fee529afd2209fab7f39b1ae491dc3d5c1320ccae1c680100000000000000c00470ddb02e84a7bc7500d90418dd5d133f4f8184ede2e15cfb43b881cb0434010000000000000092ef07386f9bdee89cc4cf8dcd1aa942134b21ace1bc2f121c261fb57388d278010000000000000026126b727ca3d359ad9aa85e71508ca3fb6a6fbe1416cec946bac3819fba9a590100000000000000d61549c459c94ab938a68b3d96ce644c8672effe9916ad3435324727eb18967c0100000000000000d6df798a261d38a33360d44a4d53e7a686562dec34fa12b7e5db309b736ca642010000000000000076f3274425ab752e21ddc0714e28e9651d4206841b736aac6db37aa0d0d4c937010000000000000098ffae7440754e3de87fca995a95250a59ea23cf595dfa1e53b1367ce20b455401000000000000001a089db458c31ac827e683803163d520ee1b08ed721dcd45b02c3d85855c8c2c0100000000000000267812fa98f9e8e92421e2cf5fc5de6a9423cb81f3b9242d57eac782b3cd09700100000000000000e4fae18a7a4c4f5196539556b85a450a0c1ecae4ef28bc52d1ff9cf3374c946f01000000000000001283692388ad47b5a1ed8b0d791c4f86a36e4422ef3857c912d68f5d425f34730100000000000000eae8a9a4316fe7ce6cb8f624a6615ea281f02082a5be9d40593db1048b234a1601000000000000008eaf59711472c3ef85e130855a4e34e0e3f2b8852af88ab561e9a49f206156280100000000000000dc6956dd35ff772cec5f85ce4aee9e6e6a4b9242495077d77df3e3c48bb26e50010000000000000080d7f21b01bbe9c1c6d89a7275a660af2c01570e22e9ca538b212a9577421d11010000000000000062c124e506bb86a27ceec1d2dc59e15802f14312b81e84f8998735ca13ee9a730100000000000000667ab5e18370d52b66a8c57cf9104d5732b22de108fd38eb14f8b7da2a29ec0c010000000000000032483a2e28748ea5b9d6b87256ae783ecbbdc3bab669542fc642d2c8a6e15d380100000000000000727f2cc64c33ac6bf5b433538f35548a996e80ca8fb8b1de84626882c7b6e1660100000000000000be210d84cead5ae1c0ebbefbfc0b35f33310d6aeecf4308529b86c2cf82c0f5f010000000000000058070bcbd7f8f06c03f9b7aaadc8678f182aa4b3672f3eab877525324f35774401000000000000008ced7755d2a8b17b2d9d157c1b9ab5b0ed63b25b9377d21230d346c7066c39520100000000000000caa319312d929a03892d06e2a524621cd9fbaef1f86d325501b4f1108e65a96e01000000000000004cc8f3fdf153ac8200029d319b8fea21e9b483bea1ada7cb9bd9265da69cb9350100000000000000346e685f148b3adfaed81980e47a61844871dfd86ce1d4f84a7b36998c66712b0100000000000000c497e8cc7d05d62aa5f59e9699962c395433b9ad38bbb84748f90c7d9f1f373301000000000000000ae2ca012bc830761e5afc8e1d81d62c6d160962524fdc3cff021ec49f34e123010000000000000018f128a7e5736dea14b3cde003c82a6ac8c922d2ef21b7e02e1aedc841033e3f0100000000000000a875111a42b538e568016c84c89f4107e37e5643d9a307aad904a7bad7fee76e010000000000000012082ef825e367087ea396f92c97868c24aea7ac2435c8acace02be51068b9760100000000000000a62c3f742cf861feabffd3f949f64703932d3f49aff5214e7ce5f7bb6966374501000000000000006a0e1ac35bef24f8e229f76ecf852ce3fcc87a884e8770c38e2677447e3c8773010000000000000004b06530357d34a7c37df9bee937d879dfe32d5506d1eac3fec50de7bb44dc5a01000000000000007afb0c23bd2d29c347a9b435a6db5b56e035a3e021f3fc9dcfcc47e52f3f0e1701000000000000009eac82acfe866c0d456b51e909c6b83dda9ea53d89b71945c3af1539f7b367740100000000000000dc514d2b1231ab63823ae49ff21fe0c250eed9b4d2e3e83d9cd5072b5f994719010000000000000078ab99c3d9ea7f380ebfe7db64dbc72ec4ec0516b143c2f66f8b06943cb49175010000000000000060bf5700590c273827ccd0dd56915f377fa2ff366ba72bd81942908b7f2a742d0100000000000000a013268290f12c0417675510df18855b1f1a923f0b513f7e7ddc1e763e27cf0a0100000000000000fe0446048559fda5d5d172dbe2ec70e54a5c3080394d055b0b15badf80b6af1501000000000000006ce1cc45c84f9394951284b950417355ff4a093946649a1ed9935cc650d3457e0100000000000000a253dd11fc5c488265b2a938adeff374961efab3789e0d9e637b566a317d71390100000000000000b0d22d2a52036e2e23ccf11b48ee7636021e88a63160754c8e161276e2bd9d520100000000000000744a5456dc956cb654fa881f1ae233b3fa4b245d357826acb8874669726a9a250100000000000000d8971835a1c90022cd05c0f259fc6407970d8fb59fc3ef0c515e07be6d4892390100000000000000aa22f189a8994b03920c2bf1d15d0699bb548c81c52809751cee4ccc794f5e0a010000000000000098f80bca2432755fc52885f1d8023db386ac20b33fe47dd516df4d9318f8ba6f0100000000000000a434f5d00ed6fdcebe637e8fdeefb417f9fa17c37970d1cb13e0ea4ac82e2d4d01000000000000009ee276b61fbded6deab6125d7b348e03c4b3aaffa7840190ed1cbcc4a07ee93101000000000000006ec37117872c92130592d0d0a6929d21e3411790f02e20cd313f09ff2cc5866b01000000000000004e4fe940a0fade721c4c3841d8d88788b100bc0964b65c53f1fc9de3f55d416801000000000000007e0ce7ca3586ea27861b81e7334e5dde29293682fa860c367b5a47e538e3760f0100000000000000549467ec60e011b6bc97752245e56aa4abb389ad87f13b914746bf88c3c93f0f010000000000000084a19d04d5544e82fb8f9f928af37029d73c82da314f437ca62339890b878e4a01000000000000002eff374cd044c1a795a453673fd050ac67510592d7e051ac432f9f1dc1080f6c0100000000000000d05a7baf5e5fe42f96c92c6243a6fe99cf7d5a2f314ea6c4672e5f28e23cd3220100000000000000b857af823b2fd4d88db51d82bbaf4d69b624e1edd3f4fc87afc632c34353971901000000000000001013f3ffe121ff83fd6c2c1f10175b3ad011d9e57c41e85926b1033277ecad5801000000000000003c68c21b23ad1e95d92b93146ddfae139e2ee2bc2c7a8b437c41e20b8a11d9280100000000000000ea8e718c93e3fcb04bbe362c8ed92207599394e6e06aac3145a3dbd6e068f04c0100000000000000e094c83c28dfb858b2cfda0bf6ac10b079e0c35b875365158201a60800d5ae6101000000000000009a14a73f6d4b1c9896ad4a10169452a5dfff45aebb946ea701a5ed94d23c470e01000000000000009e69adce8d1e72ba462dd7e226f1b34b91b038d63573cbfd2963948673045b7e0100000000000000045cebbcdc5e50d0a344d6e01b0a7d180c7fc14d8d007de35db199b31c026f2301000000000000006c1895ede65311cf2f00b5c57378fcc48a95a614d4aee03ea5fe0abf27d8d5580100000000000000003c355f55fe52ddd78f410db36294273478aa852a75c8eebffd3efa0ab2ca5001000000000000003ef792db76744a93838feb7da8b7f665689e6db4dc8a510578804a49320bfd6c01000000000000008a24331a5b0a512762ead3fdba639afeaf649b9d7ba5a939b502bae8d709640b0100000000000000b0951f5af1811973fa55ff157a38b0995e5700eea61ccff01c33096543b1b053010000000000000086e37259e697af7b4028a1cb533d906758ccbe3397c121010e5a094cbd10231201000000000000000ce0da1b140a5d9983ea7245ddc70a90a004d4aef4c5c056cd5fe3f271099a7501000000000000004017c942a56376173f9ab39d8ea985e21b703c3350280cdfb9e495795bbb995b0100000000000000c4fcf817d2c8c0c0c7adadfadb4ea60b6da2913587f69641ca43325516d864520100000000000000183a6a2a9ea985d9096c322b2ff3db841cbb158e4737965f5a98d3a53d0c551e01000000000000009e5d3be1837102ef421cf29a2c0ddbae40b42ad3e090307443f8d4af4da603750100000000000000eea459214eef62024f3fd784c5bdeb42e844e39bd272a92917d12fa66b62394801000000000000009a8f1dc9de51f2a09dada8a66c489135472c9d514cc9c9d17e8dbcd89a307c3a01000000000000000e4c162c806a25c08db000ed62e044cc8ca078f61352f0cc5c83f70cb514c15e0100000000000000100d6b2fc74287c9d030678b8ef8576c642d8ce76b9b6ce366c7ea5ad1bc4d7501000000000000006cc18e610d5c76a068185aa7f300215e2fa46ff5f1bb58307dc8f0df0b49112901000000000000003c26e68e3c41050e6603b62482ac1e6852f1076795ab0a9ec9d87bb4e76f1e210100000000000000568b52d575a8fec7299159b9d534b3268e50a59d3052fcbea17956d8c3e53f2d010000000000000008fac1a954a0e7a7702a7c90de2b1089ff57865e3181fb6e9ff7a1c34fd0f2650100000000000000b63e767df6a931fa235e41cc7bc47f939fb11dfa4e25698e443023bd150a437b0100000000000000d85c008dba8dcf0f67cae0e58ccd7e09aeda4d16e78ebfc94c0de3364bcb9e4201000000000000004e7f5c7b96db92ac862298a99bd047b0ca3f8356459c7e42bda56a2d80146f0b0100000000000000a6cfb4d5fb89fc4d3cf2ff522b7fd4267ccbe8ab89546bae641d1a583923b05e01000000000000000ed511c927dfe3a5d2ea0d858f16a008db97532b7a311fa82885fbcdd96e51160100000000000000ca1fd3a7d39bdfe8d47a70d6b1c82d19ccf792482181aade0e8d2903ee83a5130100000000000000cea92756fea9002431fe31980ac9fdd87f4139f22bf7436b182a0dd03e762c4401000000000000003c1a2bf3bc6b6c09a130633141343b91e3d84a9ee3572a00f5ce2a1bad23af7201000000000000003e7f4634849980d7f3ac6804336df6758ad393816afafa7c4932371c1d8797260100000000000000225b2615b66b0581bd26b9d2aa025900e83dde43819b4d94050de7c90d5aeb710100000000000000ee80790df6b6c2d1c29a053a364a5bab2536c77ec7c07080d5d6d1755a16e171010000000000000014fbfe3cfef628559b53f5cef7a3439af56553151254d5d9e8d07e85f10bef42010000000000000020a5bb4207395b9f205e128f899c3b37d265ff90531eedbf42170121029c392e01000000000000006e7b420de20353a979ff79c79672b543e70d70081eac397b039dc39fddb81e500100000000000000262510163ecdc49a4c74ec24e9a330d36f6ce31747090a05c4a264fe72ac3b220100000000000000bc45808b65d5f14bed3ffc08df2c647507893528e24e9470fac50962b60a3e5301000000000000006cc53152825109cab7a5e657c955cadfc846a338285027affe175e6e89d5c7560100000000000000e2ea16b3bfb7647e0bb6f5a8c01c0462662485630e4ceb1193ba0e17c52d5f2f010000000000000058af2d747b43f08004665cee97001fbdac2d7f8f3a90ba67d57de144d1be3621010000000000000050e17a1e724d93c69103ab8656f7f38022a83394a1b817100992205e0534927301000000000000003ab369c42e4d12dcc2b478b838aed3c279bc7c0043f6398c315c9f2d3c47055e0100000000000000a204e47fab483977b269636c625a6d568e6066d6a1dfcf06e8b20864e342987f0100000000000000a2fde5e21a95cf7fb0a6d25e2d1909492aebea34a623572b46ca9262d44aac2101000000000000009acd340a31e012f94cc096eb81b69913480341aabb48cb7a2451ca9e5bce3c51010000000000000026e8b6bb0f086aaf10d543e45ecc77511b08c7a5f61ac760b6321bc961cbb86f01000000000000005c17a2edbe07f27251213b7f4db4f4e4ccceb01b5f957a296ea89b83cd6b6d460100000000000000f2441030ec7780e75e43490ffe6a2918beb750128114c2a54f7af73eb015852f010000000000000024b9dcf73084abbacbffd70baf520f1edfe5e08877dc6dec17670be166d2820d01000000000000000c2ca9ea7ad31c8c17d4ad472d7520dc29b38fec6806b95d6f13c5f431a2fd0e010000000000000000b9289b629edbcb7691131095127580893d36bf86391a3497fa0642c58eff2e0100000000000000dabe3557e532052159d0eb6aed80464eff5ea60a7d4d11ea6f3bbd93a406fd7401000000000000005ce86e8ab8446889e7794b716d39f72772671300c4f286247c9b94b3971f3a6e01000000000000004cb6d487b23b0ff1c1dfe94c87adaf45ef8cc0b60b3506bf72284b8504d7ad7f010000000000000008e78bcca3d3e2ca7f2813f465a76cf048f628556415860d98c8997d10c4ab5801000000000000008658f3d380c23e829c50082e5393448d014928bb3c7432b1dcae95566f32fa0b01000000000000008ea53537ec9986bf2df5bcbb81af619d3301489bb12953949cc4d62d44b77a080100000000000000fe20cbd8720f46d2c16756fb7cf5942981e933366885167c3905d3213dbcdf6f01000000000000006c15f28efaac1e24c96bbbbece09d237532c1436ed316744249aa55700d5e4370100000000000000242be63827b1ed32640e6465378afbc28a6c83b54013b0b79cf671c7830b36510100000000000000e0dd1b9898a07d1e916784188bdf246ca60638be485f3d1a8407ae39355d644a0100000000000000642bac715fd971189538c56c5b2d81af2a5b86ca9512aae04490ef9a80c0736001000000000000009aec4750f312da2c844b78f64e6da58d6a682d4b7d3ab8ed0bdf3dec3efef06b0100000000000000765548435b4a773a2aad522351915ee05798f3a5873a52931df7caf8b615ea1c01000000000000001cdd4e72f7ce330b5f458d1988a481a6a518f81f98c6e2ee6e3cd14e247d19520100000000000000586b3b1fa70eb13fcaba8b11f82835084416ba431613f235af469cf144abcd1a0100000000000000503ec36c254ec34d47788344256e9feadf0336386b742992f38b21ed62a0677301000000000000002c1ee1c5edd89c3699d7e81bd67caab2351702c68a313ba1a1b1530025e14400010000000000000018bce81817ec3f23e2d5f45944dbf6c9e72569390fa8b002e560291bba469f7b0100000000000000e2e55d6ad3fe7564b653e1687684978568da1df5c07f89d3ef6fef6bacc3b41c010000000000000024be8c31c6082f993e6091302892a1d5e678ae890daa1eeefb002737799d512a0100000000000000147e76962a08ed7feda65a5559464c39cd32f64a7f5d2879b22d06120e01634b0100000000000000526322af40924e6aa848968ffd4cd1f64b6932a6af827e09d6087ea2a23b8e4401000000000000004e5103b8b919c61b36c3162a9c8d47c9a78a2f1bb849a9eb518bc2338a3e0f51010000000000000020494dfaa45734759c4aa32982246b29ea6e49f22e0ab07e8760372dec30fe7b01000000000000007a2d4c9c30b5ebcbe2ef461947979e4a70a2d94c3988e16670b1b51598af7f190100000000000000d8aacc82937ac337a930403e3dc610bd4ff7d241a0a4140044812e2c0b9ba16c010000000000000006924daf9bf6f8ba2317e0fd39d4cfc568409c67b2f0b584db8ce27e8e0bfa340100000000000000c23b317e2ac29311d399211cdbbbf116012f9577212cf68ead5301176574f467010000000000000060728068ea6f3d0f3622d47ca6ba9e54e49742d83c728c6c1f3c3db925c12d350100000000000000b0a42ec890cd378d66733e45c739319aa6817fb717f9c882f6646116c67c113e010000000000000066e62a41d32cf015685e4316f2c5d22026c358abbd815b590af151fc8f65256a01000000000000003cb5fcccbe5ac7fb6d86626c29fc5960a6ce0476d8cfb51e8338cc2b619c497d0100000000000000a098655547e92c7ac123facdd4b3cae305e330f764903eac5abe9af962c0ca050100000000000000a84e5174d0f7fa67088df644df104c96f66812907348626646e1d5de9ff8976d0100000000000000927bddfdb9c270ed770b442243ba58804de2a383b51198ac17afb48d4fca602e01000000000000005003dcea465f6654fc042d1d2e44e9b3491df630b39769e33df13977f081e175010000000000000062da836ff1c0f853f1e31c2cc30bcde8b0ea955eadfefad24fa6d2b4421447520100000000000000f0ed29463a12cca8b1bf44755b7fc4b271611be8322d125f6d6657acbf9866010100000000000000f672856259fab6a08e26157d63e00a8af36793f579358d2549fff83e845c607e0100000000000000ca6ce2b47137af4e77b2c050dd0fd5009dcd0da8ba2de762c20826d0a939307c01000000000000005861cdec6edbde92a2035ffa83f87b17acf09262ea38cf4729ba5a8071ebd24b0100000000000000fe9a0a40f1de6f902e0dc05b033a066eb3e82dbcd8b3542336e3119facf3332501000000000000000e43c4763bbd1595aa61e839d16f29b82bf65e942750eae7dda38e9546ff201e0100000000000000be223791af645d5d3ca38db90e6b59bf0632bbed9e3a3940a54bf80b112c4e1e01000000000000003458516691992e93fc2e1af1f445bb9f5a20883f2780ef854e77d2c1a94e57070100000000000000da6c5070748ea0655df06b4cd7b3670b182514cb2d1d0551225c3a7d5c8d62290100000000000000e2f27f21c4d6c72e3542c85e6acb1250107af219dcc1ec272f74e378ea5b6f280100000000000000fa11dea7f82cf0f0f478a16571cfff58242a1a766f510240a67dedd35a8ee82b010000000000000024e2216cc3180349a56347d56e164d38959b7e35cffd69eced79fc9b8caaf06e01000000000000005664b72ea779c079691d0e6135b1f2022e8c634c014b5b937b90f60e4e08042d01000000000000001e48cb51cbf5a72eb204d54a4689e849243adbff41d54d5f2dbe5d93a0e1cc350100000000000000ca6b5b26ee50944997b2061b81120b0ae368c5e32bc65cf8efdb47fb85bba3700100000000000000c01e3dcf36740aa070991e472d0d82d7f1904e76a049b957a6caf1564e46a32e01000000000000002e8e24430b51f6d5f8dac6024f87e451813ed4db1b3cb6b3e612ab4bdab8f267010000000000000086b1555953c5f9f44fcb3a2f0dfc8ca8331e02dee7116484884e0ac7ef2230770100000000000000009d14aa7f46f47424f9925f605599767e56f962960aa896008c9683cbc3401e010000000000000040506a20667b254d811877385dfe5663399f75dbe3e5a57f289b344b6fb3ef170100000000000000b02c947b1a2bc4d773ab0e28044e8293a875d7b8870e4d607500fcbfe649cf2d01000000000000002c52d329744a9d2458e129fc50094550facd4aeef04a717cd5c452774128cb2901000000000000008c35da616e607d118115aef27079b5702f9f496ca21f96d682d614928446b8440100000000000000daa83cfbc4d25a2336289f3261e6a4901030ed6b8dd063b824fa787a3f68126b01000000000000006e97825a19bcf21e0e299dad9a0594fac9d40bbc95c375eac3cd3824b3e7c62301000000000000001ebe00d3e01f16514c89161a99881d53753813cf680ddb3e3c6bc577829064110100000000000000d0c63d9905fefedf8cded1dc7c758793d257c74656fac1c2ea134d2c8306cb6f0100000000000000b0e486d3f6c244145d23032e222f807db5f3c553dba509dac3ab9ff4f37543760100000000000000e0dde9387f1c3fcae0bb2529e0cf3eeaeaa05e244be364a3bc316c9580d6e93f0100000000000000583e56856e4256b5b3cf4a254312d6dd09279a529d238ac9235d603ebcf0bf38010000000000000018ea5e9458304122e280ac00a656c32f68fe24f92fb51da1183949cc5045241c0100000000000000789b7f450bcbeaf7b6353015413c24a5feac5bfb72372b7085d2232146c9311301000000000000000c03563780cfbde0d9184f984939a14daa87754e9e5f74a23da0d015172ea35701000000000000003aebb093fe4e3d0bd38e9b040a6be249a10f680d4afee82111da0cce9596de05010000000000000062f15b56b585aca4194e0646c4f1003f1ec573c9563dd8409ee3b9e06de18a5201000000000000001284399e30f2a03778b05ab4c104570c30d929344d9785cbb412ea09668d7e4f0100000000000000eed0a44cce8ad788c707e7bc6454365652e7733133cf814e5d51b2ff95d553630100000000000000ea538b55ebf258d78fd7988d5a1c843c9f52ec8a519a977a9c835fd872346510010000000000000092b1ef5e4cf3ec9e8b011e3295349b53577943ffab41f1222b2091758b9cc15801000000000000008853f09259ccdb3d1210fceea0d14bcde576201f38f0fb7ae94930d4a65baa040100000000000000ca8079469e665cca6ac1dd47cdb0bdb56bacbd031813f710ea82e6e14a88b94801000000000000000061bd8f3b3ef9e9f391b0462ca53eb93fff4f3d58fff22e9f84061a53aa236d0100000000000000a086680178e3769f1d50fe93858b70fecf29743f7bbc87b11501ed64bb087b37010000000000000004f431a123a6d3061a18f9553340169faddbe7c5040bb490b4a064b1e0fa927401000000000000005a3d41d3610332f2f571541f984fe6fc38da4251f65a3d81276ed2452e4b1a100100000000000000b0f8da2a4b24053cd5999ad51d0240688269f87aa7ccba7714f46a027977f7080100000000000000bcb2b4974bcee94202bfa32a193ab42facfaf3d3e615c7d901e66d1db11ce6720100000000000000c66d20874fb3e00d049eb98daaf2aa549adea852f3adb625f905518c09c2ea1b0100000000000000705a9cb74d5adcac0f4fdd165d627e10d3aa2ff85fcafc33f2c5315454bd61090100000000000000ae3b8fe38c68695e5442fe037c7d32aa8466b10d500a5194a5708084647bf7380100000000000000de73d7b248f71d461a03af4cad901c83d5959f3e0b039dc987798cce072956590100000000000000a0b18475b63b6ac2080302eaeaf6284c78f69079a4246b9f15ad41691310d71401000000000000008621a861e5500d932d88ac38bbc44b3e5f674b0f97ff0691a9afa25a6863a32a0100000000000000c4089e51c06f4653ba21e1d7ef818b038c009df54cf37e0992d6584fa65fa92401000000000000006e94e70c8e78b8589c175e400a77f92544ded52fae33252482c902cdd20c9e0e0100000000000000e642c2865d5e506e56af1962d29ac53960c84d3bb39ea88a5c3664594313ea780100000000000000f2f4985003d63b08f627b236beb8e744d584ef3dbf5c82804b2473258bcf522701000000000000008a5d4c0de40939c7d94f99684174fab5e0a2ab8de50ff648a7a0b7b98749391e01000000000000005e480e3ab14cab90ac2d02aa586ebf827f06a4751c38b87e3e3860c06a16b80a0100000000000000a611a7e5eddc98ae160057f937968c8ac1f79a83aef44f680ef264c4f88c931601000000000000003a38fb488088adb8b7ea111068484073ce8a181cc6b76737080734cda341842d0100000000000000dac0f653da931dce514482b31b67bd3a7678aa3f8a5a173a87355aaacc4977700100000000000000fe3855e100f68bd31587a9565119777daa2b19e411fa16d9f41c86c55a947f7c0100000000000000e82aff6645589ca5801dcc40ef37a7ece665950cfd48cbd26723d7a2625ce67a0100000000000000801eeae66544febd9632341c6ef3aabed279740b6b28eeb1d1303db8ad0f566f0100000000000000f8f230459692d95fad1c21557d279d5d0bad1a3d43744f8312fd51b339f81b03010000000000000040e2c0bda3d579af7aa0c75a37cda7f8e6d580fc57df98c2e46c682b16ddb92a0100000000000000266916c0513f46eddd8b74fce853a00389345bd7f8e97783d83cbcc9d23fb266010000000000000060821cc864972a7f113473f9e0071b64c69584e77cc078cba367aba13efdfc580100000000000000f460f9d60c5456f75825a685b229aa6e887b5f19df3a7bb24073e09180b8b66f010000000000000044a2fd326ef1e0ca462550f91c247c613baa409868fc6592aa39ad478c85317f010000000000000036d0f298717ce466ec596541acbf95e460fb8c1b40d5e50980e047df4191786b01000000000000001c796f4302a0d1f0a32d1fb9162b6627ca7097d7a3a1b30adbf4ef21579ad36d0100000000000000b893761395022e6f6b8fdfa59f894bf51dac1cac6c4f52c6b8338a1dc19689300100000000000000a4f4f89481b6ee8d5ce2f50f9af9c49d1626966683a01f3e73c358576127381401000000000000005ae9062275202e049d0d4b8d7701ab0b3b70799c3f89d79fb25e79d10c33f92c0100000000000000dca27aec167e0fbb3b984e209d6a253fd1d0a9e4c19092bebed233c7452d530d0100000000000000403f4f38bc9d1481ade01182beb7e4b8b77a8ba08c1800b92b878512607ec2090100000000000000eee9adef21f4cf375f086327411732b51607179bef18acabaf37d4f92f3dc00d0100000000000000a066fc4292f0ac7db890846fbc944d62f13b9edbc0e6c1eb92e8ee916b94f61b0100000000000000641147da6ce96c1cfb49abc2f3445ba4dd8e00e90721e19ee6ba99c1dfb1836b0100000000000000ca4a99507a5f4391876a1a0be7c26716a17e9f65a96679f2c8b2228bdc29661601000000000000003c09c5079e597b9b4abe38fc0f67677609cc936eb0dbfbf03061da4bac95b53901000000000000003cc3e4c20c232b2ab4f442d8bfae67a34730b69f01b0bd2033d20b5b7e670d6f0100000000000000d4db4b7b7ab59840347e5fcea8f1cf0524089fdd64a349fe5b8f5aff6ad99d7e01000000000000006c2b5d66b96f0c473fa442e5626842993528592791e62875260cfa928c96fd410100000000000000bef4f75529d960a44d2878fae1824d0903d5052fa53f75358d03b0cd2f550d60010000000000000092b4b6988ed74ccf189bd2e16868543b853d38e4ca82c63afcd3f3ad656d0e1301000000000000009a75a1a445f93eab93aa0f3f328ddd64d5c25c32788289c42301fdf1ff8aff4c010000000000000042be82ddfb8da6ee77760664bc9c0dfe6a5b7bfa58dbe148b965284514d0715601000000000000000e8d5114e90194b8e11db6414e52c5f9c3c99a886c478263ab17e040e1689f6b01000000000000006864c42f3cbb918799d7626c63725fd96cda1134944ffdf2925dd1c73aaece1b01000000000000008cdd8b5db78d01220b9b747813806f33d6b1d8c19d9fa3fb82098a3f8d53c80f0100000000000000765aa204bc165c6f9595b2cf359a1794f42ee6bd273118a38dd7a45dff33406d010000000000000050478b2d5c6272ba095f2d3316f19aaa06ff22f948d727999bb32feaa873017601000000000000007476496c52a5cc5cb0d1475412135699072772af64302a5a4cbc384ff2b2ad2201000000000000006c6a5941354b9419623fd51e0747f8f467f31b6f4b1e38b465107f02cecbbe3b0100000000000000de09c9e8d3e9c785c9f51c407a17e14fbd6c3c3187dbd85ae23c23e88dee413f0100000000000000d6501868e1cf90df4f8bd798e6940f4121f0e986c899f5539138faf8ff50fe0d0100000000000000e28b8124bfb689408bc656ed979b101069c42adb4329a14f972c39a2a3e9784201000000000000009459d084cafd40dea98075177523fd137a8e6f35ab1068dbb420e616ac176a2701000000000000001262742c54cd31a33891c1650618ea0163c1dee7fc38b716d4ab05f487369d78010000000000000074cfdb0c8499cfe04a51d0cd85ac6ba602436e8cb00b77fb2c1ca01efdee565e0100000000000000e88db1672f41855dc8fb8089b8506989ab40a1f8566f4d564e9f82138cd5b56001000000000000005c223c2fe106334557847ca756505e801f3f798f3537a116f2890577be420d4d010000000000000048839721a668d391498431b5747d7935d55104a2eba9489077abd8777763dd46010000000000000086cb3131c5406660f6adbb0125dfed1efcf8b348126898732fe9e89df5aaa90901000000000000009256c366f4a4070d2905aace2d57295c36d1082a179b17c483f42e944851cb60010000000000000024e8ec732707e61646a06a013d1657f6a35819bada07714f7b57462a03246b070100000000000000323bb7505e17594b4bdbbc7df6a205843f1f7145aa1c40810b2bdae58a95da0f0100000000000000cc179fd48870861bf5cac6ede454991d4f354f986bcf36a3aafcf9041d6b22660100000000000000268a98a0b0182f23a1f51e5adf1e56274d10a977744e8d0fd0b6e10910ce1c48010000000000000040a09307c5938cbd5f5838199f5da3d56052449b745ea13986b5292e290da33201000000000000008eeb878fe73763486242835e2d2b3c65a1a78bbccaee2ce658fed20b793c02350100000000000000344ef75e232ec97983125b4cfd8dfc4ddb568ed357332adf06f209d0183c86790100000000000000ac1fbbd6bffd92d23bc663ffc22f2d056f04534f6fbffe6fdcc2418d88c17800010000000000000054e139e51d28832bb556dac1f9cc6bf8cbb0c729ffc0a2d1d1724c72edd6e62201000000000000001edf51c6e6bc501a13351031b28451af2a98085f614ade4f2b27113aa4473c3b0100000000000000023518b184a5c21ab39941bbe6019b68ab6f2cccf27a4caf0099b8d4cdbb920e0100000000000000e2c23fa5e675899b0de4ca207be07b8d6a436101c5cf4c023db0624d56a46c7b0100000000000000d03a71fb9750afc4e9d30f612b70cbe2e617816dbfef42e5c1ccb946099be154010000000000000082a7bebeda9f9133fbd5c9af9b470e1362eed16a603c28a209df52743262015601000000000000007e3d9d762911807fe95f9a79e45faccb182e612c98cae93e0d419f8af768982a01000000000000005cc4dde005b2344a81d2b52f427c60a1b447fe5fb50f0cdf59e4aba4f281911e0100000000000000e420d5946c2d0d4d99cac1e224b2cea6295490ee3b6a9803cd28cb03b9aff57c0100000000000000b8af11e910e884a06701a7f157684ee2d78dc205bf3d330cbe361586db733346010000000000000036c4bf964f566aa45d6a4581e2b56c64923944dceb6f9ca332a5b9faa374a1790100000000000000205e0711fb2e06eb53f561854a722e0aff2a8e3c82616ecd2c6b78cc56327e580100000000000000daa0ca1a484715341fedc1f74ce6f2d62764eef5a95a5fbf3225db0ba79b076601000000000000007a8c92d925792e50eba4c4d4ac50b8302ceb21967cd44e94a3b0a8f1f6e837710100000000000000a2751b4ac6489a8985a70ed6b3437e0649c909f3d6effce47f0a20ecc33daa2601000000000000004c0395b7d2f32dd6162ca1e2b117e8833b26cb181cfc4b6c997a8b73455f1135010000000000000032e994c3f4e677a6d4df3d65ca980595264dac5140f52b1968748216c92c75550100000000000000c6e3275fb29aa69fdbd35aa9b3c3161875f3946016e575aa2a95c9cc6ad575710100000000000000aef5d56e0a88f892ebb22a48771a10c72eaa3886e4609310cf613c08d8b74c20010000000000000018e2ffa37c2b51670e849d1ba370badc5d5abe3678c59d46e0bffdaa4ad49c28010000000000000072cf291be9f6b9330459b406d50a21ef4d13e68a645f93b80422be6b9d875a080100000000000000c2ffc531b154837795bdd573a00b6b27e177a872f311353de1640060d6c3c8570100000000000000422517e03ab4255a0870a7fc588ab1655061c7322065a1fca053d82be3d1cd660100000000000000eaabe802ed0951368cb8379e36daa67e6571aa3c365c850c124fd9567c4c9106010000000000000002cfcc84716cbd61fe5b3b4011701326a7bb865f73c99c2736fbe41a264d30130100000000000000aea9dc5a53103f390c8584cf892bb854f64ad845b382abb3220c2be0a70e492401000000000000002c2a070ca5392bec7c3f56b2ad808d2a4fc3fd8e857f0ee2d6496aabcc970a5e0100000000000000f4d8f4014d1bd8343aff7f5f1d4366ae6985885032ec8d6cb198a054d085360001000000000000007acfbfe9d17dcc8bb5cc4ce3b62c4149d51ba5ff74293ad82ead4e3d5c278f21010000000000000092d47173f0a1a3d8db3ac255c2dcdb9df605501a7fe33d78d504c4cece2ad12f010000000000000036487c3f8eead755b47b123b845418d12e16b590aa24338a885185809b85a02001000000000000005458c0189a88a6f5807d6aabfe020ee80115394a1b67c33bad109aa647fe3a3a0100000000000000946b15252e2d51056c52ac8024b47513bb9d08e406f86a7b7d4778028c4fbf770100000000000000aa1f85bf11a5565f36a54f17dffbc62fddfea81f4209796c120aa5bcbbfee25a0100000000000000464e26e781ecaf929baa74714f82d03a533bbfc07c95037032a46459f07d5a2901000000000000003867a37432ab13fcd7f9c7bc872c23e0e0e9cc20bc01b6783e3202a396cab370010000000000000092cca5d3fdc514fcff6906eee877af979b006474a4eda8cbffdd861dbbd33b02010000000000000020aab3fd9115b4f4385883164cd41e282f9c9d547a46f9610aea615d99de2656010000000000000098378caf3d7a44614084d31da9ce1d96f2f7aaef3989ce583dddee4d04b8252d0100000000000000980b477be76f9319f3ef5bb6f0de390d75f362890dd6124a0685ab06f4158b14010000000000000098347031396336c66a994bcb21b8be5906714403cfde0dd94615c082cf42ec6d0100000000000000700fb979b0f33bbc8a9a4ed3fb3474ed2b89cfbb7fa7d1c6bacf6d2f1b76025901000000000000007e33ec70a5a0f0f5b9b1a0587deb97d390b5d6bd8315ec4c69ff8599d8821c3501000000000000004af566dc40a9f1b9698d4d28977db99590560e378444a9592c519c3c7e0f00350100000000000000e6d63bfbb03afa14d919f48fe3a3ee4255e8c30f32b6ea6281ce7152ad694c430100000000000000a78bc76839ff82d83efd663784070bf0896fc02afeac80822a41608830515482054241424501019c1a94eb71ee590c490fce5ee5aadd926cd65a2c20072ca2a7a85e6f62031c1147878ceeab89b5b524dcc4d5c60fd7d312f979fb91ab13248f42729bfb217085fb7338b43c8a2ea8d2bf05028cc34a70d6327373ccf39ad57a8ca321e3532526f6c8e801480a7e7d51e61d1adf7a1eb3e0080246f0bd5222754f75cd1b5dde2972396236a80273eb54ba1f3f333b7c20f10b6b00eebaed5ced823af115bab7618fd9b738080642414245b501039800000040b5221000000000c28bfe3c51bd1b7a4429a373b0f8dc077bfb0dc27e423047a456f747a3661278e6b3b2b727049b0737b3c2e3bd767ad12bf07a5826f608bb0b7b4bfe05a9b1025f2e7ad4c53c60dc147484419940cd14336b3c34a468e562839f97076ab6d505054241424501018ec4b39474c79c67df51fe9e5136e784e3713f6f4dd5fc56a04dc8b0e18d972f2a4a22b7cf17b36e3b66a1fc4b02b28eef21dcad7d3cdbab8ad990de92b534817f649fd1e8309d0def8ead96b916d90f88ccf6b49301f399e58db15b4dcbf5fafac8e80112a736cd0d9aaa314e72e058382044c7ed52ee2bf9b4d90313b0faef7a908269bdf481865231a91256bbfe980405e2f7b4fe08c7a22229677a97983c29f4a15e080642414245b501036c00000041b522100000000024769f59715cccf3d148afc5db5d21de18ac2a21e228e06d7f2e596567918532ebc428b884807d5d87c3db118b6233cafdfaffc1421ffc23988c2c457ae15a0da47b241b4ac29e9246d4337439f79b02d77f3f3ec93c1c41d0ceebc656c7280705424142450101b2b77d59dd0ae8ad1d24b108c365b02787d6d5beee6661d105e92510a119bf384a27a6515ac25fcc60a333ab889471df95a48b662653a2b15088604350f817853826ac780bd9c1da8b7e38dae72e9865c1ad30826b4bd018b447666b4a8f9862fec8e8014069db7704bb9cfe858ebcbc9dbe1708d9c633bca66b82b0e93a016703ce135931174a1e7fe4e81ac5c97f142bd522227a8ca428785139c14d488eaa98ff7984080642414245b50103cf01000042b5221000000000be809cf52fb48ca67dec1a7ac6302347f37d73322f41969d58271ca16f174b6b6f2119e545ac4b552e3304af9d3924ec4b33978f7a38540dc29a81d5a110b10d74bcbc6f0f82cb310abe1f04eff5c5d5d1f4e31b7eb1e86efd9fd772f9d792020542414245010108e3a133563b807d92b234e6a300c265cf986483921cb4f025e9ea7f49533608772cbf76f7136e08a9e4d64f9e1e37cd87db3257fc7cf9c8516665eb08eb428f53a79f129c39d8d7ef61ce3baafa72f5335c0732411e25f5602eb03b2ee7f63602c9e801db0823951d0e903170ac65230b3b8b835645dd4773abc9422efe0e9ccd0fc126d33cd9fc2a0f7b3cf2e5f723c49a1d5f422d7b9541e41474a597b61fec87aa91080642414245b501037600000043b522100000000078a3ac36606f0351d7fa39777ce425a53e3da29e6098480cf383b27192fdb03ca1ba80a9122250f059b1f8e113a4396d1dbfc753b2bcf196881aa98e731d200748cb623e720aa74c770cbc652899e75076bde8f2c4983f11e03c4e206373ad0d054241424501017aab5c3f56a46abb0ddfc1061d0c9c2704ea22dfb1354aba3a697f0164b48011c1f28d292cd4c1040c1ef6f2b6db08895f7b2baf05772d80a08ecdfd5068bb8c94ec67e37ec36fb911e798fd2c0f03ed0b9ec16811e3327cba6a9dd66343a0cd06c9e801a0b10a10fe079b090fe1e085310e244b190bdf3733f6d84a14109a39adabed6b0cd0bde520853eb874afcd63caaa44042841c79ca1baa7afe67238888864f682080642414245b50103c502000044b522100000000058bfaeefbb638bd0d42e99bfe53b1e141cc416a6f6c95e8dd5c40243e1635d23f15e4b48b46353736a269e50b0f57c013ad4bddf82bfeb76ff7ad7c167c65800121a6015a5d1922b04c112142a80b97c3bf3f516f4681528abc9f149e349620d05424142450101dcb8ab37b1046dc37cf3782d07f54da2e3359d9cb27219d8151b3728fc2fe35682bf0f5c3a2451c0443aff6b02e21978131e3959ac9158eba45034e1000e3580a61a87a0af663e2cacd5a8ad6834cfa7de1c278239efaf399b87013187bda9290ac9e801b5472e33deedfaf127953315c4197b3a498b033b3b7c94bd154a1710876198e92f093aaa8a30e07ec3e1b7e710905d095600f988c06442a79c855100f9f834b8080642414245b501014500000045b52210000000006c1c7c9a002a68158e543e93d8c8c489ceab583056ca1bf2ab507711dfe7c839676f95cbe723bfc929bd5bb8208edd86e6ec6f035dd5e49c21bb61d892b12e0fcffa9d3b438ee8cbec61d2090d0e49df66c46bdd70b36e323687819b29e9c70705424142450101cc65fe57a2edca02d04836d933593be50d8abfd59cc244ec7ae8e62caf74a827dfd99cc3e77f724717200f14fe8ef86f1f18bc4ddd403ec9f10363c5d7934f8759d49979f3391897b9556ab0f730d43bf937a9d630d63390dd6accc2b80949170ec9e80151f5fd4a6cbecd71271652dcb61d2b64f91664a4dca60cb2de1ca63511bf3a2d176a31d7170ab4c51030172a42b70a606546926637eeef41ae7e429ce4755d2c080642414245b501036b02000046b522100000000080550273acaaba14a89f59fa672c2b8cbaaa48617727f6f1d33f0fccd3ab455aebafc7407d71fd61ba17da4c7c3851a5ae5ab10d7c10aa008c2999ba7655640d760dec937ea6e56b6be15ae2a0eb83839b82d908f7a8e7bb20f2d0bba412b70a0542414245010178633cd3192da70d750e1e6c138834b0c3e6fa95c7532c12defca2c355f4b4552e01fc92d07e3fc312a7d129c41a383d8bda858035c346982164699a9014f38cca7521679ef37bc93ac3bb24ab7419ff05a265b7111fe6f3c68a5a3506a4d7d912c9e8011fb41dacc54dff783ce9c9807a12b8ce031409019e00f0a51d9f6fa66178794b369ee3e93e53cecb131dfdc4d741c1d78a924bc2e83e27a14f40c51ff4214dcb080642414245b501015601000047b5221000000000ca1e5c4d6d272fd72e6399d6bac7165582fc47cb02c80f7a4183545c5f65ad05760a71a7288c1c865affb058222ad6aab087276815d8ca1f43a3def0a4a3ab0d9970d35482c3f2e1893d465f246f2c744152d495e00ce04faded464f9bf8340205424142450101ccdf8334b7ef04af265eb83b3722ddd1bf6929443840002e678deddf668e885aa36ea7db436813e5938eafeafd7d4969cf5e332e3f29bf8989ed09617de76686724872d9f46bbf8304ef3e3835273ef757dc49d6b6d0f61accca311951fb6b2816c9e8013f5aaa68e9e5c6ed64e52c8a941e9fbf1a832059853ddad00cf28c3514fbf0eaa61111a5df827f3c712156e5444161ba9966ff87384c6299d3f5a53508659558080642414245b501038e01000048b5221000000000cee749d3adf46a50836f444008780e5514026e77e912d4551350cc8a8623990d151a545c7f75d0c27932558d70b95a23c002c66dfbb11a13e49e0cdeb0f8d50a9f760bb494c0e47340c8d2c3bcc9de48f8cea36085226b1e111563859d11810005424142450101a81a8c0cf00541929c6c1ee486464cd38d228c54f2f858ab98ca58b33cd634720d27fa008b6a4006ce0a0e7b808628655456fea747a39621597ce11bc742c888ee89fa1855d232a1b9a4553c835d5b75d8aaf5c9e9a2ab9afd98f7614a0ca9f71ac9e801d2e42211cc5088ab455092aab526f3e29edbc0a2e3db3c1c9e5166cecef489dba7e8e7876d55a3bb6bd85e995e8dfa569347ee6efa044352f82e7d64e335aba6080642414245b50103a600000049b52210000000002a18ec30778fd95ad346fc112db5f779d129b353517e6aa012b5e45993e8ce33fa71d567ee7080b389fffbc41f5a77cb5419471bad9062533cb34fbcb8d401087bb5765e326e906b29c8fd1331eabfda430b7a708554c219ebccaaa408c531000542414245010104e17928ad596320b3511e4d7deb46d4a260ce1880a773a37daebbd2f1950b073a6fe48f9f37e06f319aa4cd8a51e4b84a91db67764986c5a30d695bbd6fb08ab7d6eb43adb30dd41c8f26dc15038856af97469547dc129c6eeef5112927b05a1ec9e801d1b39a725131376ceffbfb967bf9373a6b88ea5ae08f6825e5356d46f4a4c0e291833533e5b390c59567d37d7dd0b805376ec6b34e749af46456ee6a474f4670080642414245b50101e50200004ab5221000000000c8e2b103444ff1f06ba0e41d3b245c678e94b37956286f68c3f0972ce282b76883f323ce7642bdcd97d872afc3703382810379af879c790c2765f96334e75f0c8514c9f287f2c6d1459767c72cfb550d2ca66dc46af67d8b2c174c95c352a10d054241424501011ed2701976fd17d665ce84637690b30bc3c07b96e2c98c4a3db3174579812a5d5a30048b51fe304ca35da261b5c1f26bb0b40dcfffe7eaa7c353e4f0f31dd98619ee13abb547f78855b3ceca5a28d10f10b281a260a64d8872294097848ca22e22c9e8014507be4251710bb7fe5e15b37ca5ef815510a1fe0d4fa1b57c34588d1c31871b2ef156defd13def6cdacb603a818c90f50e8f534fa33ff03bfc6d7770e133f1c080642414245b501031c0100004bb52210000000000693cf329bfc9a531fcfd5374ccd2b2bf3971ba451313577e1ecc9abbf9dac32b10910d6701238650b5fbd6995100c2e2f6c488aed88420be187f971ee668b092c2f9cd69155d95d2f6a9ca75fbb439b6551185d7c72bea26bc505d5e5621b01054241424501019020fe4bbf83a22b094725d2d86bacbf3d9801f4f43c285db7b6fa2af1603e4536def40bda68687b9348de53e768f5a035d3e63f8b9d6fc8df575af5883a2582cd7005e147b467bbb1ad7212023341c4ec7412ba148c2580e5ed4849efd3919726c9e8012e21225436a1f3c6303317b732eaa23cccab4627e5bc6a2f9bd34c6b4d9ccc85da6c01468462ce0f952f5875156a4b2f46db2dfed4d4c4e5e8d1c9f3c270adc8080642414245b50103020000004cb5221000000000fe69df7b5a51cbd8a243d575152357d0715ce6865e4d37271d8283e1f3839d5529610d5dc26d19177408c9b2efd1763197f5dada3b949134c70a6416a7cfff06a74e1b1991ec5e6b87eaf03ec649db45a225e17c00969804744841900e77810d054241424501011a4ca15f2bf03c4ffafd0819ed6d2ec11fb9d74acee40620d1c944238a86a27d9995f13f4d3e2e82608382b110a9b3fb3093c3abd5398aeca862db433179f08f52a9a410a49c57541e7cc01544d3e45a8572e746dbf397da55b03f8c5a7518a92ac9e801c110214ea9ae1b2b11d6b51ba4aca30e8748bcf2379a5f819aaf49723095e7ac7adea734474c88f0d4cccda538b0c91852a1447a074ff9428b1878cdce9a6a0e080642414245b501036f0100004db5221000000000aef8923b2faa1779667b868a96efb16a689aa527eb90a8b7fd1679549a8c2d54caf7f7f119b7232dc505854a882d949cd973fe9054207646a268e1bd01a33f0fa7a921866a36f2e16bedb31a8468206db24aed39dde583bb4d35a7085997880f0542414245010114596df61038d00dd477014f2ec52401eb51e05879daa45f77a844c226d1a038d1be5a2bd6d9e451157af5c2b8ec1d9a700b4880fa61c0b82278af206ee2bd80aa7a1f344802376498b81463e2c45b4800e0e9059b1c549dfa3208a15f4820772ec9e8014307492c1fdc91d21d768ce462565ea1154f253dd96d9ddbcbe5a9c996aaf9fd10b6aadad3e1a9ed64ec507f748ef7ea8e2c56984b7c71547b9ea913313df459080642414245b501038d0000004fb5221000000000aeaeb697417fb1f08b94ed98e7d2ae28f57536cfde910647717e3573306d251538a4f37fcc22dc30050778fe6ddcccf019c16fe3437862f692b87332130ccd06fce666912aa9b7d43f56949a82202136042101a38c086b3c62c0aca27117aa0405424142450101d841dcf6f24c7bd7d0733623ed583775d85f27e084854de2851286c64344907b0d247b550de67fa956c37c4600321fbf7594b726aac8f07f57cc1e5359f27a8d4829c47635681b97b27bf78bdc40b4b86e482330501b4a5327dba8e4e21a512b32c9e8011ceedf71991bc9dcf770e85f93872d6b8f5a16e89b17e78bcb8b423c5a4b8d5359e515f737823d148cf63437c0b72e113685e92f03471d8eaa1e2bcc038fdc0b080642414245b50101d300000050b52210000000009e4bb5857c22f9fd927f9bd6cef4a38dba162de80600794037054c4bb43fee51f85a0d456b8cc95163c55a93498fb6f065afafc169a5bb523172bb6fb386010c08ad46127f25370dea1a2acdadb6b0ad3cc780d2ab1a5d466c03c1f1b7377e0f05424142450101b24ad2d01091175e024e2bd151a7bb285eb19c821855e11e6499e614d9a58663cee0582f5f430524ca1d008aa031d26964d2bf0a8c48e6f3555b6c53492b088cb099c47e64509883134d50aa4968d32eb8dbd59174904d335ab17df32e27ba9d36c9e8019b10b490dc19ba3339d8def52f418739b7835e3701f07e67ff46d9b019d147045637ca592d3d54b846662a084dbd3c78436e265bdae577620e2a27c42d81b87a080642414245b501038502000051b52210000000009a9ae4def1f892d6092dd46f035d7b312bf7e4af5e3a1ee079b63fc1d7f83916da2cf9868c1082ff4819bd5f46fa711cbc9a074bf21460916363084e537a800a1ad8c24a9de198febee9423c5ee6ead62f8fb14b4c38faae521e12d1746b7e0f054241424501018e3049469aa9a354968f408c2e0021571576d14e331fd8ec91493296806ad96a6c57dc1407d68efa18d2eeee4622829ee19739fa38a20c552161740756c1528bc2c18eeca9b51f4644c19a3990c30d9c7295de39698032a0d64fac6590d85b9c3ac9e80182e4d0e78c7f247c98b63a6c2a8152721298cd9ef38fc85aa5b093994ccc5fd56491c06a8301f04e8bf8f869c28b46f525c78a67c25bf5350440a5e68d03b3a8080642414245b50103c402000052b522100000000084e61166537c5380bd028fc1286b17e99c2c5bfb2900e21c6ad31aa5a9736c00abda0a7928090a2d62f44223d8a55e9574f21f08df1f31d07d2d91f6c49c240c98df8933d9cabdd78df8ecd330071075d2aeaaf5d5bb5d93078917acc9713203054241424501015450395b430799691fb65a78e0c242038c3e4baf1418aa1143fc342e273af4637304af2842932b368b337f6db55e8fbb11675baba25cfdc22aa0b6407b03498992473b15faca9f4e51acfdb4021ae5603b2dea909f994b808852f0b67e735ebe3ec9e8018964a6615d85dc7d0713259121385533cbf7e805f769b23ab63a24efbf50f4bfab128e7267b3a53232c26243046889d2f5b198f4fa31e50608620f44f9121feb080642414245b501039801000053b5221000000000084ad192e0fa6fae2051b0f5cf44fa85b43f1c1bf5bf01715b040ebc9aa01e403196b3e83b8fc969dfb9950aa221ae5d5cba4919ab61acf0d425a1be6d95280b16a199db885558377c5e560ae075db8004726fa11d11044dbb3a6dd73f82240905424142450101cc9b7d7f1803d5a819c0003cc4fd326a690d28945e93aef50800edfbe4956671d3b4039db0889b582642e171dbaa228d57a4fdfcf055b185cf2c969de188248a513883cdd926953fafa3e644ff3e6d66f9039b9b824551a10825e9bece5455ba42c9e801df323a7a5a33bf06d095b459675be0cb3f46360dab21c08321b3445523329a14cef08bc1297c140dae9aeae72f243442cf630e483ade158f0eaa30df632ef9bd080642414245b501039701000054b52210000000002c5c703692c47cabb79f8318333df8e076577587866157758118d66d1fb7aa016c9ea08137afaf09108120bee2f76080147a83a5495216fbbbb9333b66014b03c4af7fccf8e2194d9f3bb30922093cc5272f62a310f89b87e5857e872b64af050542414245010196182c546527b3be89e19a1345ebe0b4f71ac4e0ce992496e259f9e8639f5b39f5d1a28d9c7e5fbc618cfcc144ba14762e41c3e3b700a88877429f7b68eaf98274191e0a150f97f2f1dba5038eddbb5d1ed30de64bd48274ea787418ca4bce7246c9e80170b1c1c2ffacaebdaf6cdb85855428f70c0e34b4930ca0a1e676fa2e253ff0083957f02fd394c1b0379e193a74ad4bcfbd93f7ba482a889fe90f1457cebc0bf2080642414245b501032b01000055b5221000000000fc5bb67ea177710ac6238a513b24456d8d1963ab55815c46bab0f7b3e89aec5337839a2db4e1ce089835792fb00a1b370978f2d0b329a04b3999e5f40677360a0108f2a3eb5c4d2fc4c6988eda3af4a06f00b20c3b965721088c56499813760c054241424501019ee1ba77495db84e60eeee4645ee68c3b93fbf134be68f8ecc01ebc2c73f7208c46c4a884eea79a30b160dde9c29b7ca27665d0fb392989402a7650bab74ed831250f691bfa3ef7df5fc733f8cabcce1719e87922280dca500f439f1d82896ef4ac9e80135aabb3ff073221dde5326da0e9fa246def523c94df149edc060a3bb41ec1e6c0088b82947027d77fbeef350ce36f8d58c71a286087468ac937eb00ff42356f8080642414245b501033e03000056b52210000000000eb0c3ba33848c8771caad7cdf43d60da63cb82aec44f3cd7e72b27bc7043d18f77fd57499d219d0b7f5674c115a811796ec8392237ac63ffd56b0a383abac003617db2d1b7ce3164eb0d7298f416c11f383dfb46531811c7bff0b2100dc1707054241424501017af0144607052bc8df3dfad3fdf8df5f41f54c90311c5084a4b87a27b979416d5561f50c48937c51f8655f34b89d94c1039090cb99078edd4ced79921a48b9823622e7a558292d2e84a33157c7b9135f13aa13eb082e6b2601922bc5beda699b4ec9e8011c23939f58b57dcb66dab7dec850ba98940f06490fb44489e9ed779d76165cf48cb14982cabd58c7abf82281b9e40fec56acb2ccd71dc3185682be3ec703497d080642414245b50103e001000057b5221000000000b849eccf63bfa7d43027b0f9b0b2b1585300fa078d5ce1defa0449e4b96cf972653af6393ec3de447371f42954a54f7dc9da7261b5f0587ab35d69ea7291ea074700ca22dca156b248c85252ca1ec0c10fdf9a96907d230146b0a8cfdd31e10305424142450101308ef49a53e5279bbd00f83967838ef1638058c9ed8c749211bed590a592692eaadc3d25a9e2ac9065d7ab9e54ed932ef3ef853e4c4c78c362c4942da4178b85c16763d8f3f488552b6d3d7274a0470f78e3b6e00c6264e2078b4e711d09ccaa52c9e80165bf640efc7efcfab92e47f061a887a2f7b0a4e067cd77d74ceee84846b947117ab60280f2da40f222ab15c62a1d681a08c64c378aa29ba3d7c4ca4d37f08a73080642414245b501034502000058b5221000000000668820422fcbe09e463c1c38d26f1fcb392a636884d7a2bd93d249542affce20fd366b1293d625330161678822add38bac9b8e8e695f759c0c22b1c13cab180cd88e1a675327cc756c485e4dbc9819e4775396bf9631e27297f93b559cfc3d0a05424142450101e88578ebaef8054041bc591c85fca553839aabb48de21d576b5fed98ffd2be3e21ca3c418bb87e4642df2b2dc8758def0230785c6005092bdc63faff98caf288a672ac0049f9cec8dee62d68e00a2d45b1320dc57dc6d63cbcae6ea2ff380ba956c9e8012d0022e4fd2d6b27f6b2eb7c280b05149ce427f287b82d6a2c93411bf6c171c8b47468e11739bee72b4a5ecb393163424ebb3dfffe06f0c55dc421b0719ea700080642414245b501019701000059b5221000000000bc24038c02da028d6d9e1c879aab2eaab65302ce6ea6258aa6b8eb1d57d60203a7e5f4ec604ee35fc93cac3379c88e1b799ab33b8e1eef48ad8e6cdd20fffc0622e15c93800b1ea27f72e7f07360bb3e10178a9fbafd3bc2375c5fc029e97309054241424501014047970f9a978ff7bafb46c68a36ce498cafb4e2c200564f64ec4c227e5bf11a07ef563a0c81541912a15d0c401194c717178372e5d9237a27578618d7c12e8358cef9ccc78506cf0886aa28446118da6e54b212bc0a9b3739b64b8c26bf88ae5ac9e8013aa3a1188171c52c85e1117ea8ea56f06df4a1f1c2d1b19e92d3c1ebd34cf5bdb0822723b965eed108e98112cd3f63a512329b0c5c07b7b4b784568cea78a65d080642414245b50103640100005ab52210000000005645e6e284c1538a33c2325bdbd34016c41c0af437e358d4bb3097301d7acc755cb5f10adbc83d01cd80b7099b3f69643466384bd069acaa9f5888cec768c805882840159fd004d450ccfb0f7cf37bfa0684618c317955ed4e087db67fe7c5060542414245010138f2e742344ede603de3812758d1782a7dee47f39bd586306ff09d07edc17113bfb710ce5b39157cc9b6e677fbf9b4a5646ef97dcd4d50f1da0ecd3204de5480a9175b211e6153a2b42b0d199da3e9eeb0919d32f751f9d7cf68c623e97aa9715ec9e8015ef0f988568798aca5f00f970818e39336229781cfac0cec5885ce6dce57589c9a501168d0a29474f13328b171d105be325ce2afb8b86e62d6626345808eed1f080642414245b50103d10200005bb5221000000000aa8263167b578a4630f6da96de8a770f90efdf31cd16217b3b26f99e5cae956a3c10d3c555e14e9486edb7065394bdaa2c7945d82e1e261e68850882786e400fb5d4e3bfd623e4ae3a227cef4972d6dc783a66251cd84967835fdd319601810105424142450101f451a393cee52b560f9c38f3382cf5131cdddefeeec95f0b49e384d911d23e272ceda39898da9ee8eeb764fa2cb4a91baede8c00a441dd38a79599966e9de38d10f8b405b71f4b3f731e69e7f852df8b7a9072752bc1d7c8cc0dc7a8416aa90662c9e801cd6cfb19d9027c281a6302d57e981d6adb6ba4a157ec515b16e513f60e13fefa58f3906156df4a7b6d5a1b37fd07f8f061e3e09beaefcb5b4c06d730840b4784080642414245b50103150100005cb5221000000000268c4db2ca54cdc6e002373da160349f4f8afcff6ce55c23c02cf139a6105e30b9ab4b881d3d9030b1644515fcc87ba3e2e9b042bf14d77ec1a1bdd2b0406502ee6a0662ba899f84af768c5189625e065b6771c58ca5323c8c4473e2949e250f054241424501019004a9f525533c25f7c0e3c849a0e17054640b4ea2fbd3d304586721a3e08d46ae683beb9d3f03393a42ddfd9728b80ff045ed80c448aa413098011930adfc8c34c89087c637d509d63c1711082843de501ff360b8d5743f88d9ea259c32ad8666c9e80113f0947f7f5587435b4d78204eadf6b54a3eaaad1e391a233c610f743715c26e20fd5ba43b97dbc158fafa65b25efc1fa3e8feaa903c31f1a0112819630af17c080642414245b50101a90100005db5221000000000e20120129e3a3e5203c483d834269b807fef5003618c0bdd48c39b8918652a63e3608d4fcdb835f3cb0c78e17352f8648e512114c04bdfc451e2b5ba990b4d0f5816f5234ea6ed3f032cd7a4b32fda94b61da51adc14d093be6a060e73063e070542414245010144be62a62a569f6466a52896deb9ca668acda920b99bf57caada83130fbe940487734dc339a50a22d69d7c78f69570ff6eef0b5eee15834b1e16b8705c8889874048edb839031a59a9226b5a0ac5eeb49fc05b61d9f6c7bea786e158e310d8c56ac9e8013f9f9d2738de70dd40d4953c3876c6058f896644f9b159278510a12f5687922f9ab8959962f75f75cdc01557e2483ffc0edb9b045cc82d575b1c75ae987543e6080642414245b50103600200005eb5221000000000280c3d82654e330f63a6fa14de9f87d66af8189ba06a705039eb3c5ddac63e615b6a5401d9c93d9af956a45d73a196d9b46283406e75dd1a3bbedae5fa687a0214edb7362468b9e19e064001865d9d2858250f86f2099d8f80985058ff9a8e0b0542414245010170af54a08ede5335b57260fff9cfdbc0b28d436eaa783c4f0a8aaacbde820a72ebe3abbe75c84233d4d3466a1fc8c23e238b2e86ec8507a96a97cacbfc93c18482e651a90fbebfff03ed87cd7cd4f0fd86bc70e181a1b81537c47dbcdf87a12c6ec9e801a7d19c939625f887b5d7817d472108f8978c9820de1dfe7cba2245e16fa8f77b0777cbe0c26dca0c1ef3c042418210d20834b22fc8c1e7a8e509517429abdd01080642414245b50103f20100005fb5221000000000467bc98b5207808ca0f55ad1591ffd50616bd39ce9a606a6b7c562e1841aa948de710a448cf205f156ff0ddafe6e5b32636791fc76e178e3a900e18810afac0f97b4892e3c9c4573a0ed97a5703dfa28586c5fa0fc910523a2e51cf6cd685f0d054241424501013453ae3cbfa7804435de6c7774f8606afae268aa148a2c0392137b5af07f7244cb788a3cf51cbc841bc26b0b81449c429d6b18cf4144db8e2fd12ba4eacabd8b881ff2077c0bbdf15aa0b23be6e50291f7547bc40759a17e17871302d1c1e5bc72c9e80120cc57736630498ef7a29d64b338dc6dbde9f3d3028c87a5b2dfb8e8c285b6149bd6773783f0b0d94275363fdfb60e0d2a1dce5d862ee9a848c376c8fda33790080642414245b501034003000060b52210000000003e636d2ecbb4aacfd462d8deb72361947fccb897d22c96a0aec9762ec6bd58256b83705418162cd5063769cbe4f54889a41ed9ab266431f2c04c1cdb42f4080bc777ea6738eec5233a68559cce186db728f6d30df423b0d17e0504981033070405424142450101a44bc3275a1c46f15c05a0f3167962b01ff319d446b37ab83417624fb48ed41280cf71d58a3910202cfbf53532d0f22c07bae1108e2e5b738dcdb8422c8e3d8ed9d261b7126f1cbf6f77a9e207f6cc32e94b850d81958f74ae03edcb760d980c76c9e8019ded99c143c72b672fa41e00ca6ce11d03144780b8f99dcb195f7cc26bfa3c2b88255c328bd8456109fd1e598865f287b7076a4236984e81ce28e0f0b0bfa675080642414245b501039b02000061b52210000000006aa1c8549200ffa8b2d41d5ccaf4e08c6ad2bc195b275e10fb3c804d6c23bb3956292337087e999c11c3a7a46a3dfb165cfaba1f8153931a42ba1a700387070669dfecedfeabf200578723d6653c9fa1101c0d0f5252e71d092e504714cdad0b054241424501014c3ef43081ea72aa6e2f74779ad72cf39cf5243751a9e0be9a4a4cead6fcac425ed87d967bcc623d472261020c78139fc9d3217b15319c7608778437a87153857b3f97768a870a882701fd5718b77b6887a1ef1b5058d48c56f45e3f757c245f7ac9e801280d55cb492e385baa07b1c72b2f4e9830f3523c4e8978e4cda75147e99243da2b73ed39a817ac8400b45c95da54695c6916ceeb1d9632ca2b1f60a74e76bb55080642414245b501031800000062b52210000000005264739eae73a9b3c706f416248a95b85b69cb8cd04016d849144889d4a68f3750efb6915aa24ccc46fcbd7e9d7ef0ca7de4260fd501a577e10e15961b19750bfc1966ade081efcf0fea326019c35911f5b7478d3434a6d3faddcc453e30880e054241424501013a8649b040f5cfe16ab667fa00b084542e3feeac09418d4ab36971c4992d4d179ec9c49e9e4c6c1224f3bdb45cfab179c3cb2eccced4c5c703cb351dce4ab58fb72bb6b4440ba06c4ad52baa4158657b432d83529b22b8c2627a6558da08fecc7ec9e80148d17fba43e72b7f2eaf22eee7c072f2bd08cde56a9c8421fc1152d544a03c4385c595ffdda866b4391d96bec70c6155abc3c19e8516895dfefb093145a49c9a080642414245b501033502000063b522100000000096fb874f46a99c7df4e34d916347e29500faa42adeee1e296bbcbdadf535a10f1f21ea6b2f55af156ebb00ff911caa96ee4f0729902e4b3eecb00cbf916db308f4e821ecb382a758bc8d3ee702bf79ca592b39c84256e7fec47c86d066b30e090542414245010152e61c9f390f026473c3ca9892441bbff077f825f16731c2bce42d9f451f7f7b522596c8946bf8e3301b936483fa43e08a49f2b440a6a463089c06fdb7048a8253fd715098b74a6be40fab3d16cafb0d74bff4395882a0d9e244aa82cf36299882c9e801708057169b485488cac4b00dd77c095a77ee5626b3642b47ad5c6f53c0b9ef75f971513ddaaa77b19758c8d27457ca70144a899c59dfb644fb17830312973522080642414245b50103a802000064b5221000000000ce4d96058d7494afee7279212f5ecbac147a06dc07c6bb306e61bbb6e0f18c6688c0f94b7c4e782e5fd34abfde6168a2650fcdfbb184b65ea142c2db2d0df70e9c5f3c0c6df3fc7256bf5da2d85b9d701143da8ba18bc8c35bc0b6b92af3da0105424142450101d6a9feb7a9d56aa31b56ee7d0ffe05d58a00a9f7678557badf2045576230a54c6c413998fcbfe3650ec67791b49b5ae2665c716dd98400dba8b94afe50a15c86233574416eb395a8a7e4d647d57013f7cc6111a869129a90c255d7839d62392286c9e8017e6fd3e718949100fe6d2cee32503a4c1a0caef870b060b5f2c9658e8b2a122d7dc2a2fde4c0bcab1073a42c781d6bdcec5ce4bc184c95547725711a907a3f73080642414245b501035f00000065b5221000000000ca0547748d106eb753661312bc78c1a8eb8ad198c442c7a1fe627854ed4bf46616c567b4a53d4422053276d67609f39bf6f5c8e7676a71c9a2679d18f8f83b02e894e9004ed1355fed193804c1e84913b44fc41eb0737ea95a595b694ce0ea0f05424142450101847a50477837a6ecd283fb97c8fb666b8d7b20e9ac9d6324b2102ff7da89ea2dc93a69f1c95a93aa41cbdaec732e5c04923a3a116d8d2c26e07bb4018b15af89bce6e88a772553e9f5c6406974b0f76c4ba2263834f43a49fded232b228bafe98ac9e8017fce32afd022a4ae19421f6ded5dec8a3a1a6fd7ef00f3fe6a213a8f2a7a8fa73d276dc20357252a729a427c6c613574bf66d0a85eac3c66395ef23ce239285d080642414245b501033f00000066b522100000000066608159fb063aaf95fa1c7a6768c5c61ed2aa2ac6e646c33e8d545b85084839c796a93919235cfa8d7c855f4059b763fd4fef13992343f185fd0d1bd6ca1c0bac13466f432b9125c59588ad31d0edfe96d93c10f865399cd556c00db085e10805424142450101ca4a077a67136d7ef891a8ff4b55605da752324ff132aaad4321f5adb277fd305d1bf6dbf2808c651a02371c04c7841118e56a8102c4c7370360ebebce7a228f156a7962e43a31e7d0e5f1c12b7f01e4007b797ae55434cc7052d9de82180ee68ec9e801411bde9dc5498c3daf7b9f8cb7549fe7a3b6ae7f8cd3bbf380c15febe8300fceb0c994962402e086c51e5dded9658180ca1575c16d9f533c544dac3c1e81955e080642414245b50103ae00000067b52210000000001cf7f5e31c18f599611e458be37ba9bf886146f80c9871996a77ea9a302c8f5d1d27cc25381f89de09b31143a3bf00223ac31137fbbe9644bd68c0fcde90e600e194f7e5f494f9d7c130a8f8fd106f1802a57dff587cf4f8c23de8ada12c190c05424142450101e4715df2e2b318ae5b4be5429b188543ec89a86a935d13a140ae4dfe1dd96f059c651f37f6fa64304036d731dd49fbdba5b85bc742d80ba118f5c453b634378d3a24a6b4a123af0c9362fc406c10faf35f1be43f2f4ffe2288bd33993a3bdacf92c9e801d1a94f99034494d4c5e3d35bb824256179e0d7470af406ce9719c0879f9ef851764cefa4d604d6041e6b0bacd68d222ca17fc073db61584414717556735166ed080642414245b501033b03000068b52210000000004e205193e43d1de0a0e66e5097be2416904417cd33b4e74b886512623f79073f3053f79158fbe1a6f8a4256204a420dcf4b7b40a2afd8be9f5e4ef9f2d543e0c09b0729164fb16bdcee6fcbeab36cb4ff27d5372f0e7646c6451f09f62b6060f054241424501017a97bbde9e32f420f01028fb0898865f587e316c2a43df03967a67bdfa15120833af368a54f1a42c4c2abbd018c36375a3d692f6659f0fe1ca03d4e1ab972e82309cc5d84252606490cdc4116aa9ffc3713834de0845ecf6f38870399c142ff396c9e8011f30bd069a5456edfe9a55ea0f1f4e5c5816daddb93e755b906d0769edea6647b6fcd3430468fd315a48eba03f69698841ff4c818cd9c34152c0b1e736d0e928080642414245b501030d03000069b522100000000054580f03c8c885139833d62ce5c69eda7b14da3d326a51b07ecd2033a0f62c029d1a562724c0bc30630f8ed64285ac515d107af10697b4bc6bf3e2310e5f23043de941757832d7b352adcf04528c16698bbff86b076768086d0635cde207000f054241424501018451ca52f1f054e333fc9334c2f53b5796b02b341912499bafdca1334310f724991ec2f5121fc7ca7a13ab285e55de2f980db2f0426905781333a7b9bf8c17854e72179b1bce0ea9774860a3e061bed1bae4b61bb6e66016e5308c3fa21620ad9ac9e8019a1388eb06e211de999d4a22511493964572f8da25ad0afe4ece9b40da77860329590e2ae9e5f61843bc04ca02b869c12b0921be712f6323e4b75f66653cb2ac080642414245b501011b0000006ab522100000000058d4f2182d506895e22d313177ce2ac99edca85822d241469ebce602ede0082780753d4a743e1c9c87886d75d920b065197a3aef5f9d39e4be0284c532cc1002715b7457506cc3d7a834db3d2d9c60e6ce7259a400fbe1735d73aa4e0c0a34020542414245010104d3bc124adfa0bbb38033329902287307e8e7c7496964ab488bd58f1a36c16c96f404c7786b9c3a160fd36fdd500937c557303d98ddfe56759bd4157d997b84931769dc0d37e6e0e5a5845ef8f9748556facb7e9fd695cc30abb4c0817714559ec9e801cfecb955a8707051c7522d916a3498c3ff7085da75987fe9af39821d28b5eff847757276037b4827059f7be4b03e1377722aac2e692681d973a1574aacd4dd08080642414245b50101e10100006bb5221000000000b62c31d83c9a7228e477e7316c48f6cc2936a51174386f8226bb6cce884bc8778c4aabe0f5c14444540304ab05e3b20141a0ee3b9d3968e2a826d8625d22e20b5a66a8db7ae3d6bdeb8afc99f97b81707e54bb027d3bc5b58185c876c7a6300c0542414245010178400d3f02081a782f51e15eed42715ef8fbcf06cdbc3be9be6d44b6db9f825d1a226504da7887732e096eccd3a8cc87234f3c3b28bca42c01093b57d98e9b876819a8a0fa1e43e21945273915ad0cefed4dae26300aa8704d12526ce3cfb805a2c9e80187b2e99f947b98a02c60fc298b5f949eef61968e1ff98b014e9e7aa08e07c200c652a270df072e2ce96f38b5d43dd406535bc27ddb3894f001a75d2399bdba16080642414245b501037a0100006cb522100000000002fe5c290536827595dca926d3afb845ba70a55ef57d6d67dc763775d4094f601d030f13fabcb182689119e16dfcb97a1ee1f6337f66144cc9544a41d9b51c0e206568ab700bd2608fd273315569a34f2ffef5442f331b14f5af65af365adc0b05424142450101061eefea79502f26f295585e369e7eedb5b52d83bd07e2bce473fdff5707ab1a510be87f49d13459c637e230c848d3f9e867150e8f0101a7cb5c31d72e864786538096f87de1e86804522fe4fb9815ff2de3b310c44def22714b1b9dadaa2376a6c9e8018e20735df5a8d09a8c4a990e790d03a6930b55b90c08a55990fef8bff9c5e6585bc2b1677384ba2e03a045e74adae81c3032410804e7ae63ddb537a9914fd9d5080642414245b50103d30100006db522100000000076e9e96f54f52a42ba569e5ce1b02de92ec2508249b3aebcadda63b7efd7ca3be3f6eaaba83297a076058441653c851872b4da53b7df3a17426a493fcdc2210f2ad2bf6eb04e22c0ab733eb78943d8f38784c65c7ef326e7d988bbdf1fa10203054241424501019ac10c768073fc5771ea2648c9157f9b7c3763df94f51ad5be7a5c4405732d01865d5e2b1ec279c46e4a6b290f6d213665e787e588926a0ed97fc99edc14e38d0cbcf4e35ea26f6b8608b42913aeecad29f0c7a469b8abf0d72ed58ce9a51a55aac9e801230fcc5494fb113686fb67d9bf4bc7c8ccb5cd9d4cd92e37deb4ed2b57b2fff06ca3fb2d88a895d8cb26dcd87c853e7db96d0648ac9bab1b96af46f1085b9831080642414245b50103820000006eb5221000000000d81a09d47bec830b4fe0cef768ff53f23a426c225788f2c69b8edd1ebb6c063096c008c323af5ef435a28065802764162a2afd859ca85725dd91122fc31e510f1fa2a5f14d4047e3a5d93afaa9fce0e96e943a1a52760cc791609349ed5aa10605424142450101a2e94ea716e8a6253133a2431da8127fe3a6133a85cca49ceb35fc3a4a79f7289afcf73915d5e595d5c2287b6f11681e0df639b91007df11b4cb9bd7135f3b843a99dddb9e23ed2c01c7c16bcd74f04c6836296604cebe3d51548b1603d56f8aaec9e8016bdbd1b8b896ea96b3b278ecc4513b6fab2287b3b0ad76a9c506740187c06436c2782c3fd2d022d983d73bd4a1ee106ffe85bc62e4564d56b08d291b90c61f23080642414245b50101370300006fb5221000000000d48869c52084bd6112b7538bbd6a7ccfe09054da908e8668293dfd9b75e49b596b4bbf991ed593bb540342b231ccf97522a4ee7f7c0eed72e9fb48ea984b620766bc800f277fe8b4454675177b6a2d9d714ddf03625db2f69e7a239d44a5f202054241424501017cf9d08891a912889952477915240952184fc78634c7d28fcb9db907624180644fe46f07a69a86e9de9680e8efa55504d165eaef5aa101bdc9c7b96dc5ba198a69fe81a9aac8963efa6cc314b02f4ffee8276de9b05bcc8b403b8f6d565af40fb2c9e8016867c73cd0013f100dc9846fc3e60a53cd66990d9af9efaa6bcbc9d184d65865ee6eef96a920b509094eb6823199e7999b1031dccdcfd73c7d896bb5f68fef4e080642414245b50103af02000070b5221000000000388af15645d783486085387eaaa53b8fa839ad14e9307e81ab30459265735d12b85e45fc3f207be8a5caf326d8ab9a1bd3fe8b1b5283cf7fec7080becb9e1600def8c171a51dec96b0cd745d04663ac7e6400f797d3616cdef9ad3f6b4062b0a0542414245010140331a4c83f3aeef3c5b4480187003b43b0e8389aeed7b8ac0eba44b13cb7378ba5e9edc21886290c5fefd2d7b4071a825bf5153f38d533a5a8b30dd75e267822bf31b402fcd0b12f72708451f674a88c6407605458b25c7e095b446e6898d4ab6c9e801527ea878509df2f91b7f00a6ae501ff9a9f6ecdf4ca5ae5749b7a5c0d890fe2de5b8467419355207083a5fa995e23903e0d454c98ebd97820522925b08fb730c080642414245b50101db00000071b5221000000000829d591aca7b4667c59ed6da94774157bf9629f6b83ebb3143e1af6ba271677b7af3c9b21b5d11589963fdea2c460bc8dd90f9b3368b5e569dd134be345b6404dd370aad9d7258282bf49a45ed3b0474028a1c8cf8b30b46d85d8cc3df73aa0c054241424501015a00aaa5d1968722c8233996f53ab2a12feb8844d59978a41003bc68e0ed4d01986706afc84b057bfeb5af94c6b8903a238aa4f5d328a3bb54f92bb5e3e1358123b209be0897668c1b1f243dece432f8f584ff1b91c9362b1415b0d25de5023abac9e801372bb759857686e5986a403699006ed8ca725bf1a50a46663d58df80d42d979b5760df1fe7de62a95a2f91b3b843bbbe409f1d07667fc9eccb85563635c5e801080642414245b50103d302000072b52210000000009e9289a6df1475439e5974f5d334480a641c808c4e6bf5b4602bc334345ca1598701f743ffda7c1376a2e17fce54ace5717855849f69cd72ce0d7821422df7029a5b31c5bf194ef9fdd4181b73cf62591b14d902c0238d73561f9d197bcb5c0605424142450101a6ca85b4acd162dd3075dce04da373a9d204802edaff6cff43837edde7bb6a38d2575da7a96f08acd84d6c32cec596f83654b5aedc6b940be45a720e0dff99806e06214b639d9d8da7cce0a95ad59cb65b5695109f5a5eff8cb30de56d473c6abec9e801afd2f219e32de11fd087d45227e2bb0eaeecac38178bf665474b4710131dc4cd76379952931e0b6635bc85edbb7ef23cf98726898e0dad60c85dc5bc6efc2ab1080642414245b501037b02000073b5221000000000ec944e4358432851ea9d7b38a96e142e520418de2950f2fa8ad5463bffc5512eda937f944322628ef0a1f585abb842cf82ef5405b590b879f3ccb3bb9708330ee5b43abca8a94dc86df4f653ddd5a1fb768c00ead083088a2288489ecb0e1f0605424142450101c68e4772d9a8b4e66e8d11da4b3181c538f3f254af7cd117f081c77379a0e95cef7b181665ce7c0b45b35d3b62f9fa6046711b54dcac83cec4b9eba27baf9f8dca3febea9272830c1a671c1ee97e7b2e77a1a26dff38cd1976eb87bdbad568dfc2c9e8019f259a8209f0db96f099b847563815ddff1dded0af3b50e6d521766ab78e1aeb8066a13ed10edfd40df32201c1ef35242bd1e58b024bf6ec4cf8cb1e32fbc056080642414245b501014800000074b5221000000000dc47cca8247073aef07c14138390f16aaf37de9a1afd788dc1a8e735c4282b5ab50fac95f56c6d5797d444e31b85a4538d1a0cc4a9afd5482055564600614306c75d0777086546c154ced743b394ac7939a93322637b098789b25096afb3f103054241424501017a14c4c0065b032a69cc787925282674604233922f725f6a2b60ac137671d743d6ba23099374d464df147a8992083a79698d6f8e884981822049f40ec82e778e9d82dc3e31ee39d340fe337db4337ae5b59df2b6d650cbeadf2232f3d868269ec6c9e801c2ffc229846e2836e9ce5e2400873dbe655e1236893bd8b31a752981837682716c993def64fd25491c540de4021a5aff2daa8ddf6a0c1ff038991ac3c0d7c2bf080642414245b501012700000075b52210000000000a0c1e688d28e4cda709afda19517f40b6dcf5423cec661af52aa60edf6db06abe1bd8c11be04d1c3321da012a4974945a2fa99e6db6e86ead20aefd3459ff0e25bf0fa9f78a1e1211582bb4b8c71508f3e4eb32b05ce0a7fc81dc48b387a2080542414245010118430460c73f6c682997f6acd1f84a48f309e73c3aad12281304cc9457a783510e858a3dd6f851c91c62d729ccc03312621f6a737e9b9204d0bc22bafa327f8830de06410a8aa58ca036f908b27e85b7716d1865ce3b79d2d7786ad26ae63336cac9e801e8e8e5ac422774b10ac68462b7e565b7d4a2d03950def03952c739253ced2b46340a9bf6bc8a9c065162c9e3179abc2408defab4ed2c53bf5d5467ead5e2f39c080642414245b501030001000076b5221000000000f039bd2af0330bb8a29e9a91724055e6f44614d9bcb566132bf681a69c5f0c011c33a4d57d034a7201d0547b5a80d16854eed7bb3149774b1c13a89fa0f8140145fde8a223021b244d02b4d3d51ab232cd7ad62548f3111fd4cb75451c1d370f0542414245010160dee238122e1715b89563bd3c4b23e77c6b23db4d08a45a6363c691072adc7d6e482b7e8d4e0adb50e81870ba731d6f215ea97d1864d58328420988aaa7178cf23c0c12ddded1720c841da283fff00be436383b4f40f08a340d29cadd1767c5cec9e801280028ff96faf55379f40d17f101e10bb05a23a91741cea24cd54dbc7408ddb1d42266a98a8de80b3328f6b61aab4375ac06fa8032ff3e7d4039e566f9d76aff080642414245b501032503000077b52210000000007ad299ced46d95e51664ad37703562a8c0b8c44be12510ffc37c6604cedac833afb129d6706b0df4159a3f1a451e1fe0a604296deaefb2a91c0c4cb8edcbd90b9165b5f68e48dcbbc6a192f08cd0d7607149a1bdb3b988a71c0dc722635c4109054241424501013a2d84a29bb251c8fcdfcc1e681a47530b983e9dbbe6343da9bca870a4a6ca35939e16c979379d3ae3e345ed992a6209055b994f76592997aaed27b18eb7948869d2b51d189fe0abf1e2b66ba270cc99397fcc9489322b8a9d22c8e24a4e7d64d2c9e801444576d537421eb07af74fda1b827b4d579da77ff1145c8269f7ce3e2667586b243af42cd7d81db6c4ebba9a6acdf223e174ddf03f049cd041a0904ade5565c2080642414245b50103b802000078b5221000000000d0c41b57daabde121c5d834ad28f888f250bcb648b820c46ee8b217601bccc0477270f8d2ec5ec21daa6cfcb0ad3910ef34cb174df88dba9641d865ee02d3300f02a245b27ccb54ebca54ce8e976fd693f3d6a0da3ba08762f4f10e401f12e0605424142450101b2097705eaea504c9954f3491f3696faebf5123c727152d3a84f28d9dc1b61581d345c55f1f061beade020b3c629f4a4b96bbb1d4d1d85c67abe748d3e0b148eafa9c21a22b2ed2c425f30e2acee15bd098010416bfbab3c4c38ee1ee18e2773d6c9e80128cd27e791c0533bff4569ecf447213bd0cf6c45d531b9eb16aa8b5f4233bf532837c7b64b7508f1692a04fe20df49ad2dcefe685a9d530365ca2b14fbb0edc3080642414245b501017402000079b5221000000000aa5c51a61080c75094c70065f57a9bcecba7bb8e4f5ae43389aba8406aa9181535830eec78ede62c9c18c1b5adfaadf4ff12e3a0dc793f274fc43912d070880df94b506d66085eb9e0f76e41c9c8b91836b01172840516bc1847b45d06ab3a0a0542414245010182f476c8f5914a44e1576e109362a52b19708beeb05f28b371a2c1ebacf7c8310f7419d5e9caf74c9106198922e2fec27d7ec4082ed6b8ff52555f7dbc1bd1826e4dd1367b2b8d06fa9da1547d615772437692713b71b02064d9bbea97f6bfb4dac9e801d3ba0b17d9fcfe6a2b44f1ea4062fb721dd425e252ba03228246c6948954d618f7b784ac85282d93198c5cb14762f5796131b8618b998f12840cdc4343fc1923080642414245b50103290300007ab52210000000002ec92ab000ac13f9b3989a0cca3c66cd4a9c16cbb72d8b3d05f8bbf32908fe6e9ab11e053016f4324583e4fc90130c09d48e1ebd69cde05abe19c1627786d20f1693f6c4f01140f445a43b492e42217d6e05f02228c3b48c803c5d29b4523b0605424142450101dad5b5b2bbf1bac493d89a62136df686fb0f816a61a1e485bc92b863370318367ea9de2da93164378054431b71d7c59c841a611b11ecfcf2a247b1fc38c0dc8e554a7b2ede16a8ddd8f7f646f7f8d768e980027bcae8be3de41a68b313757d24dec9e8013fecbaf8eeedfb1e8d417eb0b2702c607ed9863176b05c71271a3323c39a12a23bddb5113564d4039e22a6b41d4ddd897fccd581651d1fec0004f497fba6a185080642414245b50103a10000007bb5221000000000704fd859f287ef419c7d228076b5a7c4a3b20e7192727be93345f56d1cb5491ad6e34f027c65fa3f16627dd94d56b8b48662f936461d2935b7d1ddcc04b15d0bd38256e98724916cd7a34eac93f3daba41359f2e9c8b286f1ec1182980a3c80c054241424501016ca3ea80735d28f71f509271a6d2d05d21eb0d1195321ce800be3b7c667e68278d7cf39e2d11ebb7ca1c290cab5df57c8beb0b06db28d5873a913b98c0c8398c62c91e974a56ed91cac4529be842ef6349fc698cefc8f0ddb8354c39fad2b596e2c9e801b16ac1cdd97efd25b46214d9a5c0ebd67d3d201e58ea905d27dd89711640a89d5b75ceb13f9f7b24195b1719d59baf50b861d28edc7d339f5e1589d2284a2cb7080642414245b50101a00000007cb5221000000000acd14e70615e7f1b1c921b8bdd6d8da386cbf9f7dab6d54ac9d9da6d7e7c56379d2492eb65ecc51917b39e286a6f72e48a7cb7886719cabfa9ecd01f7fb5bf06d568e37e08026cf397a02d0c1eb11142c74f9aa923c315d6a8ef6f48006d0c03054241424501014a0d0182a97303889d47adf56955e53f347ce5b6f090ba5d475e3ed696aff80fa4488a11f0adfbc8648ac601f3c03df7728109a312a5f78887511c68b7320487573213a05c49769b645cf55c55d507bd65f3c9b6f208fc8094a3d5f0146f6d50e6c9e801d2205f415c9cc2095396b35ebeabd2e44af5ab98b9324449fbb70671eb43b1db224cbf1cf29f7443f9a2aed2c6eb8a594b179d742eff33d21abb52063730dbfb080642414245b501034b0000007db5221000000000269c630cdcc31a8f8e02b3b610a77725570d404bc61b8a96d7a9216f7cb4467c7399c9acd4f5f2bc9d0fbca6f2762f9d9efba27f704ddf04d2f054e4712aa1004aad6e0b50ea17a1218afe4bd96ed07b36cb15eafb48f44062bf3fe5940f640b054241424501015e19330e0c1bc2cb132a06777ee60ad8b7c836c13d4d1f1798843bdd4febd306a3bf8a0e398163a8ef36afe66a2fc14ae76108e547a1e8dbab9a29f08993948cf815ff218f8bbac79c13b486010476f0f7a0fc102c1b85f5535315d6a16fa833eac9e801d85da741a18bb60c01269514320b223b8a9c9f7769645bf90c610b49aee456c249d89fc388b47e4b8c019c9f53de8828d813fd982480096208923c05e4d53473080642414245b501015a0100007eb5221000000000d23827f53d2fcfbe0ff3001f42d9a1c5fef510598de1280181d401f97fca5b40d01468ef9f764e3e0677d519fcd5976e9358f39cea31e05dd880feb689e5ed0fa5fc3f472c73bc09c9e1cc1c96b5eed9d2aae07ef17c8324e4fa494fe6d33106054241424501013e9fda02b3076e19dcc9945a25728d51abc29065d2e58845827dee0bd6e417103b987f0baec1233048a3ca8b719d5a309c08804bf74b65f872a1fa089027a08892a8a3714f7ee310b2dc1fb8c1a4c1ebbb83df6cb6f856b33cc63ae597905c86eec9e801a6893b471fdf431ef3cda2ca6f8686101b91685ea12c24e7853c0ae987f6c4564ef9ffd4f4a02a94c0e8126616272a2d8009be566410bd9e1d4785f3ad478d27080642414245b50103d80200007fb5221000000000c61706b9c33c589f8a9d623d9df891efec44961c99c4498c4bde71b119e54e15e2aa407e91367819a11b5ba8f8f67c7ae78bd57ff8e70ea7c7a7d84f2f629604dea58ec6b48ff2507d28cd491b8fb670e1c4daa340835a85829f843d79c08d0d05424142450101be4f97e3730732df35899dca664b41e21764738f4c3969513a6599c6268a882a3a5f91a3240d6486913b5f2d1bafe7d1e89b1c3e36fea3fbc5f7eb48f6b74f8abbdbed218751ecc8725c6dc5e311caeead8959992f123768ee56eb6d0913651df2c9e801ff561867ed75d5ede90cd3b008f701967714b32a3c075207a4be080070fa4ba41332b00f5ae559067f54426fc20c182f97798276f1dc061863598495da03f350080642414245b501031b01000080b522100000000020d884c8f2bfd5ac3a282b04fd23cba95a4b4f71aff2d4eec3c96ec9d80caf3957626ab7bc667e1c18db2ee8689b32a9bfb852e30252cb0a4ff71e02de27cb08ff71dad8ace919f2a8fe66e4c978ab4ba844a393524ebfe4f1dad00d06a2d50e054241424501010405608eda56ae4afe13013a946ac674ed36b996a1e44c0c22b034ec7d32222b3efbd6549a6507170f348b4f09155b4c0e768f4dfbbdb5418e35e6fb5ef88c8e384b32ba419706be657689492cfc5bb8802a852c8f9bf4865a6eb68421cacd05f6c9e801ab72cda10e2aa06655c6b1bda22ca6d6d985a87ab797897bb4d051a6665ae9b2e72f8a6e0e5e28cf6c5a6cdfe2661b3059a43cb72c1804b61b99d3530770560e080642414245b501036d00000081b5221000000000f6f2ee8b4e14e7669e0b0b7fcf96c5bb35c5f906f306b177a993b081a6008631e3159cbfd14135c64ee5b25715bef6c68f8b259c74778e98073959f768de410af18a2aa3743a3f725fce45ea504f328fc15f222125b6ff1d14d2c7c3ed157d00054241424501018093d06013a506af6423dab5e8422128499d3cde79ec97614829eca2e1fafd2734e0cebbf0c9e64ba53c187bca15fcbd1d293ca307050ac776e0c72407bbbd858e559c25c9483d72a2315cf8069ccda4887b11871da0d0583135c7f449824c15fac9e801bad6d571e5a4e1057d77b7b79157ecba28346227ef0298d6099d25d16b036b670533477b86505b8e12f8e9ecbb301c3e75e99906ae253735e3a00f83d9a997fe080642414245b50101fc01000082b5221000000000b6e9022c5686b2d5d65b65451f51abe6c700c54a66822c824a4d8f8fcee64912ee6b8b3414fc6d9872d1ec20d9a9e7d4e1acadc47fcd13c78cd1fd2b34db7c0ded49840823ade5420c04cd6eb890262110367c90ec349cda126d100e43a9950b05424142450101ba5b4bc6c6c59a4b6c75d547db436e43ca8ff35e94521d5982a3de138070f616c8d844d66ba0d1a700143256ef72ddbcf67f86c523c8014250c739e4a05a448e81f0d37059866372f3fa2610316519996c602e7e2aca5f3157c633d13aca3739fec9e801a01ae39e37138a4e7423b5667413f88795ba33f7568898f607ed8d2e7ba5d871e6b3ec36dba2fda44f744b79ecd1d64af56ab907ffe7fc6201473fae3771f904080642414245b50101b800000083b522100000000090986e18c53618a11833e6c662e84ba69ceb0c138ffdcbafd4ff3d18eba2095278447b5c314dfb0626f47ea5721beeda2dbaad35879711243d8c25d4edb644031e1ef6c7ce3d5e550c8f74b8945c91e0944e86ab025f4cc66f117670584a0f0105424142450101945cef58f2aa93cfce5ac2812e9eeca2e9544ed6ae71c5d3905e5a53a4ed596a3e8b73808f3c98229a443e713142fa99a19d70a1a2de1d1ea36ab288c4ae568cdb72987b9d5100facf5bac3e8eaa36109b55f3a02125cbe8b9c783269d7b88db02cae8013596d0c48713fadbf07b2784f582aa6783cb15e9fa69ceb80ddb54ea71cbe05f70e4d91cce0c8657bf3badfe14e5d2c2bb560ecf87780fa8c3ba4e9fd51cc146080642414245b501019602000084b52210000000002e0293182b2e76bdc3f896384f45f5896d9ed00f13ef3576c1173b55faed5f0b9f8138c840e2498249e3e9a6633880c23fae8e86f7c369097c7ffb883077060a61bd3f2d7d032601d9eb92fb13829e12b086b164b3086182c5cc9b4e3554e10605424142450101f82a2d77a57b39ccf9ce3974e0650f8ffece82871704c1c6445596917c59742f4bcccce05c08e90d4e8f1987dc820d787db565e14f0d0d0471ad3bd30339ee817ddf83b90b6e723778a74e522b0ab439418bc85871dc3a37414b2cafb456e65606cae80146dfb63fe0b10a8d881c16b096c2f587604df4d10b5e313efdf75a7d1ade4af40289dd84c75acbef30baf032e285b81f20a0a95165cb44da33454e8957afb329080642414245b501032d01000085b522100000000070f9818f9e74b3a88480b19c4b0db450348fd92ee2d52486e5fc9936b500630ea4efc0000452afeead07ef6c54a6cc91e9547a4342611219730f5c812831570b043f094745a1d8b5fbab19834e6fae38146833fec8f91c54c53d9035fdae340a05424142450101004675fee844dbdbb00d69612e97887e441c92ee294a992c3476364b6dfd20451ac0b5fb6a2ca755360ebe786b0716d7de5b29576e96147c482a3c420c70b088586d117a0834726cf01050d1e402d96a7d7233616ad37b39c02a46ffb2fad8120acae80195d1f84aad83a494b773ebdf699f724a4e5675f1f99c614d275210b35bc08bbde9c37ea18e107355c8e34944a2aac57472402c26d8a9457a00e6944fc07bdea3080642414245b501035303000086b5221000000000dc57e059e08ab3f17a10c6bf6a236a706b704d1bf73f77f41b50c1c685cec91c646898d8703f0c6d8409405fea213a60be3b71b65b4cb4654b98f19a78b49b0b48b8aa8a3fca04e41f21ff7357d71a1ebd0e90e3499e22dfefb211d9590e800e05424142450101fc1dbd438bf0f2bcbc33d45a7909982830fb089886838b0885e4ec2e2453706c26b1efeedaff4fea1f9a127f92e791720a40f252833efc1181eaf7b63ac5a0826e33b474023ca9d3c3c0753ee837e3ec0697f6f1b35a4feee30d982aa246b0990ecae8012380e4fb1af2c91051475d7582b4f81ccac5665093d99eadded3be8d8101bdaad39c5044c74ab32479ec178ae1b327748ec02797eab864d7fe76260fb43a0f5d080642414245b501032400000087b5221000000000c03f6334ffeec4fdb6794d270c07ad82f74bd88fa807e8bcfd43ad364287b06e344f129eaa2828ce4466830c6a03c9660735b01c921b5b533e7acc5817440501c488a464d5331ed193e567ec8f64df191e88114e810c37fc7b02930f0023e20105424142450101ccc7cba4e52b40bdce3d2803c284a22e604156040914917d574b85d2c3df0258926a9283b574ec946d05ed69ef0c984d0c603c2409adf03abed9d8708ea5708b98f1deff84787b776a2a70522f3fbe591b981a2637971e5f73f4719d8bdad9fc12cae8015b3fedad82c5a427caf6f1d97a0f0a83f174eefd0872fbc0527573f87d646208b14fe8204df6ed41da94deaae3029af0b583432e5b3628271ac1b3ba7aa2fd04080642414245b501031600000088b522100000000024168548e36ed98ca0cfa91f6773f1d6067785dca48bf7952124ac3cf8251b5a919f1a210b15f15433d8a8f398b4c0fd091d599e6d05a91b9b5c72000c56ff031a7e9a8f739c2c1367e0d4ee257967b193e39ba31e4d12ea2b17d897657af3010542414245010154f73798b90b7cb355f69bf13a71ac4e59be884d3b3e2f106896884fc5e0b30e7c4c68b7afd9d7a8f20eeadedbb0e073e4cffc08481b6af279071465b6fc67844f7a4295cc5469b3eb0b854a55fe9460b7d7f9ea9cc8e3e25ca8d5d0a10ce78216cae80169ca687d6e8b2c0c141b2be5c7477699488b3349c5c9191d286d34162c9a134ff1ef83c5c90b0d6a6e33c9bf5e09544154171c711896af3a27a7d840f65b5911080642414245b50103c600000089b5221000000000b4e0f6cdd10d5d263f6bc77fbfde9eb36edd5c08909f4ee2785217154729c234a0003380ddaeebfea8f29c0c9c48b6917ecc66142141c84ac72bd8c0edc1280e48fc8fcecd68bff75ad9d836ae12611649be9ac4cda7185f4a58d37eea8a85040542414245010100de858c9f68f2a1cd166ca76f64e1b3b66cbfe34a521e09160ba0893a533729cfcf3103df7c793e0f2a7207c546dec43db918f589aed1a09aa69b082ee0cb8853b0c0cecb9a75a62100799e7bd217ba3dc14dc1fe00810adabcfc8348953bb31acae801e560cc559c6eb9fae0583eaeb3779188bf41c510c0fe47bb8f3b964daa7c1665f012767f789d010d3f526fd27af6a2c96a96868aecf8c9894ae37d97a485fde6080642414245b501033f0000008ab52210000000003ef4e4a9bf55798a35e3b42d8fe9c2b805936ade95d0933b6cf1d9658e918135ef3e8d07bf0df19dfedf4a1e6cce7551f13a22fa4074010b2141ea651bcfd60dac0444fb870fc6037df2ea74100cd84c78bbb374a906fd8dd84a00cf6939740d05424142450101c0c37ad4e3e450b7fe5008e9fc706e93a03ee2f6965794a2843d51128a99e10229703bdcd26958e98df75b3b7e438342263b7320ded0c0852c2423848c6f5788328a6047ab79b3ecb22aa5eab81d7f67e2355ae5430b8d0563429b4b8488e1fc1ecae801b32e3caef8b9db3d2bba8ea9a51f2de5fc9a4912ed0958bf97b961be61242512f2062d22a1e2e5836949d8ba2a4027a1d0791d18607b0e46369449738ed007d3080642414245b50103ce0100008bb5221000000000907d53efa9ae6c8d38b8fcc33bed9732b467b9454472e6d33b19933d6667b849f0d2e364fd8c588cbf296eed33e7735517b1a397b14d071d9c641bd895f9b20106ddfc4689926d3ddb232c80bc9019cf361d4eacf23565eab26dbfe256173c0305424142450101e0dd153bdeeb86455097331f751dcd42999ecf681dae195a291ad89af43fa92959ca072900373a3f4bf6d5b2f451684ba0bab5c85daffd1372b4428b78f9f08d4b0f70bbd44148640d508aa413c4e41f876c42e7b7c669078f2d996039f9dacd22cae8019e76aa6d1b66843e75eb3482cf573581446e525cda2660837f10d5faa0ee56f81b9a7f06140772b1425c5ef6e0167a805f6fc794ace664d535765b15fa1c3764080642414245b501032f0100008cb522100000000030b8472532795310e6813d56ebda6d09f3d509dd6cbf4a3848d27591cd826112a6504eff5ca9308398762aa24715421b9f8e7dd8e928094ce07c01c4cd1b380622d1e8d833ebb9ce6519505ef02741e2134a01ca19aee19f5fd8c7d3f41c0c0205424142450101a27ed1af5182f3d335ae641b772f69d801e5b8afd305394fde0891521ae25d02af0049a45a459a737ece9a3ce3582c8122733af1b5cf5655e22e4ec84921eb8c8e322bcad9b5df1ff74f12983b0a6845baedd040d463a4dc303f2214dd51415326cae8015514295dbe99012658f6019e19e1ffaf11d825aa0b5346f83d41ff1ee7935e2da3da1f66bc4f7d6033572d7ea663626ba954d34ee916b047171ffdda093fe845080642414245b50103880000008db5221000000000f28d7d2363e7e1cded62471379b13b73ab7ab6714aa3674f09b79eec3b70340fa515e23b4199320a74befa2da573dab45d604063a87e7b4c5cd7267d30f3490d8c41e71c4f3cc2300f8e06fc16f2dbaf173aa043a4e88a87efa4460445512501054241424501019eb446a1aea09ef860802a02bddcc5cc54f01a856f9c7bb6743f29e64cfa192714320bc2336cca5d4ffdeb9eca920c311a2dbe493c2ab0a98cc8834f1f8d198ed7d531a1f2eda61776d1963f8a47024f545bbd8eb68b3e5e6f3886dcecb64f4e2acae8016c99a19b522048a39fcc6ee8b793265dff3a703ae5d413f7796a2ae795e0f89947a4ba8729f709ebd008813440b8d46c50f2e9f0c59467b16b07aee520f1159d080642414245b50103170300008eb5221000000000fc5c72def271fa702e7ec623ff9f8fcbfcb938a9eb75591a674e1afa2fecd90fbb3710df26e9afff27a06071a5cd5c54514993d2ec841d39f829fda6a53004091b2c42ad311ff80b334ade8e1d291b9830f932ad227b875183022112a24ce60605424142450101848d67f20d4693c94317467914f3e035d0a0ffdf38f06fb6c4bb8167d27ace4008b7532a234b27b1d6201a4e6b6b9c2abd21da3aec0944ce3bcc146789e8c3873e930d0aaaf54a32ab8790abc8390c15c7ae2ae32cb151d4eaa4fa4d9a2418452ecae801c8ded1e5d14978a65cd3d1955f626c468aeda7ef490945a5fb35c846b924e2b9161d5cb54d9f76f540ca9779273d00968a68744aa36894fbb1d86874402ecb0f080642414245b50103330000008fb5221000000000ba81d4410ce37b63b477d51723a87d49015d0f04f31b0018c6a847a945bc287e748b500e2b3c09f27c9b1fbfc2d20e6dbc4816dedec52e2bc46bb57c61fd09074e12d9c7aab12dbeb3da56bb93354442b1a5983b13ddd9e91266bea79744c60d054241424501014c427206234e97ff11209ac9b8c8339a65d9c294c8aa347dfd4ff87221f6035e05f6c2cf097daf484cf6bf882d34d82f1b04e829448c3f58d70095c86ab05588410c1e5ae21c8347be7085fac7d63349eb7222482107c071c0a9a9fab7ac28ce32cae801992fe45117efcc018b8781ec121575ea7deaa4b838fda7d30b8e8ed59210cbf1ec2b30e97f301ff716e34e6fd18164cb41128f5ea796e39b26bdf9a545134bb8080642414245b501038501000090b5221000000000e6c94e9f10ff3b917861d5f0651c70e90ea313a69d5f2d726dcf66468f8d9f4d436e818295004b4285aaf9730f86ec2d7470e083c150c37ae48629bb39c4bf0cb7fef07b7f9b10ba0c24bfa5ad46a394e46deae4242e7a23f3a548f7b99bab0405424142450101324eae594264a775aba939f2d39f7364704d066e975277807869ac3d563a847cd11e358d328a739a4f1cc46afc0542f7b0723106a0d681aaa9166c8bb05db08e9151ffd93a4a1e1c76203e538265de68ab9a21cfb2944902cbc6f0de47d7308d36cae801c8df60bbceb5473eebb49f0a009689efcc7442d3726ffffa90e86e5aa47d1ddfdff7aed8c928760c2cd11582ef80535ce7eab944e27a13a6a0c30418e819f948080642414245b50103fb01000091b5221000000000b4d28be2f6f37e4f41b97c796084cf6bd78a46af58aa0692c37b2ddbbe5aeb2aa202c1cd7093b5c0dc08155e431160bcf42ea8cabf3a6dabfaae432318a963053ded066606cd8de4285494db1ac47e6a1481dd68f193052b88f907e62af0c8040542414245010120c0dd4d2d104db9c94581b7945423c12d7b6780a9cca9c1548fe5a4b2c4674c7361aa259180783ddedf996a3d121f9eaac5579a8fe231cfb5b4341da0bf0a83440a4b2683a7d1a5c190f75319870202403f7c0b5754955db6fc5ea648c57def3acae801d32af7ca4c703130e3b749533a4ff82dbbe4e80a97d7125ad73e5db8f00a1c2615f5577ebaa6796e30cc736d14f511678f4e75a1b7a833f883ae7f11fb268185080642414245b501036b02000092b52210000000003ef84b4a944776fab79b41e6d99367a74593ff34c91fb9e507880de6760e05769a2d4817f96d166d66f177d36cb018b9bf109c66028445cc49a02981d0ed560e2fbc80275720137238fa0ba39b036c1943351d3fc22e6c240d7d41d9f151ed0205424142450101f0935356a5a1b5873c9fd8b9ca0eb2a38bc400d70f119481a37796d64c72c94834db6a34172cf4d1534a9bb585ec1b2cf885e4280399b935d6eedfa4ba6ff08403e3dbbb6b8863c7d7782fcd38d35e752a49e82e5d88c0e9c22a8a57e14617bf3ecae801d53ed061d5c832cf3b1e35c8597edad0f157feeedbdd09dce413ae2da894777eee99186541261cd079e08dc79d77ba69b1f31fbe34440023bf1133ac39edc4aa080642414245b50103a802000093b522100000000052a6c02e9a0957afc6117efaee7a3e53145a06e83c59f9e6f19c00c8731326294a3c326decf21787caedf3a34c3d0421b128bd1e5fdee9a2a0963dffbcb6e205f305403fad9f38ab248f7e7b533ab32fc5cc849f893d718d82d9ced4a1dd1405054241424501019c613ed96c1d79a8510bd8718b03e1b2e1ce351f32e7439dae5196f37f79c472aefcd13bb8192b1891eb6e81476d54571cb4f7e1b105dee77b6a3bb683cf9e89a85e1c2b03e33a2129853f4aaf819b5c4a7b740c5648f4ce59100a08afbf7a6142cae801c4336da0fd0f72806d9ccf49ebc2f75fdf76da09a468aa74d9001b0a7615c966240bc4f9c755edcb6a790bb1f7ddd3f30dfcc4be2eaae8459acffbf1fe83e1d1080642414245b501037b02000094b5221000000000baf8f6413907b5ef19b4a0d1bfd779dafb28c24b0d274aef4b31f02fc701991a3d3ae7c0b543b24e681f7417ee0f3dceeef6a2e29e149d03dcc3ebee0a8dc80cc7c6a98641e6d91702cc4c5ee9c14322d40952962398f72af3b9e32cad76a60a05424142450101d8205a4f9ce0cab2d1df09ce9ecf63fd308fdfeb1b3b5a1e4e3c363b33f9f5047c5d9cd7d8c20271e87cee13b8b679791a5719f9a51ad0b680ebb0d5fc71d68899c14a11605a06529ae0f5b17931cb93b40a75fa3e2fdd286a2f24a805140a4246cae80147abac26e95be7c44e681e7b173c4275d1acd0953ea3ea55ed15f654a18911a8242ff906bf2f99be36b3a459d78cd22757dfcfae3408f21ab8334fa1c441bd38080642414245b501036801000095b522100000000088bacee7db670cb0af85e4e98a2aedafa7ddf683c53c1c88c5adb73555148e2406f4b2007350a99404dfafc45833c4a2075b4e67d402a18c5597bc98866fa40114f78b74c180060f9830278cd4070ae6c49a6cbf680dc89dd0e0dc4ad09407090542414245010140c798690cfa01a2660628abd2dfa2a8c434816a189e545f14ab55b32d179974adf8d27799345e1067bb0dcf93bb7bf44117f3a49dff53bb8f78d361c8bbf183d6c80de53050b5cc89936cca1f7c291496682313af06f4c62f52f869c679e3554acae801fa9a51c83f0d58fad56f16006cbafbae9a26a0b606cc76a1aeda5408c1bb6706f9e1c4b41e302bfbd006aff44ffa4c6b18def6d9588fc6e5960a705a10c86e67080642414245b501015c02000096b52210000000004e4d4de482a227c9c976854f958823c52e264ab7b9d6643dcf8afcf9d6abdc6591eacfe710e39d1d264603975a4ff357e1284abf8a8f0d60cfe2159a5633280181fac8e82cb9baef183c6346a07e4c7d2e5dc963a612b8d4d034b468bdec980005424142450101aacf0397a55daab03394fddf04d4a11f30cb186e1ef5cd3d2efbe608f9081f2a260c8d1de0a73e17848a9d49a2d5fc502b79d5b4ae9900a64b9943ab0dd6088e4f2063b12b98d797a480551ae4fa6670029c0c962b7bd4f004da4c0cf848b6024ecae801010d2a365a5e125c1e60407c0cd79bfe6cc20efd24f120f122c624aeb0775f42000029040ceaf5aa70fb8d3d53ae95451599595d8f0bf3a6b1f0627d0ae2c072080642414245b50101de02000097b5221000000000b248d18df8c96cca91ea9e1d4f02059e0074b447a2d028fc4abc88e57aa4374fd90ad42d85b0fec5df8d1035afd37f5649cfb5ef9b65cb0dda61e84f8ebfdd0fba9733342b0391079a572722163e69ca15544c34fcf754ae77538fdd3b7ef90805424142450101d6328b132c72a48b09ed17fbc6160577d7d779f023619667621d60be8288046fbb0370a19ea9a771fe5f1e4cde76232992cd5950ef47e34880269fe42947958c292002d8d23b096dbe3d8291b264474a94632e1987009da9463f2e71808b1f1d52cae801f33cc8f242d0a499246dea93037bc20b1ed1149a503c097863b65f7a33813b859acd15b5dcbc36f0bae6251d07314101cabb0d98f5168ac15aad55ad8acb7339080642414245b501038800000098b5221000000000d2f651bceb7b788ed190776b5cb88aeb3e7ce770be8e1e0b8de41694a66343111cb40b7252fb758591d3941ceecc3d5cd81ad42e2fe3647344250c0d1a428e08918a7ff5fba244835a5ce14d8fc274d5dfe1528067b673d17bfa7a465959070a05424142450101c8a327547c3fa07ae13564a2d9dfa54aa1c73f55c0864cc018d1583d9df6a204eb655c07146b9a8694765d1419edc1788c141deae40fc5a33533d2954f97538717c5db30e33ae9c43d13621e2eff7ca6b308a67cbd6c4bdc0cdea799d9d0e35b56cae8014fd3b4876925a4301689befff7968a274597c937f903f95567e73a4611b373a37d6870148b7d770ac0b5062b75c766eff8db030816fdf851b9d538b0f2398dca080642414245b501039b01000099b522100000000092e45cd1dcdd4fc92db08bd983c4494c7245e561539e4b4e1433585126635a7fbfbb28caff991678ddf87849bf60d5444b8c8e318bd07b6b5cf473f3720e810d47ce9384b839ea40f2d58dee58e07835914d778afb83178d636628fc20833d000542414245010176544221460734dc74967bcdcfc17c8d291d3b702805ccfd194aeda7e675304ecc45e592d11388e43b353c9c6f26bbbf507f5e721f635c08f0694e350b68b48ad61b0e91736676179e6f88f3e15362baa16ed0b354eab65344f9041d993087175acae80132706719bb20e21e38cc8bbdaa2ec1463a1d73eb0ef17fb1be464743f82a2d558d4c22f6694c5b217ce12972706ac817e1b524937573f873ba6c15bcc26fba49080642414245b501011b0100009ab5221000000000c8995ff97f0155351becc89281bf0fe6b71b595e57fd420db12397f7ddc5c435edb5505189e7e9df28924d3f88a429366b26b05a6a7930cc6a2db515643ec3088b0933deb15f0fac56719f3873fc246f505f5a58650ddce5f85ed1f9c56e000c0542414245010134fc5372cfeb0b4ec96a805ae0c385cb8839d7fe56c32322afca5ce8938e0a7654d9fa5acdda471b1ee8aa3f939fe7879b2fad24a152b07086b8aa8771b093870cf53f7e94ec31eb9fd9c67b38e172ccc8223a4e0214981820fcf1b0620b27715ecae8011a18d0fea92045153ebb0a0f114774260ba136043b471ff7d0a9888c1586a3daf6d67cdc885d2cb86f6641e137646b7e9e2b7fda6698b45ae035fea717858d0d080642414245b50103c80200009bb522100000000004d6f5d7e037b5b6dddb8e1869180784e33918ddfe75c0e672e60c95b9c7eb660d970ee708ec90d2ba8b35999d8491853943e6f517dcfb7d03eacb7bf226560aa1336851f74a4b4d199c79da90451865460324fa01037a4863e996e16604540d05424142450101466462f799c7739511cdb23a44d64af261f1c9b5f23eaa58e40549912074fa03b4f136e644e5b60dbe30d5db3531613020063441dfc164596871b1d972b2bd8536e926378c47c4e506fc8c5e38bfb84f312a0030b3892bf832a6844814f00cad62cae80180dd2f9e79639d13a1b00c985536bbf266a0f9f5092458e1b4866752797fe456fcf2efda5d1e956a8cff127715f5de300a263ad5044d03ace85cec33eac2194a080642414245b50103ca0200009cb52210000000009456365d92b5c14d113255716827e4acc6469ab62533545e7ef3497374ac83118f6b6d56d19a1f9bacc7b6f77df6cbc623cf5edf47d99c78f82ba255739e110697fc2efbb561e5aeda3736429a271d4181ed6cd7c886bd8c2064de15951085080542414245010166cc269462d3b1c66dca9929edefbd28f97e0f8dcbba9dcbb9794959f93caa579dbc8f68aaaa572a94551af9316387143a6608f98c566c9e78d7c23acad3108df916715024d2c1eecc95b9a3686ea72b1bc77f0fbd622723abc3582391cfd02c66cae8017daf024366b78fcc0f4686f9354660141f6768586a135813bcb23614d13d2a1ea61699f225a7380660b1479b5e0f0dab93230b5a367d32e2cd98c48528b6e859080642414245b50103640100009db52210000000002eb6689423446882d8a405ca0abb2db2310cf68edcf014c40ff8ce82b4541e1442019afd274ea27c02e7b3421ed8131f24aafc67df1394c6ef4baf865b39f403754f71b5d41b45778ca8f905dde946bb169595171ce4445a9653d30c95f1190c05424142450101261480ac8e78e6b93f3f03638bf4bd0fb718c5259360685cc5f27c621fef9e5f1a3a0310a9cf6d69e3da7319e4ef69002e9345660cda7058db93e5c50c4cd4803f325c24b02b7138479581d9c298e64d6d2bea1b86cd4941795292aa0ebe633d6acae8014b00ab0dc932245165b10d0e236c47141cb7d3993898bfd5b924d12499d682e91bfcbec289d849ac63d1be73f437d3fa5cb31461e6c3387ee4fb488c457a4bf2080642414245b50103770100009eb5221000000000ead256f881ab648ce74c6408ec961f5ea09fc4e04d7982d4d9e6dad2d24e351edc3addf3872cd09dabe0630d569e25c808a8a017021d5fcb7263fc50d1e1a0012986b5b86e15d8f6501d37b8c4a364ce6f79b8f4e9163ffb7746e0d28b6347060542414245010166ce2dc6abe5261e72bf314ea157029587fa88d77b9a07de9536afe2e56cfb583f522e75ff3755b51b91db28e6715883a814a3a879a79ea4c46e2570a1f8c280df7a283981e106df2a976b99e38ba986a0ba27ed9e366584627653213ccb77d46ecae8017f75e0289c2c8a7922106399aeb660bdea1b47dd464cca824070be39bf33ed201d833c6974195b31d112b8833ec78ff4516233409cde4e256eda1f1c93c2c462080642414245b501036b0100009fb5221000000000aab677a37e2040fbfe2afdc30e3d462aa7772cb3a64ab4b0897724eed2d9ff4aa2768f3153af8bce4f4808830c448f0c36800b8efc31cba1a9e916baaefa3d0b3dbef6256fc173c8e46fbbf9c2ee63a92281132975812df7033e04539b9d8403054241424501016c5e078cf6075cbd4c38fe16bd76ee7ed0a93384de761b7cd5b0d763961c38454f64e73f0d82b02e5c00edcad871054327e87221422039f7bc8515f4a4e25e8e687713df5093878ba3cc3c3af174abeb609c86e55c00b2cea7b3b5cf8bb9501f72cae80154ec8eed481b25e402b371385f2267f0d345114a58eb96ba2bf8dd7b1ebeecca962c627d6fa5dc216d55236f9fc633fdb9afbc3f4d2e171a5ac9acaa0bc3eb02080642414245b501036b000000a0b5221000000000cc4c0edefd6a3f917fce5e543adf8ef37a3af42743b87e95a627a4a7e73880528e2a3122984fab9ce9071407276e11f3543c419e3d0c54469cd9e78f03eb1605bd55f38beae605463f42b363419900588cf305a8aae05a88569fecd11757420805424142450101cc658e7467b4a7207c52eb44627636dc548dabe89e9f9e4f9782853905032f6861ee5b1f387d8707a89026373d2eb337701f067d8718b14ec3be4b540f5445873a893f6103a7972391a40112782677956acb905ce09078c0afa38091770d046976cae8015350b7cc79d87a74299b4ec82a16767b5c74554a0c2eb5c8611cd219e3c4eb808d5107833e4c8dfa06e69fb1062edc3290943b479ecdd38e1bb82139e11d7e5b080642414245b50103c4010000a1b52210000000008edff1c178f77e55dd9d241c6af9add3736d3b08b1a6c203967ff1eb32d7a6560ee2c23cb027c6cb22fb09bdc3ef148a538e1b5b67687aee8ffa71143cfff4029a9bf658c7e81b29ea021aeb298621440dc9f94ab155690e11fecb5480fd230605424142450101aaf7e40558e3a0a16eb3af2310546b4da665f07e5230babb01b7c7631b1b9e110831f11c2c40da715576b00462887fca150de6bb9972b30ef4359d2646ae638b48cfd17486f25ddbd47f262c9a0ca955ce9e1f4d37cb18f2827154a156f106107acae8019574d513fe6a67bd7b082ff77e281338dbbe420716e8f81af487433d0cce54473f6b9e55dc4bfecde5f15518b7324c85c672cab6e1f114a7468a7ffa7b133757080642414245b50103ec020000a2b5221000000000c46bc0ec8ea322cd6663992611d56f6433a5d5f72094ba1bd972dc4b0bc1b2493f914850420f84188b306708a91eae16dc5cbab44e54663d33b325f851dcd2082e46adf403e17e748c2b21060c0982262c2f592c840d402e2d14a50eb97aeb0b054241424501018c5bde6e188f2139e44537b7a2a76129bdf87f51dccfd85a9cd859e4239c8968201a23dd90d222df530a212dac16a504f1dd5e1c8ab29518bd94de5313024b8cd2ade06d61c393fbb7bcfc538f6a105382273259ac9a87c4bd07f7b45cd751d07ecae801a415af5ba43c24b9d9360f5ba2c393bdd8f476316cf7a3ecc4dc0efd755d0113540991f4e701183129c5f6dabf5c90b3092b29110b730cdd05a2a98ccdb7017b080642414245b501012c000000a3b522100000000044f8ba4351d0dabc9580cd7d5d23feb5f9c4530cd6938e5746f57a18cdf75a6e1a33bcbd67d58e53302919bece1c6c082b7526a37afee485641b5360e8b18d08438a04091f07f0ffa4f494cf4dbcd8c7e6c311887488d8f95a93afbae7671d0e05424142450101349dbef78078362f6ea7c547b6d8f3d663ff54d82b20923db70f58aad22ad64563b3a97ddd2e4c8c0f165867d2d2fda9ab66fb0cce47b1388ad0980d3d6faa8f2ac6a626bfa828148686ca82e5b65c9ad68d424471de9dd396f38bc8538a00c482cae80157fc6fe47503407908f6063c86c00d1ca8b4c3614d68dfe1833593d4d09cdc7b71f2f8f0cf38f81c2583e24dfa90d0d69cbb2073c7201be5af9a2dbc1c0eb7a7080642414245b501030b000000a4b5221000000000f2578178269c775d63f813cb7387f4a9eda999810590e884a6cd8bdaeca8ae4acbc685d05c4f054b5204ce0066ca26f96cdc61709f22c7957ab5b388741eb00200cc3ac61f7d03130dce73606d27ed882cd233af6fe4ea4c10f4dff1e5efff00054241424501013c771074ce5ed7a4f9fc15c52702143a5440a4fa977c323e8648cf35cb4804720ddb4d822096de09e86423847945ecf3d2d4581013329583dba1ee0976ba0487c093bd6d02925a1bbc32319d38b7412a4618cfddcb570b7fd2e84fccc6c5c3a886cae801d862710c4d7dec18ce7d7f91cc2c56c5286f314a5fa72dd346d8423f54aecc7f41865cffed8016eea1543dbae16f6cf8448f42b91a08eb4c0e6a57b4f36f1b8f080642414245b5010353030000a5b52210000000005001796a2afc61e3b85c8de439eb3808f4dba8198be4e07d21c71ae1f00e824d9658bc4ea1b3e674b6d6f791013dca968f010e11f8a0cb00b6634de67ac63a0aa85ead95f255674ccb80abc8d63f314f03ae0a9ef9bcdfc644b3f183a493870e0542414245010196fa61bb7047bd31b59f8bd145c3ef80e5c94c165ec12f928258dac3b058293c07790b89db36e61c62460ceb667b29049e62f3cbedc6485f18a19f3dd929cc8078e43ab7d70810d21ecbdf0251d5675ef1e043504443072a1bb6ea43cc5093088acae8010f52584a79f9345f8213a8d0e472a1733a44f422c5c546bad46d8000c4a5789e0e843b4e8049a3e0652263ccf089b292fd609feca754cba0ac5c2f0196ebe57a080642414245b501013e020000a6b52210000000004a3cfa9cc0095f33a6370ac6af5e73c78d84106574bf75a7ef4a27b3a16b9b4575cc091e09d5f4e4f10a8580bf52a922085c8ca915788ef33ad57459a9353401b80cf680bdf69a11713e7b9734cf919bfae40e5d766ccb3c18486e380c4fda0a05424142450101a6ad70b611d2310ebf762535c199fd80fbea0ff04807634f80f0d05a40d5f104b1c4f9a31d67e223a427de0d8867cbd084ab83f9dbb2f76e5ee38ad9301e0186349e5b23d8f8ab058dce834843f713e9b2ac50818d9462df837f6b6892ad41bd8ecae80105ed3d335ab13a9cd9612eb30497d748e6d0f538d39e1dcfd8f43c882f5aedd1104e2a5f9ab099227a0a1dd203d058b5a18c604086ef9dda0770e4055fd0fe93080642414245b501010b030000a7b522100000000042596af11bf49f3c9194c1e5ea71dfd1547157d0264a21531e7700bbaba6d10cd6906112cbed03a79a3142c35e08dc10be9db42b0d29779330d6603261ed6a04111208cea62f18ee1a0f1281c0bb0c8d033e551d32c9061756f52bed17b7930305424142450101e0d2eeda4bc5d4d16bdb2c3f0fa4d2a15416329b88cfd1fee3c179434d931d55abcad475c1ca1d59c427762f3b1e3f781bbf6c227662b1cd1a3f80b4753d0b8ac70cf35781d0bfaa5faea5ca4c4ea00eb6ae765f9264651ac29f056a876bfad992cae8017fce4366616cae41d239d4e2c1ea37c47012c6159795566321b9ddc73e2d002deacccdd5693c1f4847e6ec089a908e06a90a3f94c79bf495f60c67418a36d418080642414245b50103ff000000a8b522100000000086062d3df26b9c9b4c7918a5b592155d77bca2723729fe2918ccf31e763ae7295f7f71721f03c42fa3cdddfda6b09c764ac5fbda045b9e23ecbb7f122a406e0b620d32710750fbe10619b1a930bdd7ce75503fc737217964dd0a8b7b495d2c0905424142450101988ba3dd6232bda6a906ecd29481a9bce8165744a2d3285e833cb4de4e433e4619e9b0ed726035b685ff2b0b661de19757c758d728d96ec3eca8dd4b56556a8956ecfebd04e9158b808bbf12b5ae9c185a2ce4237605e41d37823f3f648de15896cae801af3e46228064e7456ccaf85e2432e2755e9fe4877efb799efcdcd21b12362c538f084f75611329ec66f72f42965fbc9cc0ff66f14f1316cc0c46e6494dd13dd6080642414245b50101c7010000a9b5221000000000aca91777902fa03ce9e91ee1b9c157225615ade66415160c29aed911501e3f43aeeef18df6090e78d70296f8d5d4e1d2bb4af3ca683b85e3b2ce20a4c545180be8b35a02fac3e9f8afd6022e685c2d237c6c950cbe4513c14c5a2084fa0c580f05424142450101485a7d6f5fa7934a845552268b5e067a0d5e5d7e5047ddbf66a57a6f23b52749416e4849795372d0709ee7634149c2d844282fa24c82d202937c2f1163a8bd8102414f1bf238856fb98648dde658ccbd437131589f0a9983a104f1e524ced28a9acae801ab3d77098bd016537e5a728bc8da5373b99af2afe4f8fbb039d8b918345306cbedbb8e1838307b25a4fe168c0f57ea117c85a587bd1e899b68b0ad3cea7c746b080642414245b501039a000000aab52210000000003a91b0ee15938802c459e690271b7242428f89e435ef3a6c5a538d41be2ed905d79cf4cb1d4fd63081ab6508b0d201649946a5e9d13378a49a0554725d76ca0734aebf9ba727d9ee28159db0e7edc8bd9c9a16de9512ba5cc0fc3c1e3d52e802054241424501014053c9479bd068af8461f46c8b478f39aafca381d12c853876787bf01f3f4373ddd055229f03e7b278c203b6fe104f925c68ed856b338efc9d3173c952a04c8d3ae43050eeef6e961d553c42d781bc0f6bce6ac0c9010be1886e19aaf62c66329ecae8013e354fa1f5c28ca9372c3f91756bb505ddccc5abec1e75b233a3b5393a0601e04c3dbe4167b2a339a5bd4b5d47b765d7942e3d7bd1b665d22a1378245c942429080642414245b5010374020000abb5221000000000260e65fc761713fbaa5e879e6df4037c983d55d7fab76b0c3d5defd4c7f51b50cb8f624c4d9485a3cc900002dfcea54e1e9e8436e95e7e8bdbe63e223296c60253e389fcb98e211f9b160baa5dc3c2dcb9bf01d4f6c673eda7d0cc14d844600805424142450101d6b1af6f26d3cbd496e486dec073b66af4097884ec02527b43f477b0daf68b5c9101a258ecf4225f9d02ec19abdee30c8d7847fad5a806a6eaa693a6dba55288e2dfb7ec1adb0309199284219b1e9c1dc02eaae864db62cb11750887a00b3920a2cae8018965e6858b53a72a18166aa844951e19b208b0d095067cf17d91f050193c0181cd08802c7e7323ff072d263f913792e6490a432c43082c9deaffbed0bf6c62e9080642414245b5010356000000acb5221000000000582ee226b488657f01feb224b1f1540a0764b786935fdc366401d2f9044ec454c055c7d7d05cbb85af541bbdd38fdd071d6c7caa6f61491f00b574f8da3f69035a2dff10aba769b836f3f6ad69bbad06e8f494b13de1a60856ce0a25213239070542414245010148f8de01aa44780b8239cf5acc526a2a8f512139d9efbe17942157d9b359a741fff51286ad35c34e236d24a59f92cb86d6408af801488df7dac8e1cd5fb2f68c5b71a0174e0812eafcac14bc6b07f74049578c221f6a0fd86e7dc3646bf28608a6cae8018a4e56640cd0ca28fc408d45ff9473b8a71df208cc4672deb1d847f7afd0c27fddde76e3f88cb151f20c9955590c3ab856856416896d0501153abe6972bd4fb2080642414245b501031f020000adb52210000000003ec9112d5f906275ec445e21b06cd625b18c5fb27fd733f18bd90eb1efaf8800114f21ddb94583f6cbe9c0d66eabfa030118540c4892db08ed14de5e10ea550d15f763e2cd77fcb4ae9878f53a86deb5882010536909c697d242b4107c5d630d05424142450101de85481485a5a960a4d3a5357dd684217cbe626041c896fdbbf63b103ac46f333db6744089bb8788651273e9343e21072f8385d5936de6d202e4c81c08e550843f5376fd08d3e348b61c896b2a285ab54159a71cf59548cb76eadc18ed06a6b8aacae801678d37d220aab76f01bdb7d0179cc9570a0734d029bf9f1d242935b0a180405b8accfd11a406455e0f7775a3925cdb5d1f35c05c79d4e09a9f0121e883ffec09080642414245b50101a0020000aeb5221000000000f20218e3582c9b6dda5210e7bd14523304d3fe99d7673c3d1f9bd0191b306f1562c709ae7383a22e0e527fb11ee51dbc0f26ee6f50216486369717a7ce6cc802e98aa756e9f58f6e36076ef5ca35a4e85fbf28f40ec0121b0be84650c6806c0f05424142450101b8595c08e1f56f38f18cbc4f5991b0d5b8074c88cd3dfc7bb4d62b68513e0b30a96993d26e7ce4e06896b4a9309ad9ed9f9d8498cb8e5fbb243501b50c0e0b8aff45c2925dbc20d49b8384a856212edd5fb25270567cdeacb9dc0c0d0c7d2028aecae801006d1191067d14fc60745e9199689567507b3a900a12997a582b4fab5a8791e8c7b38bf8392bcd412d42aa7d57cf1e1c3363372e3670a343055f7a05c2e4d76a080642414245b501030a030000afb52210000000004e9ecd16509ce22edbe6ae02dfa24a0733a72d794e0e2b285a8d57508ba0d53fd6c8ffd956bc74006d5f273193a20e6c1940de2c078265b792c56a8c2d88950cd0a1f27e1b7df138386d32417ad1d4962a865177dd488eb55a929cc05633570b05424142450101128b2c71f76c815118347c22e85845f1d8cded248256fdea8a1f9b48faf91c6e5b90cc324b4033911802ad50d82a6a8356389820a3c5300ab89558652598528cc5ec83fda88c1ef0bd0e794559e9babacf5a690e7dc9f7eef27abe86484f2944b2cae801583bfd08f12317fc13cf7f5407d69213527eb5125ee26dfa2c97b00084725cfa745671c7dfcaae8802a88b6bedc1832cf3a3e4e305fdd349add74a4ba97153f3080642414245b5010387000000b0b5221000000000ca01a2fec530bd8a5cfdae0de760a004a66b50037bcc931b580096711c1b2b70537d097de3faa6fbd6c17a2b43b69080fb7dc5671424ad875756979603be260045c70d769c68f54bd47e060624318b1b6749fd012f85d2284280792eb895bc0c054241424501012ce492d8f4e5dfeaea63367ad5134e0da441b0f0f8e2e671be7ebf8b9dc71c1e6ba36a777148fb069fa89002e59590b0a64e2341126679220ad377b92d20d48ea14729400ed88e6c0c53aea613e7b747dc4cd2f2ebda5a0743567baffa266c59b6cae801645a3b201cebb2b5cdb72ad5fa392433d09b4a993047155cd6050d006d1dc8ddf269740b31e659d9d703be2311c667ae8c040f02ed42f20d0178d265e14e26d6080642414245b50103a1020000b1b522100000000026ba0b7f9c005966735a2caa30f707cb707dd106c1c09419cbfd96370076df5c0cdb39f453b3d304f7c8d41b10fcff835087eb09988f5d956fd9e1c357db69070d2c43370fbf6e0a8ebcf54e98d360d52e0478fce41caeb3c545758612a4f000054241424501014cf8b30e489c4119f6416abea0676a46dbd9a419d458a5178f0e151378eead3fd4b8b9315c8dca0b69bcbabb9a9265a29eaf081f19cce1c35f778bc2b8b4d387d41ce1dc6a6d031d10d407948b06ab374b615a6681c5fc4025556e40be1e2aa7bacae8011997d29067c9c5545ec4288a5cb34cb06efc7ffe6e91acd6617caaadf84def481c8ec3a568d8766341d2f3725b177458c2e4166f6d492a1c2c17285d29083703080642414245b5010334000000b2b5221000000000748cacacb15455569d657de2016207e0322a7c92bfd2e6b8b0e0214c68a4ef43f4a204589648057346110d918622855a6f4eb55f755ca3b59475cdfd35a26007864ff2bdc899e99bbba6a82698e4a779953efb3084df78ee754e6157a84eb10d05424142450101d0023e2061c8e4e8d7cec7a946931de4d45b86c83f7c0ded1e5000626830363f1339f403e88194db3cdf3d78bea23533125cd10816a3042d0ea1d0a647d3ce8f90f29c92990c62f07554b52504e182d747a9da6dfdf273bd06f57f0a5e4ae7babecae801f501bb220855d4d00f1ac49479d393fc36f6ed9d4b51e614f45ff5e882fbadbb3ea0aa4f2c965f330ba3cb307e5f3b5288f3fd4e37ead6647d78457b92c8867e080642414245b5010300010000b3b522100000000052f805bb690043d0fdf469c64c3d06035b6229fc01db291d7180cd91a6131d10fc3cb1586cb00761cc35678932bfbcda3ffbfbb383f790d3b542667894973b0036850374f6c709647c700294916e101c0066fc69cf3b635aa40e51c7ab91770a05424142450101002380597586a4570a71b950fc40879b0cd6e2112bf09c1b76902d8cdc9b10275d8ec3b47925f65e6cc54a048c81156052faf907b6840942a520c90506f5768b4f6741658aaafe0a6bb1baf29a73e6174d52f337fcca766109a82606086ffe0fc2cae801bf7fcc6c2d82fca5352e3d537f10ffdd0e467db04b6300bed4705b57a161b96a4616e18d8a48cec6cdafd1f9e0115b85935f34e9dfa294f8342d835720103244080642414245b5010304000000b4b52210000000003a803dc88c62261d692c92701fced0f3ae9c68a9f561aa78fe949ca787f9dd4fa5421390f38901fff5326ba987f0584a13b9a52b107b76e2238d8746a421a405e529977480da67d1940261f49d46d1f7e2b2f9258f6a30a196ab69959709c509054241424501016ec132789aef66c1f599c33296fb30f2854a39058f8d50e926b255d92147a57444c09339177e8f5099338d9ada345c53f2729842437ffb839a0405bd17ed8d81966f47de9193957417620a066d3f675a774df78849a142fc6b3b5775108561f3c6cae801f9ec1c3513b2af204c22f882c1939e2302703d6e71e95c27f3c577efdb05118cd5343eb30bcedbf554e2f69c74a9ef724f4ea3a220f0d94c678ec85c7a715172080642414245b5010180000000b5b5221000000000762812ce98bbfd1730173131f32f6e23826cfa35fac639a259decb817bdda04a4558920fcebcbc4f0e5bff08de8ad40ba51cd2685d152fa2d48a58b5c9835d035ddcf18e975614e008071ffe6f63af5a99e0e5ba83cf9a34c885203ac720af0705424142450101405fbe04ed9bc5a2286bacb7688ad1f664149b18cac2648b2b7da5a2b46be305447316ba12c35df4662517bed8a35089182b2479f5e10a6a4d3d4ae293c8e48499e6a323918244e48c8c8d73fa21bf9843807ec7567f1850f9ac784158aba465cacae801ea23c1ebf7aa88b231f920da2fc5e346c3788eb6974606446288cbccd523b26d8f1a6e1dc7c26fd110755db3d4642cad6454915599b1b464f9d920a1b7c95d58080642414245b5010391000000b6b522100000000010466c65b1312cc260ade938956a1f9990160b4ab340104c35b09a7c4274625788ff2c154d5a128631bb6a31cdc910451db999a247ed6f492cecf440b411530b69c6ec9945a209608729a23a570df3ba0c3c85dde23aa1bc2a016686cd02bb0805424142450101ea22261e39b583f6ab06afddb4f45472f06577a4a4648b5353e182bb23307e6316fbf7776ba4db57640995116713a50bdfc80d836dfba018d2df5b7359cfb180bca559b6f777202eef1a05ada2156856fec9134b0123c65285014de64fd6f8eccecae801ec078c72954b3d392c5b877df38323685323290f01cd64365b7058942864bdd500719266437b105bd8de2636064785e56563c5ca074856d2c14c298579eaffda080642414245b5010339000000b7b5221000000000bc527cf138030588071e558bdeae857b1e5923b66b564d8d6f13be8f7d4aa91d9b59c72c3d3f65b16877d37da48bbd9d1028be6a950910720215949fb851130dbbd78e66d9820401bb0a5d03bc7b478799be327e85d40e61202cc85d6b83650105424142450101be1ee31408ae12c9cb49de0dd7a286909e529a15cb95ee74007ce04e03c2ec39dbc8437532a433b873176471431bf37e8947027b7425c7952175babbc2ecea83e498d8fbf84ae278641d23e88aa71a69c48b703e05037fdd97ab60c41365472bd2cae8014e675b2e4ab4fd785f933636027035ad855bf18532fd607444e58b60d4e29c59c8c28df87b345a4710bf90f978d8263c8bf4f7df5f4c9d9f39da2e5dfc94268d080642414245b50101cd010000b8b522100000000024846c2a2699283c53eafcafd0954535e8ff0764466b1d6e1e23a606d3e6e20b9d6653d36a72e7cca4b28990621f8e6ccb4c0234317659dda99d0b9708adc30ae372660639d17262fc7a6e6f6485a89785aa2f7ba95e5bc1616298381e9a31000542414245010102f243ef2bb514e36af22103f3e81ce861e86b74700c754ea0e3fbf2f0506131f18258e404c34e08d0c9ee4f9c0b00269da507c7b7eb6b790d211a6585bbb08f7f596890e583c2b84d648062afaaded87084d34747043936085577f3f67801c0d6cae8015269a1bdb371f467d481f0a64bee3f2efd058dc56fce877edf4f593edacb4d648c8b76a02b6c54f907236394cc5363217ecf375a8d54118c52b0801112ec3f60080642414245b501033e030000b9b52210000000008ca7aec6f04848f2f5e4d626b5dce1c4eb283f9dbb1aaae8f2400120625bc943f99da3d8338314ab44caaa395aadc00e221cb5bd790302b12a610a3a6cb5ee08ec1830bebc42bb9cf5654e45a3af6cad2767e7bc1ca107de4220b45760ca300f05424142450101f8b6ab0896975c1adfab8569ef4a9d4a6f43a75d164e98ef052432873701307f361195210f8fa77082716ed8f2add29f11455665c78fbcfa79bfc98d76ec8c81976092060b78b7c030a133abf86a3284fc6ab936ce4360cd22da7d974194b69adacae8017b576268b1eda93ce2e2ae1d23c0f405b4f6d28d4be8fe8796347a90dc93e8d08bb50b00e6a25f3325217c9a846cff6bb294a12a1e074cb6be37394a391a17b3080642414245b501011f010000bab522100000000072aad68c445653fe50051ba4cd099dc5ca90d161c8626f5bd8e83126d4947f24b9d39fa347ffa568803ee28dae40306871a2bd5ea511ac6ed397927d6ac94a026b9bfb4218dd5a294471511ac615ac5b9c4df808de6b040727c40114c55c110d0542414245010144bb2cd9bb76f6a4c4d375532bf3761adef0d3b879c602583020c69c75479e557822e9496418c1d087226d0dc93ead1d96fd161b5d7fef715a7ff865a468f88b2cb7b31b035a67e940133dd89f903cc04d75d5bd63d308e7ee9704132e74b909decae801e333d9ceecf4380f07172f60110fcc83bf4ad34dcaa44bde97e829b35b4089310c40eb93aa56bb8174c1ed773a09f6a01db0dfb9e382b0fc0f1b019167a98d6e080642414245b5010315020000bbb522100000000074d34ac786bec7c705d6edacef23566ecb5916f8d798125b4d1070a09533104229c0666125ebe3a00b8cd2f9197ddd7dc77e3065fab9d9ad0cf9dfd2eebe350fe440f644bd3fa01ba1e1ea5c9cd0d9cde92e4d1c04127783770acd1b902d3e080542414245010138e77d56da4948f56694e9e110290ccdf4ada5e59660c428326f2a01bae637604f3d6b3f9ceb786ce87d81e46da74b2f5571ac2b5c3c4da84155c03892c04d848802353b83d827d53635b80aa40ab34f8af2aa625d96219feb8ab2a661d18da9e2cae80197d3e6121efd3a9523f147d7fabfda04833132df8edb42814e12e2051777620c7dae116989405c7ce7ff81cc5617e48096d96d60f7a4e41a202126f9927c74f4080642414245b501032b020000bcb52210000000000e5af69457142961ed10555b1ff80f7dab7712c397b56abdb32984eef8190466e8d51f9198942916dafd71fc09b8c17e599cee1eba8db8756435686e3c250b0671d76278b7aaafc2f3ee2e8062e26659b93865a271d2c91fdf596962ada33208054241424501010cbabc8479c7bfe207e0d3569c8d0191fddfc5178189b349ff20449c2d0eb061defa8a58e003bb2cef828df93d39d3bb7221044a1aef2333fe08a4de98b70b85de92bb10a1bd59ad6f4f52a1e7824f78cc9388993704cd2894ea82ba33f2b7bfe6cae801d4501578bb76565b06160dc758851b7e2f48395de2c4d11078881ea67a08c629710eb815238a71bb90c67619244bd5bb18876cbfac081aa5956b14836feb207c080642414245b50103c3010000bdb52210000000006cb31f7167321623805489ae25752ee9550d935c2462967cdac4d0e8fef0371481582c442e94a60a10cca73c5b3409aa0731acc7ca6851db32023b5bb5f56c0b5373dee4bc1d8319b7c4d82190a8dcf56cb87aa92d66b33224404d7ad147e30e05424142450101eedd695808e118730540098857ab1603ac4d6089b97a5155a85d88dd9870ef65d331bad534474cbeb1d4b5fe278c4966708dbac2de2dd2941fa81be5c779748e928a261c2cb556a67a5dc5e9ddbeda48d72a1b20dd5dd8976a3392635129e367eacae80149f66af9d583a18e2f857ba61788e9ecc9498e14c7d96ad2a9ca553a601ebf56056e3920171b3cdbf19ca1d43d91940f4e5efbc716766ff5c69e7195370579b8080642414245b50103ee000000beb5221000000000b468ebc96f4fbf849b3b0044fb068e489f1da9939ebdc9ce99b631c0da532b0d7dd10ac727c312b629229635ac7857b23fd84681b9bc3882581f48cda96ff606b99ce385cb9a8c6ae222f5f92e9ee84b12987ef14226262cbe21ace6172df50d054241424501013449a4fd30345acfbee3f9ac903bd42b5de4cb74392ac771cbd32883c92695075d76f55380e93c899b47349c921df7ec2971a1c381b1402daf95436e196168804c0628a41e3c5e87997b10b50b2353e35c104c80d82d1bf4b581ef46e8d77efaeecae8015c73c18b76a614ad8b1838c4816929954a4f4e922daa00ea04ec926d8133eb018af3af11c2fdd390fbac5f68b530c036f7252edb146849b7954e81544dad5e73080642414245b501011d020000bfb5221000000000fe0143ea0f8319bb77e7b397bc48868b7921940b05f255455c3720ac5988077b9659169b9f2a8fabf70151cf69e092f9a7314c7e47690324e1620535d94bd701e87e5115ecd7ae29641229bfc8f859bf44d6683cd136f8cefa217d7b28380b0b05424142450101dc862158ab8ad4c7148e322b43ff491951501e263a293f44001d47faea493113e5084c51f18c70f0021114c51afb3acd6014cd620f9173d5fe31d8bb11f4888f252bfb393b0fd290c3f20ffb77f5beb513bf7bf1b10234427adc0fe5b6632feaf2cae8014645a867fecab9ec5847b0d86421e4b639e4a62d35736101dc248ec4c3fcd556f2f8066b497a85eacd5ae05b4f74e5f864ae29c45ebc7aa7cd18f53b6bd6a217080642414245b5010385010000c0b5221000000000f443a0ad78169821bb3d4b4798be179a2c60d916df982d4e4b8d9a019877cd56c34911ff5918e20b1ca326927e6e1d3c72ea951b6667d02165a4cf0ec1634c08a18ed53fc73f9469741e9d93fa33ee183dbe5822f453aa658276644f885c810d0542414245010184ead142583376c6e08007343201adfa09b0ffba57b5db4e8f32352cb965884c359143b0b6cce0c0660d96d046478e04be9db6f6ce434a5165661e312c982a84a29fc6aa0c3fe2fec6086e29ac4a8a9a9b9fd3bc8b7f764d7e5294ed3ba8904ff6cae8019e97b84f02c88376e6674eedce76002c0d2b0d3cbbb7ee8a210e372693b7d277171aba47c2a68042fc1b1355c2dc70f6fa4cb542b86c2f211a185f6f32c3566c080642414245b5010396020000c1b52210000000003a4cad297a463513b10a7e6fc44b4bfc9e124c297c4f6ce8746e0a7a77e9170657110fd679a870d74dd7b76848bc7c386a59d6c839d784439656c67260fc92064ebeb6e5428e70e42def575b5ddf5142ed4436548866cb88bd487266e9ed3a0905424142450101d8355fb95d1fde4333d9b8dbe12f7196851a80fde7b17ce915329c38caf48f2f9ae5fd087719480bbe4227a96a530c9cd72f2686b90a3311f6a431039516598f820d85000bf695e5e5c5d15ea7b2252573dffb7013e7e097ce4f968412aacb3bfacae801bdb03b5d885b0ebae1a966ee49f6d188ed2c0a8592b2bb71b2c61a904923f5e3ab4a7076cb53260ce7ff6244e6f68733311e1fdbb209d8a6c1ff56a9b608c25a080642414245b5010186000000c2b52210000000007c2d83fe6f55addc686622c5b2be776e99e9f2124da0153c20921d9ae2af24486d4c025ecd85eb3109989b28eb2adf54b719e341be2d946ff32fb604d1e1c10ecfb603bac3831c7ceed9fd20bd259e64e631bc188d9f8534b2eb08e0be01440805424142450101a6304bab7c502d279608ba954173243576f530f9a15ca5270e97d61ecd2f9d72abb36f55c5253ac9ccee4d213bfbb2627c85039506b6989c0cf2c8878e589889866d61e5344e91e8be1f117439549c0b5e186985aacf0dc9720f3c7663cfe6defecae8013272039f4094f35962e49f375b20912ebfe0f2e3747397ac14678dbd19a87bf86f00dd3716ca67c854237e88b87804e9e1da28e97fcff7ff11127b495de99f40080642414245b5010357030000c3b5221000000000be46bf85e3e4f0c528cf93f2118b39227937eeba91a7b8ac56d0064ec8038366e4567142eb86375de98e6fc56910814ed03408dcea8ca42191f889e05c58eb071480e6007be99969c0407c491e403a5317d8c49474e4b4fc853940184da0300a054241424501015434fe153c96acf8fb7f9f39aefc2263fb9c6e3038a3f6bb4469eea3ac05b5395934c0872b5128323ff648f5cf5488affc8513bdf1b529e7fdd6335cb04c0f876c6fbd20aaefb024b815fab03902ae2be230bc9fc7d3a3d95971db33959df1a702cbe801ebfa9cbedd59bc39558a13ee7aa9c95b234b7574b08e8f97363bfe7e11c4431d778ba5a25fd1f258d4e1023e17a78cea3f418bfeeaf273a57f6e6db8f66114d3080642414245b501031b010000c4b5221000000000d42c316b3bff13652c5cc12285b61bc932e58319a6521aeefa29535384ea7109358d886804f7f849ebda79b5c90a4696da5dcfe24414e99d0345b46253e42c0068b2d91d57bb2d071dbe174b7380601c2a364f48935ccf54e21a2a8b471e200105424142450101320bc8e97b2620bf8065c7b02b994104a54568270e492e6cddd8bbd42148cb6f12bd7aafd96d0717cb720d760b71f8fe27bb67196f58a64e41245cffe7a6a8884efd02472802296e7547fa5125e890a17be7760c24254cb8897a0d65a0a2446506cbe8014d058892942bc81ede6e8de5fb926d50b41e0849d7ef56035c0e72810a48566725ee2f122f22c5c163b3b0ea0ae12359c0e05e6ffefa415153b1ec38c31022ea080642414245b501038d000000c5b52210000000009e3c37904390fc62cd44064a631b490181573f91cbf6dbe5b93f4ba0368a9f1bdf986a31cf25c07e662485eecf5d46b676f88818542249dd2fec331490db410877fe6c4426c47483180b14da9d9a239ffeb06f288695df15559ce55aafcf420605424142450101a69d3eac30412306b70f4c279619e6e5165656f4f421a991a99f03b4a1c76d0249fc94c5f726129a8a5bd41a4cf980473b63edca7091d01bcc6810223b7c2f881d355edc3a65af03a4aab8af0969ef726be7b06e0e9683aed3866a9cd28d8d810acbe801508e8926e8405b8cbd649c5811e21090b151962afe176c60430c5d181679f9f5a454397a7ec2d22147baaebb63e4b61aa9e3c54dea3b5b582fb8fffe8df732aa080642414245b50103ed000000c6b52210000000004ee9d8bb5f2a6010623737ebdca00456ca2c16ca95beb56a962f63a587ae9b22903b8f5a84eac3bb2dfd0c99cf385ff3ba6a78c876c918e694ce371041d7520b325610480533692e4a6f48a14200c453ad3bd1ca345bd41ccb379f6b8826680c05424142450101c46ba2e8e8917bcec9009b4e1b723f36198d684bd3f14caba165c1bc1bce4048de956ed87a2e1ea978bc44c00572622b7e4390a981859360a657d32707039d8ed8f2fc04f5152a3c5fe1f63c7f5b9e690ddf44b4c0439f8f90083f9c4e293a9c0ecbe8013ca15b2f18d4f01103a450bae9c945a827c11a9d5ac25ee99e1d32a4aff15b140b5fb41bd63fc8bb3065751ddfa579253669879a3f52624b856dbcd8d5ea6a90080642414245b5010319010000c7b52210000000009a51ad25af5f3539325976b84bfa7c8ef3b72c1c83dc81086ba2f57ffaefb6530d0951c93f5fee2e7879eae01bf12519ce47222391b608ed41aa152773cf990a16052fcc592a1e917d1ec67233f2ad7cc8b08b79b41c013e71175955736c680a05424142450101c0e938e1c48d4b629ef8156c1534a2a9216c0001a78a182302ba7b0f3dde7520552ccf153c01158502a6af7bcf949af16edadca6e71b9d2c4f6df7ee190ebb8329fcd31dc552ed788c198163afb882d918411e09cea87657a375ab85e6fac24912cbe80150af4b27dca31e7db245b076051b9406933fe2a521b85019896331852c6566a0b91a2dcb07129806f61f3b206ddcba3d1e9d78d4f4f228d153d2be44ffbef3a3080642414245b50101f4000000c8b522100000000024643d9834f0874abbf0fcadff559ba471bdf722ce334f003b3b56410f24af678c40c063acde27f53a95e2ee91984253cb20222fd6240b9561ca662f777b680ce76230c65e469ad48242ba0869db173e90f70cc6d4b081a30b5dfedf4a60d60805424142450101cc8329fab8839acbce71e3bfe3be62cb317e41fa0ad5809d019a6b5f3a66ee715350b0278ca8fd1506b4329b4379d3d95e8c0b3611f2198ce4cc313adb53c98d39aab651323fb7911377c53a1c6bffc41aac12b49910b4e179d1baf50b28b90016cbe8016cc40f9f99666d31f540f052ce75410671f3e5f259c92df5b0ef03456ae8fdbb25d34f4c584b23d8f57fbc46d8f48807bb7086ee10a0439e965ef7b584555f80080642414245b50103dc010000c9b52210000000006edf86c101c7edc425427f201790116dac4101020a1c9850759f29ff2131ad013852a7f6119d98dee3629d97d72fcacc4a62db9358c6d6b925de6780514fae027ca883d1da4f866d439c62f4945a909fb64bf368edc2ebd1b1f30458d38d490905424142450101528b7ad3803a1daea01f92feac6c92ffe63a520dd9c92707729f8af08a8dfc662081fe9a2ab0c14803216262571717aaf401ad68d352781b233bbc4b3d9940807dc87d728bd83484706ca4899beb948067411b481c2c5a1653f531de2fa16ff51acbe8017b8e7f643d64e8ef09de0d6908a54c2857c8219fcdfbae6e91e34f8e2798e169e93de8f198d5456e3791c92013ca6508e82ea762bcdffefbb5ccfb6b92b90aee080642414245b501039b000000cab5221000000000fe614bbad8b65cc81f00151830b0f290a691a9062e5dfe025a2539706aa46461d77155936f052db10a18073a3d3c1ae7cf668fbd8aee2a0ef634ae241a01360c16378b618db5255636c89ad195c10326ff71f031502226b845f6212ec483c80305424142450101f6f64a100e0bbb3fde3aa30aff3f0f03011d648d7afa926004b01937e36c0f4b07154e5f2c77a3bf523a491c3e90d66a1746abec8291bf584700e020b269cc8920d35ddb73aa72fd94f49af3307782987e755a37f44c5702568caf820a9799a91ecbe80187e8917ac8543493b1fa5e8557224db0f64931b8d758927781059d81493fb0fe838f9b263551b1592c68e4dfb695c98d5fc7ce2d4fb438f2dca3c4e8c9f91f06080642414245b501033e030000cbb5221000000000bc4ada27fdb5e7f100f6cfbfc5eed0d2946e8e5b8a6f424a0a2cd1e7bbe2771b30dd68c9fa9df71185eab57e3763f6835dcbbe9e008504231e7e7a6869928b02709f3e7a5c29e9ae896a16778bc66643c47ddd349295bb08fa801b3763734802054241424501016adfb483b78b35b806d37c2262a8ccb1827a3c232a9de9ab5ffac41fe6718a517dfc9beb24bc55f4544fd39f1881eeb113cbced9599bc5826513068de7739a8d2cc25a461163e3ddc2a313ad76f7f6291dfe0c7414967e897b6e175c41a8d50622cbe80161e920c54df6acc908733ea0425684547aa451682a7543c742b5bef79b95cf94613754edbc702238ce60f5a5b14ca64251b307fbab0c1922cfe6dac31725befb080642414245b5010313010000ccb522100000000028c08d16806fc633e518d47c56d201736c7b99632d4523615fd246e8732c100c1bbbc2bd05518f266dce6d5f0e98745e899fc57c2bbee0efedfc98c3fddbc804362d5ea7f21c8a7cf7264e6c66a6b3b2378665da7c5510523538f5924afa650c05424142450101623add1b09b65affa425d45473a6a0aca88994957c74bf970d68637da33cdf321d832eeb047bc0b6826ecb5a3e1b5ef9d0a69592e8d488df36a38dfb6a0a0b81c04619aa028a2bac7ead70335bd7ed68e71c9a9541bdb4f7766346a36d2f783e26cbe80111e665f04c0bc29a11b8595968697c5a51ec38827e152c847228e32e5b8aa81fb1f440ffe7715a6968a8c5426b265da0f2dd9ea92f3aced8f4645d1bcf48973f080642414245b50103b9020000cdb5221000000000904245f37b57138febd381e0036c97861d078c57dcdc06cbe1f5c234b8936b0bf3ec7cdc8ef8390b9e2b3c02aa2af7d5fd52d719f04585161eba14fdedaed20c1fe567aa6c71f62995cc3be343c78a842e0311137b4db6fe961a150024c95a0b054241424501019e0eba3e28b850dd2c47d9af616b579d5e6ca630419c8923c87441397843eb5edbd9450e4aef3cf39fbff6aff4535b34e7c1e138b7aa6d63651917bcee05bc8ed86e30d46b21870e621a2a11403415d892136fc54f638ce64a2917d427f275062acbe8018e0288e12509569af43cfa98a25be8bce6d4188f378887015d0cfd659f27e137649433adba1ed00164e935a5b1775c59c1fa41f0e6f9d1a5b1d1cb89872d46d6080642414245b501033d030000ceb5221000000000f8f54aee659e10c96c9bafffb88fd6c0c23f7eb7225f4f1b21577ee6ab9ec97070824dd5994b653adf4f97715b7961feee0be39c328a7c92a0554619862999031c59c5371ab86b9d7328732b3e6a08fcf9f395f5c6e392acb2f119a12618a70205424142450101f61cc4286edaccdcce22cc1e842a07ebeb44aa1c4564351e47f56ef839f8904419f7b0c2b19c6b0cbe08eba63b857d8522d0de8f6c1ac375ec92d6d178f112802fef77edebbe19b32acf737c45bdb86c586674ec89d4c53438b888e571aa44852ecbe80104080e8169f417ec3a904c01b970d97c55a79cc92b36412c8f8cdefb196adf040e201fe032303e2a89a7e9ef82a5f8a30953a76c399f189f0efbcf65581d9882080642414245b501030c030000cfb5221000000000144532d5fae98268f24ce537d5bb50de08073a0c7885487c7819e2d2a9f93a6303a8739aed58d711277570a84250634d24e3c2077a6a996419f07a884cceb60ab2f5c085393e43c651e125b2218c34c7a416ded5f60fb4967b214ee9eb72f90f05424142450101b83ec798f6dee987e9d81f489612624fce6e310c50cae6f5a4240e5a12b795505ba37b391c5867431825785e1e91e70e71eec31806ba8e8a5db224333882ae8ea1a1eb126677d41eeca412bd4695efbbd95921af28b295b81dec2fb22dc1dd7e32cbe80178f6285cb0edc7d9d5053a2339679a2e6456747d6859efe3dfa22ff0556ff9829fb08799f95dd248184ca1a8997294c9176e52d8b642795df411fe4aba6e32e8080642414245b50101a2000000d0b52210000000003ea6bf420d9eca470c8e3727fb511e7cdaeee741ce579c7ff6693b38ab3586370cfbeb751bf74f42c80c449df339c2ec3591886509d44b256f33d9ee0f1ac60b6d9728084cc100a8d500bbbb9d2a93855307236c41fab550ab8f40f8a3c6660d054241424501013499229ef3eed5b342e39fd8856385b3913951d5a27476cfb55b6dbd78cd762f27d337c5c15238bad81f773a9e006d6030ea2848c8a746fe08bd89d6c7e28c888867b98bff716f4ffcbee1933f37ae2c9a03ea5e2c98b426cd42dcde3220948536cbe80133763788eff54680471af797c461b6ab83d513928bcc4a48780a0ea939889d0bffc9c579bee8316719b75d2e7c5b51a5567dffc43a0250c5c99fa10621b94a03080642414245b50103dd010000d1b5221000000000324dbd985f5cfc2338aeb95077bcbcd12f3f3e9753b5118c32016879f105a312f9948b85c82c1073bfc64c7a4ec3c10c9d040a6d0eb60977158c48137877bf0f8937239a047cbb5c14377f0bdcee25462a3345cae1455d60f9d266363a196c0e054241424501010466b8d46e04e751dcd315245353369f09a5b87d0e5f274069aa793e50e00047aceae451f1b3e959b76d77a3fc8bf045ac9dbf45235162b534ca5799bff246884b52f59aaa93e21fc9d9f28de8557b15c97822ab620eb99d08a41d589163996e3acbe801df357720bfa95fae388b5a8c410c550f1f7fe9339e5c7fdc32e4ec09404282f29265d299dcaa10006c3e696659cb0f2fa6472fdd69dc7d70854abc114a7fd9e3080642414245b50103c6000000d2b52210000000009cce6d84edb07bdcbc1a47b79e59f69064c5ebf4c378e3b1124f594ae6e8023bdf3cf60ac164992df5a2b709e5eed26797ae255cdb171bc304e6ccc7152dff08ecd1fab678b1a0d28550b14aa18473cb4ea0e4df3281370725433b9a4f42920405424142450101726e5fdfecc930b1a0521ed643f2d5777a48802b5c634fe723fb0ed07d5265188c33621786985ab296541a8122151fda0d23d1ebd8eba29fcec8743c3015af8137a8962fa3f82e26242386c3f75d2238b364ceb36bee861618c3df4a8f0afced3ecbe801e38e5161e2a21f9033202940ac6960a0987dd282eed0d2fc5b90313b1519a1301fba413317d43135987dd8b1e87e645cfeaa3a8da918dad268d8da7e27812260080642414245b50103e2010000d3b5221000000000fcb80d9720d4ad9b076ac5090d1dd4f7128a8ce7699cb7f27ed4e468babc3c45385a25a8b39a4bbf79c163c79035a4a38c5e1e3a07b8e1e14605b05cced23c07a7e17e7c3c743d25b834d1ed4175ed1ad668a7da5e74c3a57bdcb529537e950c0542414245010196e94f9f4ec1ed8d7857d02484cf26f35ea34aeae073902d89b8a6e982a750798dbe1add96c3fd577607c0b4d21ebb44af993c260cd4e63778e244887c4b2f8590d9f27fe638d2dd10a76659c2dae6b92c18b7c140e49714f7ab535d5e88bac342cbe8018970953db988e992142fe48910e26d562c9b6369ce89234e8303e102015b4223fafc3ffab82756df1de486fefd5d15d1b774ce89a4dba0475d3f3ef47e3cc6ef080642414245b50103c7000000d4b522100000000080e1714509678eaeb02a6a34889b1d9e27031f82f39cafd6ef0c8ecd0d7fd401698f1bfe8a48f3c5c86ad5f229c3c3b99fd459429bdc09d86527782e7334e20a100f5bd7b570c8f3ce4733d3a841903dd3feab2831e15a57ff6dc1bd25f6680f0542414245010104b071ae1625d19764bd6291a598ae0192502881256d199e8f3ddfb76521377ec2392b7d652d4f708a48e55c69ace258b37820620b07bd9838f4d427249c1b85b6b241771cd7da166f4d7b554be68833a4c90fdc274455148148f9fd86f9ca8346cbe8012273c2405f959b311f08b8699f53df29d4128855b3ad7e52d41649e33e70c879b29e92ed87eddf25261c4189f4650a57bc83ddb145809c6712b8b5797e5235e6080642414245b50103fa020000d5b5221000000000d28d5e9df8ff10082fb411e9c5de2c1339556973db77e92dc4eb1f8248e4833a99aa0554e09b92c03e3700e8f4ae97126b4ebe3f969590eeb7c383f9e9b58001871e65c698676841ad348aa68d33f945b2b3509f8fd71017a7a671989aa64a08054241424501012abcab6f5bda4cd0f3360b350af174d031b427cf496a9b68b8ec4cf104bfc40ac5a5447a1e496925a3dbc99f4c7aea13ade78d9386c0dcd3f788b39eaa190d8dd3b3340cee52d12fb0542e43cdbea527ebb3966975a67a5da30b5ac0d1d8909a4acbe801f70bc4d5a80cfc723135eb890761fec65e09c01c1da404f0e9c42142aba9d96075c960eeda3ea82f6ac3a50bfad403273f0030a592128640555b09b820392501080642414245b50101ce000000d6b5221000000000b8c87b539a7459efc1b42ad02eab5e58135890317203125d613c7aeb0bb8174f0a7d320a7e60b462f5eac0885b672a8b24b4dd1c326f8b345f16ed06756ed304ffbf686765f2a67cc8a6feb56a46163fd99dd2e4cce7f0f19235389f3a2c440d054241424501010a835a032d3249246d295d1e2511c04d2c0db9e66ac5676433e94118c046962ef9d170d957a35ae4798667df8630dadb3e5c0ca05f2096ba582473d5a97eb083fd39cf007de5eefd2b92c0598e6dbce06fbe5b2f6008bfb344839c1590d238f84ecbe8018028084fcffb1f7c75df40c456cc6992392f6e7d781d9ecb22e0ff0f7733809f429731791030a616146e79ad0c6e92419965bc5f2b8d1052a8d3ea3300f4090b080642414245b50103d3020000d7b5221000000000ac04379814aa68c569482cc0228eac5989aa15d76ae2506f024236029fe05e5426af29f5b59f34d5287005693a335ba6dd72aeb171c031d8fa95be9dd6972001136c6a00f4c979c52ecf77d0128f5dad8551c8d0e507288e6a2a5db4ba52d608054241424501019a3cd418e17cf5bcde231f61a57972aea4504aac27526ed1fe3e4f516d4c535603af8254409bc6ff8bda450f42c257f2f7ab664ae96d5e2c313f9264648be88291990ba0145d2bb459aac83f2f4aa657bf210a669ab8ac1b5526eef96a1beb5d52cbe8014ebfd44e367ba089ff33142596afd3be2a167c80b2899838229f3ecef40c0e83eeeefde6d31c95bdbecf40531fb3b79192960dc01139459273c1a313803a443b080642414245b50103bb010000d8b5221000000000e86cf68a33a4827fa24e7ca46bb1ae0469e6f3c875931fffdd975c2e900f6162fbce2590c8382786273695c3e9684dcd09f9b99803fd874324ab42985bb3d407f8babdc780628b4a0cd6a6f395b5d89e2e3a030b3f1589ac02628c4994ae780405424142450101ae7fff48ea61c40e8d4ca7aa5bd07eaad88e2014a6f92a802f3c7169c3427609637ece47265529da201727e0ad91570302dcf90258eeae24943c5d120873a18b0c34565e975cecbf843c58ffcd72f556ac21727425594cd8bfebb2387ff2d8de56cbe8012d867bc4fba900aecf76efadde690615aae03b3b44dec5e1f194f0512f5496fdcb9d8261f2b1d54a2ff4984195cb923c7ad08e31f60fe5288ec1a6c9b14ff971080642414245b501033a030000d9b52210000000001ac43239ce6dd44941c5d2c652d3a398d5b8f4a8d0e5918d6e421f912015b55658bdd1e71481a04b680ddad90b2651e774cdf355bb9d38094388ed1922da68030e179ca26d124dbc6b110477a612cfb385ec800e7235dd0ff5bba603989b1803054241424501018a7133e6ac8bd4bb1fa8bee9c8cc209e93ad2d13dee80e8b4b75b911fd60a5414ba11d074ebe14991146115a10b332f623a43f173b113c8177004fbd3cc2f388ef9a2ec1929aae54de26b52c9bb88dd98c4e16e76285041445a9564713cbf4e35acbe8019a548f598a181c96f5cbd16c0a8b2637673e6097158392c03ced015e270a639a89aa21962435f235061af6225f1701f04efbe6b2528d105329857363c8b5f03e080642414245b5010358030000dab5221000000000d21c54f72862ff694b4bc8c7bccb36034df6ce9d37c7a7ab47b97f89baf75061e8d708a3feb15385c2b2a6763c989afabc5cc28a10a98c665d5a553b88e9660ca11fc27dc485e27325e1e253cda06f75e4675fd90bc97a780ad128b8e266f60f054241424501013ca474f1e6c1bec1fba30e8f859348c8b6b31e8a27c961eabb0fd81bfdd11c1a88223002d5edf7ce170e870d4695cc435d82ad1cbfc9fe91bfb6dc98f814f28e8e812dcef1758e36b09b8a0177c4c1d609f32ba8f9dd10a1867a612ab809bc4d5ecbe8014b023f3d8cb194684601505e963cef5f7a9a174ea33417352ed817f31b242b82892bed2f71ca1998cdfc11954d6d6b6d37fb80d6dd2b7f5dfe290bd706e80ce7080642414245b50103fb020000dbb52210000000004e8fb322f13b71283565db8c946dea241b0d2ce8651af139f6a4313243e8e923ea7e1c981a9fc2e0a916fa5dda11f346d539f7c516ff08d40ee82f62bcc1e103a24ece964aceb4584c4d0fdb333dddfcb71a1f43706a58799ef2167c462ee40d05424142450101fc3598b913577d934e81c415a8dba567dee1d2c6d6c039af0c3eb65583e82a58c7f7e9f1b4533a592d2a8fe5166361cb3366a46cf079f44d1eed01e53d3e908f4dcb92a47e923605b8f7e5ec6b55fc91d378c367e15240cf4958ff4d2248de0662cbe801940f5fc49c1664ee734b14524e3899d9a4a2c03e12ac376a67bc3d1cf5638d8a6d59a01d6d9fbdf0170000f18c6bad7ca62cc904441365b06a8b2aa130517a03080642414245b501035b020000dcb52210000000005866fba9fe1f556e4d69052e411e91a1a6fe78f74696e7ed0cd5491a4dbdd52214f46670f4f767bfd3c40856e175dff53a65cc7a595feeda203b622ea39b190f63ad65755dcc10498a23446c1b2036160254e149c67231792b8cbf870e82010e0542414245010122c77db56e0d215823e18ff7deb50d6eebe96a0b54f7636998858e9f5d27434877ed757ab9faef71ce81d60fe7467a2dc60192c9365c449e3983d2284724108da3d4256bb405f0cfef9856d41839f2cb84f80ac8a4b0b508ae6f1b80d4c1a27366cbe801b035a16df665e1222c3b8a1b7fc5afc456e844d3e9079b8c2e527f2d4354383fd19b3d631b873a510aad55c3825aaa0a5258b60fa9b95a36b5c4f81a0ee70d38080642414245b50103c9000000ddb52210000000002eb0955c0df58c5c85b9adc3618759d8c968ac6e1b685aff15e15f551ee0b5380128c43183ef8538c4b1785e1b13283da047e5f19cb7e4168b26f725a8c394063def6c5c27bd9c6228e3b8a072985cc698899339c35419eaacbb6d3b5965ae080542414245010116e5d600c86d59ba69f3ad9df981d35f1404e9ad6c2e783c427d4954b812a959ef6423e5e0fc62e87e479f513af4861fbd5b74c8bf12e280355c7f415bb612818ce8be332ce6063b94ff96a5e3f944bf2f7c7f0bea7e12d00fefd2ac2ab4d5846acbe8013aee646e1d9498a3e262b09f5a9288daaf80b61767526b5d7341661f16f37903868859d702345659d6e144a3bba96cad63da7f5f0b8f93a4f468181d577ef87c080642414245b50103e1010000deb52210000000001c160c0c57008ca09c4a71c5510ed89b26ee1949ac050f62d4ae28accf77916ccc4b7c634e93a3c818ac344e44cd85a5339d5bd48aa525bd68bc21add3757005159d38994d882385bb591e55402ccffb7698a7dafbbbbd2792eb864d1a41fd0105424142450101eee55402236d5e9f98ab7f4f20663414caad79c3a957fbebbf453e6c6a630f3f47ea49751ead9c998de8fd58ff789da6f590091c06191aed8962701dbaf2128670934a62191fdc5ed0cc51fab0eb159d1c970e77a2a6bbfe43347f55d2b4225b6ecbe8018b8f5811a7e07ff8cec56e4fe4c811d60c409955b0ee71d139089ea410d0510ea62a6f91deafd447d67fe0df25fde214fb8daa0d2010c7b0aaaedefc2489e9f7080642414245b50101e6020000dfb5221000000000acb6c5753197968e4681d5258f560cf583a679aa6ec44ac7abada033c2ce6021ce87602a7e54b589be2984adcc49daad08d3060c459a159fbcb8a253282d800eb38acbaeb82fc6f27d635db5b7629f932dc189f63523918d0ec7aa11a6e6510e0542414245010152bbc5159d604c306bc0214aa75963ac9fab6dc6efcf77e8e3b5adc2ffd4d5657b9033835c83fbe1edec450ece839cffbed3e9534ec8622198cb762af1c98b83ef7dd06b7a88038cd940ac19851891bd16afa6bfd0f0c8bf1d87a2d764d6318472cbe801d84a72cb4734f4ea113deb024fea71430f141b029cad0fea78ec8c1e83637b50ef09ba9da58212241d701cc7838cd825d8e4f97955f23636d3b608bd3e7a4f33080642414245b5010348000000e0b52210000000002a26a482a157aeafb0c8792786a365f91d7366ec63a8316a51ada9becd7c5e7f979859ec02ace9429cc674f77cedbd05bd4c2396c40bb86f4a223aef8d987d0a94dfaa21f8453ad2400766aeb433b6117c2a78279202a2dc61f13cf5aeac5a0705424142450101389e8fec223441d9bbc09097fe4210edd1584789cb6e1475d78a1b0cc8d37e1df5a9bfccf650afec71c2f38d8a0c24a5ffd3403e77ab3956d27e882cd1f57384430b3a42356ec89e2801261385e58e888a5b9f2b299d8847278d5421b5f29dfc76cbe80187415176d8057bf9ddc3632cbe1b0e3f182c1adab1719ff864858982cfa1a08e21ed5f833e82054be75d82b1c2042bb25c946299816636f2db643ef7d5b03663080642414245b5010387010000e1b52210000000005028738d976e4bcf135e27d9d182065e213e45c14fd165d8695b68b85bb84b768a954ee58f090e17a65f1470f1ff60c2b890535097a342d104b7c6734e7e4a086f4ff8c2ba9e620b931c8f0c0872286f05d8a44557f6ab3538a2b96ae24d3a010542414245010128e13ac3a022cacaef299bc648b118eeaec07d771dd75a25b6d5df862e51d666d82f913223882dacc6e422057429f8dbae496b7aed3b4ea772a70baa2d8545831389b48c8d94029c19c8752c0a92721090063769a72dbb77c115534c1280712a7acbe80178a7474715824e6d7c36873f12979c9be2527f2932cb5b805247a6d313958fe3a53c14ace0fb69e6c187948b7f4a0d1e21f0bb67d5c5415ae9f194b9f4abb8b7080642414245b50101f8010000e2b52210000000001647a559fdfcc432961046e648bacc70bb954b28a8b6e47ac748504e9d3b33244d09e20e607ef7e9ded73120285ea7738659ff758a5fe67e986aa2b964c9580e751fb178ff37d87df6aa9ca823b93085a43c37bd91103da77d25b1c633a37b0b05424142450101002da2c3669e70d1259997138f65dc247ef181e030f1e4d415cb6d520356d2579a1a3c5528a45fc569a160792e6344c3a93dd47e7d57dd5152ba57473030db84e94c6da32d8df0ee216045ce1d416b199914f4a30454753c239e7f8e4ebba90b7ecbe8017a9c6fd2ca811f5ec98769619676e341ce00c5ec59a2855aa5302f4846fc324e29af31261afa1db059356340eb2d37f03b6e9f253ddf63a0a42d2d361df0b592080642414245b501018e010000e3b522100000000050f7da70cbbcc319fae98c2c01bd7484d867639398ecd11c38e1501be27dc8644c12691fa1b06abc77d5c88df6e33f3d71f0c795ab745d72b905233d4e0d280efefac41647b7649690609271cf619edec54da23a86587c84b3f2b66c20b7e80a054241424501019ab231296cc67bfb745a9ca7981131c963edfdc7d567080eeb1e7ff27eec816a34c7167fbf4b28752b581965076d28655e1307c0fd3a9bb29afcf257c8fd728b73b3ed0ec2ec75820200c11f4a7c1cbe01a09af82c64119963ea6a3525cd9ec882cbe801d8ab7032c7fc4a7b33e8bea94e53806db40c0703567117e8ddc97f00c76d540bbe044ab17c9f9fcd6308953608636c415dc2c1cf7d8b54245763a319af7d466e080642414245b5010321030000e4b52210000000008e816586572e31b67ed4efdfd25875c98adc9334b88307bb7d9cd66690c558579e41ca296a72bf271a98d65ee397469126a423f6e132e31498570549ce84e80f850527ceae096b914eb73f544146f7ab81a242e8c6a88620998680e92384630505424142450101aadcb4a377d1376da020577d26f142ec04a98a994a55e7f47144f82410d3214018216e248a91b2b26b3694880d3a29bc2d1e642d26f512c252ae8288b5125e8981826660b0124f9502aa6f33515cdec3c3027ee5902fb0c3428dee3da08dd2c686cbe801e08c9e3da7ec271b8125a6b09c13325f5b28661a955666086df494318cd66b16fad1ef8e74af05c684288887e23c2138bb0839b768c0a922adfd1382fc906ad6080642414245b50101a5010000e5b5221000000000a2addcccac5a0d83f31bf6361b4a05012985616e8acef205b0fce21dde17c804a6599fbeb96fb8f7b9485fa0e059530a9611c81bd46f59e9df8e4b69b58e5b05d688cbcdadf635b0a8d2f20b2d6336c04a7078937d4c9e491bdc4ee44eec8e01054241424501010addd1bae4976ccd871e8b44d47b77ffc8b1689efb3b06001b0143836d0f29730b8b073542f1fda1b712d1b6f49998d4ea7e0b97e1457e1b8d02cd8ccc641b8b4ea800180446f35ecc5abef8879850904faaad75b1c12310c6d77f00d2cf87478acbe80176bdb05ae6133bbfc9b73c5a5a6e92d4d0767d5a1b5a6de05a0b3c23047763592b4ce9c1fbe5ea9eeab19a7f18ee30aa673228ad46b714ea029a05ee682c9a55080642414245b501032c030000e6b5221000000000da1a244dd293c7405446b47fe77c10c52d0839ceafd38b01b4cb69edd7bb4e14db7f3830e0594075dad776f7531e357d58261d3de85bacae5b1dea093f14650a760989302fee6177120b5f5f441a00d0356a2df7044667ab60c2f46ea07bf10d05424142450101cc6e4adb66d899f10d991bacc8e9b83d7db2d106abf870629d3ba9e767e2bd75fc395fc4d81d43f19f438e9e4c3b0b7ed5d63947aa45ebaa8e5e5af139a71f8528208f2a86db8ef8a75fc6c63cd5a9a8351846dec39da687561bc85a26e190708ecbe80163eeb346f7a6a975dd26ed1b265ebde8d2557f7c2926030c5971ef37e4a70f34d9440e9c3d1aedd4d143b9772552efcf001afbff54f893120eb2ed3cff320d59080642414245b5010335000000e7b52210000000009cdbe00d7ed1b843eab3be7834743f21690dd1508bde26702a5c5ea4a87dd941865c76debcde8ff80c3954c6e0d7dda3110128192efa14587d5595bee237310638ef4a1301a14340aaa66edc9af82bd3542ee26a93b8f9d6e105d65893c90f07054241424501013a775e8354cf0eb44e09993f0ab96dd5cae3e5411bdef57db4b5a50e175df15279a52130a1ddadee14ff7c77a218c68fc3b8a0fc33fcb89b672cef1cea3a1b8009ba3920b607f870970646d77abc001540a49fbe840944e7140d5767791a380192cbe80144efe8d7109f8c0e4dd6dacb93bb11fa21dd5d52cc9deb690b907202c78fc13daacd320cf8d2dae50dc314e8b759394b7378a53113c711635c10ca6270f0a1ab080642414245b50103a7020000e8b522100000000066b3df1963e6aa5cf01f77ad7ec3f9d690ef77395691f84ed0849e658d98ce5923b07213a9bf66a539cb8a8a8da63fe59caa71cd1e7bd841f7e0980200a169059ec72658b870f53edd34e5ccf614c5731df8aa99bebb36f904ff7b0ba9849902054241424501015c40892bd364cec7aaf3bce0c90ff0c6e348195a4c2745bac0faf0c8ab636c475bddb13c8b465c6aa1b49146122b1943d70dbd3803a7536078dbabd9612cef8f50b51a76203ccf906b8ef86448862884db7a1fe84674d94dd22ae6d38b879eb896cbe8017fb9587fdf354ecdb201ef2db9a7c7a00f0c31068e86040eeab8d864fd23cac94b21a236abd7f487afbc92bb59366c5454e3d37e8df9744ea910af6ddeb220e7080642414245b5010380030000e9b52210000000000034324d6ac472cad23a0a7e04a74bb530e72feee2fca897a75698281b2f907daa8eb775a1bdd4ac52b6dc135873c6d3b01c47f37c677d7d2ab343041fc46808783187ad4400c9c71d2104b33ab7845c25511061def84ec0b46ad84d75ee0b0f054241424501017014b4ebb19eadd2c9b109f43514ed817f8f4a02525c3016021749daeba9271455742b37be8a0e1997a68fefa0419bb5924428058a26412bde5e4f5bd809bf8a12be4a1d662ab25a14bf8b972133bacc946b5a019442800edc79c69dd606637f9acbe80185b5d8218f1a931ef4954768c74b2f189e3bdcf154c6c186a5939193da9297ed057b40c5d099cd5b38c3e0cc954eea4299f0f010806c06c0a55a2a0d64e48b1c080642414245b50103a2000000eab52210000000003c234b398beec648d230d968d2f929800dc32e84838690733e27cc8770b832327d25215429cb190486a54b546ce52e28c4e1cc665a035e371fdbb790c410b50943678f3d2ef3ef5ccd0b657e4b908fe1cd2dd7bdafc2d8772febb1879924d10e0542414245010194e6f264c62d17400eba10e5a68aff820a5a6378834027eeed48b674e6401913f4e2c10c1ae424892d26549a71691fba3a1462480e30489333800d12ac4db288d755532e136669606afc87de87f5cd84d8d2b1fa8f36d0a48922bbd8be2cb8a29ecbe801c4ac233246c2702d5e7922048c710584557e33bf200efc4c12ed0910a03f673d6bae5c1d584d5ed36b4d45c568bc00b7a66109d869002fae6db1923a7e9443f5080642414245b5010349000000ebb5221000000000a87baf0fe79efd5b9faba15e9cafb1f3e15357f4b11ebd85efe3132668ec8d04c8a5663b45a3941c410b9cc16040f724df506721a7731bd1db11003e210cd504ca7fec6ab0755a031ef7744cd0e92d641ff597decc61b290061bf949928ac601054241424501015ea6551aef4ed05d8aaebedb227303d834723e3df6048c3f4590cd0382304d2f9455d2cdf066fbc18ca2ec358b06ace4cd938609c39cbf04029344747aa5fa8dd5924c1e91e4235f11abc94f6bd43e13128ba1b0d82c7570078f2dfad3a585d3a2cbe80117cb131e52ad6f12c9d07e0417398289e9f1ebb68626fa8dd6bebe3600d74af3049224a9e606c0ad5a323d48c30224f5d76730a449571f26db2de2c45b6df65f080642414245b5010374000000ecb5221000000000a8b2c1a5b5643592fc13b393b8d843f3ff791782920152f8ebd82027cc743c79299dc89b240e600771a6dec87170000506e9cd14db40ba91b39168bc06b886090972ee8a4a9834a566d01dbce2ad865eb2e6d1d52a15ea50f6ad100a8fb30b0c05424142450101f8090c2e3f653ba25345d929075b666822f78a02ddc937948dc7915727ae1c1ecad2cab385248061da58e5333523c178dd1f9fa08175cd6b727927d4b92b7a84b5109a404a79b48c96b82433288dc0b6db2fd1d0f3433938978d221cae32f5b5a6cbe801c6c28e5855bb2ee71ee9b5025637bc0a5aa3c6cc7ff6cda030ac0803b1c60998ec7a226024fc37c76299080bde623740b8192459bcf95f9bda232a5d719473c4080642414245b50101fd020000edb5221000000000b4f81ca32091f9e85238ca237183553488c66c61e75a9c996cfdc9b7e38c8467f643178d7fafae248e21fd4370eba264aa7ddbf6f80ac5cf6d56356183508c078ab193dc9147546364bdc00b0cc0ffee0848165630effb5ca060e9dc7118e30905424142450101b8328c90ad434305c09a926ab3b2855183dc8cabfaa06692ae73ce76d196211c33c3e0134c59dd3397dcc6c6ce913c29bf73b45e72ed9621e91c081e442a93874f00abe138199264e6ba5c0479f8510c55bc4a4705402a0f32f8cb1ed239be44aacbe801be42a6a4737b6c925d36217ab0de3258906b613d34069ee19d0f61658f9f870f9c0c1bcd22ea55dda9e202faafdf55a2eff5a2ee28bd22f66a9758076f397f78080642414245b5010147010000eeb522100000000082c5a56142c4f4a9dd7bb030cdaba8cd1f5dfef892199916a94d0d81a590a26b069601b75556ee6d2fcee351f6553ba9bcb001fcb7acf581f121d0273bd6180a10ec46c28571e8cf7d910ba78b731439b9e25665a96bd4631b86096f4d8fce0b054241424501012ea5d6216906f139bbd327424623330f817b8478691c11e37fcb3d54a2b1ed57eba98bc33b0c4411119595fffff63bbab6f2f326127664d05798230186dd1d801b1d40523c052d4a8acd64499dec1d28ffc946fddb9276038f8283eddd620b74aecbe8016fe66e60cce9ae1d497b453f00d92aa9cd4f94fe3a9b390fe129c9329de2898c89f9f568ef7847bf972ed26a585cf39abfb3adfb0acdb6d74407b37434e94f69080642414245b5010165020000efb522100000000090664e52ac33bc90b6536931b0a1137caa5eb4a1dc4b87c9777eba93556a3575ab65471564bc9af417de93bdc9fc572f91ea3ba66c0a5b608144d521ace6b7067b0736d7fe1403179979b621d58a08de7823eb679e23cfa7347e607ad969d1040542414245010184e769fd0c01dd695ceb339b8bb26bbb937d4c413b482f1e263fa9e144d93c56387a27c999f1d3accc42ad6c644ddd4bc6103cc14e598369a911e3d2d6f5478421751a314f20ea289746a954da1851630fe7d9511f2708c01ba182508def2933b2cbe8017aa6dbe2b89168eadff9e23910c70df28dad68093928c463368e94c9cddb44f54dc140475292eaa19ba62f1e9503316671a051151da1cc67401211368e01bcf0080642414245b5010121030000f0b522100000000016e64954cb32770b3c0f1b7122ed6a73ee2116371d49ceb054bb79c7740b126fd4f5d21f5143aa7f1d16909dceed8a966bf89075fc67a36bd5044b517654d00ce14a7a35916e81479c54da7ce6272b551792c92aab4d7f30f175d25b427e62000542414245010172a897fc8c2aee34744c2d5e8555abbc057329877edb1a4dd1589249c686233d42c8bd3754df879346f7e3beb343da7f440fd2d5369abc83344f1d3fa9972e8b7e1526ec80ec7ef95bb05f231b56481afa6abc2c42ac4229337f06c3c39f1f32b6cbe801f282d6e6246f77d538b78fedff14b6513d7999ea143b024b2aaf8ab6537ef0bb6b11c93f3b68ac8d44d996bdd4cd51ad952499ecdc83220075e8df42c71ec7ee080642414245b50103a9000000f1b5221000000000bc87047b7956adfb8988aa69142ad3dc73cd419dd9512cfba688032d0ef1bd3f67c1627eb7d5b5dc86ac1bcd56fcebac271142686074c9c900dc64864cec4b083351e800bcd234917abe5d18aad65f2a4fb19a972b9cba7a73c43783792db80505424142450101ce6975ddaa5bc111576a99dd456a27f589a0b5079f6d2ac47cb996184796a572cc27d8d75208d9f6b692463f900b8320ca8616c46931a80ed2499a87cd76d287c29c6b15752888e93eb4137fd1d56c7ea16a170b5f9d84ad3c458722fabfeb8abacbe8016c9790332253266497f2567fd85a1e9688dfede1ba3e83e305f35536148a99ff8617599c42b24af2aa45dde6648efae0b4910fb72a308e85259e341814230ffa080642414245b50103de000000f2b522100000000010754454374e91b337b60a079912615166b8ba9c929d65a431064fe8f13620677c95654f0a6a128e319f47393958289a49dea5e65da09154443aeb1de76e3e0b0959fd0bae99b973589049d4629fb0ae8096c767d4d95165906b679c93dd280505424142450101de924ccd6c1c2313d861e240be6e4830234889abc2c768cec78d0057ee140c16ee0484a0cdff978c60486ed33b9ba7a3aefc5e575669827409e1f39ca4c23c8877144082f7e937aa7d52690cf96763dfe07e5d051ac8a0eeae7038d2454bef56becbe801ee2aab858b781c056d67457adfe9a5ff52a8b45440d84ed235e8a749527e42e41d7cc74937b948b3cbbc1ec51d97c6c4d0d59ee93d6f16fd69c54f605083d90e080642414245b501031c030000f3b5221000000000343d6f36c73e2483667dc8beff11e14f5f7f79871c1d3d6fc3253f3c83437b03a31f6911ca516198546168118b6c70c6990ba4eb35e0f3f90c2b22543940f6096ff2bf4fea86ca23d10227bd471b44f00b5ec0094e9ddd7a187edc1cc47c5902054241424501014ab9b26c7d8a1e77f4ba6c5e30aaeaeb9cfffbfa49470b5e7372f7661ef1c85b10df1159b3fceadba88f83422b339147340412b30234008b693ae94a76a4c687d935bf2f2ab628d2fc626287371f7caed7c688bd6d328eb710f591d2efd6d804c2cbe8012494e885d71ca1a151639e9c1d4ee7679a5ec720310c55581b5022bf7f08e31d1d3c9668b09d08653a9d853a12ee62351925cb8ae2065d0bf8236ce5b63d4288080642414245b50103a7000000f4b5221000000000e49dd056f6701faf0d9ca88804ba5882d53ad9ec1c2e2d0ae5241a02b10b7a2c60b76298f5c75d26b4f18f4621d49c46badf9ef69eaa6b31da6a52bcadef3c02f79eb323afddd2185f05d32e3c6fa52462df6c0f57114e568429833274106c0e05424142450101486c00982b7b58b429cd1701b6adbcc91c9b528db49cc6829aa7f6f9d76e1552a0709fef9b42b166b5632ec3a75875deff09d0ef90255ff264ddb0f42fa5078bc3e20b755643dedbd1edbbb3a77a367b624cbe7b90a617b021bd207f0bbaa0c0c6cbe801794e8250fc113be7898a856fbd689ed1981e1c7a48da7bdeaadf03a2fef1b8f80e62971b7160c25ba4d55fb7350538564ddf58dc5cadae363cf29b85df0b5d0d080642414245b50103a8000000f5b52210000000007805d7d18a79676e3383d8234d9a14746a399316d4141c1b02d5b8cdd8d0f4713f627ab20756d6a9e0b367998ba5e0bb7b663a988dee17754ea3924438c1570bc92764fd46b13b636a729cd1689b432003d1b71d49b8ce5f06caf8990fd9ee0905424142450101c415ceb4320de80b7a11bc6c2b66c27a56ab0bcf863a2bf667b1f98e826ebb6d474a6cc1432a702fdbc19b360d5a0ea475dad6decc5e91d86f5aac4a4cb7478baae7f1bb898a64a1d09695facc18c686317c23a45e083bb2a9ee3cdf19128cfdcacbe8018060a68e3704ced96acd8a8aebd3156494d86e80c52088f03a83e42e09c4dafa8216634722b2987ca4411a80b69c99d755fee38e6b4343bba311a1385ada4edf080642414245b5010102000000f6b52210000000001416f42811f3f878a6d345a9596c7fecdef6c3d44a86872b774c40cbcc1e5458d72641eb41358ef34b605dd09377d3a8847813a7ea83d4c1885e165ce3111e02d6e9295246f825b962c8e5f1518b94ffe6701f8d1a130f8a03240f5cba51c40805424142450101d8f2c6a6e5f417852cf4cf2a72428014399718375e7c455d0ab99a1ccb86fc2f7358a44ddeab4292d24046b33daab097096cb771b94458f3e6ccef88219f6987ee6ebe1bbfa6a290d87e8382f98553658103c6248818fb07787ad8fc490919f5cecbe801c477bb5564966757b3d7d312e042afcf5a9aa8bf30b9e44ce3b91dd89825b29cfd147dc711c40b09407f7dc3c12e1b27a3066f4fd5ce3410881c72d6e400cbda080642414245b50103b1010000f7b5221000000000baeb899244833eaff933f3e13034ce20bd4a2e663b41e2d948c944b4540772431e761c95c0c130f99b657fd4598bbc84795e5622c5b276ef73b1baff8d1b47060cc046d0a3d7aaaebe3782e39986702f280aeab3b278013142bddc6407bd0d08054241424501013c2b4080c6bb12558cb90da9cfbacf226ee4d3d8690a52a589105cbaa53a21069a50afd3c86e6b54d8b6d7b0de3063d7ebb395d1ac912a0b4c719b3d1b88908b0e00a833a77a6cbfcefa0d018812a3252a2e05ff01ec5de6e431075488962bfed2cbe801cd16f2c4e160e8ab4ccd6f820c6e619649b51183e300259b38fd809d3813f9b22e9311cb6224b9947d4dd308f46ee34f62212f40cf0e4eb013ee1c1dfe691dce080642414245b501030b030000f8b5221000000000e8ed18b9e75feac00cf90b3abbfe0954375906030ece7d5bc306b0bd212fed092994b8a1ddc430fe598454895e47713c90d757ca48d1e9618754e6ce5985600c327ed2da703ae472b5eb6424d0b12c485690c3a3d4f760ff87b55a310b64a50105424142450101f680099e5f4bdb478c715ef4726323dd4a6e068416f3c6b5392efd25e9a8526ca9eb0e3abb5a0341e01be731fdb9187d140cb0bd31aae6775bb9c0a9b346f186c0c0559dc3ee34738cffeda2f03ba8785c7b98b5e97e42322c64b60b70a64bcdd6cbe80119f9e3bdcc4f8e386ce1574738dba119595d5a729b553f957b92c4b4df0c0d4e983be246761d23c34c44ddd9746272ef420fee79b0dcc48a1b5b62202d447eff080642414245b501014a000000f9b522100000000020a936b9ad4b7aeb35df8576b8133ffd5ca74d1adf092c3f336d39957e259c2b080adea38d1b58afd13b49f648ba693c1d3f3680a15dd580f387ac025599950e3bc59f0388a12791c0fca4ecdf20858aa0b3577c4a5f44ca56827095d31b1e0a05424142450101905df5afb5046ac6c3ecd378b48191e795e8658cfeac25904df0bce0d7d6bf05dfa389d59ed559a25c12c794bf3f4736c24dee6b473611d67db011bb80d31d807e02004f047b8fdc999d9d67e645ac7ba2769d07a2eb1a5a9772dc0d6b68f20adacbe80154174ff679e208994d3c7cfd787e68cbda93d7e1747f0a101f8024d37255a586632119f11a57cdfda3374c721f6f93b86369867eb07610883bc9c9c893ae0e05080642414245b50103b5010000fab522100000000028753e58c917124ff0a10c26203318f21b313bb7423044051ee13e85bff6983af3779c54aa575be34eff4646e526e9b07eb0f2df9001d4cd604f85bd6cc69007f66a71c95cf3bf150ed8f95a33ee9a794f47cfb475c85e3020d70e322ab3e503054241424501019aaa68cdb537e567650d055751a84521e5af18c5921d76ef5c2e0e13e9c98d2a840f71d0b64e7cc8955a50d11d49a5554e40be4ca48199e54c89dbc652b4768e750538bb65cf5b2d4082600f5014f8f47660e398a037c201ca27399f8ac784c2decbe8016613e7dddab5dde96d22628be2e2df7dfed29202b92b9135c26fcf4b57dc6346f015f04e3abf7420b0fdd7fa346293a707a2d5c2667102f8be2c0240e420b150080642414245b5010362000000fbb522100000000076721d21eb8c9a3fae90060b54315fec6cda980e411d9cb46abf03a78e343037d407ff66b6d7b064990d93e90b29596a49b5d126335e778bb23cb9df311dcd06e32d76dc15fa7cc9df54bd6d273105c275fae9620113ca10707fd7f6ae369f0d05424142450101f8e69d92d34f67c4130534aef18cabae868fa2ab6a817b26f799002c2ea4704e285ffa3ae0b188bb37187df8b6f0c60c8bf265725dbd661f42fa8b38f47a93821123a94ebb59cca0404b66f1d798f49e15094dd88462f515844d8f9b22f2e0f2e2cbe8012bb2a6c94a4b42074e09401ec2c958b2e29aacc0f3900fa261f2274a66e424d0de9112ed3b57215d22f9157c6d183f3a8a14b325c86fa9205dba77a47f817fcc080642414245b5010369020000fcb5221000000000d011e3dece6d7d23981efb30592229f1ad7e1c27186dcb6b2da6cb32b1db18492cb9e52003c66887cf0c276b7004d65010c0f622a6e83d475f92a5e2869ffe0d70de02290023fe18551d009890770db420b636b63885db1a86aaa17ed47e1c0c054241424501017c0ca82475b6430159aa6ccac597d0df1df1c874fada70f754f233dede7e5713758ec56bff180123ed0e1f4230cecc88c5d580413ab859cd3f63fe92acff618bce2108520cdeaab9e13013eefd5bd30abea84a30b3737c399a55b02756a25e2ce6cbe801e13a7d1faced96d71432cf1c1d43eb4c516df7b6f53b7343378209732afb69a50ef0cf700626e0575796b6bf93253847508f6a207f50cbbf9276f20ef93d026d080642414245b5010361010000fdb52210000000002057ff318cc799ae4ab16a3ae5b10849f8c5416164b9a536ce104f7ad0961b3dc886c0368dd34cc65133945af11dfa542c1549bbee8a6217f2bd615d4767bd020e8d386ee07f3e6362cb2d28e32ab823851df41a70b3fa367d066053ab16ce0005424142450101b6225770190d9049004eb38bf1db837aec2364e878d96cee07deef4d95de835d2bee054c80adc2289e753af6c68deb626ba8ed9e082f0e59e0313574deacf485697bb3099b753fb81b569e3ead8b559d3e524c8da0d0c51fb166d085d154e135eacbe8019d35f590e64c0496494d3589dcf46525aa08a01e0bf3813d15631f95704d1c5d470934a954a69212957fec5faca9b03f64b0afd0681110966e583963ff989a8e080642414245b5010141000000feb52210000000006a2a6e18e049ee9d1d1c490878de04f332fe94c0ce5530b2f4f1e4f0a75e45510f2251c7b3da61ec2070a2ad258ae6a6227d433b6787569b1e0ca70e7d5c1c0173ce986d70efda4bbec1951a4a2cebd72d9638aad298c2f13df60f22ecdb680a05424142450101c4ccbfe98969e4d9e550e93a223e0dfec143e2fef30d2ff8cafb8843ad6f4d2ae730c40b4b1ccc6deafcfca23517a1ce06f22822745e2f3056e56e7d0fded18ff81c4d338dad3012e855a789b66dee6483ee32d24cb573dea8575c030b026e2beecbe8017fe11db10b7f362f354d936abd82d593b78616ecc1a378f8cc620a2903e3ddf0bd474348820ab08d7bba264c50203ebe11c752ff9900b668cc51a8453bab4908080642414245b501014d000000ffb522100000000068a387a755d7cf6fc11e0302760569049ba7dbcafbe9a9ef5ebd1a275499f90ac9e8363f9226a07e117ab25c3f69c4212272d8f9ec64fa0f54bfbfc633ba260bde79e13c8008fe4d9ac16cc1a379eb73661d0fb49c4c5b8c8b493de14d74750b054241424501017ab657cadae5fcc7baa7baaf56b85b8ea80d3b7a3832e54c299c03857b000d3014003d08bd14464d623be0c6045b100be62f5ac0f92fca25efc985edffae15865d93dd35c9d8baf2a30c97f4b0694b677a6b93201e06ac3885e74951f1240479f2cbe801d01c124f09123cd88fc5d653a619e94eb4687d42f0cd0aa4a0e0e5f69e19179fa59eac223668eb00f24b3402fef170de7e3d564103121cae8db7c2ac9b45ad16080642414245b50101d600000000b622100000000032a164d62a0e1184f7ac1b948812fbb18202034346d10e93279b66e9cf6ca322a87fd529661f2afb6d0fe1185c9779b02ed5e567091183c63d81babe3fcb9e00689a2617c39c7948fc3fe27454ea210622d7c1ef3fc7393db77b79df6f3e310605424142450101f687936766f06f54618ad6832e903e13e48032c7a5dae2222078cc51b0be8044acbc8e9fc225e63ccf7aa62e0996d512809a095191d395fa7063adf78b7b5a8941e99dceca2b7f0723847146f304b4c6c236498f73d513d794e3832b2f5543bcf6cbe801a28496dfb4ec7f746db0c21c77c4fe49ff71208b86021bcceaadc702f904fed31f216dd20f9b81e3de3e72c9655fa842c60bcbbeb1bd49e291a6fdd17dbc0298080642414245b50103db00000001b62210000000002651f2c13fe78e29578d602c085fe538b7b4309f9434386fec8f1a2d088bc4561c2d6a52ce85391b39fe7965058053e5355ebc555d42852289ea7d5988a9b50bc0890cb75322d84e64938a068169f6cdb69e70f8ee7bd539476ea0bc46fb6204054241424501014ed00fe8a1ce558387fee94acaede38c60090165e80bcb8546ff760b6c87a812f3d448fd74db7757af7943907b95d4caa33977d8d52047205491ae25725dc682df86593c642aa173ab8c1416073c9b4c2483227592c373a2156240065206ff8dfacbe80181c5b41c6a8be900517b7527d9e073ef761435e3d36b9b71892e1ef584f397f73c8a1f639dcced25294d6e5108877e6185074437d8acaf3208a861b0eea69f26080642414245b501035600000002b622100000000014e60c63c61961e50a5d792f9e1721d59b7e6d1c5ee4843b805671bb8e73381ef0b45a2467a5ee15de45466800b1562e4ed5fbdba427a0ffd0a9b0d22ffaae0f7c170ed40bf04edfb8b9988fadb57e4dd4e9c78fe024a6279d8bfb66e530a00205424142450101084b79c2700bc4ed339f7045f6cffcb33274ed7c3bdce5b0b66f154d9c3e9c1ab48ac5cf3e2caa2fa54cbf5e1b07b69205ccd3d0dcc340b65aa4c1d2c84993891fdabeb7e366af2b9d719edf402104be805ba1c3ef67214e5c5852f913aae482fecbe80151f323b7d8a0a4d90e65a3d22c9357161df05f386ab95eabe239c7523111adb85c20119b4e1d84445b6752d1fc2c00f63c73cd98c05e6302dc19ca7b023520d1080642414245b501030203000003b62210000000004099f331f18e858f7f2d0d02ea127d6547de96be0c48a72e5e4d7186adfb1141c15f46831691268fb6561b49c4810da5fbdeddade2693c9b82700ea19213a00b1485ef79ea65a5a2e40ce00bb8ecf977f15111c232283842e4ffce9fc9982b0a05424142450101ea03d79658c557ba8cddfcd471a4a4d4433445c25557972c953e15c0f16f6f240638c9c74e6717ed9cbcc4f938cbae7f22dbbaa04bb3017f5bc9bb19ac116b811f9fb5ee576f06646246fadc82050d4644121265992bd6b58cac46699b45fc8002cce801d48ef28a41716689394da35b5caca7f820fe41bab8ecf02c31a427f6814be0204152b1945e13997200a7fd507ad167f52f71b4119a287ed30eb5e690519003a5080642414245b50101a100000004b622100000000094a6644d775c6534bbca164289c7950547f6d0a706e275f485a282ff27a671007e0fdcbeef3d3b45114f2eb181a6ba68bb9a934949d37feace207a8514875e0c9d3def3cd61041352758b77dd510d573a1d56a5d0a9c7b107e6bd0052186c602054241424501015a8c0b2971086dd3e43d43d333441ca69d8da184674c11397a68159d2c10a44ac779eb1a15977f06db87e083bede0c5e8e364e1bd1dbd6a80712846c94e95384ee025fe518c0c6d05108ccc67c2d738fac3ac505adf2c3c5654700b1f222477206cce8016cde969a1c2acbc5717a7df1e8bd473c4700e988ecfc947a5bf2e42b38c95443774a41c8d1006822af33e6d430a8049d85141769e59fcc1b531d7e15935a23de080642414245b501036800000005b6221000000000cc6ba6d0499aa1fab305d07dbd398ca703119e46e35d88bb6bca8edcb2ddc423090e20f5a1f0ed0d9aa96054cb8c5c96840d9d922225a84b8299de6d4cbfe20dc173ff8e5505735797e8684d69065639dce78d6a44793bd7cd9a286fb49ee40a05424142450101bc5d2ef9de5fc12685e1c4661379b85148270a237ff3e340f8bf29b2f4e3c656d5dde0be3ecd4fe986bd15d93b6e4393f115bad1697043927ef766743f0aec88cd8da9ba4c36b6f4d88a9f6ac165527ba44c9b5d19d78dedf723061e9ed45f360acce8013701e2ce4c86340886df30d6402a21cf75e069b4edfa577c26a899f26c499c6164c938e206879705380b84230c9ee60638ac54c37e08a12e4f104586d3efd141080642414245b501036800000006b6221000000000e68080e7cd5dc21273488d53336bf393e857175f0799ddebbf802bdb34b6491ae1e79225018b96466e50b64e9b4d29078c9fef47a139e789861029407fe9920d6e098bb57248f724650901af5b3eb3773c57f2041c3a9c5d88370180bd28e707054241424501013608837342991d50d2180b3374aa332287d599d23ae4b4dadf3c4cd568a8347168594b6af9fac8518f436939a3c08cfdb4ede3f10b690f897a44f60221270c86f05c475356849f3364c1455a5dffb3de16497abc5419855a7ba3dc7254d5ba990ecce801cb61b17e4ef758772e13d4df10893d37bbc885dc0fdb8446afb9e975d9a90d1e3d37f881db45e1a32488993513ca3e1d63bee5189dfedc2bc0021f6db7adcea4080642414245b501011b00000007b62210000000006ab792800611620712c9dff81a9d78e4b89fa7ffc39c0485bde44174a9a38259b4f4914aaa9738ee362bd6a3726ec6812b0b052895057e1d2ad8469b242ce20c80dfc798917c1bb874c8b0b23cb3ae7db5081d286d2ff62749274326cccb4f00054241424501015087635608cdb738ad664476b460c61b44434594278923cbe3f23a8fb65fc374498201c45fb27a5ed7b1f8063ddbbe8b9f1b6cd4dd53cf5182eb5e1a23615d81c2696fc8997497aa47f624f8716866c1fa7732d54ee84c904dce4af9c3bf86ae12cce801d6d035b04b11787ba13c3d3262d60606a1b9dc738ecf3e1756665a2417eceda5c54e01d7ceaf00984f7b414c2bab02fd2e2e23f76bdcf0db382d861c13c0c899080642414245b50103d200000008b6221000000000a67add63b2557cb9dd5c857111aeab9e436b08b6a00d950706b1233400c69e595b5aabb0594316fb6b1218e5d26de4debee31e75fa7a9190eb755311fc8f3c0b4d42698ebbf859e743e9241cf00a223fea8a4ba7083f334ac432dd3189ce830c05424142450101cc57bc560c12e10b48e3c4930dc35177c02d483e3fd011f98a9cb2fefa328c4d0a769a461e05ccc07f8ad209d03388840091f659776b6226e88e827d11e656874741bf93ca2ccd546b4e296a38024fd106badb9b89a34afbb8bc0dc30c6ff0e316cce80183047b05bfc9ecaaac815bef6a2122123df52cf9c6035e36678322954bc043a435fec4c7750df1bc2f9485783074d0c8f929c61c0c12dcf9dc74882d9d2bfd59080642414245b501030003000009b622100000000006d510418bd358c25fbcfcd043c62173f7236cf43e7ca0b4c56cbc3e255de250465cde4f7595d891ab9ae501502b1c2e2435fc412709978efc73db2e08bc7402a89be878578c1240133a729e2098ce4b0da6bb5bbd27837dcf65a58baa4f2b0205424142450101c2f4ffa9643d9ba31ce6a043ae3cd0eb7b78beb650c09f9b78d1ec49011484047a3e665814125d8b6f04bab28de048c01a7423cc4cc23bc2cf0f4064966e518851d8ab15a1f8e26e5f4ac8efc39226d546965c06798ce2ac2646766e1b1d26071acce8010f370cd2c25f2d467b98453e99c8c2bcaef4005e381f9a3dd37f82bb0fe497bc407e677724bc2a44df2697009768beb4d50736f5185e857fe15545aa14bcbcdc080642414245b50103ce0000000ab6221000000000249ab1146cf85236bf7e8f2486c2a492fcbf80c7e423b23c08d0008a85ac4e643232c35ba8304550b95d3f89930be88229ef54b6fa39335f49ba41cf45187e0f92cb95faa2b07fbeb34b5686f64d8530b9f7599c84ba734ed9fcd3aa09cc5d0105424142450101ee7f768104f9f590f3d95eba89167315a21522277fb0c0fc60fc884d2fef142cf2eac4aeddc5e6405935313f5b538a6f4bb341806aa8b042f3e8a47498f5208301497159820fe823a3e3987f0b888ba7e6dc6f1e2dd24cbf11b4bf48cd119cc81ecce8018e69393c7687c0f4a4db685efda8641fd2c534005cc9806d7987b9488f9e4687fddf0dd2d81d22ee40e19dbdf5148decebb06e0fd1f220fcf2967ec7bdd72d94080642414245b50103830100000bb62210000000005c524250e1b9d5c7a8ab6b81d70a23e0522905ac5c0afcdfe50ea60c0352b2386de5a77c3f1934819e5a7ab118f0ca4c79ef7023b28f11a11fe38bb3b177ba01bdcf8f425682e27d29d3f3fb2f0039cac793643429dd626debc5ac6efcfb000c054241424501010e0148e1ce8a2076c883525f41527f02008e020af52a4220aeac9b4b1e6c091ae65b1b1bd08b27b696a2d013782db0cfa6fc1159dfba1b810d518984f954548e6dca79fec269a589d388f8e64ab26e605b7906cd327c8c2e127e3e63d09140cd22cce8017ee1cc0bfa2bdd09d06e8c37464ba4e51205e91dfc6b7179dd33b87e0baed4ccfa4bd92f3c5ab9b3b0ffdbb5f34ad2a6fc6e3bd9c84e67bb955acf5ec048de9c080642414245b50103b90100000cb6221000000000a0ce117ade56042e060217232b5728ab5f8299a818b2b4f86b943a5f83a1445403c6ff5227593563bb9e3c671b4acdb8b3948a88ad69f3bf9c2f5f611bcb7a0d9321860361eca2dcd3e6fc22ab15e2d3f25f42d27f49a28d9a0e010061c01b05054241424501019a06c3773884470df76c317ea68d88eae6346f10e299167e7fbeafd6c45a615a858edc497b707c068b768afb51cb76a36d5d5454f53c5df89acf8534c115ca8fe2a4f4d55f28c3dcd8e7c33cb4589a1f39a37fc77f96738466bb9771d163886526cce801c6290ba1a0dbcdcc3e8d5b37a53e85bdb0e322329030571292c348dee7b864e275b744da15fb316f1ccf4b125dd304fc0c6e6d667ee90a25cee4c9cb54473949080642414245b50103400000000db6221000000000a61609ad8c447473bcb8c87c8243e0d3697630a22e54b7ae37d9c7fdfad82a3b64cccdc437f93851a3f209f03cdf744c52c635df6e52de6330381c34598f2d0307b198eb323740b05854aff6049d13b6ba75a9662313feb083b13009091e7801054241424501019682ddce35db4d53e822d00a42b0887c5f078ccb60cea83b10dd3d2e3886a92161015e286ddda8b0620c0f9eaffe34fe32df161b306a2c357496c012741ce48ebffc3aa8dcc37877973f9f14188036fd5a00297edc6fd5fbe01d7c1d7c2572202acce8014b9d9206091d42028996eb1ba0344f3570f6d34b5ca8b85630dcf9cad755a711bf1f968351f77e5a57fb1ba3c897c6505be036f687564d0abd4b59bfbcf7146f080642414245b50103300300000eb62210000000008e6755819aaf817219eb113597d624da7cedd687476049d9ff79144bce6fef3da00b2ae96f0d4a31ed3d38bdd39675a2ff51d0bc89bed5944449c4f90dffc10c8975bf2777dfd81f3796c99a554db40c99bebafbcc2980468d81b7358e56f30b05424142450101ca1994cfd98e97a0982a63c3231304f440311f2a19fe29f1087d050c1d1d71083fb1bbada8e3013f5796a91cdafb6a56713ffa7c912bb86420158a73c984cf8a34a400703544980cec3894165d2fd57a798099d16d9179a507df9aba1cd8be652ecce8014cf21ce8dbd68bb995396ce3b07b24e6907337f24f4f922c7da0341806bcd4a38eecb5ae374f721ed5250a899350b43120648ed04c8f86f6a091f5a28d578552080642414245b501015f0100000fb622100000000028d5fca8514e7976f4ad8219cb26bcbbe65cefb8052b95fd3aaa7ccb1a34655ba89a0cb4e14a568a8c7edf8bf068f7a2b0397ce929179220a633fcad6790e40c23ebeaeb83a45752a33b4acf4609b5b4879db84653a15b91ec4a5e870157f80d054241424501017ce798ea40b13475121931496abeeb14ddf667f21f5c83b543154db0e5744e62df54090cf4cb3f16cd26d9c6adc760702a90e446fdea14985c317208493edf8b1be22ab36e3970f9807d3cc0887fa54afe17f47e5be3d31e6c0325164e4114f432cce8017efbce4cc7e181575bf2d36406b97fe04242ed1724a63ff2161e08f67a08295422ddaa118423fcabe089480f28be83c66e059cd2adc7c41b36c00169ffb0cb08080642414245b501036a02000010b6221000000000868ebe00f77709c46bd56190aa6d5e4a466b95175aa9bb7ecc2be34492b8a230ac7530f2b4ce7019609c5d7acb8998f4b3fae5bb0cdc3bc068868d8d0cc80602e2fa942d2319a9522ff55c6cae8fe8fdba8a16c96cf368dc2011015db0a1ff0705424142450101f878ad398f94c55861399d5df1ae67acfa3b7619f312a176e877bec3d0e7a04690a2ef4eb7f81d38d4e432961cde0b997ad1fec1edbb80c778f8b26a6e4b728707d5dbff9936b58454e5881502f32ff44240e506c3044578e28b4e6888666aa836cce80129475b80fd1582762ad22d1d720a5d163cdeb9d05f3c19b510e58eca517e1987c616f30e207a7823b63654691c2983e51608acb00e3bb7c74a27413372442f82080642414245b501019101000011b6221000000000b6e916034191be029c6953bbb1d6d66353bcdb332b9bb188bc272f0a890d8a25df72e62d8431e3af894305f427926d851a2aac0bb75bc4491a1ae622b85b070f27c10fa0b60e45c57934856072da89c38aae790925cd395549ce5e83c45c490205424142450101faf463b9a698d2f7f09813a33c9ca7823f1b38c656b2d5cb25631a8744fc0c6d0b99d7baa99d6efd8766dd5bf9f2b68b6bbef5c93ac30b057dbf5fec218f4484408e0c097b6be6f95d45cdd54d0b1254e466b1c40050e2a3fdba4974422288963acce801a3091ae38047e12258735ecf7126893f5cfc21bddacf31ca06729f825ca42a59085e0c75a5d9af9b496cd624db80fde0d5c07863ecb3660eb6d1096bcd5c7557080642414245b501039b00000012b6221000000000f6415951113eeac87fe81f87469ef4397eaadb36de8c0ab995d2692d7d1eef3f78cda1958ce9cb7fc1931f02c6b69ba92d45923d4846239b1a5bd28902be940c14932e03b4be0ee5786f8ef851754962d56d69ab15bfa9b017bb8efb129e880a05424142450101185f00baa68200e9d577a52cfa744a7cd511d88884b5c3be9a2c9013aeaafd5b6fe811362d3508e6f5b23a0839fb2fe8784789fe0ed7c48395a9bd862ce0248d7e5524706afb1ec36d84d263043ca6b4102f169968725cfbf22abc509293785e3ecce8010ee0701f01777d87c5819888c462eddcdaf735d765512f8b6d6f30553d4492813a94383e0c43ddcfbea43fadc3311df728139bb852c0635ff4911230aaf31127080642414245b501035703000013b622100000000024dcf4ae70643686c751c6e1f2c6a58e5cbaaf3630ffcb7daa2c397fb1bfe4286655de006835dd0c1c864f2b9482d7e3c9ddf643dc3b749560c1a2c3f7af7706fd80b48c92b4f6f7ddf78c0058534c36c5a0c4b57bde24f0e3d5d0ca2152f50705424142450101044ef8f78f965ced940b47098ef47b4c1dffd2145337b141b2203e457f29937f9757b21a9c18f5205576f44ab1997f3822891667f30df2644a65eae838aac48502a8e202fc41cef0e075fa3685c07908210d367fafcad4aebf72e56c317050f642cce801855feabed51363efb104358b3a197590be17939683af4da95b19d10822af41e7f625e05f71014e84cd928557bfa635ade1cc5e8a19fe14cbea45553e88423a9e080642414245b501036202000014b6221000000000f0161eecb4abf20cab8aed4655e1e0a3112e43d5e0fdcaad8142fcc9beeb9e3cdca570ad20395cd0c256959066112d003e389e2a775ee29fbaea107ebfd01108c314dbc373108c5b8965d49abe8f2bd2c53afec1803cb924f1210f55c94f570805424142450101f881cf0ba535adc86361179e60ee413f4ed211e2d1b2069669f838986825a013419f5a0e4bffc4a69dfdf312d2d984afc299dbb6233c96acc7cfce8e1909f5884fb3725fc45cc2b223878df16902544563ce7466e6b859e2a7a01ea5c66ca3ea46cce8019e159d90b6106d47213b79615df67af39532bb922166ef8556cfb2ddf9298841324c8a3c349f6be5120d87b1d140fd54bdcd9f7db734c2430bfdeb1e6b2b653d080642414245b501014002000015b6221000000000b4707c492dd8724d729b7c27ee6754c2f593f1ebddfe311173043f76f50c5b559fb9d97cc853ad936ca022b577fda7c7a3d018e4f0e795f5e93813ed81c7fc0e48e8219c6223806b98505105a6349626c7e8b063ea757818bdf8ebf887ebb70205424142450101ccf67d418b816dd20e0086851f6f3b2aa648ddf423b288756a6e46fa05985f6da367ea0331eb80d9cf4f0e18ad2265a5ed1590bf35939082e85b991f4d6c5382624b8b0c7e7edfe9d59699c15f874f60573ff67bf586ffa88c6c2d09459036e54acce8010322678e614ea5d443092f411a5f06b3257fd92b761034fd094a357b765cca42dd223d70d43f4c025553bf42e10c20b3b7c55f96007cf50b53d37ce6f07c6160080642414245b501031b00000016b6221000000000be3178e0c4f2cbc9da6bb3256f7b2b3730e556c209b25d3445f5f490d0e470611429a903f83825e4bd1cd086e83a41c5731376c11d422db64359c10dc7db65036645ba12e95349f852044f96e0c51ffda4553faf9a0d5efb62f9fe220782f70c05424142450101cee20ec937e188f5635bc4ac858bdf0ee75dccdd31e1d43bfcccbf367b7fff55309bacc1184ee63b3a4bf3ea25e8c9993b933703b5af7db1782aaa4910c19280081e9779618f60bb5b5504e9ad3653efe35461b913bcc52b3a71c436db4414034ecce801ab514097b37de25da12c9448a317efed86b1fcf1346a7ab1b12a97d93e560aba56b3a39793ca8e46199295f9ed5e5c7d4f5e268b2578901f8f5291f1b8bc28c3080642414245b501033203000017b62210000000003c33b6cbacf63543ecff7db2db52f15ddceadc1bc9c218ce56155310df479b3b71d3d65bcbc22e1437cd371b1647dfc534b8f142cdbc701e4dd4f7d891fd440075a07094ee5d6f4f1d06976425993adcade1ef84303f6c40230194761bde000a0542414245010116611eabfc1735e603583d9a37e092490be419d54f92b0cb7626eb5b78958f27e3f261a31bf24493fb6e7036ddb8b8ef95f1b52f5f3056afd3b60492446e358030ca6c23b75ff1813e8b20d300a5975bb35fe451bfbbd816b00d2bce4a3ced1b52cce80147477cd44e0040ec0f65dc376e556409b48ea7650efa7426dc0b0a693bed9809aa52691103b6bb0e61d086ccf7f51eeeab1e97a386982f59e6f236fee4b8ee37080642414245b50101b600000018b62210000000005cd3a3b59c92f984a5b9472a131486875ac66a2c4b321636382f60ca969b2f067a6660d632f0eb61b7efd6b2837f5c90bd2bd7379de49b1b541fca4bc779040264e7b180300fa325e98ec6a5c14575d1066c18274a6dc6b12b1c7c8a9b2d810205424142450101f6f30efb9ec80607d98e7bd043627e780246fa6e40a5b7d8c4aed2793bb1247c3cfdcb7f494cd51b898b0b0cca15b0fd62cdeebe097ae95ae5b5ad05df727d872e222add7f8b7349387e8b11cf96c725782cf4e7864f9e0a438127aeee62bc2b56cce801bf6bcc362a9c72fe50ff9df10875a352d443958fb77f6dc6eba68c4a877c9016718f40ddbefbc731a9dd5e6d116dcdc3d48ebf5a79389e0ac997bca42a6da64b080642414245b501030400000019b6221000000000be63eaf5d163f844677ee77f718d2a81e7e39973df220e45351f7a139eb3945e42e7402b88c18b2fc51090bcab828526e8ea8713451676d05008adcb3710dd0aafef4e40a3a7039ed07394802c4efcfd8ef4999b59a47fa8443c5b053a59cd03054241424501010ef7410adaecdad0f78a5c08342cb354f46ae40b6b7804e60204f08323d1ff3fb9b818d5e82e78bd31eea4ec9efb2760adeaf4d87301b377132f8f3848df19805b4ef65b0aaf6c6841ccac66b78feaf4394c4d5bab4ab7cc620ed0ecea8a00cc5acce80145fd049ab31394007440de60f4c2198d44cb05c6c9056fe02102954488946f89a819f4fcea571d1d13e6c7435cb9557e36872bdbfd2fa0d63db03ca3c94b0061080642414245b50103f10100001ab6221000000000b067dbd6ff05f0a05dc1d2d13666dc9e7eb7ef6751d96c3ee950abc6567e474055ebaeb3ede79ac2d5e2a6ba63af8b986d2e0f28f9f54968f9cbcddf7b797b0ae90fcd7aed0744f480f7992e9e0787deee301e23b0b673bb9129488eb6c0ca0c05424142450101f4e7e66e85187beabc1b934f515e26c9600d8a076e2f2d322518c94549c67d16c1b440c38e76505130f6500421358c10e0494c394d136245292a6159b21a0f8bf8f11b404757569225dd8528bafe0fe902c09c70ece993f0468423a5e5a086945ecce8013c0c419c6d11e2b85a7cfc187c04e911e8071749572b95be75b05bc5be830398a060d152aa6bff90528ab9613ddf07a094a0da0aacd54c7b760e1053b0eaeec1080642414245b50103ed0000001bb622100000000006b253866c16d8cce08c9db77f781f6f50e4f0bd7dcbd9070b5dcbbceb67e34c8cc5878c9c99b2da992d082a71b5e69e14bf3f25785ccaa347f747783693e703393b7a94a22713b620ea4d589bc7d3800f7cf5148d69e5e36e068ecd0b18f00005424142450101109b8c333afce9f803a5e2089a4c6fdff214fe9e5ed7474f06a6a5af6cfb1e537e429526e9a4b817adb8b57ca18da3ebc45ed77a0de8141bbbb42a821e6ed3870ad82eef3b6ffc530384e8d0c408048af4eac80e0f6e0a693d2353a328dfb30b62cce801007fc1e166a82163c8394f6b64052ed612b29053aa53ef34edaa77071c39c26701fe7edbdc762ed0b216a87c8ca00784df1e9c6023fe4975ccd24ab13d358937080642414245b501031a0200001cb622100000000052d84810a7fd92dfd102ebebe440aa1e642de275e30fe14f9954abe2954f6f6d78e57518a229d9b426050f485fa027d2e0e79a46a86359a5509d9a483442ae0cf663fed5bd8a23e4edba8bd722e2477fe49c705298f13eb8b3626b648daf120805424142450101c8a40bfb7a3d00bb75a529dacd569a89e53ec6bb6005d61c099117a776557152568e7370703dfe2fa7988e28509751b3e1c65bbeec75084240f9c5818e9cb18bbeaf9c55fecd1d0e16ad0ad5ff1b2520c3a8782cdd839189355f930852531cb866cce8014f9baa9eb1195ab2b5c24567eb00af15401d76a60909a9a26bba4ba147f45d516ceeae343abb943a519ca354c12e4e1fed8ffaf1bf5fc62d58ec2ec13b7b82f3080642414245b50103740000001db62210000000005201f31caf08b4f15f714cdbcd579beb6968a4b321db2d4cdaf38e768f2f7b3616e46d2c09c57187423685131f7c4a1a1b77f1cf6147e1f13fbf4b685a9fb70b4a88e14979b81797cc44d32d2a50b20397431020438be3e96aa47f011646a80005424142450101ee1308dd3138e3475e4f55c36ffd2d9d5576741ed63c4d487c77701bcf24f771536ccdc99200535cba42e31e1877fb42f5bf4da73167b025ea91bf32976f348dda890f345b588fb90331fa12746b784b2cd458530d562a89a0161ece6adb042a6acce80144510421850e09d742441e0c3bf72afcae65ed6b6c31d282f3be45a42948629d8d604be905b08f3f46438f2077497625942c478ca80a388716c959314607c423080642414245b50101840200001eb62210000000009c38b1aa560e93fb17a87c908ae54b190f2990956413cba8bb8f79a28d6ea015bd62c08334f902c31f849e2c9855260ebd37bda1975d33615d26db827533cd0b12f0601b786ddf8c6058392c939bbf26d14482a21921fcdaaad677c69933800d05424142450101f4eb9468dc5ca4d0810b4bf732148d490f61faba51757fe0cb6327b43f29de5d84a634c137d8f42c09874e8db574b9a02bbb1576aacf6a77598840517cfa148ea6de05bb07027f326137c2ec7573aec715ee63471c6c493da6868227d9c19c3d6ecce80102d10974a96b83a8288bba1451c54d319236d0256d4ec2b3e858dc30dc3cfab2703994c32731927f0ada38d3765f97bcc70fff30e24ee654f25e00015f33932d080642414245b501031c0300001fb622100000000032931a409dad55f44a74368370ff3a24933beec22a370d3e1563e405725cc82872eee4d91354a007e71b5491d3a5b54918c5725630164d55a14c449689086308685ac52285b8811ebc3120ecc826196145898811e71b90ba7e060b60698d4d0305424142450101f24782a09315c487d916a49d97e9cf7cf2823bb6ac75ef156d814df605a6c12475fc5e60a74c0acf405d51f03493dd4d16adbe4468dc6b00c8bf84256cfb2b842ace722fede94ae23c0cf0a0393215bf3bbdb30e553f03f353415dca2b7334a572cce8013040d81233935f023754f43c46b4a649a3185932dbb8a8a0398affc2fac7a9a260fcd3f56ecbdeb36935058a19114bc6db0ab2c71ca79a47acb6213f99a0f0d7080642414245b501015a02000020b62210000000003c8571f1c0097c2adf6018c5775b77a8ae0021ef6642ca3ada349efe5c6ba256de57f57cd4829879f7578b15da0a986271ab6396948183f40dd782f090bed30056bc33f1f27e2bca163e5387a7bd45a69c0ded32731cb25e72b903af0dbdb601054241424501016e9d3dae24d8f67b7f6d2c431e4730dd512d53ac4fb3dbc319ca2604a6665933a71022c4e678acdd6ea10c60c577fbdafea6fbf9dbb44c17ad59de6861fa14874c45854af1396982a9bdd0420dbb52c0c9aea85c636de6ea7f584bd1f2a8027976cce80189578103fe841ba68bb0cc9adf978a5c6f61863bc4f5ebba6591b0e738f0d30ab4c937f38fdb36e93f08577045d3898341acfa727704afc20af3463a6e3731cb080642414245b50103fd00000021b6221000000000feb25761e4f5fb91154d94427ce94d0bf36b04922eedb47f0c02cda40e9a1309e4676adaa67a681ba7fa029101f8ff0b15b50ad816535c4e6777fcbbef1bb805b4e3facb472eecf2d18cea359af9d7ded630f1557cafbb44c3112469e71dbf0305424142450101766cfadedd4e6f48f67147831eaf06ff08f06ef65bed6f043dcde9054294a201359336c1ff584eb7abbcc2d9bd2c3b8ce7096c5d0bd32957ccad77bfe4a73c8faa65d50bae53bf4c69ce0a7b3697ca803c7aff4b433f39e80f7986a369dee65b7acce80103b60fef155fdc97a8c7846328271cefbac43688c5e093a8d1fb5743baef427bc6c8c8b215fdd271c1c340d3c0a1ea5548f1bf42561ff108f06bbef41eb5b63a080642414245b501035002000022b62210000000003690e35e33bdc726ca2a33501cf825c1106e00215e3256165feae63f1ff7f408016f8f9f9759aa42072b7259dbb29a41b4136478d39b5cc0a6f13efde37b9306cbe1a14eb810f4376ce3500990c714cc33e903a42e3056a1a842f3ca80ce310b054241424501012ea7a2ce48b34b2c287b651358bfc7f5870688e29bdeb7c18ee25c1fcd26d5620a6b99d823698d21ffc6f48c933ee5d4606e050f531fa78cd46b8012f8718d8897365b9f059a2d5afbc7c93865c016c545fbffb3e14b640d52252aa45184a6bc7ecce801c0b30d1032e7228caa24613215cf9bd7d1e4da5ce2676419cb144c15f65acd240b4df27906d2c0dca4874636ba0d126a1824d7e0640e82cbfd491584dd1e59f4080642414245b501030003000023b622100000000098b1af4fddac20af56503a4501ea5d487ae52e7c9d775772497cd9b931c83d13fe1df23cfab97387a3fbfcbab1419d34fa26a10cf1ca771e08d55a01fe704e08a3768ef283c7797532d968715ed60d2153ba0a302ebf35fcf2f0df0063d1310b05424142450101d62b80b293802682b274b7f899df179d04bba15e564004383b2ead7bde7ede67068fc233d944c72e9737456798a55f9df567e6c020cf2b29cd205752002deb8fa62b5e21e8e4957ff761aca0e26301386175c3162576fdfff6898f4637046afb82cce80158f89ed6ecbf11da09af0fb7dcc951407f0cc66fb6137d233a057677b1349a676fc7bccd1ce57d4240309ddf81c6734772cf7398cf23f92474db0d816bad0efc080642414245b501014a02000024b622100000000094107f88dbb0852d2a7403dffe44a47b18c2ae9e9a0b63aa98e21dc1509cc85d9ca898358dda69e21645dd6fa220f648405ee8dc32d9fec7bb3fbf0bd30e6602281198dee45e9358dd4fe642d3669c058a0633c62df56c838a41da0a6a948c0c0542414245010124bf9cc4ce9ca8ebb850c109e668f558ef80ae036cd6d893a1734c893582ef6d3e97cc4bd2e0d33d480eabeb71786fbf730227945196ccd5f456b6054951108a623e67ba7a657e415d94e7eefe7967b7ad6ec04b8b98e044f6db711d9a17604586cce80101a34b73af764f5c65ecf2c9d09c9de70851795053c7180a9a1355baf231615d1d48e30b873b45d17605c855333f98aa7a820440315dd4302d46047c2407f2df080642414245b501031402000025b6221000000000fa77de1e5f68ad84b12c25da66b5b066f56d851d244a9c01c5d792f4f211e904609c3921e97c3d63db718df79946a1bf55db993b8be6a5296f0e52a59f293609c7e3d014221daa60b2b1c84e0c94d09dcf81d0eb888ebf7dd69a0f763c6c3a0c05424142450101d2b1a8027148f417b65917426af028907623fb58fd8438795d286793de88bb0a8a3211ac615ce6975580fed5b12deeccc5cdabf8bb1811093acee2ad69684883e5672ad750a54408186fe6ac3abb480a8d7b6dfeec4a4cbb98623f44061181fb8acce80146f31ee1b01d2b2f313be8b9fc3753d0b43ab2775827e3a5d8c6e0e2865f359ea67c6035d32d8cc2eb06146e5468cba350e09851e8f87fad09a7a19fa0fe0eca080642414245b501036a01000026b62210000000002cf3f13afa4efe3c4c46ec31c45a59784cc9708543e1ebd5d7bead7ec9c9a97b014f3011659f316ce9997542c724c6c9604beef3e24f585432147818ade562012bb557b397ae002151caf995600f9b5792812b7ea029c1b088120539ac76890c054241424501013279d3171b1778180eac6603093d651074e0906d8c1de2ff299659eb59ccc33e8ffba6f5b869723fddcabab4f46453ea4558ba914071a9cccd735f1daad01181a9e29d164b9ff9d74fbca18defcd87a754954599bde475c8234c94a15783d6ba8ecce80177c829b72f094604beda22ddd2d25292f2ef385ad79ce2e214181f348e2d14721efd989a0469a01628d137562372fc36b9da4f13d5e8bbe899efd70217455af8080642414245b501035502000027b6221000000000b60b8113d163ee429153688d912873b345d8a2626d90e2023c681cee774ac42909185afe7ad48a8524f30bf36cfb820ca9c6f5cb1a04426bb055f69c91402a0bd68e4cf9480466ff60687e16a33c07039c3a0eaf94cb06a19ea18a12a676d90305424142450101f035563ccf98b085542254f197c1febb9a0fec9e8535b13a55569f7f747f103c9c7fea2bf4a34a690b6cd34cf7cea990c275610ef1287305211495a35821ab8a88417c644c288f504944ff4a89fce157ed4892811ebc9de210f5b138a78b722092cce80195d860634ed8064e11142638d8021865bc92dccf49c605c6e74f2b4f9135782f889f5c778d8f560e1a4cab570466fd6de6f8e7c970f338451320b6912b5ec7a0080642414245b501030503000028b622100000000004e5fd907fbfefca861a795853fcfb92dcb58e4c1413d75d02c9013b90d4da053914bae7e3263cfd32d80e7426cf1b04450121595e7fc34f3416383553964203e64b96f950c2360a9e7dfe55f2a7e16735ecd91a43cae93e7a2cf815c80a100c054241424501013c549eeda017e9e51cbc7cdbda6417fc0b95424291bebaa6a581fc7e0ff457668924415eccdbe81ffcf6a3d7d95593b6b7570c9f54a2079dab164a81864a8b8833edcd4e63634b13d4f008930b3dde43ff4e819bd8c7b7d81ef7aaf3370b94cc96cce8011b4323bc70f06bdf99f3ba25a9fb8d65da773d2dda97f20a372b977da944f882c2fb9784805914a2e1eaea2bffef000f83bca59984749ba1a769f91a1a8bfe9f080642414245b501019a01000029b6221000000000b6d25869ff825c8a3316d07d9b82e90b64b075c5dcfaec3df0d94949ee22e86c7b51555c8361a0d7fc1e6c4687a9f64eb99fa9ec97f657b33b272ade09e20e00f22e9a22d0a5abe95176869f90658ed5613147eb016a1bdb66b45c03610fb9030542414245010148b971101112122f26ca3b712611c61cf3c5d41308b89c61e6084666a98a68094d2b06ffb2ead223d0807323d9ab3b7b7fb0fa938794f2325757f663adda788d2a680560bf3942824217ea0140c41a1453ad7716a20bc971b5a6260be386bcc49acce80147af4633bcfef5894ccd464962de57706341723017c9acbda59c4f998caba3a40054eca27666429caa197e80c0f4cd1c13f1aa9a9726600f22a49b330922327b080642414245b501032b0200002ab6221000000000d40d24eee14f3b69d28da2a6b2d6580d32269c9c3d7fc0300f9e171ffabe927b8a847a71032bf11162b6239785f7e66bc92e692dbec19d893c4db8ac3a42a006de5e7452ddbb14bc52469a4f86d2b12aec07b00ca6b252f3d51b336f946b3e0c054241424501016413644585e836688e84b7e1453f76dab63e3de51ef0ba0e9c105d078d77a92251b0c8821e1e37dc541b5c06faf8c633560a48a91f480bf9ad37bb99ca308d8347db0e5425c8ce890f1dbd6dd1b20b3b20014f15840f6efc3a483c603046a2e79ecce80195e1f80f72499fd69f8ef52764ae6820fd058f5854669627b03e618524f297524ebf7b528feed83f50ccf38b41e6e05fd68717799dfb7d95289e0fb465849286080642414245b50101820000002bb622100000000064f872cf8b004849aa67f3ad024c4a0f5d09022041cfb71f6543eb23885187353e1807bb2767ea503eb879c98cd29889c806c41038dac3ab6f84098eaab4990fc221dd2405a0cabff427b1d8362267166b72f4921775015f026ab63757af35020542414245010124fc7e0c141ca099f81e63d06c120f22d7c6dbbbfecc65e899d809d41644b86de27a5ca5df27616fdf15b0a150fbd43777c5e74f716f00ffed3e769adbd4838436e71c296075c05322a8c7d84c5468214bde9ae311c008096616f01ad5756cdfa2cce8014afa56158d48310d592cb87f5a15f30e86c4c4b9799f83a6ab4265d2485b9d57fd811c363eaf28f8467564c98624dd6745496cd62270a21e79a908bed80f8e9e080642414245b50103c70100002cb6221000000000a645c59bb8575f723796ae5c172f405155f98267c18a6ea31acd1a2d79d0ec6cc94415c3e7cd5573f302ed5686c87a8702baea4d232bfaf802cef0851754d00bcae7ef6bc466f1dabdc793c6ea9190dbe8731216c80e074974a0be9f72476004054241424501019e063a69b41d5095325bb8cbf8146df0ab427d55b99976f1d7c6f85ee74e6f47d9b7d20670ddd070c4917f10d643b5e713274ae341d340fca87d20961d4e4085e6011b4bcf5fa9a2d91d0f1ef3f65dc36340f443af2c2ec1376c2b115a5d7f3ca6cce8012a792e7572e3fb0f28c447ef0646d7acf71966050925f78333fd86e788c99be4ae84da07f1a195ace0b4c725053f3f127c819edde73cf60c65dbb2d438a03c4c080642414245b501038b0100002db62210000000000aa07c9a317217f7bd1f6ea09ce31847141c62be1708f3542bc49aa5a411777c6887788b309beded8aa292292fbf905e03007f1fd072cddead2e31a554c2680a8aa5fcfb8727a426b4d1ef5191bf36f61268cb48a19b3a1d5c55a87f1c427c0b0542414245010138dd031bdde50ffb70e1c3f6a3b63649cd80ed28e7800abdef8e856310985f7a931faf7f50bcbb642951c3f45035187e97caf98662dbb60f4f0f7c7d3bf56881d30b9917d3aed3d77e9a6b80b86f794d4efd35bbda7512567026c4bc58daf086aacce801f4f21d98ef305daa633b2545ee0f0e8e73aa3cfc366815432e9a8d548352e11374fc0cd392e698bd9303e2d6e1e5aee2290b400e257056da73a249523323eed3080642414245b501036f0300002eb62210000000006eba8bec5223bb01397a56ca28f725dfd9db28fc48f58da20cee2cc8a6277f4da789728503983d8f335c2716ee4907423de97b8dc4bda2a7c4da7eaa9e9710019bfcea201447a47342ab7c8c262f0fb0f85dfb460d07f380f5781e52ab7aa20805424142450101ae8e990ff5c07cd5af1d6c4ca6fba323401292845d87d550ac24badd9d79ff19af3af6ec8aa28121d2730c1059457bca7bff8db666c13a38b4e76bcc4b87cd8e6f75ffb9ae8c5becde2494caa517f817bc18bef7c3485e10fb1a3ea5f839d62faecce8012e7882eca8b09680c32f515a7c2463bc2907ccc37bb35ed8da8210bfb77a0833d7459df6cf11f3a873c29ef6a92d6e600a92bda351a7a164f0333252cd40ee18080642414245b50103b10100002fb6221000000000046cc5feb79194aa64785cff88495137bfc5e2ae8209b247dff8105c487795695bf4e36ab2357979a76b280543d161471854e7ca0631234b9c2cc054d57c0809ab20bb9592f8a3769bd70a1d8cb0348d53fb5079330a629a2f647a324d51a40a05424142450101ec0c8393f4f18ba718ff99285e7cdb0089b7eb1876c08dc8b7a5f4c66271646fb29d6b2a395cc55a8f760a93aa0121567e154c8ac5ec8ffae40f4974d7505a81626c02326b134d445291a64f5d62b3f67d2f110c5f0e27b5c09ca40e771154ceb2cce8016dc7db8b38853ce1ec3f7f76510f08441c9d378a86cbe7ba6f6314f85f3f8af26470442d6359e32db4a2951dae14a249f13a21e0e6248bcbe6067409231c44bd080642414245b501035b02000030b622100000000028a3dcd64e514cdd858a307193a9f4221737d0f2e22924b614f9fdd301860a0e1a9ba9ca0cd45dc9603c7b2582cecc5b99466e879021e0da59b820d78f3ba208ef944327f48654ec2309d99c7ba182c65e4b36a2d48bd25aff835d1f1179810305424142450101e4c0a65675ddf8b37866a4d274e3c1ee341fa22d29986390428133acf0b9a815344ac2d95541dfcedaaac9f46bdac925594428c199c62ef9bb398056a623f384476801ead68b525ee01a6c1e9cd90772fb4c7c11c4e2252dba5eef6f43913282b6cce801aa116269ec3d9e671d626abed757588c5f9e2718d5dc59f5cc0cf24c37315cd8f88502f37998a4d101883b840b943aa58a5b713bc7f6b2b083fa2047f1537efe080642414245b501032201000031b62210000000007e55afbd739c1a79673fc774dc689f734b058140b25b5dda27a24476f13446047049920db4c7951ab11878cc8cd5bce4b177412a5da9924d08deb8b9accbf60d4149a9515126f5e04f01f869d71124b71b95b6caf3c132b0b5e985d99c3c490105424142450101920599c217498cb72a3dbc7d0ae00f851fb40eb1a50d54e9086fd61789e2c6066715ca275325419f8bff71f8af7af7903e4fb15af798378e2600591524651384d429fe981b1d8639c2859059c2663ba2a474e17e3c4ee3bf1568016bc7d53d8cbacce80157eb320f697d9b6220b17111db3ed1b24e27c856badbb40b24f8ecb388c2e80665f1ea67c604be2edb5a0d691062abecc4ac51facb83b7a4118e136e50c128d3080642414245b501030803000032b62210000000005846bcc913329dbd14de9ed6615714e2219fe37ef3d6ac855163a0e1fe01fb3d2545b70d7e6d746f4c27dcceca56ad0c7081a682a3eee16ba2c58ac4479d780e108451d88db9402e4def862b949884b6bec99437678083c05c3bc8ba1f2e3702054241424501017c001ce7758f082f632df90a76b075c86667d620dc3d4117e047473e18a04d364cfe65b31be6445aeaafdcec381f042b2c03fea92efc2f8835df71533d400c8cacf870e22e5b9e1c89c5ed9e4eded08189d8fe17c663d25a52dcf2ffcd7dbde5becce8018a019f2e83f5b63d0e9f6bd5c126bac8eb49a4250a0b010d410bc1bdb4341a7d608d42faf182cd4a15f230004b6200e00c556899b6f1734cf4f5877984768655080642414245b50103bc01000033b62210000000001639a54f397786fb067b284e0953a0d7e8ec82635d7c9d98977fb396467253004bc222809049b5106dc36ed2598196afe32eb55655e0902c43811ae81d6cae04e8136dcace0617fe4233293a05974fa9c74fe8df47ff3bf26f9a45310c84a80705424142450101be14fe39fe4ac4dee1cdb6b4e269701b3d0fd029d02050e6e8c756ac189eee4c99db66b9072becfe1969b9c98adc906cb29beb39590a1e6f3e2478297246b98eeb70694e69748856e1be48aaf158164bd307d5aa8760bb3e4604b58e34d27a96c2cce80110f3aad3e0314cce2554ad1004b468a290f393c6aa12fe064573e52355a348264b447fa1aeae9a675e62fc6dc60e0a870b516b48a4b0f9565f0bf05f48cea613080642414245b501030901000034b62210000000007c133b59f709f4da1efe558087e3052dce07b18dccd6558c8d2934491219590640d1b684eb3e5c694986bf30c787198a7f47038ad0896467ad1ab5c2bc53140bbe110e6c2298a108a67d964f47a99db20b363ba8677c957b0a0c21ed3d1bbd0d05424142450101fa85af6dc6b78fd6e61379afc7e892fa0d76cb7bd38128d7667ca89b4adb9919895a046baec2713eb38cfbff3a9e132499de4f15863d3daee2bc09d48169ff87696ce84b71bee86a4f2aa657d8a47d9b3336dd3584d3b24d93047f2879132b2fc6cce8018322929c5a92612adea86c6d86f46da4fe009648a2b709536eb79e8d0508fa06b8a0b7e34182ff31d2bd74b1f3524be82d1e7f7419b6763bd62503c6dc4623f4080642414245b501013f00000035b6221000000000346f74459caccaaeae97cd9f5ed8f8442a0fb3819872e388b1b78171c033c70bbcbb18b317c7ea4459aac4d563c3213aad3c3eff19648a8bf98431d4eba6c0065b93d181a97f49b897d46ad193cbba4d81131adbc42dc19a42e273a30893d4040542414245010150aa15262e99ac632adf21145672a8b75ea8a67092eae8153bba3e69be18b53a41e3a4cd91f939f7741595d5fe8f1fd876f264b656e4e1c6f5a23a0f7a8b3b809461da785da9600e66ad51fa79a87a383912087f908feff1a0bf676aee2f9584cacce801e38f99e9cb0741cc52d1dc24f698e7925b43e4e1e8c728abc9a5f4a9e84cfb6c298ec6d8b3ce966ada87a8071d312721f0735cc6a2bc14456545666de0b51b25080642414245b501030f01000036b62210000000006e5ba573dbd5eb660a276a4b417d912495ae54dec4a885b42f0bedd2e991ef6dd955b40fb54ddd7a7b10dc4a9e758957e026480c873b63ce6baab8039a30f400551743a1db385b60a73158a033d150dcc6cfeb432520e6b8854d53e1fd2174080542414245010186abf459cd7617f0eecc24e04699d33e1be5ba9dcea5d7f08bd633e06b1537637b642020ac80921cf0209426b2bc648453891b312a7899865efa0cfc82bfe689cdae17ac37f06d39ecc8ab4ddea0fddd60191a11291aa77874d3898b060415a6cecce801e49d6b7b0498c87768dfc55db2bbfddada48e80608e9d65ebdd6eb50a488574b33cba042be2cc1cb3c4fae94b60f325da93c8b4170fbf82bc172d7ac25bc6008080642414245b501030f02000037b6221000000000a0d7b616b0da70053dcbcd16a3ad91bf53c45109a0c7a9e27507bbd2e4d0c17a62e05ea82f15dff5791d60544ee4e8caf7a76b282a53efb9c4a4f488b2c06c02fc9760cbc71c9355f911868758b9612d520ae13b499ada205e5e38ad4de9190e054241424501015202d16ddac545b6b5ae35f92801ce0d7fe87884801815120c217ff08f3df6301a669541e86473b38efae78d71b22b67d6033774e67d62e3b6519a5deeadc185dbe9527ce9ee6d568a16e62271b8151564d3bcf3327f1910da5bad815ee5e256d2cce8011221d28e625368eae2250728a97b6a9ba7d5256247317f7193c163d4a051f1eef7d770ebb4de5f3d59ec1ea176177cc75b82041526ebc562153483ae112023f8080642414245b501036701000038b6221000000000609a8d43d19f1fb372eb246a921c40460c5dc5c114c61aa60a9d7f38abbcf0222e3450c96309da30e1d6cf971c03796b929bf4010dbdd9130df822b8ff524d0e5f165ad24a7e0e1ce39b01f40195ebf5fd1c765d5b8b6ed9bcc31cbaac33ff0f0542414245010140a9dd62bf242988c304fe700f65c1ca26e25d21b2d58d5222fed3e5fc5826438ea284830795f65f6641596b31089ca8593116f16829027d2e956dc51969af80a0d2c3ec7f430a2599d719de733d64652b19917949ad2f75f77342c81d31c121d6cce8015952b007feac019d6488d10ce9e7579a38731cc895a0116146229e63d4e46f568a1c938fb725cdc682dd054f9a2ede530e3dd4647a41306507280e9d6e7803ba080642414245b501017101000039b62210000000003a7ba439e4a6dcb7a6cc9019e271f25258789edc23c9f7cdd980e675bc51d72dde60e4f69e51bee27b4721b67a9475c6acfb55c54aaf52804bd319d0a5b3f006fb43d38f5af74d5c99f698bb19350f22be3a7ef1855c28a19e4c0d368c3a820005424142450101185690fb60092ad76c84fd5dee458fa8767d4e72c8f1a3cc8e3cf2816ed9eb0a796d80bf32d1f016d54cf9e086a03c99b898b46dc48a1a7a4772428949b6ff84e4a747a710699c7c210e9bdcd2788d4e8ebb3913364bc363df39866e6aaa0cabdacce8013dca9e808562a232bbf98133243a1d9d4cdc51cec4915c1bac7a8ce6219c51d5b685598d56ef6fc8ce6c10aca8246d813f2eb645593fcb591b863f442df2edc4080642414245b50103020300003ab6221000000000804bb4793a780af9d45dffa756346b8bdc655787cea27362372208aa81b92f12f3f6c94ceb95233512c82acd6028da26f945115b26b229fa6509f51d5f266f0033e69f0907c9c5bd605c58172e00411e5dcd98653943dcef6633bfac5352e301054241424501011835230fc07af3c6c6251620c2be11b5aed5b183800763ac7007741f1938492445f6cbd886846bc429ea0139746324de1b28821fe9d312186b5954e6e13a8a88818a3f49b084c1f8594b64ed7af3671b3e0631fc770982667e051fb78fb28a3edecce801a20be5e0c60e9d5949a2dee3aba1fee3faf0f24c61556fcc4e10954c73e771343c2799e23ea907d33d754b7b77472e3360ebb420c78918155985b2fbb6789ed2080642414245b501015d0300003bb6221000000000c085b4f4c8d9b3d902ac8692188a41e802e2108e93145f00732b438b716a1d539f10a5ece7e58a21eb308db4f68eb178a2ee8d92186c73dce004933862389d0b601d172a63b4df01b1a9242e60a9f40264d71e7f1f4b24ba438da9c1a945b70a0542414245010176a4fab163cb6a7ff908a8e9d68042d17943ae73f902da9e7a2ef7acdf3e0a5e12e4fe2650d450cbceb1de52e57bcf6ce58e06023f2e121014eabacd27db25868d843026b3609005812848786be5b821b18cfec786718064dcc87215763c24a7e2cce801caa48b7dc008283ce58ac295b62bda6ba37e24e554520a8474c38e6b8e9289400ac0ef449bd499ae5453c25c1086047a636e8f52e31faee1e3c8b38480e7a5ac080642414245b50103190300003cb6221000000000fe749c5800fd52e4713d7f5aca56892c6496d2d6316061dc531461e8a0b98a3835c8175d9da8d396c1ab7cac964e67202010ef95212548e1d2fbacb6b3100a007c7e77952e8770ebc6025976529baa59d614d7c316e91a9a2deebf7521f5210c054241424501014a563d452cc21b6800609aa27dbd1dca19fd11073fee9a4b8fad289f03862c591e7f7dce31faa050c226c61688ad492e015497ec2d56c8b8cf5b5783c88e6a8c7bf3473525126efa60d096e563fccbbb4f71a7e8c10d7c078bed2ce75d5b118de6cce801bb429fc40b3499c26da637ab9c10c22a5c19a93991a72ca35a18f70e663f3b488c1bd01c21cdac535a059ae4a0200b68489a20fdc2eaacffba0ddee83fe82928080642414245b501033d0000003db622100000000030a48e14a7966182d19699e3b61141677c1f40c10e584f84256712bdeba86c64a978f1b8a59895a2ea0e9a2367860a8c0f411c1b8c9df444c72e10d97a3ae70d32c643213f541587d287a2c6cb91f263a3227f8248360ad6d9ed30d497691f060542414245010142d96becd1df50a12065a5c234c429f3f38457f61a165c81a40ddd1bb110fa298f53e770613a10a4acdac6f37397a9e6fefb51cf6b778221472f1fe567fc4089182d296c6449d886afdc6023539e37a5244655ce5d1640aa20efe3f3d26c2d79eacce80185ad1419cc14934fff62562ec0f69f48afa15a8ba7d70a0d797b9a81d57c7a0655b02f69575d1da1117b1130f91b61cfb1c433a2632ebe9518c65162427a441d080642414245b50103dd0200003eb6221000000000e47f7f786cce1b7521232908aedffdda18dbfa2c1fb3c5b9c00441c1d56f340ac95150a40853c7268f433103f3c3846f237ab0ba5a2dfcfcc2535e7835d37d0ee8eef2e833f449db6eb4abb7af8e4d681d1ac68d8919af15d3c9a14fb0f8a60e0542414245010120c715be5c652d2a64346ee296126e58e130b90144b42b0fcb1f8a4f9c80503ac9dd828633e91d1f63350e990da3e24c58774800f4997a37f493ea03124cf789ed5e0ae9526c582e34346b0f9d1c4d10a8ad1801933f7a2719d201cd3226224aeecce80118af951ea4fe6541de5a65ccf3355fa600f5fe5b7be240caff7e7a02ce3e89373afb5ac2253b0d7c9bd5d1d7870839abdb82b4dc89fc58446c60b71bfda502d8080642414245b50103970200003fb622100000000022fdd24f8ca358bc58923d44a9700b22d1a3652519acdf6751d078fb259b6e45b395096d2cb8eb548d5011d8f9a03416807f2bbd167ff789a39c96945eaabc0f8ca6e4ab902cc73152bebe64d4792959c7cc2575d986837328b6f2a5662423070542414245010168199a21be8565db526c9e4b71a506e31c0e721cbc75cf357bca29fbc4b3ed77a13aa932aa9ad8d7e264220fe1ba2cf6e2213f27c6199025c170a4cd28d33689571dd37450b06dd2a44420d789284acbc0f6225f5591ee2fb20742d42f7d426cf2cce801054bfe16848ba8afe8d1f69bcf2f883217f9a93ed5738c410ac3ec08c5be3f988d5242f9e5778f7f44f42f004cddaf212278dc63339c7442ebb60123a88002ff080642414245b501033501000040b6221000000000360c12e827f7dc2f1974d79036c0b1289e318da5acd993a7549283a0b4a69149db27114439617cd59c2d35eb807efaa2b4a65070933003415bda6df313dcaa0c536e99d0591ff0c630c3adc4d22f654434f8309a57b101a50f167267e60e5c0e05424142450101e027c2609ec40f508e8d084df7fdf1624540e06bcfee35fc2bfa3d13c65e145548202a197e8a212979e88714539cb01091ecbf35f8c14289fac10c15bf6f1786ba6d9f8ea57454d8ded14f1f8c58261094c79e492e50ad558e3b90e280a15153f6cce8017ee6c19c6da48a5cc1e7db033896fbfbe8a07c69fc57dc04c10fa6ca7391d96eb3bc580c1e29d0c13d1ed20eec30e58f44f9287fdc0183ba27eaa66ae23cc055080642414245b501034502000041b6221000000000d0ff46b86d794a7bf7490b68f4753a6531f7948ea309146ee0e615b02d36390e6c298029b6cfbec7f81e633873286aff2b0d5b9455454cf44c3fc0f1f3e5d90cd2a3e393a6430ec854ccbbcc7987d4cb11ddc681db165d5178a8104ce0a2fa0b05424142450101d4a60bed31f113584269627866f2bba7cb5f4fb81b023d11d4c834ff844d1d69ca777b76f0d880349a0bf95ed78cfeda23d604ab79596812977021e9719ee98d17c69753a2ec98019d914256834b1e6a02e9fff8854a42a841102a0d08e1625afacce801380c9682f84d76472004a4ad6cfadde1424e962ec6d81608d8cc1e3ecf6d976961fe52af72dfb21ed6d1977d39b3ff0f140207d7bee6fc2a162c3b185fb13760080642414245b501030600000042b6221000000000b019d1109903ca85ba0769593d257023f903cb4ed55d6c458fe1219109585a6414b87c04b6928d1f5292de09eabe3dfe7bc7ecb744986dddf9b50cc25808090528437803ab60d1d6f9467eea1eaa878c9bb7c8de368dc4f8418c60287025f2080542414245010174b65ac857b6d786d0d095c03392bbc306dbc0dbe7146efb0afc447cceb95176b93981148faf757ac4695dc50a19b518283703ddf6b0d463ea7b6adddbdb498c0f82241bdd90bfdadb38404d8470809b34c6722942bab3b09c004bb68d28e535fecce80155338b48a551bbf25b3375e6795f772c133f5a6fdf575a46ee611389cc92fdb36825c0271d9025f5421ef86fef87b7666ef2f4c2d35e1a3beeca5d477eb94d05080642414245b501036203000043b6221000000000a4d799346858ca7905829321cf77c93cd40a314f6eb8903e326b2796c6bc100e89c04afbf17c21a9c20e69d2d9258070c1e0f1b89e6d082891f2288652a4eb07ad1f2d390a8b41d11de57d0d9ac357ec63a8e2ac1768d794b806138ea6a46807054241424501017eee72bae75b60a54acdd079218d0c0cb6bca632dbe89c21047e7e85f3f4b859a04239b7d7e47af7922786181f297dd146ff95d5473d4311a0a5fefb1af28488f9aa071d117adda378a7c70de371225e423f76a7ec521183918d3ada5b1a770402cde801e6b754ea289533c09c449424bfe9c1462aa77f7b3067fadfb14708213c6efa460c315dd3883d7d983d04f1a2daa30371c75e245ada4d3614e4df2bbdd6619790080642414245b501037801000044b6221000000000d6b562111d29c856bd17eec2090dff9afe84005d5242cff123d634a392d8dd5383e7a9d25a79b10a858074253dbe663499a1e0471df305d74bd26de229def4043abdc8e11c2b3892721e7443aad19d20b56706784c837df9c677ec22242b380105424142450101fe715e28699e2d40e61d7d171fad413db06ceadb6d50ce670d20930e961d145b661a15fd1638eceac15d94f2174ca67fafcfb7e773527a804a61ce89c715e68afa15a6e72151e9542ff4e3cacea14c55b4c0cc5b54eebf3351c9ec8a0b1f5a6106cde80138f8ceb8371172276a6153dba30b358d89a4b47c9d53c0d3c37963675795974e696298758a247a7f57d41f084a17cdb44d041a6e58d0da13f8af078e23bdde8a080642414245b501036c01000045b622100000000024ce1f5decd38a9b727472166296a36a4d413240b131f9dbc79053e82091a31723bdd2cefb955bb4d6adc9642f2c61a5465d80c66de7dc179465d19921c7d40b9b2dfd6056c712d75dfb863a0e65a096ece87a09abd96c1112406c39fa9ff8090542414245010126c8ba92f649c91b629a80cd95cf9f197036ac6e8fd71c83a7d0858f4eca114b5595a08cd1473692c806d3d242d77707394e5b3d2d39eb253774075b68afb08a20e3c234dd35791f778601112a651d79fb95761d208b3b0a76e8fe447efd19570acde8017d9ea1d30ce12f2caa42ef4e6144523c1729cac05be8bb90284749618338112a23168f7de0a5c183db0bda52488f3e8d6a1b5af41f56409e7dff16a26bf304aa080642414245b501036402000046b6221000000000da20a3f9fa4dd0554b17f3126b967a0cebf5b3d72da1287c12009710b1de543a008a63f4b63a0a44b0e07dc6e251e815122bc08e7b7314b4869ff7535aa716046b69dc590a6b40de9c100910b93c039f1739b01f96aaa10477e4ac420887b0020542414245010132a54a568ff41e4a80a6f9a76f4dd2c55d992b00e4d55f70e14f9a21463b9e25dc6458d1040ac8b14874d95a94bf3bb8fd6bc7a92ecd4884773afea25f2d258147a9c616a0def6e687ebe87cda7c052efbae2f171426f06f189863371dd2b5700ecde8019e5cc58b73c09841f60219627ad25ef9ab2ec3ac3e1cd84280d3d6443168a6f13ebf8f4cafe20c87a80afb288d7e3cce21f939a0248c035d5a86e72bdcd2374f080642414245b501030301000047b6221000000000ccf0c1f705055a8aaa6fd432a4dcb8ed0def578d8aa8e929a9dd975024791237c4a8c9814f098e4e571c8be0504985820e420247d0b65ed2d203832dd5be450193cb3c129b524ddbd41dda07e1883d6d21bef76dfc26a97a49543a08ec8ffa0f05424142450101e6103601f4dd36d8dcbde07ceff85e45a32007e65f360bb533e999d642ae51099fea88973668b6c9193a88461b738678af8e8b743cc3e703b27074f5211fff812d4637daa1c038efb0e9c0d4ce3dec26bf9e766e66e97c96fb340e9996cc643612cde80139c7fbb58b5b53462bbcaba9d7ac6ab4cd3a80361f69a6f4a8bbaa60ca7c6ee50909a071260a2ebab9481ff27997c806416a0b49e1b4a72705ef833d1b71769a080642414245b501017d02000048b62210000000008e2364efd7903492820f2d5473f2c1ed01da55efe94bb20267fd7fb31baa750432363603e0a72e1582190bba9f8174213f74543ac757ef045d02553b2567f20552c44be068f7c724778ee2e71f3a83de5733d9e0231d30d811a63400dd166f0405424142450101b4afbbb2147b248afd103fb87fb7a4fb4b8e20765fd51f499292474d5adc586c16eb722b8538c179cfca48ef23d4b3ae4893c844fe0fe2552437c517bfe077871149ab268e45508e6fc88b94ab9fa37b9d36007f91d3976f76d3e27bfaac76d816cde80126aee908f0cf8def7327f7358603da37881e74173430158b6b077845e4eb8f1a5ac13e4e5907b2ca12ea628189b1de9c3cfb8d8e98d12371648285cc29eaa9e2080642414245b501030702000049b62210000000003e04b5a7958df85623fb169fe67e726b4d62c1c602e0ef60fc3045ed2a25b779e079efa7b7cffb55180de983b0b95e315abe4a3bc42d90ed404ea4b7154a04094fb56c39448dd4e62bbff83d2d71d12adffd440880e85127517a7853fec288020542414245010140e87bf3ca27632ab6d63c8c645adefb96cadfd7ce98e3b7cdbe88c80ae8500135f7ea607eb8acd8e8b65b7bca78986f84d7c432be1ec96e4786d570be24b0840917019c4c26eac8efe0469d73e8bdff1c626a5bb7a7c160e73673d1f495d7311acde801bb5d94438d6e6d4f02aaf5aee9b32cc6fb3a46a5a7523452cc8c527cae242bfde89706bbf3a98ec4bb5229e9cc244ca0b89a85dd06b16fb6b989dbdeee5897ea080642414245b50103500000004ab6221000000000020ac6bac58d19de44cb8154b1c0ba6c22202a21fbe67949be7579311f4283771374d16e2ead338582431da41cb0699e678dc98a937afc071e297629fe4a4f0a9e9adf836e572676aab3f1a04651486c870c6f1b5d000b11bf55276b7144190405424142450101c0d13f55fe1faa135d6d66e3402de339ff7d6827f3a477d45b3e0d27f2b4c03117607ec1b6095e70591e0d147c2c1266dcfec6cc6b5c5cc9bb6940ffd2e40a8d907239b22266ddd0912b3e9911d59f1865a4c7332001e92b42572bc8f595dd5f1ecde801350a0d3227165b232320f28da8ccdffb801a36b7a2134c5a2ef812a2b1ce34f81a8d1c38c6b8a5e22cc21af44264601b1e1799f337a6c7f0b4ab597de5eac68d080642414245b50101480100004bb62210000000003a3aec7c5a927499e0cd05dfa609ba6bc80ae72ab104f3cfa485598c8cd127689593bb2f6daefbfffeb30a94951939eb5b1178cce8d2c9d03d86ca91cc2f000e5cd77059a0d9b0b8be06a174c8bfae63e105a435f0018cbec92dd521a28341060542414245010128895a17c5e2ebf91bc91dc31fe375831a2ac171617ea923161de270763ce65c928801284a671a8ca51f98af3482b5ca8c0b69684363e3de913c1e0690299184e6710614145ba4ba34d27640f14e593a84621bb85d760b65cd04937c8bd1cd1922cde801a81cfd4db506c4164174690439a06688bfd913b26bd3bee00ac57e35f34f8ce1ffcceb482ceee73a7adb32c92414fed4bc12aa4981b559454cafd1926aa83592080642414245b50103650200004cb6221000000000b8aa76b3fb30ed81cb7a3bdb1e678fcc8d7968729ba8af5863ab149717a27a4de719363644c0d025b7ffa599ad91ad96b81f7d0f2ba09465e91f26885023310303670c5ce0279b38909f4ef790e2dbddb8057aec9c49a3b94619bfafbc61060405424142450101569f1a0e6871be8bcf62afdd03d7c9f0749fa331a1fb8d1209a1682dd330bd2e486a3ba712706d599360eafaf5869f5561442fe6d0299bc91b4e6ccb59d122893644cf44718f1e3d67c3c3c5a0deceb54e3a640161875f46dea37deada363f3526cde80129ddb3ce3e893b8319691d69f53f51891899ec42f3ec356dc621f69864b7e0fe6fde7af95589c66cf097c51b93f7bd49dffd1ede9de178c63c69ca623705270f080642414245b50103120000004db6221000000000e82c31cfaa1768a450528361693b5be5605ce73b6ad2acdf06cba9cc14fab82105290ef4343194595d877fbe2bb9ac1abfd236ae89b10e138c320c7a918bc603303b583382f6bad4140834eff18353617b977d0be2d190cb3ceb6e593ff5c30e0542414245010162c32f816fdf6c3852fa52739130e83b7955163baa66fa168f4449dc967a2e52af640d55e3d85dc307b4f85ca939120341f79314fb63fb5ede965bf9bdb63b8cb5e45e067e47e3fe387373df19c968c0efaf4bc078c00c01c4d7b3438c1475e32acde80176d75ce52711e0a9b487b86dcbe8db0496de8e89cdbe4654bc71dcb43a6746dbfd7981dca754f4dda35057eae3896f734dd61f32b2c0f6e8961500af0b680afe080642414245b50103900200004eb62210000000005c5c4b5dd6dc8a0e2f120fea8b6ee23caec3c7852fb26b6f8c8696eabe9cc92b39bdeb243a78fb06bcff36341312b8b3302ceeaa937659a095f07059f82207091cd4085dc589d5b453a602659668a7cff4fc40355383a63187e38044cca98706054241424501016468c31021dfc63476633283a3800f46811d2e8311d7e220b2f212131401b25483f2ca138d73d2410d250e9444305e5eed79a13881fcd2dd54ba278de3a1368016dac906ad50c802d3358ddd41d82cd72ddb446f335bfa93065532b374bb88aa2ecde8015385a6a0b99649ceefcd944eb478ff826476bb0d5c313a1aa4afc6b08afeb9b2ed904bbc16a0354848cbade582dc5018f67886b153bdadc2097d19aa661c2fc6080642414245b50103e90000004fb6221000000000c67ac747b543b7cf032bd192558cb000b6d1491d29ba462b4e937953da2b9340d4b32ccc824b2e9f3ae438614fc55948fbb3c68a6bffd3539ce9b89d5adc640d90cf42237e296e6732529ecbb6857e8d95a01b8f2f5778e1225f73eb6a512402054241424501016c437ae8eb688e3c000d4450eb72fca04dd73ee1b14f8ecebc3aa657db5d4e325e76ee21870a633bf2bea1c6db1a3e85f84329e7543861d345ace0e56a0556867b63a363eee59e3d07621b96956e2f34a6c276ac03c148cbc947565f4465cd2232cde801ed081c0dab81c8cbddcb7c141e1e2ffc89af28dcd28f5561cce2b185d009fdfc5d726ff4f7dcfbb5eab2f0171ebbbc45826721b9b31409e1d3598b3b7a5ee1c3080642414245b501034300000050b622100000000060382cd23667596f80d9bc9a222a9d1da8f2bcae6ed77053f8dc8ff81f76925385d6ea4d2767f775b68bde424bb9b5c93799248c447d58fc6b9c16b0eb229c0a06eb34e3f287d36dd44fd7422e3037d5471e1cc14d169d3d81344e152e62ad0e05424142450101309f3c243874f597c3e1811aaa4ac48bd6e61176afd2b4ce4579db9d1de9092fec47ead4b88fc99df68662a540b8377c0785733a1a2163ede83ccc7d753117835e84a7655e5b3babfab4053edd8590d3e0a940833581d5f84aeff95975c3a5d236cde80197fadf2e3f4850e4bab6abebd37da670655efd19df8af2f8fb0d5008ef1ca8af9ac5fa80525f4815de55d3e98bbdc0e732fdce4c6ecc253df1691f62c753085a080642414245b501035202000051b6221000000000a21f3104973d0f8aa319ce8b4ff40340982a47073c7cfe7df800e10f8492512cf483c4ad893cf6960fe527951da96f12f0b38f5e614c07d23af92a86ae0e0b0c2bd3b22f4fc5d14450295b92c1b07b76471ea91192a270fcf4a1c8933cd4fc0705424142450101f8c6888d3c5608436f6000df770ea3e78905853e4240019790d88330f587a611bcd3b5d521518e15fee455676380b5fcc528efab951f400de2f7977e9f6eda89ecc01b0a0d34736c2fddba403d7a88ca2bdcd57d99fa319efc52f5d47a82ce643acde8015cb5d22c1975c4139b48519fcfe9517eef05a46dcf416777e1a5cb13dee814617aace25d2cb2fd6a1f906f5edecb8cbd057cb049e17137cacee48d7aa9c69b1c080642414245b501035f03000052b6221000000000621adf3547f5e92f4777047cd537f4d5c3ad2a8254cd9526de55c90b5fdfde4ead1dd41c9e56279ed8260da4516f25930c83a0d7014dde20b9aeeb5fd7cac203fdbe1c3aeccf33e47e4056aa2fb7d6a9f061dc7b82bd68143c5234919529cb04054241424501010e44ec630554286621bdac7451d20a62260fccbfceb02a8e1e786edf2dd7990e7dfd119591a7597e75a9270300aa833420950cf978f4ceccb98b419550d0cc8c0f4e5f4b27473aeafed39e2afd210b023368fa2d8e8f07ec26203158ee4d80d03ecde801481a99410eb41479e8ecb39c3c7a1941ea84b4e276cce5ea9dd44af3b8282b4a021b2c3b81064f7dcb8c9e0829bab3a81dbd4d536b378e015b256eb0852434b2080642414245b50103c600000053b6221000000000d4d932cedfdf83b050245062e22c9f0f273d1ef4e7fa85558463af444f0d1e23e342d10b4d441f4a82db9ea5bcab7b306f7ebc486107d68f852909330d9f6a038ae859c070ca577183a18c0e392aecb8729e0588bdb150c883827a71f1c0570b0542414245010194fd516b969997ad56683999144de31b2e8ab5059b389eb8b17716088ddc7e30575e2323eb4f2ebc54994d6a1831b99f1a1b841d22cbc3d000f56c681cc8648681a11500495c3fe33be6da700dbd072930ca050f8a03e2dd70270cdd5095e17742cde8018b4e9ec6ac9a0396bedccfe2260d7b2a33401b7aeab0744239d850ec5609ca5a9c05df82c6114d4421459e450cc31053530abff88a15643a63959f6cbb881148080642414245b501030902000054b62210000000004e8e402266e52ab07427ec0835b4a8918976c7b45faf62fb58bdcb94cfbb7b5a3f2dc69d1c3bb7bc9ea2ee47166fa522e9685ad91f79c7f34a7c33fe13e0da0a6503610aa397e8d3726f9fb1bfb109eb19ab23af7c80b5876d12176e7d5ffa040542414245010114e879a7b39059f361e6741aa3de7a98187e815894a2e37c0cf17803025a11795167da3f3c9416163d87f6295c21957e1203791095d707cf947f6c099ae92087c6cdd05cfaecc9abff6e3c8263f36af109a5a58cf2465dc04aed381765102ae446cde801dc5c1b6a28b18640cc7825a025ca9f210f28dad379ae6bd8fac515c7b71ac6aea27f8f1f5b7540dfec06718d048af87267df5970c80af520f4e832a952ed5f76080642414245b50103db02000055b6221000000000c07911f5cd31118dcfc152b67d990b185f76306a6cb9f362a97a51560d6f2c2e90a1d14f12ca53bf7ef42b1632c6a886f14335876e4b70d3149c7f092c7eb407b5c0592e7289db8aa246e212a98548e4fe2131ad34d3495e870c41eefaaf780705424142450101f25fcb824582d616d89712542fd741414b97384d1c805eaee29ca5d4dc210b664f6315857896838869994aadfa082758e1ff153b69ebc602a71d7688f5e5618333494c2f4cf856b030dbc8f27f9ec0304aeaa3c9282f1d8ccfa9e4728e6f831f4acde8016b5437cf220663a10f33f3e00c85c148350438e0bcb2e1195ca81f31dfa7796d84243086d2f3bb5bbd81aa0ae739d575d52e13df6632ddf4e1722165d91dbad6080642414245b501038402000056b6221000000000eae1911f23b004382848a0cd48460979da6db1cd8048329a41238185ffc258099d0af46259e01907b2643d3cd3b31874ed58f0f69d1589381f63336376c69c0001c358d4f438c844dcdf57c1c65bf0155fd94f8828db77f98eabe8866b832b02054241424501017676bd1e9165fa2e3f4cd26f71b9bb37c87ab486d2fbdaef226a2dc3fe1eea757fe9c982020c4c1293b74fcb4a37cf259a2ca44973e986ca712a24141719db80cc7a1d7666cdf3c189c0a8b4ab6fa2c6e767deb8b8ca0e9a247a45d96dcb9b124ecde801f19690814ca9472314f031efcea962b7cfb31a96a92661c54c8a12416e311e46792972e49d1fb063d6f08e98001a3c636b41ea8cbd599c15b70e35e218fcfcbe080642414245b50101e502000057b6221000000000aa94fd27082cd07cc5272006ec0e53bcad9150d5ec9897481e6cfb81361fc428aebb348b6d52c8dcc51626f39b02e712e0b99156b1481ae67bde4d0573d0630fd51b006fb2ca101f0a70a15965db0dccc6e9ef89ec016e74989b7599c7cd02000542414245010104f4808e5f229da4f3ad3fa8b96e556c6b50a1bb367d691b7c49c983a277b92fb50354008da6e0ca3036bb0686777f011031b6f71c1ac736617cdb4c71e76189b1ddec433b5fe852914bfd533f7cdeb2e783c88975e17c736e71aed01b20f71952cde801c98fc4bea24aae8312f7deaa03a7208e8f8c425ed68cbe63f7dbf7674f8297b713392e3a0aaa767928b4f2362adc5cd6552a029c13548e8d00d71b686a75acaa080642414245b501012f00000058b622100000000044310a2a9518a5f8aeca29becd91634f305618c7fb0938130e3b26c3d0dd8b01be181c24835c2a699dad2d700e94b4c5305d96bae5bd47dbcc9e1c109d8ce303cf712d95801140916c205a99fab70afd6090b60f6e4136a5e923c890f808270005424142450101a69fee30e39f9203360c13f75f24eec55a39157b55092c196b1303b048d3bc50f032482903129aa917f4335cd6201a4377bf5eb3d773e1d82997757f73fb8184d5d90235c7a0c55cc51690d29d2a4e11cff80455bc660683a4cf8839a6fedd8d56cde8018e06fe3e72254b272727d99d8df2b408b5d47fbc8390ef3f4dcf6a9b5bdb411138ebaa0584f2243fad2322549a319c92b8917c79051e697d2dd0cd25e36ab8d0080642414245b501036d02000059b622100000000048cd5567b0bab7499a3225d97b7ea921ca3f290e44e20d2b8251061def14f623e7224acbbea50ea2bcc59f48d90480beaa33e4f916b85011e965eee22f57be03062dd8075b4d53815ea7f576ce7d2d1164926049556f70b371984e8204b52c06054241424501010a9223835c62d18e2038f17cb414250e1c3d0c128c5144b9b2b79ae06bdd0309b004b6f0633570fe4794522bb0d9db22d8dab091753f9c04e3e384a993b6be829eb08ac47c89edf38f6fc112d0b4eb7adbdc37bb500dd2d149b721e7aea5f6af5acde801cacde33deeef1b91e7b6886584ed92f418ad2d9b095b0301b0f1bf5612b0a5c788d2f03067e9b853e7c6cc90b369d7bcfee7977b16b126ad3809ce88491e5554080642414245b50101e30100005ab622100000000040f0c199df8f8b2321ff9d85a5c11133be6e4f49e6084e472392cb39f3ee356f64a78cf7c62f6127050ee9b33c379d7ba0ff6c34d8f5cf685ce343365e3420088b2a267d6dfc8037c14fa67f4ce83977c38c7dce089d78bff67e8df795f9420205424142450101189d40810e18478b259263287a8a62f759463cae8bc0b0f7d199d5ac21c7c24a74e756e54ac9a4441564a65b1cc865d8890c7e74d95da3f1536522560c9b1d8634af4bf9d4248ff024c92daccfc0e7d8ee52082ef195133fcd04580add01aa5d5ecde80165351027ec6694c63489bd3c3e818834bd2f800bad8271c345ba5057b42d34300676dd639f48f45d60e89f75e38b221c08c4a4a31c6fa37d0b117e9cf84a5488080642414245b501034b0200005bb62210000000006e26185ce71841d5c8985ed953f31e6ee9e2d327a7a4b8c187587027ff15ad373a130fceac882a05325ddf42ac9256b6427d5f18fac4fb2d65d70d4676277c0de50c0326df93aa52a5d18887952cbd3d727b29bafde84996f6db1adae06c54060542414245010122bcc11e60015fb8cb0b5e69f90c4766e79e25d2e522e10cfd29327bf54bac2ee2fb3b9470327902d011241b3e00b104ca1bd4f20c0536bbe30bdc3db133d58e7a6c825cdd70fffd2fcc4f04892ec8c22db2a2099335f25a1e6bfc0d0a48032062cde8017c162bbe27427378b0ef84e3ac8e4d2ac1da9e8ecca366cd17a9c648a706266ced4bd5463031cc23697eb7ff543cc89168015bb33f99c93aee7d681fd4809480080642414245b50103030300005cb6221000000000a0ace90c82ba821a0f4564acc714dcd9cbea514ab973c4d5dd36a48e29de746a21d2290f4a4b0bedebd7412e5180250bf7078659c8b931e5c33b3d0152febd0440195fd6f685573bcefb78bbecb9e4620aad7573b67e3bc67deff1083daedb06054241424501019e7c9ea6ac6a4b3d38a9c51aabd938856eacbdf8e6317fc599f895b36afe6007085483a4c5d2f76985e35609d5293404c9a79520c06dbe3f693a53bcfd8b1282d77b0a34e401b3c30e89b31d73f16636855d04169ac8080aaf562b0c1ec83d2166cde801c6c3c26ec35d112a12ab583331a0b46e2ebc3c5e858dfa02b5a01abf071b1098de22ec334604aca6e48bd41adb1f840223a358ca78b2d579548ef5a8d3274eee080642414245b50103d50100005db6221000000000c83765f12c1a39b6592df07f27049a2c400608600b56f2c71aa3009d7bb9235da33eb85bb51e405dd8e6b9e2f4408815401527a29220071424f8c28573662901ed106d298dcec8df7424fb0836e05eaf18b3b04a0e384c37a26a5ee7df2284010542414245010144a77bd302fdd7a406872704e6d807d5d8d617e31823fbf29e621603ef9e850d02d5cdd8b23411bf8c45d59fa6472421140d7253276f9e09ddffebef97848986138c43d5505177a4203d3bb85431d8ee0aad7aeb5641e84275e80ca0b57654746acde801ee167bdb1f5ed5a098f94ad3242b2a10423604c855bff461600b6b9928efa8f4fe528508f3cfbfd2f1bf5655b93e134226e9fdbc791d17df98932f94c436c13a080642414245b50103100000005eb622100000000034950b131c97ac6f003b7265ee53169d16ed452c4799c7b1b334bfe0fb43615eb7f1bbdb2197b82e77d03d4007c699dee026d574ed236285e6b5cbf868ec080dd2ffb2d4733416aedeaa38bd51cf9a8eb83bcca5e15b1cd130736f3adf89a70f05424142450101e49c623152c3132b093cda5ece06a04508b8610eda8097d88c01e79366ab160451962f78fb2cc57e5083bbedc5e7037b1ef4f54a3ee0365ae5fbe32c6c507582305a10c87fece83389d98c94cf6689a5f59234e18c71e40a9fac0808017d90756ecde80118410ca260572c4c3ce0e8af06347944bb0094612afc8d2d12e60d59c7e008f7a9bbc426a6f7bfffc0739d80129f6bb4b1b04c7a538e6617f743e1d46252a1f4080642414245b501012f0200005fb6221000000000dca760ed014511fce60b12dcdbc4cca826771c3ef8c53e23b8b347fb6db7e3767cc0dd08749324cdafa877fbb29540056155750048f54a53d9bac6aedfce5e02ae6de3dbeda9b2c98e8502d539787b3d4076d9abe54bfa56f9c00ddf9982d80305424142450101bed426a0dbd73981f43aae03248dcf8f1de093b9b513be20adf5d33ee3c2013dc0cd0fc3ba50b31c59eed11df4c882ac8a5596c00c9510b47890ecc6328ae18fec2fcc44607de05a761bb2eb71ae8305c5241059f3cedd2cd669de34256a526f72cde8012671d0b3dc59aadfadd6b2427d0ffc51355953a30195111230a41db9928a0ac9c502c3b5de928e740df459c7e1c1126d2914d6d3ca0a89ac6ad97c5d81005ae2080642414245b501035902000060b6221000000000f2c409924cba00083104890e0a017a31e791a34b893883742fce0601421a692dfbd32b2acb6f3c38d812e15129e6e36f1b14e20752d5844d38f869694684590880772dbc6e754d8ea4f29f74058c75928af3d291919807403164753cf22cae0705424142450101decebd2053064b9e815779d22acf160bf063d9692c8f5f217aad6ba196b820445c21f5b95320cfd7a7f3f1d1e19d70d5a3a1dd4259d3b929f8880c77d1e7c089bbc301c42a0624663a66a64bbc4fc48c547d14bf2a6fed27f6f5507399eee02276cde8014d12167fbb49b25c80cce787d9ac30dc1f3e051f4ad092405a6fc9566f3b955807b6a8e99058fd4f544e052efa78ff2b07d03da9cd15454e54405b5808f90ec1080642414245b501038500000061b6221000000000dae3ece837f3da92cd425bc89e2dcd3ad822ce49294d85dfb5e1cc64b6f3e30682a8e5c2b4013aa061d0127b3ffc5e966be4ec1843557ea23dbf2838915a490a3a71c8f25f2fbff0ade5ef129a7558224d49cfb29203f7d8f6938f2ee0a9320a0542414245010168a46fc2cf3546aa7821e370fea5bca5144579eadf4cf0b072566ff369e25c56823042afbaaf7dac8869da2512fe0706eaf1668487c677bc2b00beb2c2d3108a0e1cd017e990d59009fe60213c4a5fa273a97da3867c1073dc1f624aa4ced00c7acde801552d56ad0f256f3cfe8187b0f2b1f8cd8b8b0ceff990ffd2210ba23b01a5feab82ba5bbf9f4d23e31d8233b8d2e530dc70f8281eda161f26970b5ff7ba45508b080642414245b501030e03000062b6221000000000541c7595efd00cc5e15a9bfdccf7da85a389b1efa48e203d23b7a28436e93b40a04afe214465d277aa00828abb3c1044dd66b8e1fff5bd1533956d4b3d934303a323f7492f6565286d32202d3b2f4cb7f627de4fbc686749b57647d62ba8e50105424142450101382601b185186af668237a46210a465e6c6ece966d9c44a05f5a79730f17e876e46910253e3a785a1006c9f0853a57035a0f95e9561e7d1e41731aec1cae168960769eeb692d8c67a30ed85d8bd67e798a35fb9025599d97edb2f9f131e0d77a7ecde80108af85ddf8f89f622430e02dec196ec86189626500ebcd32635e7ed8561487af146d2b81df9b981d1dbc402ef4b4b4357245d908abb6ba7a37c601e7d3faf274080642414245b50103c402000063b622100000000088e31b42611bbd2ac15712970417aee7a7b52f62c69ff9f3a7208892c650211d6a2dc237f5f9f01781d771cd844997ad557a86efa5fe46d97b22be1b39ef3a0dbe8d26a1eaecd72d42d6bad076607c3f66f76a40ea7ed5d05e86252d4d6e9b0605424142450101663cb2afe1b13786e0be58aaf9f3ea82db410187ffb07fea29ca7e68fff4da5dc9bbf21a145ba623763b7b144e6f8bdf2f31d17213540882094204a0c1372b8dceaa9ce2f0e652cbf6d4233d470413112ceaacecfddab464987ca4079ef01b6e82cde801b821249db508d26cb57f56c8b69595d6fbbe01179643a56af1906a9aa0f0bc6c7b1467ff56cf571fe0459c3c5924d8c7c0c82bb6f9622b8f6a4576d45348bae1080642414245b50103a801000064b62210000000005a94337e5d163108526367a11c785978281897b94590542b2cddf4849e53e7490afed3897ce5bdfc9d9aa5c8f4eae3ca013713abfda9e0498e7d36689a75ab08ff5096988eda25b9c91d405df8fae801cecda600ee1d8ae02f7debc61f8cce0605424142450101cedd2a89b873cb329eb34ec5495152fa2c54dcf0b141c875435c9c4f5c69187320a080a813d9636432a500a79baf65ed689b63ca40d372a0d3051e4125471d8e29bf0c71d0855c49c782487425fe66bd24c941a866564fcb0edcdde6ff98feb086cde801dde82623313b9ef7fa97e8d0f8cd19a884e885ff01bb1babd350187e406ce0782570255cf783a70cfdf6ee7dd5d638f2680b83f996461b81afe28e8bee6b0963080642414245b50103b402000065b62210000000000084dd705dab1438cd221e2d96f5a7d8abeca29a64079d0a4662787ae3615377a35b8096d311df21cb4dc2a004a2a0245698ab13542f05af85c2b43cd5f7150918b7df45ed9fea2499e51f098a56f5156bddd2c8ccb4b9aa5772abe12415590e05424142450101383d08e71682bd74c50db2ee0cecad02a51f464dd62ada9266c31a3d0d16492d850f08492f10a2c103f3006c66b6c5fec62a14c0554c4e60f3441976d389d08fefce1c451636446924ad82992b23db1427164c454714ebc12756978376ab55958acde80185e522ab17cd2d3f5ae258239ed08e762ebdd519e273e00b6fefb431bbf98b2f1eaa468a99d6603e9bb737056f30209bf290bccb986b637f1c2ca2b3accd27c3080642414245b501010800000066b622100000000034bd9148e3e54dbdbdbea29bafd613516ba05d52947fa37569ff071eb6cb8661db839e6dfc8fd0696f774afc063fb49a70dde9f576a85b6cfc9b4064d4a3cf00c27939bc73030d1746e71742dd7b11b2fb90d8f0ca8b77255d52af35e906c60a05424142450101e21d1fb200c81747e6ba2abfa748389ce2891b9771f2f6494308232d021ba243008d31291302fd200e3c0c5c37fce786705854b7b49aa5fb6a99753962ba9c8795332279c0bbdc2616f1e27d17feb5b23b54b7c6ab7bae978368a2aa71e48b0a8ecde801762bcb781c09503d5077f8411ba138cd2a6c91bb93e8130e458baaf4a42c0f16f0cb64a8694c49a4cafb63e6fd68760bbfdc0e4f4654e868a3773df2987253cd080642414245b501032e02000067b6221000000000fed441a1ef319514bb66623250b81959e9401e53762679d8bdc62c9263a2131ebb4a2499aeb0b9b6b4a07deaa10ae3d665464d4988f4953eb544e9a3227b540f91883eeeee72c2ae998f6814ffa6ba7bc35e64b5824f30f36c1911b858f1a3050542414245010164f090bec727bd9c541fea22178c106fc221e2d261d5eb70478f682dd300d10fa62e7e968a355a60a62f6abc722281296112fa2106a3abfc0d5901e1189aee85f094ce62fca1f691a1b4561d85f8b21d852b04bff038bcec2408bfc9bb1cf53392cde8014a1057ff8ad54bac9ec4f814118fb73d903b769c784d6555e1cab4c30013b1bf99300ce525185223f1c5281bf4f7171ab6e81164b70cad0c43dee3686867e81d080642414245b501033200000068b622100000000086e199091e81c072c84c3cd0159d1c7f2b6510a55b41959ddd54eec259f89e750227a2644bd18c001cdf1cf90808447a5cd5bd73b894900814bffed46a307406142e701ab5e8c5b99c57b64e1fb79d5c0df0ea909caa6d7b7f7289104b2e2505054241424501011ed2cd058d8c0a35ca98479caa38c5436cbdf5aa81b1134f355387befeb6ef5a28a24ac63ddf32ec34af0bd4820287e0cf3fc2fe053e5bc2d753885db4879a833693eddcc2b66287df37382746b53cd7c6aeb627c12f6db33b289f87ad054f7a96cde80102f91c3da8c9a6b7cfb2529810ca5bac3b124ec158edddc002e6b7c6c1ab3678ec3c9a062a3ee823a10cd66213d5a7e9a10cd143663bca80fbbbe87c97c1aa88080642414245b501014c03000069b6221000000000b2703cc7fc34f159eb8f9557a62ea75c0285fa30a1d0638fe990ae46f0dd4e6a3be77d0378dc5f55444d450e5bf8d4f6dc1eabf565fd4dec86397463ca3b240765a0cd5c1d9c0f2e0269850999f0824dca6cdb4c528540bc39dda662105191060542414245010160e3c7ae79c8845346d1615f306b5b03472282d1158dd56c3965eedf16d1251f4899a79fc7055850df50a2051014291392a89eaf0802f975224a2c7fbacd5f872ae9be15786f535a193de5af3cdde92e489a7fa6da5049da33ed8a75ba99f0979acde80148bc3a82b5efe2f977e59c22aa9a16494280fddf29883ca630894d8088ac68b8e17e62e1a938e485888b5ddb6c40b5e273d0c6baad0b0c15087c7adbdf307d06080642414245b50103bd0000006ab6221000000000a8b738d63d68f990e3be49c7b2ec7d31bd06987874e6375b6c6e1bea257a921c9839d58555173b51883974f29e71d28625f562a1ecbb3277843338b161f33e093183a5741513dbdf8decf5d8e3dc5649ed2c6c73eca45409fefe861dd65c5e0e0542414245010136531a63bf5f513ff5cda739f5e30f6226d8e84fdab1280d63e08adbe79538748fcdfb53696be8c5be0b84f6fedb98aa7e9e2e6cb6ccb4327ff8d70d42788187ffd77503824a4f396e240cbeb88a5686cd3d42a9eb72d93a6a1f1336feeead229ecde80147528bd329758a8894560fe6c8e0c3c5b592347e28420f733dcea95e13b9998a472c9bc9e7bcf296e04c3bc05c15ed3cf7e77db9c1f931bc886930de0e5165b3080642414245b501035c0100006bb622100000000072617c179e269ae527a21f4d7d559ceaf5133b3a513488564f045eee34e83048ed20b0c12fff2ba3d7e4cf306293d30658b6716c7b3d95cd5400cda51a709104caf3ea88a8010460f2457084aa543aac896bf2141a54dd0ddcd751de2d77160605424142450101a4c5313321a85e99b73d92fc77d67c3f37b1c12042a0d38af24e7052ca8b801dc6f2254585974c2c7f97638be77ad50891e04c2a35115e6bbbed618b8dd4a6878fc1f8c08280e36bb8e2375d000120f08e91b874411f58a71c5440bc3452b5dda2cde8018e2b0059066c768be0f9caf3f9760f013bbf3ce21e710a2dd96c7de75f6fa8c0420447a52d103b44d6a906108e8ec255db2a704fb720d4699ef79de50d57bdbb080642414245b50101390300006cb622100000000024f657f3f2199ae83d964354cc9f7bc136dfabea0a2349ce2212fcef2353a11f15ada3363ac5dac90909f4d766bed90deab39e64dfbf80a2a18964423a31410af0efa9a26a634c8358268313c0c0017ec239e0485b590111646f1de5556aa80505424142450101e068b0df7c33e4100dc7e8d58404b19421db00ee6df33455ce2203432e5bee658836570d7f4de3fbca885784bced6491a5e5d4224e17cf2b86c8ceb934ba778789d9dd064b67b589e59299b0ee5c81f66f145b09e9e2e396e705c3dfbff2b937a6cde801485f9b8cb3bd06ce7fa63c03a92f3a67fbf4d49cdc33ed3f676c9cf1ba02623172a7aed4b2329af161f58430dfbcac614ecf9ce6814c3ba01c112429ed142139080642414245b50101f20100006db622100000000074cf95d1447fec1499c3e2c8a0c86356ea32108a44b2ad74d329bb5cab19f9338c2e82d39041cd9da31013938bb8502a33f17d31ab9e17c7f78785a7a9ff41074850d3684130e5f82664f0829735b0b36059eda625f65bd0f938df99de1f3d0b0542414245010110c34cf3488e604cb2435df4d69d6f19eebd77ec8bf570c6868e574299393f6cd6da010e061b509c8e25a090b2d15bc07ded69996116bc3683807d5fb98cc9844bb52f15cc7ec0e66ae120d13aa4649ac66df9136853f1d2756476b46f505661aacde8014c7c72e06eac36f44f3503a7cf6d3faf9e499a8eafe958672b79e59f622e813645575765a8f27202d4e816e41f155af4edf6b6e77f59ddc20ca8a13da3f14057080642414245b50103ad0100006eb6221000000000b07f67f1dde431af7286bc1a165ef909570c0991bc992547bacd9bbd6178bf5badf368e3377857c49e0c1e1b6de5dbb18fb88949fdf0e7ae8bffc645159b6d021f7a886479cc58514bab42943341346900071dda18e95703289214cd4f07b40c05424142450101e81e831c8455d8166d1cc0c5e10a7daa4d25ddaa80d40a4da631f1af3ecbf92ae60da6c4c62e6ee91052c4709c1192c4b29b82ba0319f5b26dde1400858d0189b49e8e148d0821dc777dbb687e40ef39654f8bd1c378b8a6b307253c9271d009aecde801a6b60cf9fba3aece9ec075c28e825c4ead4ceb90d1b46b8bae482a0a2a91c136189f5cb78c04a7ed490bd6cb782a33f51e15297b93ead7a0b6fd17170ec59f5e080642414245b50103650200006fb6221000000000d0751e417fa698c4055e002d96d9a8a23ca811658df5035f69cd2f05983b8035cd58c14a41d97c2903bb8b2221524d88990e467616b7d9f77c79f736db29b006221a9e328144295fd16fd37cee81da67521dfbcea667fdc2f4e0a0da3341ec0205424142450101cc9d7187a20ca9fa488e126a93e9bda5caf241f13b826d5de303e7cc398449122ae758106b5f3ac257d7f28075e34e213b6ff35b95272be0688c31168c8c8781b9f6a4817521f9439606026eda35e1c5fe1028cd4e8f338e340d1ee2a07583abb2cde801e5ad83a1508bd685a01e525aab52681df92cd7b38f193750220b30161a6dc6720bf7b77683da32c36ad21c01deb9de7039778b9834eebef1c3605ed3c71fff82080642414245b501036e02000070b6221000000000fa4dd098a7db65b79223b1a2f94ad71ce48fa3f149ab0316fa606d76022d2539a27e7e45117cf964dc5589cf25b3587f4b504f67b73db9a2fc06e70d228b3403511fec0bd0b0488db8762a107d3e7882635e35b30476b8b8621f1f5bbad4b20b0542414245010198fc864a383fc2d13db01091520da7a09dcdee1810904c0d950df557dc871a7fb64f5260a3d43269fa6e392064d382e0fe71af0cf6ed815dc6f7170855a7378e471a07ad177172d7151b3de0424ec50b226daa5b8768bfc87494314aa486fb52b6cde801ad350edc2096dd3c711c7857e671363f6478a6c2d1da49eb79644f7c4ad772fa2b919c1182e4d7e546d2147d2d66aa6d30f38df018823fb690289aa4da6378e8080642414245b501015802000071b6221000000000e268769193f831c859218b8c80e95b1aab3eba1d9e1aa4a3d63d57ab3f277d1b2e77a6d27f117ca64a31796496336b9bc07bfd9fb4b80e7c438d9d3865962002863b421d3af5affa2de40438577853a51a5087255f203c67fb8c8312011f4e0e054241424501012caf235e9287d4f040a6eb5342e023ab6c08871442e537a8efada3d852c22254735f5e5c77bf31f1c1ef122a4b17e586a03940e27f4ab9cbac2c7545134014855c2f8c72937f4f4a7f18ee80fd9793c4db7708fccc6038f31e617fbca4f889f2bacde801ca1b81dde5aa05c24fa1dacfe5a90d7f315c94f0fba6034b3b27e4dacd8a05c91e579740543f9ad18db1e245ea617d9912271ed00c12c77a9a1a1351df06857c080642414245b501037502000072b622100000000066994f6d37b3e7ee70544a6a53f175509b3b0d4ecce1b375ac9fba20914e935526d1d5fce013acacf4aa36f78b7b8021205d2f0b75ea546e1a237edec62776093290ca954bbe81f510ecfb1095070e1259cc60e5100acf0a0e528470565108080542414245010110e1d4fdcda3669572a602f5887c3a8cfef62c877a648ba3479093a4c0dd25259f99f29d6de7e64b74b06442f955bbca0d59a4c3d33e82779f25f74608b8018f41d8d0f8bc6f98926e7ac8c77f16ffffdb648612c75ccd90a29ca5d654bcf4b5becde801c5859be4c3fca4ffb2e898dc3a674c7101ca7451fa74e812d5ea25d7cbc7a51cfe34895324b5d9aa836125fb26899465df8d0894cdbe2c1faa9ef9be720788da080642414245b501035803000073b6221000000000aa83a013417469bbda17d267398fac270b41f0ae8e2517f86e025435d103d15d5a8b87df3c260f240b541d265c0583ac336ea971cb5b3678d379c4f09bd3a30be88a5c9834d9da4de9f61fa375c5f13cba74a6ee98e686ed61f3f318b602fc0b05424142450101d0ec41e834246075528b69784cc00be4b1b4876d2741e014cb723aaa3ddcdf7bd7edf6659f4fac1ee2e1e78b3d86a01f9f6d41313cc80ad371481d6e0946cf821fa20c96167119d63c624c9a511ca8c5a748be037c9bf8214e452c79a2723aecc2cde8012f426bc351f75f673973aa57c3f704aedbc434e920963a3872a387cb4e51d51d2df709119c09cc49ce00be644c4e2ff95b247dc2d7d1b4fbf5fc52845e0b800a080642414245b501014400000074b6221000000000e436cb05e78db84fe7df70634c434c2ad60a6f8223b921359a2f49f45974654b9ec2c207b4a97fb1592608c9541b5452c442c5076c1bcd2040d16fbe21cf8f0659e61059d5bc09c7dc51a1e5983ffca1c2336479d11fd42dcec706e41a11280905424142450101b81307eaf63a5d8083732994289b7fd59025014a25f6b6a7d5cc2d335c986708b03f1c4205a83937785d0bf7d1482eb00da5967fe564f8daba19d1c4fdbeb28e58cfc0b3693f3abf3ca984d2c69d3aa356ffe2721b3f1d2d1e130aeb19933cb6c6cde801db679ef7a67bcbd6d302a343946b0f7f1b2f20ec0be171fdd91f842d0b87f6f53f0f9ff7bb9f5c02fe2dd719aebb833a1222e18862f0e149da495d328f38e49a080642414245b50101cf00000075b62210000000002675d706e438b834d44474e7f13080ac4a3909b0541f4270dc13f43ec50a7438b53bc3b1a14720fed7ce3ec282438ff7eb332a4a2a0e8ae2ed48a319f71a870ffdb2963bead8abd4ce396da8c2148fbfb7cb35c9b4dad9d1ab30499d48e86109054241424501010a0348ee35593515a900c8d5656b20b174c304d3f4666ec6aed4b48445a2cc0635944e482dd4f9f9414af72299e583f9d4489551ce7f4ca76b20cdf8756fd88486fd1b129381c629b7a6c69cc7b6c87a9c19a4beb2b5917580737eb7ca1f5977cacde80134eafd151a6f76dcd6b93c5446363aa7c81936909927832148619f76149f8bfe401157acc8df92d09a7cd05497e9d57d0555d5a1ae87efb5d81ab5c53a8b1db7080642414245b501013200000076b622100000000048a5e9ae1d2e584108229a2f27178d0e84bb3544bacaf70c39bf2b6f45e01c54e0d5dab2724ce6ccf35d34ba0e34914cc6c22ff809435b08b102aa39a2b4b80ce1ab8988fffcb821f5885ca0ad702e27baebe18af9c36340643f3c4f7a676a03054241424501018ced463c2f390770f042216eb60d8f0fa49527ae9b9d303c787d9f85cbc8b021b6a624928edb48b38b1f0459fdf7cd634504f7279c19c92db7a9d85a0c561f86f74ef38e5355b72f0b1687decf06471d273472c3f28f2a15f1bd20a1fded272bcecde8017bac5ae5c51664bc7c769ba7b16faca7bd7805725dbfcd47d464856d88f3e2020ae6c25b36a73602d1f43f03aef8c00f60cb83bb1e21611d84249d8ae525a4a8080642414245b501036a03000077b62210000000006286069e0318054b21733d933c19d4958bd5a024e5fa6e420c7eba80f9002f521d0a270c956a6ba0e106d14c5bab5c3c3021e091b631d120a0e119d8118e26080c012b40361a6cc6ab152d22b140f56e8a24a5df2597f5385b28ed9204a6fb0c05424142450101586e1e4d9c5bb10f22e40efc2e8e27bc424d7a7b5e1e0879ed8392f22329c0417a23b7af527c9ab8dc513811f351633d665576fea56a22efbf1c7333845d978f31852c7bfd6101309989c9fa44c9760fa14849cd6bf4f7eff9a557e94416ae1bd2cde8011dfebb7b7afa6ca7c41edc1c5ce9f795f3ffcf4af2930c154242b36fb59369346ca72a52235fdafb3b5234571752dc01ab56daf8d8873089c636192df9115f90080642414245b50103b702000078b62210000000001a7e4f2feb39cf1dd7d518b088b5d06f3f25f0d9a9cde6194d8c192f8c27e71d73933a0ac1ba855338aee117181c913dcd245c7ad58179a1d474a5dda0d55706a3a24ef74de51f4d5c040b4a0503a2ad77cad98a9c959757ae33a84b0862c50f054241424501017cde3d7113050846598edffbf4143b86954ba89bc0be03a85660331a306a1233fa48b5b781bd359266e8a8cea0299e8feb80904650e0fd76a05d4ff866d3bd8bdde42d4fcefabedbfee76370ff2cd90b17a577666065efec651448710e806f45d6cde801746ff6062c3818d0c596fa6730c7d4ab0754e29e3f7104a79a1ce7a6afa7987b1a8d874647a63a5a5f2ed111d7259e0a6f5f370937dba600bbbd8fc410aa4b40080642414245b501013503000079b62210000000004cbde4f4267153fac5f5500a28b77be4961863cf3772180acf8d6625c7772e74c4a0338168c451608e0e3249829149a2fc491c098a4f0fe72e671bf43be5e701519aa0a86d0df88161871ca97b4e2d34cf32e16816ecb16b46bcce4588a0fb03054241424501017c3e99a754ffcbc2fa821253f11761bcfc2996953d474cdceba8e5ea81f8a467f486d9e6ed7069150306757a245a971df4e1d536d4712080b76e60427c8d378995cf1a4a7bd6fbacef094a857652c4ad66850f00e5f740ef5da4a32a1b61e503dacde8013a5829363b4de4fc26ca2d83da7eee23e1cf5e279625e3a6d5cdf4feea33eaaade629e3e59dc22349f7a7b0b96956a05acd0dab3ca248d8c435f18a7e018f755080642414245b50101f40200007ab62210000000002800ea39cd87d8fef264680d9e391e0003ae7a14469a9263778198750c49fa793d8ee653cea100314c83e112df1620794139bcb923be2ab1f5348523e71e4a09182d357a86f9298654f5e126a9c261de9cb41449de8862d47bb282b4acaf830b054241424501013c6e2a058d06052db95409b1b57aaeb9bfa0c1f42921e11674e710325134971ac95abf37cf85425a3471b8af6a167ff8df2766c3fa291b381012f29ca8c01e812032d65f64b1fc797b4aa633587e92eaa92e2d4c093dc04c0377036c67f721eedecde801928fe0f9368edb5fdfb02687aabed2e2864597ae77c6e0095555c74014e0d2b56f6c699a9d08ddbadea5000cdad2c4f3efdbbeb08fe940383a6af1f1fe3f4f45080642414245b501013a0000007bb6221000000000a2facd1b10af4c2a00a17b5ecbb924e19c3b1131c972ae317f575cb623cdec25844b64ef39fae9e933ba47a276ea48b42d705012a7725aed1a4b12596ba3da09f3f18bd23dd3fda09dc2ea8c4ca4907f30525e2be2c5dc8780ecce22083d160805424142450101d466ae6aef12e55d1e6fee423cb4185e35ce3ad1d437289b1073426079752f4ced10cc438acea8a134e6af32db7e10e2f754385c3b70a6dff655056add781384ab45d51ff2dc0b3fc7a65ffcaed3e897f1c7b27ba22ebfb2838849a0bd8ce2f9e2cde801d8696573765baee75910557c9a811ccdcb3a6cd422b9d4d0ce2c31bda19b6baa622bed37cfa201d5c96a56c3f25752602fa122c16119e6f4a6b10b58d005bdcd080642414245b50103710300007cb62210000000003a0bba9c37705226b8254fdbdd7a1de652adcbdfaec9b32fde429b3797bf5b1e3a35084b54cc6f9437306158b4e8b328153fc3231824d936cb9a87ebbd8b2109328fe9ad6204096ebde88d4238636995c1c376fa37c261fa7e973d3cca4d0b0805424142450101222dfbb4d5852f7e4505afbd80fb58b40081cef7e01bf2f376ad242fbc37110285c322cd916a07859573425a1da21f632a9db9e8ed4b4bbfd24dc67f1b21ca8279ba74b27d1983ff6feda87eb9482d847531d1e43a72b93a30e6fcddfb5d063be6cde80102bf312b86313c2c3428a5eb8ac8571528c6cf2ccce66c243077f11ca86b458aef236b5d84b3706e148b59370fe23edfa2f1f7cdff05bf2062dfb44085d7165f080642414245b50103110200007db62210000000001ce2bd69c4745da8647fa175c314632141f6dd3232d9663653cd319c684b2b0482b66e6434f82edd2d680d0f7fa9f343527f54acfa2dd2b587c841748ec4240c838b859713491170f6592f4fe2f553b16c4fe91b0441399906ce83993ef2ca0f0542414245010152136b30da97096f19a80f66161821de23ab3436fc12f50af3e9d8ecb18a1348dbdc75ade5af8214c8214e20097254626b7132492b51c04a0d3a098e78e73d805fd208587416f627d9e54776f14ea8e652ca50ebdb32c77d579abc5e5ac8a816eacde801e2c7cfdfcdbd7309c3d5d56a968802eb968816dc6f8cf211815e56ed17776577dfff4151316c0dda068da4a9f91ab7d928def1722d8f701fa953d32f904351e5080642414245b50103370100007eb6221000000000d2e1befdb334223906e4a4a9702e3aa4f85be0e572d8a90a83a3e4c479dbf35ef8819d49b5dced8aa489a1555598a2a81b94cea6dc7c95a2e1eb47337183da0f54422babfaa0daa81fa11f65ac57a8fca34946e6bdf40ee076fb4b65a4944003054241424501012897a28e12f40c0a4c2ad69b26d58b34eceaaed167d7cbdac62b7cd1103dce25b799dc6f62570f66fe13b7c355b289ae52bf02ccf030753e321df5bf9661a2843e95284816ee6dc5513dbe6efbdd2e7b11cb07e646cd14cfc953cac0c99791f6eecde801bb8cf116e3804f978ded52b61584351bf4d4684b68c858311c79fb42163ba1ae25ec0ec5fbd26ea93b5b221f43cf739e55d7b609552774ab022cbc998aad0076080642414245b50101f00200007fb622100000000084bd9a312f7e456586e437a7683b04fb666e5063e2f13db018e249a7ee3a5f26a1b5c4dffcdd2f16193e5a925050696fe5ed0ffe05d48d067bf29265d10eed0b42d7f5ec29787edf1240bb446193ff202021fd79faba4a9d18d815cfb88d220805424142450101f6219e9ef9ac4f37a3259c71d1096451ea4367c8fdac0d01f7799d14de56c7423468b5f93d6cfe527a1e6801431814b7204cf0d55ad0af474da1da3b9dfd5a8f17e5c8e003e016d41bfd5b7f069500c7a49dc5b37bd5cd6d6c5d755faea8714ff2cde80181f2e4df6c59046756007c058afcb7ccfe6f1d3d85d71bb61ebaa71883da7404df7553f11346d25010634fc7d6e325658a5992ddab28eb82d6e8d70ea07b7956080642414245b50103fe00000080b6221000000000623545666a35824385c237b24027fbe03f1b4b375e9d8c318da62057d9c0f12a3c608cf21e4dd943611875b89a0d3197966f7f49186a893f0fb2cbe05b4b6001297b437c2c276824fbd39c05fdd0fbb03df8b634f4dcadc094675703af632f080542414245010108f24bd498b161bcfeb6e9559c9542f0ce56d739515569acc0920bc168b5253da0cf7f124e301695c75faae6d41b7a6273d9ad041197866f1ba87e6d6b06fb823712554461237dee7b6f5318310b92ee9dc564295a7d995b3ede7129e9fd37faf6cde80126fca0ed6a589ceee060f1ad98468ab8a6edbb6d34270f5ae4d4f5d7ca889b570440f9fd83dc3589ddaed8acc591048550c51ee761d6124d9b60b3a2af346391080642414245b501035102000081b62210000000001ce3c8deea0f5f038068ace730f4844ff5f1d9055978d5ab54117ba30195ed380dfd1e9a84252c611045938f9d6e479f1732cc87977114bc73ee6350f8f0490c03be4a6100d1772c239c6629c666f8826e2b50f1c702a10da16313979357780a054241424501014a6f29fa2029e2a1eb76e2d3c5a210d368a0d7609c141959829fcbd4f0248e6061a06883869b9ae2572a792d540cfce31b6674a07d832517660ee5e2372b8285adcae4074eab3db732ed881236c899218a587bed3aaf1dfd2cb56c7533692646facde8011a697f9896596399a44b8eeebe79eb3cc7558642474cf5c67da4a1c9ed78b82d75c0620660dbfbad9b73d9f1920e760cf5a4ca3ec2dbff1d80d8a6bc93d71e28080642414245b501030101000082b62210000000000c8f4b7763e8294c3e3d3ade57701de584d6d12781d0158dcb7d342014ca6f137da389862d0db12db490ab2997bf9a94b98b61d301b2815c8e6c3a60a617ea0861696602955531af05d283684be8853dab984e4c5f74203951d33bb215175801054241424501011e280843f7a0c51f062a74745313707dc59d3f66cb0546bf88d85b854191e874bab3bb91a91b714d37600a1069eace233083410ba59dbd78ceb55626c0e72b83b19a9adad60c10b426e282b298c28c42fbfdea968d435669e0b9a154d4af7055fecde801d2e42f35e9f74f7b4663b8d907515d76fd341092ca6109ec8b2c2e98162bb2ed751c8c9686f77772839a4ccc8a874c0897c593f89b52ed19362a8c1b9db7213b080642414245b501030302000084b62210000000003e564f5981218d7179a963ee3448e1eb1a6ac6a11a5028f16b74b98b66e56f2b02d82abedab0199614f14c4cfad06bb6aee2929f8988983f6de12cc98c12a405fe8acbae5b5d4b219fa46d13bf99ed2ee2e07eb2d6ef1d1f3c6f7586333fe6030542414245010196cc1a4985d9a5b686b8b1b6a7e12239cc6489083f0316904f6ef8ed1911263d1b67319ca912760af6b6706765b9fa3f62f956f80c75c4411c862a2c0106eb8be746073789abb90688de35b0b3f1c044ecb2335aabb9e0d77585bd7e91e472c502cee8015c3ccd2d1f033687c540ce9b2dcb586598573e8b0bc265926a743cf38d8e8d7197ee67a4a16838ac5474b9ba076ff30085f423717a906e1b8c132c0a98a2450a080642414245b501036c03000085b6221000000000ece69707764ca6fc2231a6828fd07bdb1fd5980b30ae95558696dcc9723fdf63486bdc29b657479329b18e5e0ee5881a2c4105cfbd23d1d823cbfc2e3fda580a7550e6c896418b8fbb417c4dd23fcacb2c4aaf8596e869215a900cc540da3b01054241424501018a8e9cb185fd49eb34a35df30b20b26381432ba1094be9e9763654f50eb3354b05d67097f60255f7022072227fee5154178a7dc046b4f0c90e495f0cb398448879612f3d0adeca1e6c27ae69a656d48261879fcee99e336207cf35fa1f907a5406cee801957bfe41678f4f8766cc6b9e39639131e66fe0af399b94cbf21fe4408e4ab228fe261174b08f69d002d56db9f9038a4eca47597d12be026bdf5f67b0f2f80e59080642414245b501031400000086b62210000000009c69970c3dbd3de0137d533d935d4beb67e7dc20d02216e5a80bfef5959c98780b78e1a74ba40acc0a854de9eca34e29c013f31f1899830f5d72806fd6071c0da9be00cbd16a18cef23b52c6898c6970e1f31575b1155da97e8dfda17f06ee0a054241424501019a69f4d99f9d846306ecff4ec8f3c49768e5d47ee568dc400079b501412eec15bebed2394ebfdc85ad155b93b1ce8e411ebc909cc5e27250d57f32965b994784331f035ff361223398d58bbc4bbb85c6a7d4dc758491902d5ee3ef08011c49e20acee801f08140f4cb5544b89e88e64edd00b4db11b6d9541ce60001a26f3cde191c67f2dd29a49a8dc12717abcd6d265e686bf81d31eb1266fd4ad9d19b2804eb7177b4080642414245b501032c03000087b62210000000008cef1e4301e1628e2fbadafc34f711b682a673c94708c87386860eef362f4d6c6f1c4ed7b3af238336dc5eb8c57559c3e061d0985b8383f329b8676b8e26ee07aaca3ade494d64cf0d51b54dfbd540ad91f578492fb3e88f2d090461d35da50a054241424501016098ddcbb0b66e6100c60ac2b86cca138ed87f3116c229d18599c432ed4377334aa89ea9a96313d895e37554ebec36291dbd8b570c61f5a606456feefc1f2b80354334fb3be9cbf0fa59ec07518b620814ccb23cb35e26a333a84b9ffc43caa80ecee801e3d69f4dddb3fce77593409f7aa01002687e329f805b5192e6dd4bbfeb783b699c55fd49e0b32b80768c4802ca8ffb022643616574139054f374db5700088115080642414245b501034603000088b622100000000036f7cb8c4fc015ba7b53c436b4b254cef30ae8ceaa7559c66666d4c1ae1d63322e82c2b5f00bbf7acd5ba5f49e3acff6e350fe6202eccd97e605eeec89f4670d9b4674e2ff5967692b4e7b7821b7a89eea734c7849bfd4b2b2e03554eec2de06054241424501015a68111ba39b3c6edfb64abf48d57f4dd58ce525784caa077ab66dce0d07784a91b9d3faa5719c8263cfc889f19873b253c1bf58d8edad225242a98f52617189e6465f0d6c753ebe71c738fc9de05ea0a5e4a1078562109040ab4adb5efa7fd212cee801d1aa2c7380facf5da3066bdb46513f84d055199f2de03f53595090c1a21196e6aca650e0a9aca669ce4edec388054fde6d6ab3643c281ccf38df97cc38481c0b080642414245b501039301000089b62210000000005460197d0baceb2404b7318fe264db3d6e2a8e134343098c1fc73048b3486e77bc0a2656f42509ba62b010efb7e1e7010113f340390595bb2d3e66c7c1de7904717eb5e558386ce82b32cc16251d8c6171f97b964cd475980e5c8de0c0d0da0405424142450101cee3c7b44baa832e2cff53a864c1743ba263556cc0a37f84a9d2f8d4659aad69f589aa61f849ae9bc32a61b378fdc5eb49f6c34880652c791490cc0f216a07865d2cb05ba934e38b8c18c99b5a53bddc4640b7b693d4dbad338b4c006363a92016cee801663a69e82db816511645a73cc85680ae9407c45431cedfe93e436a0fe7f4f4f57d3fd7483584ef9c9e83b6a24156990744b5a10f0f6bb472c1e5799b1e84edd2080642414245b50103920200008ab62210000000001acfb0af46168f4f515ccfaacdeecba9ec41c89f53c6aafbb7a4e3b3249f8a00df3f132280f89a86b3a11ae12da61a0e8e62450ccdc6fd9726e44708bb44200c0bce204a8d051225a7d665c93afa56f6fff7251a978e54d24f77feb6ee74ca01054241424501015ef846b7b205dba74c8eb68bce13569d5d9a44da9299aeb4760c059af2f15515e1e39038d40b7ad6be5279fbcd85db93a92eebad17167e2fa019d97418a9238653fb0ee6936ad553cae004f3ba4cdcc2d79fee4d7da62ceea6a64bfdddb6461e1acee801619ce82c47d644f530f3438555105136dc1a15cd9ee795cdfbf0d4f58a428e29e713d7379704783d3ee4839763f235d5138f153215433da64e48a8dcd80fd7e1080642414245b50101980100008bb62210000000009eb4bd50fb92a2f34e5c8802aa1736966cf0b6457432a3decb15106c6fbb1f6e2d5202ed175ca8e4806d18fdb7e895046da7f730717fcbab196d0cdc9714690b5959f747db6dbe27c52485e76314a7a47b7cb905447ddfa70ccaaa6b03ddf80b0542414245010112571da8c2b82180a6d89092b020021742b29c4e6c785e50a9722712a04fb32c7f403e54136ac53abf6051c55d47d4e0ee5dd8b140db8632d13c1e89b423b283f30e37198045b38c5442668d157313721957feb84bf893ed09683f3a46d1e2271ecee801436bf0a745bd60c7bf98d144782d2774bc01d219e6095925ea870679cfa8aadbb6b8926fd260fe96745c45a32d36661f7865df32ae7f52c873ba65a2a8319896080642414245b501035e0300008cb622100000000040217c76de8944cd84a89a33c5af94d0d4278ddf12aa1b6d36c57a6e2ea35838894653f8ea592ee449442c34d43e4bc2190ebaf1cd8bb0d393370c63b388340cd86fdfce76a42e82ffa449558e9042390198804ab105081328709dd47ee3e10005424142450101ca0c650ecc005be33ebe2bf24b20d7ad25a88c85ddcce1e96641eed120b8815ac7a402f2a39b92817f39a2cacf6f4ff36bc531519781c4298b175f547bf29081a2cc1f5106cbd2b50cd038aa9a29ec90fffab89b1ef21f9a99d82360b5d7ec6c22cee8015900e7800ecfd7eb5022b84fdbd644247d99420c8e88beac206fced6782bdfb7ca8968a171e9712f83f03a1c68678d5f0ffc91224bbc616554f6f522a310e7d2080642414245b50103a80100008db62210000000002899fd410a7cd03a8cd22e32b6f41cff5ebaa950bc29a303e174a62c15dd313fab51aeb4ca3b04cf787d0b68cdf3118201ffa11fcff8f49eec32c004f086f60f90c4194e69a8b6d0655ae2e55fc0ce94639f30a18718865ab8768fb52a0d1d0105424142450101a6e23a1b02126abb16a3c7f2e8899315f5d9308e5a2a55d41cf82b107e2bd401cc802a8c2b8459a386a319f39900c3f000746913022f00b61ca1f5b32b1dc6891669427713c054b249abed6ce2802f94cc76ae0bf858fab11e857731b8f3eeb926cee80176d96f7a3e16876a2f27eca60debd098df11b3a119c8bc2c3759c2f1825ad4e873cc08937346ca75e5ad3154f6199c3464abf355a45cccd26f517e693b61f54b080642414245b50101b50000008eb6221000000000002903545ac5727ff382768e859a27232738d560e73339de392fad2955755e446a40fc16442124d698ce71bf04f2454df4b35c3d539300d2bb2275af2a4a3303250b1888cfb820569d34edb5f071fc293d99d837367636e4102eb93a6170b60505424142450101e87446ee66257c8b8ab04ddb0bdbcb22e4c1cf0f1ee610acb1f0e1f7ca461a6f83ac7b6bfeef2788bfbece83c9288f194c20d201f5caf04554bb774a13d05e80b16e909c282ee1ddfbb6c48bcda51bd9125c58c902f4294548edb2c54bb7b5772acee80110ef9fe01664e0327b18c597ed64fa1857fa4cf61af4b5acc2fc07068f65838ca884741f5e0aca958d75cfe762453f9a0232db1aeefcb17b597c0acaf1ef1cff080642414245b501017e0000008fb622100000000066f3a440e344c0f88600af629ed75018cb964e5325b68ba40405ebc7e7f0a75fa5eee2eb8b7fc9e189ec51a87c8b2385b062968f501aae682becf8ec09fc1405dcc268b8a85a5033cb7a2861696ed7f949e3775a3c0fe486086bdf2a13d2e80a0542414245010104a8f93b2f572d2c2cd85277212833bf0c0295c782842bd826dbe7c413ed0103d5b8839ebb68c2d327cf30072b279fa2f20b4fb666e3e641d2a42d49104a418d48d900f6eb5e589b5f068c339a5024e4a0088738b859fe6cbecda7aecf34927a2ecee8018bc2b6d00b75a4c6e7633a2ec0b9896009020f00fbd4cf5d0d33e9f5b78896f21d077cfe50261f22de4b09c9c258e2e89b8657ea3faf7cd473bca875591c3efd080642414245b501038001000090b6221000000000bc580a698eabc99c944102e2072b9b4215904789bba060e7c29317041e0d6c62fa952ad88c4649407d50bc7375bf9ea0c34a635a2002998ff7c042114c0a3f07f36caa9d0d77db7905aacf35e128c384cae3d944bb6a29a626a9bdb9d189020b05424142450101625e481f7fee008d6d5c734a45f51c6c2c2cd990e7624a6ae84cb49b5572513a052534ca7a024277a1b883a0e58f7776c7a6ef3e10e14ad3043dd5493d1a8f861fcdb511ef8789b591719627e09462e894fe4336e024d5f6cce523ddfa5b9c2e32cee801326fa39cac3e4c087ebaf747f40b2b41a956e80ba709d18bc1d8694e0f0224594504eb6a7d30849ab8e63206921d0c7c4f6774f6f28fab66375108fcfd5688eb080642414245b501018600000091b622100000000002c76725843c1971041d9b1d93c3074a7dc6dbdbaa22db3f9a337543d5f82a28d1f947a36176025e22c90a549a5bdddcb207e749f6364579d25b743dbe6218036f833152f39d37e36d191441b3979700794f9a4266300971f1500f3d3f7efc0505424142450101f69f542b1a4930b6d7d1380e7f92ab197dc5dd7867859b00dd502ab9fae8ab35e8f82407517c5bdf6bfdd3d59444bb0c90eaf7ba42f5ed532fcd87d33efc048baf354890ace91747eb0d5fa1b986d2ef9aa008bd948797de76ab7cba1b233c7636cee801b08ec6bfc5116b212255ea9814ffb064dd9962b48dc7bf61bd5f6beefb8697aa4e7dfcc1a4fa9656f35b718011820eb78fa6c2ce321296d5f1f0c947a0086f8d080642414245b50103db01000092b622100000000008cd7da0bf05e521ce63f862916396d92d085b870ee3a3cfe03f87083f8ceb1c0be128c9f4b70127c7e0ec7a4362be179a2eef274b2a5cd3202245bfdaae670d9cb03f916afb77369a03b4c82eb764c22006ea6822de02103a08017d23c52e000542414245010192c728eb284dd9dd2e49a5355bcf65a82f2935a35a5c68457e6da03eb0047c0a1ace43c4bc3306a686bf920172d5e365a00a240b8374c23a5a3d729547e9be8703b31c6dc4b4399fe63ddb1b43ef0dee9472871f369c42df1a0a778db4ab1c703acee8016b51b56cf1451ab1bab220716882f6de719bef8c6a4fcc300b616e6d0f9ba026df782fe6b0e9f09451834ed86f74551577e5124a6b828638f60b071fc2c2e900080642414245b501034000000093b622100000000086985a20736b56164da3fa8d180ee94213ca94c0722dda311ac2a8bcc506de05d327c740233e27c6dbf60c0ca17b5f479234836b3f3db27ef0a32c831f39e708f1776c0cff1ff618e0e66784897066e68906ea0df59fbee5069144aa8948560e0542414245010180b4f730c5a7af24b341061fa8233f6fa64f556c9b5cd34a37c11dd54dc40801286371c91c30f56c6f15055e70cb4d3a79df66b669c4a6b4bfe29402a248818c63e8bbda0afb81d3700868a36a315bf0d29b4d8cd433ad7ac9674643d5f0380d3ecee8015f58b16d55328301908f88a728fae84d55814bee7a8e7e691fff2e0460d3a164dbd63994b7943cbf25ca839f6872e3385181a6ff334e7c60db4fb06d5a6746af080642414245b501031f01000094b62210000000005837be7b1cbbcd51bc95d110d8ec581af23c1d57f924fd46b9b77f3e5e2366044bae404c793b19120d6f2aa29a64a6ed1ee78842b0f910e9e33c922029cd2a0ef553fad2a19f8b6bf3557ec90f2d0f330b7890451a2bac60b92d32af4281590205424142450101b26a1da3802571460b131c770aa3db37d5276d65985515cd657f9daa07937f7e18acdfa4f6d26568bebcafaa1e6e0c34b49740f06d56ec7166ba29c1579c6c8da246a4636f84e40bbf2bb609839a980e1509adff908c1519b248e9e2f4a2a62042cee8019d2381f07ccdbe4f738adbda7150a0260e8a86d28ee8b359e6df5e59b43cc3c8a92c86348803caa0ed649558e9fe6aa2b3d9407c3970942a3724b2d119ccc89f080642414245b501037501000095b6221000000000346486836bb8f1fcad570a48838089a99c9906b4a4fffc28e672106e71ffa84d787986727d384d31b959560877b86197bae3e9e263246782125670724218960c02aa51e5070612787070a990ecaf76296f4ba8ae9f2d98363f29f645aeb7cd03054241424501018233c833dcfa0fca134ea5185b19e2c5e6eb5750d823a061e9166a7f10bb514c866df8222296e9a4ee7d33b1f641ed0c05779444ad86e794db4a4adb9f4e46883578b7e2942b0994f182ed32603ef68ecc022c8a4cb6bd6916e7600ca78644d146cee801e82373f1bb4608f2e69d0c916a8706ec0bf7c842fbe3f667f656e40ba77a6cc7a76a8ca4d7781f4cfd02069cc1ed0279ed3cfdfa544b43a5f1e0157bc0f07f75080642414245b50103de01000096b6221000000000ec621c741780cda81dc4a0f4980d706f8a29e23df7385ba45fb4ea3d24417d431610dcd69c7556db32262b000906b80b13bcdf05c93dbb619bd57a57143afb0c13ef384e58d024a1011236a61619ada22c56d6cf8abe50727f3ba3fb9cdd6f0c054241424501017e69205650a92898df29c1cc4f4f10aa8a21b690db821b444e30dce50622546c465bda92e0d674966b8b07fd53c32f9b9eee3e29d7a0be9d90afda3648714c86cc0ab69531a47d8a4b08c2008d648059453f1fb619d11b40659ef4e46b3c81774acee80163868b9cb7432bc33130fd8f780f8df2865209b041c18561e52a131c49184751fc0040c3be3d2a170960e0b3fb560f60129ea9d37315f62ce773b72133c7f94f080642414245b501035800000097b6221000000000ea405bab80da3f2db30ee4aaefead30274637feda95b6db538db526172bfad471b14c00f592b6a5e676501d6e45e0ec7ca368246d9561f3eee359f7c58c0ec02aa4662ebbff61f62cca829f698467a0486f4fbe7af1aea076b7588318fffc00b0542414245010140bdd440df89a04d577201e5f377202e61a8d820f68812941bf60fb200f8b0449ee02df0b6ec537dd7d898baa13be42b46c9e67f8f95756dd55327a3f2928784d6580d52c759a8e0a2b7bd199390b863525b477407fc217beac2cbc5eff716884ecee8010e8adc2d10c66a7d99b62ca21ee1ef5f69931a8622d1a3eb13b463238575f0c7477e9533d34445e754bf35e3244da309a587f31723cf69d75fb9cf3504dfd86d080642414245b501014f02000098b62210000000003e65a3d1fd7dcb16a6a6e709b3c375758597b5ce04088c867e6c5af1e7ef1f1d329add8a81a4bdcfa8bd4c5a0a5a5ae67424ff4d6e3942aa3f8ad9defd48350c166111ebd43c9b56f3ca84c2249bcbd98139b1793536db0c4d559065a1b3fe0705424142450101581fcddf1a37857f425753321a7fb4d23bc45eac02141787454348ab36a7d82cd0b93fcb22e403ffbc1269f5abcb8f2163c8c735de82921e109f806432a1b68ca3349703d89b36c046e175f0431924c957a1d77eb59f8c7200c1f267a024961d52cee8013570c98dcc624f0a6e981726c6a76b32e2c44d6ae46d7dfbe18e1d945c41c1e102d9e9b18cef4dbb5a95fdf6498e94d1a5e84ecdbf2afc00f3e62a5d46edfc48080642414245b501039700000099b622100000000044f23f26a890000fe600aa6cea9fa77d84432c1c33b7463ed14e374de8fa511882af2048e23ea92c21a82ed02ae4589115e2e8afeb52edfa9f7f9c90dc72e00e97ea69fd9b88030cef6a3f0d129e93ca21838139eff4bb86966b66b6fb25c20005424142450101da1b64a58373f487459c71919698c73e762fd956ca1ccd77640d46e71d0773183b09e481263fa54d3f981adb87c5d88c60249a653f257b298d358c3d7ccc9e8d127ac1abb86b214beb4c7a610a5e44421865364f1440ddce566cf5f0ddbcf8d056cee801612288bfb438d23a5b394a9fd60588b14c45c05650dfd3585e172024212166e668dc90802e87e363eb5099b71b4e431af4849ebf724c441d43f5ee810352fa8c080642414245b501032f0100009ab622100000000066f131a98fb0ed60f49d2a9b1adb86cc14dcb3442335fabf1fc2e9f374674640284166d2b16133e743406984f0aea88a57cf647f923bccaa1517b50b3dbfe402badeaea2bf56174a1837f1cd073cf2dbab276d72343db9aa664f1c19e3aafb0e05424142450101f60ae2272991ca3d832e7336658c68b52c5fc3e82f7f982d4614feb41147715b1c67764aa3389d4a031b705653055a7ef13aef263e4f165242710577da411a8016150a4d33bcab8ca39e051cb950c22e6e02f090202ce441c25b3a31cd8a0f545acee801ca6bcc35704fe7f1db5a07f1abc5bf5f8367dd560c2b21866089554f419ebd28a3993fefaed62a4dc9dfdf71e4f5ddae2a11b312c03fe1bfb0ad5a35b1b3d11e080642414245b50103290300009bb622100000000044ece78d00b29cf88fe731490a5ed4d5800a9fc10e39acd98ef220f6175d1d0c93c196ae89cd379d9ac0546d96ddcdad2edc6c321749da216908fca5521f950220087abded0c172833bb3166e332f938f749d0e49e83585b4d5c1246a4af2f030542414245010150083d8ca9682a8f1fef0c98ae89baf3c3740e8abe465c1b147bb2a43053d67ed5985742860f96d1e6cd68f8b084d95d4efd3daf589f8777684ca56523f7fb85652d3635c1bb25d552927b9c61f60a40517f7f5e08d7f339fdb1420c3114da625ecee801d6b2958813d08ecf27a5ba18090a800ca356bdc07b1a6afb709df74df69aa789771f41a06dde387f0755bc473e967c9c565ec1086641931520bbe73e54456057080642414245b50103450300009cb622100000000054041ea7600f6d6857b1082e62c7c6e1fcc676f1d46cd4da16ce49f7618e7476b4b3e72b3cc4d79edd53aaeba293ff635ccafe94057dca64cba89a7c85f91b0ae9db3cbd01f7d5c44af1e71fd17aafae61c4116f90e580894cda3198c2aba40305424142450101c496246123ff32bde81f70bbb8cf3b1354f6102783832d713e9c01ab6ffd295be352baec033b8409384c30f34a75e0e5f46fc081055e651fd178e74bac721c8d050aed5284bfd1e5e9fe719eb2c9c50e954dab2bdd37ad04c69da8d76252a7c962cee8013899ce1f6404ecb8039baca0341389b38b39256884a76618a7c94a5eb8ab8ec0ed7fa5c56091f14a3e528f0e988a875ddb41b5e7b2bddac402380c9b15f39a88080642414245b501016a0100009db6221000000000c0284246c6962d28b8c2d3953f3a2337825200d3c591180d5c157ffe35880f55a9e6a4762ec63fdd13aaadd338a980f589fac82a8a9cee889757084c6ecf7f05ae3ea2d4a21f75cafe28bd03c916d0514b0a3cff0e492dc59808a2568c61960b05424142450101c2891c28f7de7d610b603c5eb634a9047b77f17c77037adf0a216760a6a3147f7c4b86f590a397abe0d509555596b0e6e077ed562ad7048769335ca3b2bb1280016362f35ba17ed945318281baeacc51f3a572779a7042a6adbe48ae9f4f71e366cee801f40ede3b48fe4d0597e7d5577a0f649ba76b2ae515b70363020af6348ec91562b3bdf2b7f6952886cff0675be27f6cb238437a1b8ea4d4f8a51944a926566055080642414245b50101580100009eb6221000000000526aeedf0ec5c4726ab0b36fa14903122e5e3659770d4ed6db1b7c10dbe42f2e58c1c1907e4b81e5d04ba35ca83e6a2442e0ef47957081b3f99d9721527b9f0fe829662996149c7712f5f2dc2d0b6d03d169d6610436ca676b6092f5deb79e0e05424142450101940f6acd3c15c2bb0fd526eca31d420805ffa4bb6126649a9dfb4967ffbc004619144ba8526f17990c8ee33b9bb78733d473304d34e1a9f34a0cd7eaf0dc508345dad40f560df61e3cf347bee3282feda922b7028ff6dee9f56b23149c0953556acee8013d15daf414cf5d850eadb2be515c1aad3a143b2eedb66cc2da880ca627af3bd52a0ec43aa96e199a80ea45ce3b9d0340bde003a65558741395595137be47e6e8080642414245b50103ba0200009fb622100000000062812f39f1fb8cca069129a54d141540b6f80d20cf580df9ab15bbff7680c74596d9cebd2a4c94614f4684dd7535b10458b7d3cbee21cb7e7b9a7a7956e26a0328d1aff0fd727ca914fe39b27d39b3a7460e14969077e658e024f64fed805e0d054241424501012accca8684b013b2bfa82c4da2a8648b28b21e2d8093aa37f00e0f274abe65322c8dc931aa67ebac7eb87b2b8a9de8bb086c38ea6360172a3ab83f6a985c4f853d9f5776fe2aaeac699e397fc873e1e614a18bb9e1c123be482df0c1e1f7c6006ecee801ca7f23c1530c68d0e2b7c23cf4baad02bf50dd2f3aada385c4a0a70a96b60ca970805add7e7c0e696882d908e901d9a83b2fe43ed8538c235538d607e180a3f5080642414245b50103db000000a0b622100000000022880dae1b218f7ddd7b42fa8ced00170da469f3960057833cb46e929bebca0197739845ba2f79f0efc243b280ec357459edf6f50d047463b4e291ac14bad30db1a7d26c2515753b7b85c561ca49dbe60db31aca45cbc6bf1be77d8de672d203054241424501012ac3eab75359a5077e75fe216cb89cbfdef8b7b5b048788eef255b25dd2aab56eb049a151187a7ad44aee51e83eae596c3699201737b718411528d874ea7278f3f151956f0cac0464e203e855aa3a5d5ccead64846950ae662d04c6b8657114c72cee801221e88a1fd03e8906d90f89c2cdc153dae9f2ca58ccfbfee7c3479c123b71cc4884fe86166e5f71ccc186870c04ccdfde0c1dc1bf5e6b52374b76826663dd194080642414245b5010398010000a1b622100000000052f60dc2a1a20b332456851b639c0bcd7c71288194d64a09a43e25ca388628133cf24b226fa87e7b49e8421dcf91ca4701b3d0119cefa8073f7d562b3b738c00ae2b1165f8cb43678971973cc53fa2488f67c30204a18c6d6f69de1c8bc2e10f0542414245010112ec3c679fd02da906ba0c43051be24e5e3bb00a8030cd680ad5e79420014a1f3ec32036d084c2c6bbf6716eb2fafe62a39176fa510f532ff5007d10471f9181e5794b1ad88d7ddd0185efa7a91bc50af4676bc18d8b4fbcf5109c65d30febb676cee8016d9452b46dbcaa44ec7db30dd4eeec8000433d191f2d7d53afd33f2666a4da259a4ee2d494a64c56b7124f17ad454f74772e5ff5aa21c82a5546b00a80ab1cbb080642414245b50103d9010000a2b62210000000008675d4ab1780c068d67c3464d2c624664dabd29aece5b210cf91797da02ac320a038bcc30643d808b002c885e37876765162808b4a82be9bf5150d53a0f1fb0caae9aedf4ebb7ddc2a6962d80551bc160a70c02be17ad308730eb8434306180a0542414245010198912c36e3ca5aaaebc550960a9b8d6f0983ab24196b303ca299359d637f40263261647c21bf63c2ca5fad34d6b2af0d88427debbc3c203611be513b96bca087b09dc638f487bde6f5bfdb571d0868199c980fac4575cfc40fad2eef728966537acee801bbf7a89769ba3ca99d81b312866133e6b7ca9e0a3a596b449cf0015d9ea1c086562c5e70900cf636671a8073b43bf7fa0a50e75e9ce11665e84c2b6958e7a31e080642414245b50103b1000000a3b62210000000000e5d531813030c51cb40f1fa47a7c2df76dd15167bf94dd52b6283c4c12f483a073c5f245041d619cb2f012870a08a97ef7f3cb2e0c5372cc12cadbfccdc5d05622f1c7878e2a875f07e3e85f50641ee48ab88a5d117fe0d265bded6cf269c020542414245010166a6a072de7c63d743f0986da030e449d0231f825a425f2e5cd4cd35b3382c13a1762968824790307cfdcc6f9942e1c3d1e9209a6fca0bd7a3024f6cd097b2852b9346a107410929f62787605f9a89f60be7a918e0918ea9bb99f74bd7c6cba27ecee801f3acb10e00f638b4a333ec99534dac3e78e4aeb110c8351d0f1ebb909982a0d26577fd1b301113e341a8503c71e012dabe9e63202c90948d1985a0a77e75187e080642414245b501038a000000a4b62210000000007871dc3f9c76b38a81ce180ebd8712930b0df31eec32eb1d3a15fe2c7aad2267719365838807ccd6ff98e19ddb9419baae0cd435bccadcef1c4f23c2b29289060d1bcc3e6dda1199a43d0eff9b9b4e48b1e906b6fa039a99e5fcfbf0d795e10105424142450101849b935eaa18d43ddca099396865964345a4c502d89c68da2dbd3cb474a0af588e6f376f4be2df9cfa1892119fb40cdaa403a0c7055e2815579c906b102962829620450e146f9b5e03405e58c1bf39c22972f65f82643ae124310908f24e8a0d82cee8011aa557611c7245c828e9f7397327343dbc8feab9d94a1d00122d185fc8f2056df14905be3396fa3ab5a3d884a5a4f8c3c1d8c5943678f2730ded454bbc3fc2cd080642414245b5010341030000a5b622100000000082c9589e202ea8ef8a4d5cc92b2d5b2dee40a1a7e1cbe15f3543074c820e7624af40d0f46ab25c40d87dab3c82fe7560cc2c67344a7d5974d6dbb453d8b78505212b17d39b12193017bbd5dd2af490522e3094e6d5ef136a4350dd90c9fcbb0105424142450101121be51fd3edfd47e2cc45994ac9ca8fc45cb94471a481cf45481e6c0c35a30a271653cac198ee95905b97c8f7476662ab67c88a558bfb1ab288a751a819758af7ad41930cbfa9d4a65df0ab9735eb9dafb39922ca713af055fdb3a05b5882dd86cee80191f57215574138fa371e7e8465a2724a84af11a616d2ae4fa11fa1b45a2e8be6dee1cde0483318e9725888cf6aa5f0ea0e4251820aa0fa15aa2f53106a5dc4a3080642414245b50103ac020000a6b62210000000006054048c70585c5bd45f31db6d611cf072a9a4995c8de9f5d81fb0b0d43aa15dbf262e923ad00b2c5f0a2a896c0e97bf98a797f5e7d6f4c39a9c8c65989a590979a6702af9f59751fc956dc09c5a6263080d9ff205a65fd809451e1d2d1cdf050542414245010116bb81a3fa7013c0c934cd8293eff06c25fe9917c514d5acae5ef3ea630d2911bd211fc7372d78616faf60973dd2e40042979e6975dfb2551129403686c64687cbbffa9b90692ae07390690a2b1ce85912cd469eeef9e3813073851644396b648acee8015feec4a0d74de888411f69b4b85497b37449e2f6a6914dfb0823211b0c08278c415aefbbf537a54ee08eb8f2186c15d005c3f62272759ef7bffeaeab683bb3f6080642414245b50103a7000000a7b622100000000042211719e38f7ba286c868694cd2c60a75025f6a41db255c56988fbfd93cda60337fff79cb65894a67ac9989bbc38c3624aec01ec65efe3a5209b068d18dce0796311f8d43b80b123eef00bf91fb7b1b03ff5248cabfc058db0e7ee45c23670a05424142450101e4089264fd46cf9b7ca9cc8ce74e8bc0d5321bb1e50856aa2e4b8c22bf3f92272cca017c4fbaaee9c22debb3224f663e5f9e7533674efc2d607e680c0e8cce803c1d8ecd5f7a2912d01d0b55e99a89f75ca4a5133c477e711df2d207fb17209f8ecee801b62eba72d9d2d0fe938c95caa863c81525fa295bfda235e99ebc69c55dd489e8d713f3878932ada79a612e7131a38072459e3fcf73ca3f0ef05110eaedeccffd080642414245b501012e020000a8b62210000000004c30659c40095722613c365d2477af999348aa03098af23f03495fe1466550593fd22a149c7b3c3d536b64c4219e89c9491a2a52371c73db4a6bdc70ab7ebe006111427fca17ef26f6ab0a7eba4f8ce7518659f13c9054100c67b5249fc6070c05424142450101b40d42a8d541833962add97c113ba2ec0106b37b8f30aeff5eff48e14577993b2c7e56c3aae05229babb59056b1959eabfd805bb0827cb5826835f6088abe98e12058eb4240e919198c9d09e1dd65fdd3ff4602aefcfc12bd5e137fc02488a7d92cee8016ac4e77ea25569f441a5d4c732c10b0e792ca548b7592a5cbf70605c0fa47ca70189da461886f8f4e32dd5ac3ff08dc0a19656e03ada1d704901a455fe6a11e6080642414245b5010124000000a9b622100000000000dfa74dc24bdd87be267e38ac5e157bd61b225e6d292290d0a0183ab2fd48082ba5d14aaf2c37d0c218580fe3d696261a88df7e4f7e795914553bd478c324037b1a243ae9f81977ca4bb8f21045b319536ab7cff53c6e187950e68e0d295a0e054241424501012c605621b2e9cf41012c1e2e7aa029c90e220713eddd8eaa1961e5083429ed2ed10a907086e29f8d24e91c47fb8f5a48a39cb4dced5b1169ef59b6e4772d878809c132937724b4633966ca3f336f74a10a91d0e23e8dc4b0d3df835ffbf7490696cee801855750e478ca3386a828952f0c0f697a59dc4e19002449bdbde3290b38ed3a13d98265d82b1227e7f33d4c0fd0fe1a2c74109432a04825fe782ca6c25cf30975080642414245b501019e010000aab6221000000000a402b19432ff3556a67bb7b5c2fb43acf10ce5af044ee42ae746e520ccfc0d67f0cdf0a546a77c259f7dde22be83f9bcf6c45902f644c0499657075ee5bdac0a9ab5deeb10a4913333f304999766e00ad0d526a460d95314d69a0ba4712f4c0005424142450101ce1669acdaf5118385791542ec254c1df6607502a85ffd880aa109a76a133d2a3339f5672176f6ebeda1096b85abafb2774edec2bffcdf76db3eb9b111dbc48d4ee870b7d960b262ab1998e8883c15451b688f013eb526faf71b779ac351fae49acee801313509ce232ad639a723ccfda5de655a4aff9665f36e10ad47556ec1568899b301b6585a7c216245cabb2eb134cf012ffe154e4d8c419c8623f48dfa21368436080642414245b50101fa010000abb622100000000094d586f57405189d0efb387a16f567fc6fd2cb1b6770b0640a7975eea3e75d119dffdb35b35fb335618d1b47c6219035dc92f2f8409ca152c8e4bd0b29efa101eec37ed683b396159d141d8524654aa0007da5fec8f133cfa960fe0de44f2c0c05424142450101ae9dc9691fca36fc0bbde950d9cb82a3c1890bb99e9e55c24017fa5946f0ec2bbb2b8bb226108964c0cfe886c1b230387975ae49ac8158ffd4b8355bb627998b8d6498832817c9470fea99ebf10ae95ed8bec92ce8492f0d3c5bdb7db9484f3f9ecee801613b72960d696a9211c662f6e8a2c72c0827a276fcb2c924169ad620b858e5e4c2dd8d3dcf8daa7114d8bb25c9c0b13b829d7c02a1754c776c24d74ad169ed23080642414245b501039c010000acb6221000000000d215f07360f23a747fbabe08ac9267469d9a0a0a3927598b3c034c1d1c6dfc4f24e8e73fef5134c4f39edee1258bfbf8d2ad0e9b86b997fe33defea575e6ff0524478d6ed3fab5f2c0629ce4373a6751d1187e4a3c92dfbc63096750b268ed07054241424501017abf5bad591894ec84c8f39c49cf9ec95ef660e7f066027ad95de4424103f54c8bea61d52bf5afd057b70912bfa087a17809867c0704f9bb205d67b18d369f89c1a5fbd9f98c088dc5ecfbcedea2bd5e9d1efb1a2fa0835a6ff70a6f678d5b07a2cee80151b80a2550d3e86c818628ec12470177683daa17092d348c53d00c6c928633100df972561acd69442686ae0dc66c277950b9002f5216c18faaf0ac10f9fa2820080642414245b50101a0020000adb622100000000002174faedcd2ae58b8f6cf83a9d4f378c6220738cb1fd2c4f07e6dfeab0dac3bf8c715b01d9069fe51ed7b76f41534549a3cd966af96cb2650902d68aff7ec0eb054f965f147ae1c2f1984889d37be19fbae468f622866a2986896c80e4fc4030542414245010168bbe0d7129ef53f06e121115676f61652574a2ce4b79c821d2740eeae9339252024c9223d31478eba18036b003276cf607a7953ca1ce715386123e10b03778b042a31be6513a4fca9ec2065ef38c355389b6d2d8c18a180e5fa06757ebeb65aa6cee801ff5f3ea47b88d6ceadd6f2308147412a19ea5f40ac591139150600afa51abb439e0b838f414be9abb986a4ad2f2d2b1bff6f5c6a38fa631bdb50917ab65f6fe4080642414245b50103d4020000aeb6221000000000261f941a4418f12c4ef92163779844d2f3881b923f124a176340d4f274714a2f528f0bc9146a012132fa3c466ddc19dbd3d4d920c88a027b3ae24651f8a2d300712e8f7e8cd35018ca0637a8930d33dece21bb6f41c2bd7c42a6e40a6f1f780505424142450101e0cff8105b953e8641af439154aff6896a8534f1a8bb95d4579441fa65d8813735ff82115ba74d0a2017aac8ec2d3d8b69ae8865ea4ed4e665c9d85370888e89aba47350d65d767866a83eb5f71271b6ec1c74a6a23d96b55439cff352aeb44daacee801a8fda9028db82c18f3fd426f81926d12721524b00bb5464086de8389b7604f61bf5184da3bcd29bf04dc37e1d32d80dcc7a3c135357968ae4f90a56f31de2533080642414245b501030d010000afb62210000000008694e21e4e41930759674202886cf1686e3f2ba5c224afda0461fb4b4e447a50c2c14f677d9879f2a7d3311e0f6d05845461b7c7d21530743bc52479bdc3290a1e3a0d61359fcfeff494aea840b7166761f7be7d9f25f01571b408bdbe46230005424142450101fe75a955cb064c7d9acaf2bddeb6d60482baa99c146447ef9e0a9a7c1a2259358d62b0e3f6b7e2b58f2c4b157b98b57fb4ab23e4c841882e838a89a46fee7782269c905ddc10e760b46c7a84f0ef609d90ef6b006b035fa4d6e449e29a866562aecee801ca5ff5193703a1eb4bbc56e9d44af3be58f451333568e31ecee0d82e31828490bb0c548f0c14aa14f24e806647f6a7b662e0e2c8d14ad3086dd66ab6441f5e61080642414245b5010382020000b0b62210000000003cbc334e5678e39f218f9f3c603b0f0b1e959cf71b15f285ab91ac9a365248581757bb7ff6bb33720be3eedfac87c6f4d8f60588ab678fd4b91e6c0d50d2200f7541111038aa8015e7a61732d66d1ce5bb39a89ca8fc0ffe5c62f126c1892f07054241424501014cc0caf44959ee6fcad702a6cc83330d730d41ab0066cd796f2645cec6616b0d1072f0457b2c14d22a9d71d7492cdceed7a7bf02b231db0439b930181a1e0886e83187d7c990767e8062979da23f9292da5c00e4d5debaf455e3e83d5479b92cb2cee80191c20a474a908e1e459c5cfdca1c7422600f8e7034811a98cc59945096886b17b3cd44029dfb5c2f868b5e4b796afd4369bf5374eca208535e24cb147127054b080642414245b501033c020000b1b6221000000000fea6c0617fd14c5039fbaef7dd48a02c0a2aa9b32855ce2ef410405916aa3176d64d723dd8a6248f6e735914112291ccc3abc3294602d902a1f5c2d98775000d4dfc27d30b60ec3223fef6e9d03ea31b88ca19accef6bf703b6d7d4470cbb90605424142450101f2459c2db3bb4a589aa36456094923f70e3d152e5264d7df46bb0674fb44f81aad43c9f29a033d6e240506c9140cd4382ce14c8c44de6493babff02da378c98f63c3d845bec0ee036124d64e7597fb04c475f414c376d1c3d34221a47fc52f54b6cee801434b99b6510b683311876a59836f420f75dc59482ad85469de79ab410b116c872f4145d77bc3980390c01515ee26dafd6aedf7f08bd4802b05a2fb4aba11e836080642414245b5010124020000b2b6221000000000b873e0b7edd4c6753296b969077983add9302e2c00fd572b09ffeb6da477e4560b5cb3b96af984a1790940db2c02ea2befeba13fcb62d4f477049e1a29c61509f17a178d3ae7bbfdccdaa96f877964d0d014530cde9b5cac864d013eee79b40d054241424501016ae8ed0b482604cd07f7afda97ba8ac5ba7d06df86eb3b3a9012c2f496c26302e654e15e67b7fc9b72104fa21c85ca59ded997ac0bff3e5b169e84172e2cb08cc082fef650872c270ea482f0ad871de9b0f8e335b87f8f4fa059e9e8a1f6ed49bacee8015831b1b9fa91db9eae70e15f015cb85df0e5f3b4d00bc598ca1cbdc647c65ae6ebc868d0693cfd1523b2f39b8340b38fc819ca453a9c79c76818bed47c4311d2080642414245b50101d3010000b3b62210000000006af49bd94d9fae8edd0fe46583620cf13ea9952d8a9c65e244c9d739bfda0928a2403a4d133f8982b4c982be873137e2784b4c6c0c458a29dbe95759ad9d36084be56368b500e2d530c47642f696e380662585ae8fd71f4ecda6325713bb850d05424142450101a8dc7dde60d9ea3502569eec35740fb596d6179b4666c66fa24858fc3b54ce3ba194c83699ea3d61bf94162ad31e56d43bd4cb8946e03055a9470d70d7d69f845e16c5cdde601383414e760bfb9935396373c00d419d2172d1299fc1be8f21eebecee8011e416ebd89fa862d9d99041a5cdf8eda44d1425e09c8d3693e2a7d3b774de01810a4b6262e7cbe558a503a769048dd22fa1e7c3d823fa4812589819c38ad8025080642414245b501012f000000b4b62210000000009a4f3f178bb8bcb598864b0a76f4b65e5b4d960a38c92ece2175e0b4bfc8ee0b049eab53343a412eb8e6f1fcaf637887bdf62f9c78abed117168376e625cfb0deffcaae3560e71802f3d481cf83aa258f6366555db39be0453fccddced86ad0a054241424501017ed85edd6aec5317702b56fc83de0ff07fa31440e04c815222a877b217f690310a37dd22397991716a4c20a09938cde192efa86f4297b6602263a3cfc947dc8076de0f383b7dc2490e43e2643e75f9457895f653100327a42721278261353521c2cee8011514f543db5e2bd324563783107189ad965054b21dacf676dac818410b12a46a8a5962ee54f5541a1ac0285d94c4d5c0b4063549fe02632a696ad9d5731b4f6d080642414245b5010311030000b5b62210000000004ccb88741f356b497dd7d8bab9cad5da74cb906192c6e1b4a827c5b8fb996748e9727f61a28e08ef8fcd427c925734da53c50f39f4675a00a53074f90c71d802b4f9086e36260646b53c5ace5246d19873d631af61685666e691fe59a1c0de0c054241424501016298414013d836b998d1084e4a9548de2958abff7e9749cace211fee31b01c640fc926ed70017a40da408d4dc3f8ce56fbe1ede81db7d6880af948fec440778682c412b67f202a32130709bbd96f52e166ec63f8bd5f72a8260f3601d9dac4f0c6cee8014cb648cf792221c5ad3296ca86f865435c2caf40a0a9c768e6d939f072d772e89d45b506cdb93792d5ec5b5a137418e3e9978c6044a796f2506fb737740e5b7f080642414245b5010128020000b6b6221000000000aa8ccf6dd002ddf901ca55bd3efac9ade1ae64bb4994c6b5792ef87abb852b6e9a17ea85f005cec1886e844594278c49c541469a45e08d42b10bc883c68ea305dec44e863a6c58cc8697b885d6d679082edb92a6d21daf53bd58a470495ee80405424142450101e8cdd6f5f09d19cc57be0256ccb695eb58a6cea9f8e70cc78fa2ff9a931d783f408d9b02359ea26f8d254a7dcdc757d219ad6850fe49b524e20ee5e0fab90c81a0dee35f50e06f4605401b86afdbac7ec0e3803d436b7f974430bae84ee6776ecacee801c1017c0f21bc84ebdc69e69dc27f07b83e3152d9412cbab62f8055d9961466ebc0e1c002b383fdce33a3df0bf8d22bbd25ba383f7d95299680abe223f0a546b8080642414245b5010340010000b7b62210000000001ad2cbdf3aa55107485757910b7d17976ec478a27d80a38aa116198a7e002d184b9dd42f20124ada4e0167afb6b3c8e935b4504227457de44b725af8cf03f80612bb06952e33d60aef327566bf18e39b9f6c05dab6756759ae24ca1edb58b50505424142450101e45a5d2a051c41829fdf14ce34a34026ef88fc909e7d8eb881922c53bdb8f4615b4ee6edfb018730e39f717923de4d45d883f1d68922f4308ee779172cf5cc898b257ee9d1730f053def5a187be8e3181b0106ca0abb2d68be605dabbaed71e1cecee8013b6bae67e282fd0314a1f6ed6b282f6f877a300f42179810ed5a5871eefc6b770c2737168b13014715c3f7f0ca101f9786328bd85e79989209e236431428feee080642414245b50103f8010000b8b6221000000000f8c3068ae01c0d908b45c41be988729a0e41f3123b8992cfaaa0a21348d0d456ca820650122587298d2d28f44567776d103a989ca426568bebe8566bffbc77053a7a25ed02450b4b191a6831117e7b16b9bd8eb364078a2f845b737fc196da010542414245010108d7280c85b64d8c881077a0a49686e54faccfcda766a6a2e1896bd3b21c8703bfafd647d5d07fb4419c8e385120faa123316882cd014ea6503be8127912778526df5b9312cdf4d0bda41f9cbd50442ac87d3e2d54d70c4687b38f560e8aeb0dd2cee80152a27dc8cedb2f0ad5c6fcf4d0778758a319f43733907ec1b993302223f89ea7432938aee6167cbfaad7e64140b7c058d58ae836be77a797c7e4293d658dfac3080642414245b501030e010000b9b6221000000000b22b0839b5161d902c254c61ecd123f957d9a59d3a6717ee394f686be51b1506b8ee4602ff670e810d3561f30c8b4d67e321e99f06a6d783e15c7a31ad0c490106c3ceba1b1df7a450c9c171a16b0213a04cec93b4355916be2e511557e66a0605424142450101fc1b3452499fc3eb1be9e7bc62c0d0b9248f7db69b1cb3c82d04342087267231e7ecc6125c10bc5d19ee71b410a5f240a8657fa468397d7d83078991b1d4438c391059ed40243b25fc7708b50f061ea6ac650b6fa9453ebb2b99f6aae7fc86b7d6cee801a54e28a853644a1b782b9e6cae569c42e701259fa3985db5e63998de30af830a7ac92eded92320c5c825e2dd262473d811629297c55c1a36b64b5166778d54f6080642414245b50103ff020000bab622100000000022b46e032430a4de50398537c595584cebda5da8a443953d032e2068d19bbc6376f9e992c20115c93cca17067f8b8f3fde6e2e60d1e557b4a4de472668726c0f61841c89daad616527eeed67dfb73806aa5a4472b81c9f1a299862e574a4130c05424142450101b039ab56059a620edf35036cb837f12f06fdc67bbcaabcbaef32c2d4febe1f1ff98579674b2105253d293bed08a1be8e786c59b028cfcacc51e8b9f891996583fde44036139b8216c5bc2f3ae137392c8b6735fccd2b7309947bff69528dd677dacee8010e0ae592ed312212c3284b69ed5b3d94dc60a5c105c4e8aefd25e7efcb1b262a420ca7987aa36db0c417897aad88fb88cdba3a4ea8b9b629dedab8177bfe7a8e080642414245b5010345000000bbb622100000000030a63cefd6ea682e5167d55a8c17b676bc01451e5bc8cc844252e132363a3a0122b8cc2ab0d14339a940c7eb54314855d104d07abddfb4bf4b5745dee35abe087f02cf210373c5960a8f13ed7e6b81be282aadaa74c858abd67478d4f33c820405424142450101884e98bf3c9f039a14dc6f0bd4837e845ee7ea4aff6490c9d776652d53f50f529a83eff7ea9de27e34e8af616468f0fdd51c6800aacfa515c6ec481dcd606b81db295d9e9e30915708f54e1fb0f861aea3a523eb8ed921a0e5d8911d28aa14f4decee801914d63e43bd810bb3dfdf67594923612f3a53f613f6a56aed089c85a1726a5f3e42bf6e132000275a6cc081d69f5d50b93c1021751c597aca235eafd4b1fe045080642414245b5010389000000bcb62210000000001e3dcf81c1c82d02521ed35af8e0d4681917bbe7332060e7fbbfe4004848250f3e46760eff8cf3bbfc730091a3cc7c47116b106bbd8848bda7d1383519f4690361a48eeb658b367185eb22efa972bfe88bacacec708ef4feadc89a9e3c066306054241424501011cb96face0597d322da118f923fc56cdb8de4087b0b1dd6f37d12829a7b751657ccd1ab12fd3305de739017a4ae3b626698b02ca710fdcbad057938b6912ed811f56c81980d86faca889e513041a3870fa0492361c54a781de7ee428c082da6be2cee801c18467cb8a9fb3cc718478de8776681ddd7e27eea1ac7ef72b0cc9b23b843f901a7f975a7a1c2f895a2bb02a6983406941e5ceca78b11ad4bac82ca439f1ee86080642414245b5010371000000bdb622100000000090c5c9e708ee7fffd0eecfacb9621be73a4f5148a3e4d0361a9944981f811d744a884e2d5c7160cfed340a88ea139f141269a809e28059276c00d6152921cf03a210eaf968fd8ce24ac5ea094e2b9ffd29e30347bbbec48172a9e2802808940105424142450101f2469021d9c178805b9fec3e82d8e8f6f33c75eeb272d5635c990870cd010513bc6606e0870f803e7002de4b26f9b220449a5519f9de8751afeb38fbb7635d83a37f4e24ac029b257712412c4ba9a7831470f6db41dcc0ae883015f43258c76ae6cee80179651dc46602c7b8ab8398848a60b00514d82d7eba8585a82e4abacb963e14325d34b34f003278781287d8ef666f452093121f25b7cd84f688870980468ac6ea080642414245b5010300000000beb622100000000040a18aba1920f188d53fed3fd07c73bd845881a3a1c9e08052cd3c0fbe08ec00b28d05506569af0ea213d76ec8c22c3b131aba59b230740f2af8f37b80876a08c8a3434b1de09896a43c9c499f6142b7dac4eec0d8acdf8ae19f9b3b461694040542414245010160333aedd493a0a7c6b886356ec0aaccf20afb36f73861e61c107858531fa2308f5c99b761d8e30bf5212a7ec553243057d2f77869bd06dca15c31e875c1408b621982478222c2e92b22ba85d7a577f170df85f1176b476bcb4da91b3618c788eacee801f7a94b7b8b07fcf90b78f48b914e449f82972c39f89e1a0ce64a9dd052e9a71e5e95cbaeaeda5f53d59e202910bff78f9ea36e54fc83a4ce92ff467199dea630080642414245b501017a030000bfb622100000000038c895a1e7ea9de7b464ca2c6fb03c673f4c8b640d941e84972d594e00d24e2aa19980cd66fdc338034497a15b32061783ab982259553c494e35e91cc91ffb06c820354aa57f9d3c8d0b8791af342de21af4c9acbd003304464105e81064510a054241424501019464b100892f0aeb5650d31980164734067fc2f6350125a7753c228f868c541e5e5c6c6f25f46b536dc505f28ef07802f8996d774c1cc22afc36c3a096311785b3cb2b82c9f43a5938e303e605769ebf370cb7e79ed7b015dd8dfc8c9478500deecee8018a237c36341f19ed2a7b6eea47e8a297fcd06104e7b656a43107281b42fa23a4c5c28a3c3f50e854d1f27d05c5394971a7444a2c36c2ab6d82107ed1d5598917080642414245b501037e030000c0b622100000000062bc63a6bad5aedc9cf72ceae9c1088a21aa25bc47e820ed4142664028169831bf191fffd67223fa7d519d3a0fcd61e30ebf6f6c204a57227e20092208b32900c35c6d275ece02f3492f1c164c408541686db96b53e9530e85a7c97bf889e70505424142450101d4458024506e2a9b015f49124a65edb56b4c3156737d892e41dbc11df85bc006cec59b9726dee6b96465ed6eaf8093562e6b84bedb7b7b58d958cd8f09cea9885063465eb919628d36ff380d6543d860352ab2f6d62c03834b3b1c94a46ce5ddf2cee801581abc763108de290f244e8958f170f69e4eaccd4ad163cb9a6fd202b57afff7db2f7f5baa1571884c6a6e0559527b4b8a62fc033b568dc232bde529b38a593a080642414245b50103b7000000c1b622100000000034d9e71841cea5b577eea6e2e010c7890904d9f50fab0f1ad2853a223ab1643171c40d8246bab990f1138816eb3029d51422d1d4b4cd4ae232d1883530737b08da043152c3a4d68270dec979a0c8210da8b8500de1cf1ed3dbc206472a89d30705424142450101d8aafff63036d949bd5e6c2c43a5ae78d84c76ffc11bec79a2c5a3b80edea06ad7f0a78dd1f37840a1c45ae145e4b3b992e4f352a91f9dfbc88f895379928f8db96d191e7ab95eee08b03f4cbfefdd17f89a5568f2a95810ea636c72629dc5acf6cee80146b25e5ea04b3a61611ab63e613a66877dec06f98f65ae11836ecc1bd51c5ab4f3d2399a9a3b9d98d1dfab8f8f6efff43db5be9236cb85c00d50ac9313673630080642414245b501033a030000c2b622100000000092b8743167a62e7fdaceffa940d8dc836d72e31e3eb46869ce7a47578d4c2e67005b7c098569a9fb95c565665760f19de13333698ca59ea98a265115d40d0b040135924850d239d5196317bdde4f6a74c8f8a3bdae9d054cec6a3a67309fd40205424142450101ba670e8bc51c05f012cf9ff910e8abb674e0f191a1a703ac2acce8c9351b34063ff62d5ed54f0b87ce3a8d3a511a7badbe707c41b304f9ca74f13721b642fb8bf4fd49cdd7a8ea3119f9fededd7eff5ace2604511a4c5b201b3ed2a8eff5d447facee801d6635c791e5b34d3ab50fb20a81921cfde3cde99c6f8a5216994e88fb15e3b3d36939c37edb3edb2282ad47f49cdaf2bf59c0f134dbe79105f3186af65c38e21080642414245b50103c7020000c3b62210000000009e15c78d98e266fea2b1d79be439866013f7af06c8219bcb645c616165314e7b2bc223d44616133e4d1806e293998166f4a8ed864b399ffa13b6034b8d00e50a3ab85968f9b7eb501c658c1cad45ebae4f77ea3654f3e48ea12f75f5bbb6fd010542414245010174f46ec7f9a9f37cea26215efbc8af0b8bdffebaaeba1a63170efe504ba64e646323c6f9472d51470c5e2dc655a5804835255c8e7f37614d426e19b2f509468c3d5c035f922eef978cd6d30cb8af3f9562a444b1be21964c654383a4b7326237fecee801e6fb519d95f7b43ccca63ac368db6714000df0e42250f951826cb8ea470004edacbb563d6a09c7269a7230db54c0aff0f2335a440881b6920f0c4f3c9b5a73f7080642414245b501012c000000c4b6221000000000f46792b4668283dc8b4e58bb37cbfe31fdb9c6b5a3594ae77e8d144b1d6f8023a5a08b2751a59b582b7b278c9a61e1d32dba7d47d5b036b0d15487d02dbfd40570a0b250980befcbc9fe7505bf97f870688524ef49ed3487b06c895f6259210a05424142450101f620008d82879e17d932cc24b536068ed876e5eebdb9e84439650aca41769c4aeac3213a956704cd8c69bde279f00b91a2962e639a01827e908e9734abe52f89fca968fa651bf46ac52de7698b63fb53fcb53fa10bd0bfb3157e04da7a1b045202cfe80194d073f51deb002177293c0e2d7aecb59b90ff5c7075d4eb3ec22363fffc3ca82fb671494bf837cea91206fc5c3af5c25a37456b0e178d7e898210e3aad80ac6080642414245b50101d4000000c5b62210000000007e4eed201c19f1f045c8cfcde88f97d051fce1f213c5fc874995d5283bcedb244e0653007cc2890b48729aa7fa315505add63deec632b6cc7b4e6cfafa9fd3046f3e2f3e560fa5308556bab1d87c464c0a5f6af81e15ddb6850d408c2443d70005424142450101d823f93836946f830e0c844503f4c273fb060ebe3e4bfbea74d2d0ac1bb7330b5575879fa4a3a4ba35469ff2d4cf3c476050722e6929e88dbb0292ffa6bab782c81ddb3867285e88b7a03d737b37eb51a3478f86f586c0f52db9a469fec4bf9f06cfe801fcbe966949baa994d1c17f9236f63b0a4ebf7d8eb876874720c311d0f1d16a4fda126516aa856726784d6354cba3deda6f2c0fc8cbdd6ab29fe698a479a234ec080642414245b501035f000000c6b62210000000007497d716b7a5c73d5383a113adc20a7aaba7e2c5e5fb15154cd2d3b67952623469f60523e3cab63c3e53b3be954d72dbf93a1a13637914e6a159b6e29b21d1046d212c9b47c7924a21ca40af009a1a54f72e179ccef930571ad9fa46d51aaf0705424142450101d6def9b4c95d8af83a8731f0546b8da2776f3d800f3110747bfb050813200078f2415f772d552c154d541219b83cd9b021816e4309322e400fc85629dd067a85970158c21cad0d6f1dcbdefe6c2a33b6d13c1f5ca41d520b0922eaf2ee77defe0acfe8012de52daa2169de2c8093aa03d56c3edb7bd6974a61ce7968f6ea30628beac3e47564d0b50b33fdf2082b20b18154c43a7027d77c3b6339be2af1d5d0aa61cebe080642414245b501035a000000c7b62210000000000e914de088ef129b1b5caeb10f1a00c80aa07adaed692912c64a86d3c9a3912d8bc70d5a133639bfd15127e2114f007e4c5188ec46fd71ffb0472692e2d5600e03626610b4ef1fe60b9e87fdbe7f0960494e31181678b15cef724f714a62200a05424142450101e48e2eb4d61d5bcaec7616c141c2c31a8e557865b0a7a4c7edb2abe88ec0fb251092fa020f0af3757ff97837a728ad66dc5103eacf74f5a8e83751000bad0d8fa3d496db7af8633bd65793c9d7f6586c7b401ea97c1c96418d8e3cf30d70a31a0ecfe801f38b7cc2d3ad339000567c05d000486662f3c2976138e12646a0263fbae2c8b8897fb1f14b62fb3692660a3511a1704426194f3fbc35a8631b925e64e1803ad0080642414245b501036b030000c8b622100000000022e0e75c923b793df0d52570d40914c0466bbfcc2378594a2ae6c4bca715a340a9d3bb8c1abef47b2c0f84e5cd0d661d3181081a0a6a612615402197bdcaa00dc9a9d81197cb6d6a68d070aa7e6ace24f70cb21c211155b64ed27a5d8325c50005424142450101e0bda7ed3327b4da462e12dabaeb602eee8815d176f22ea8647f168289ae212e7321cdf3c3e9c486c6ae2e8f1b0bd45592179abff4dc4f14748145a7e7677488406c659cb58ac0ad24c993462d5bceecc1efd54bf5cf7542c63ffcf0f0bd4b2412cfe801913ff1e268806d9c7d83ac65e70726bd3824d23e3334f3c1693232246df5a0ed4311fb2fa1d1f70807c4a11838d7e9c9c3838b0740565c8b1f3624abcff3c883080642414245b5010330010000c9b6221000000000d4f998870b56f57318612b7148c7a623584b970fd5409131233bb4c80b44d706159ae9777df18b86913bfdbb281dacc98a46812194deeb43117624bc504a550d80a098281dedb60140ee5bc4229f04693a25d45069f2f08fa96a43c9ff6fc00105424142450101a42290408a8a9faca6e8cbfdbf666519204bc4328a660851b85503e00bc0011733b069907871146940344f57d9eef7b8b6a47895696413d9c4d35fd892e84d869a7c26d0852bf8ae4c7462f380832705e95fe0ef241071a531acbd68d3c7285a16cfe80149c3110aac040aa7e2db64ece59aed659a7002b87570d70b269747825d009732cf51426df2429a4f52b20dc117f5c84f58a0e02daafbce23f5ea8e051b8295a4080642414245b501019a000000cab6221000000000bad8d66a1e6cff24c93124b847abf0a58258cd26d966e023388885f9e7858719901a5289696ad890bd92a8f05e2782b5bc3329b5f483dbf996efa71a05fd1803d9623a6b44aa4660bd50901f3442fba858dde7f078f7f9ee11f6203982040c0d05424142450101b63c305c2bf1151830a86eef6bb383e4e79acc31735de3f6ea4bd7bfaf3fe70d8ce5333e7ac2fcf70cfdb2d519cc55022da9df6fab1bd08dcfdbc2c300f0638e13915443a10267744649ae12721c122ceb43a667d477d98f64c3eaebcae65d571acfe80128890755d2faa41d52aa456517a1c0ccbcee2bb4bb373cdc252c36da755dd3d7aba14e31d67f64567189f154e734c49a72a27d44e53aaf00238476efbf3655c4080642414245b5010383010000cbb6221000000000e44b49609b82d4a423fd51c3ab87cea5fee696d76fb0931d5c3b3d0caf2bdc0908ecb7e9d87bad7de19ebb7ca7e9e38247a90b700f18bf83ca2ed0a465a327064d56bcb3abbd6f8ff09da7e51533e4355b1b2535d46c6ae81e66559ff792e10805424142450101b25b11de2e52f77847d9649d56375357a8d3b2b27b6498971d6864112a056c1e124238761bd84271996b9ed7532807777f377b2ae8758592d63dea675526ca8240654d05417e7796c9496a4902ecc6caa20c3627bcd77337d0d9ef7bdb927fb31ecfe801f3bb9ed619dcfd7c22ce4efe0ad092713caccad2416d8f72571c878a39843728c696a3a2d539432c8f9f553fe3c9757e54aab508b4fc9cc4d81e19827b38cb9a080642414245b50103af010000ccb6221000000000ca8f5189c427abd01e6527d4f13fdf5ca61da4cc019d4acb494632473ce8444efc5d82e523da2a745a5526b63c2fcbbcc8355949cf3ba64b745bf266c33f0104e558e3f3ddd29cadfda85bf37fddde12f934eff1596c45e12ba153f991f8930c054241424501017662ce9441c364aeeacf8f659b14dd84f084ffd19433307c053209a2a4e491668c875677a3678e265b4237b085600b4a03ac240bc62f9cc45a86d1d88fd3ab82cefbcbd41a7d181fed4f2588c48d19c94ca443cfa41e7e4c2ce9b706e88889d922cfe801d68e7607a65714ea0765ac8fb60e5914f6877378af75b0ff4ee87676bd48d40e1fd95d908ed28cb335c9046f8324270a5da971838034e63ff09312ff68c34a0f080642414245b501016c020000cdb622100000000026739c1a8229eae01a746c913ad9115180ed6b2e26cdf29ae2e9efb88a633a5c48f666ea01bf873c6c53cbf39d2a9ccd472cc1064be29f3f008501cbd702120ad9b1ace1904cc24a858f2e292221f28fa026a1d0566d24fce2210a1bab1f880d05424142450101300c296d5132a18f097191410a964ccd90cedf3428356e9de88b52ba4e92b178f50898533fb655e04f7e9959ed14888e04626d8cf00bbbfa4fdb1e7160b6ab83b4c348ed59206fae0c57a13c1fa91f112232671f0700bedbffcf1b82a0ce7b7b26cfe801ca436bbbac23f7b7212f55fbb3a6914d361ab2f1cfcc0ad3c742f4d681ff2723aa76dbe52db5fba237097e73761f640c463d820c22c25c75d2403bcaba4b180c080642414245b501033e020000ceb6221000000000f05a7e75e6c638888ca60e5852c5ab5bcdb395617e48e892b37578f5e29d6d514ca7f7d370fae748dfb3c33cba50f8fbff0c5387296d0679c1a5e273e873770d7ade1873fc9936778c4a19049a38a5fa9198a2279d3f5c7f9c413bbc0f644a0d0542414245010184dd6d173ef14942696ac8b12a49d4966768f11615ad2ed58cbaa5f032702012d1da2f023b2e57b832ec6c2ff7f4e09afbf7cd27be40a5c92281d583ad13578625b158ddcfe4144d61dff2f209f5c4f512f67dbc2bddb5c32a102b32e462de392acfe801bf4f941228832a20d8788623f80f377eb2e5f435debd5604d65f2e0d7e791f11dc296b40b76582ad7032e96342095356012d01e76b9bca9ccf6cf7a2f582a11e080642414245b5010311000000cfb6221000000000f807ca5e083acde81c9082c2f50c7b60e17c7b19d221d352ceaf529fd306ea2ee21f9649d6cd963bd52925d7f8ee617680678cdac84bb8f85a4681ca3115400479b9c6b6922bf20c953edc2825ace537d13c8dd8e5fc9185f7b98ce7418cc20d05424142450101c270923d843b0a71362532bdfe5a137eba2eb6f9d09afaf2c3258e492b9cf0241bfce510e034d4915b6d13079d7e8e7fb163661e3f14b27795406003fd82c08c503424f5cbf1b21434a6bebf61fd4ae6d2b0eae93e8266dc8673de05e7940e8f2ecfe8018d8d227868580b495a08b4c3cd30303d650eaf36f230ed69b23e810275c4a81154551025a80396aa3ba816657be0d494a3f72fcb147f091f9f2033de094c9fc9080642414245b5010331010000d0b622100000000080afa3e7a5987f498e44bfdd865ff1ae6ffa09312937ec63f62ecaef84782f60ad23fe92044a52f8cd86737fa94feb55246e4a7d09ec4dbb65aa7667d59e130f4a3a49a5345923ebac2ca39a2c6c0d9a30487aa1c4d4ef31f198aabdfdf4940705424142450101009cd43b0216d1a73d7789606ead80835342344c89b5a5747f9c112af67fe313112f1a49f5ce4dae2da55199516bdc327a11ad58e1057201439451cb153d9e8675dd4673d8551712d2f17e161f3eb5c5ab2ffd539809b39e35b6e5286d2e38c432cfe801923cef319f7939739f844fe467a40d5f898bd3687bce70765ece5198c630625fc0f906451713845130f9e90c8fa453154f47a569438fcde8e63c8a6df4570e0b080642414245b5010327000000d1b6221000000000d44cf86384d2b795112c9718b49c3ac7d7d0b9b91571984a87ee458d89a22603f81b53b29fc982f784033d7fa1683b7d1f5518274764f92c14da08b9bcccc6007bde84d6a346d45cb7040dc9b9ab3672706b5e36cd9c3a0bde83c4ede2d9ff050542414245010154cd4a8e257630cf12c3e62c5686334224423f28f423b3beabedadcc506a6229ce8d7c7d629773666539ce46825cf63c2e9b368828c05fcf06cdba1f7c25bc8dab712af3af78e495ae3f5191c2843b39e7a7fb8cf1bb63b362631720ec800bae36cfe801cf5b9146a2de58506dc9e020e1f9d6a81ccd643601df963170e750d75a79afbd8b1ab0759e26e23ac1541fe0c9cffdb07ab0b49f6e3c78a7a95fe52de89c67d9080642414245b501039c020000d2b62210000000006a7b74beab5f0dbb756104ebc07429bc215c8233029db94047d72dcdbc87c82b8d01dcfe389b070db7d0dd2eca4f35046e430b9d1e2c231e68656f4f41647e0c49ad66f24c394310766b4904f09d76773e61b514cc47772c6e3b9f2e6023f70205424142450101f29a0b49d25d7cf3171294ec514f31135440c12e47d55d2e59d6e943ee49a324ec81fc3fa15998d09f3cd150fd507cc6b98ce8c42bda8616221cdbe2c30b8c8516ab1409debf01b336c538c83ef847422360333ded2b5774d9ad5c8f9db395ec3acfe801dfdfebeb521e5475e649fb6609913e8b3a331cc29b1a00b3037c91b7dc8a344236bf5160124ea81f4a550149dee8e17e7201dc587011ebe909f2a8706da4410f080642414245b501038b000000d3b6221000000000c2623170ac6211754e25993c4d581fee14fb12fa1fd15a0d4d0cf3b332a9df238780b98606efe59058b852208e86d1b61671d64c3cb7d7a2506fa0c9d8cd580365be4186f4db5bb03426d2ce598edef8730d37708a6a4bf4eea295444630b70e05424142450101fc2f2b4e92cc6c5866dddb11cba57c58ccf94f920e9cea1a0dd4050cb873ef148770b784cb8097e475d8539bd9580f0823cb8ec2fd669f8fba8c7e98b9529680687bd8546b03473b6cee9a00fe2fc703d99cf7a072bdb9b8fd20e062da66ea7c3ecfe80137622e1addefb8a9b61fe5492490a9ca5e8fe0c7cab56cd3df950844a8811a6a0df2e7f5927af4227e49cdf2ccd1e569783bc85f1af3260d98a9a35372a9ab1b080642414245b501034b020000d4b6221000000000388734e93ec22378342cb241e02608bb7eb99651607aaabdcb2d7c811c837f773eb89c4828538df7063ca8a940d2f7888b46c325fb8b8b2c591a5e2c2b182006663ac3726be8bda51af9d0c1739b07fded9302979c731e62784147c85e32f40505424142450101163a8dabc76cb0746f2ede1a05f814b5ba3d50417ec8274a5736c05773b4395eb7d8bd1bd51a488ab8823be00b746508780370d88a27f0cc875f8e2727007e8cc1c5c0661b08208cae60561ea07e9898a154b0c8cc7909ff3450a123f524abb242cfe8011ac2024ac4aab25059c695df8e9496488b13be91fc35a523ca4a98c6a7b417d77d5ec6e022386bbca9a537a7dab90326ba3a802fb6a702de657a96f23fe74d9b080642414245b50103f1000000d5b622100000000044dad6ced36d6c06242fc02c763142f29dc29635c136a4072f6d3551ab3d6b0826a1c8be62c019ccf4cffcb17aa705d60db2a914f9f7e9a7fc2cf2f289a98f0589b7f733cc961a1b0432968cfbc0358c8b86c6a264b0b409204ad50c15cdc101054241424501019440728db4bc9d2c5b48366235f9be19673e1ab339d08e0881cd9643344cd234696c6162576e3808b703a1ebd75938f70b571eee139b745c21bbfffc9e902586a2266b64502b091975a9eb0861b8aecd928a140de910fec09e39daf42de8d4f246cfe801751b36145450b1e4e7cc619616edd08a9b9927f0732ff2062175539845c8f4875fc1feb292bc825210dba7a875474944b9374307b8d45f696cb3807e8aad41f8080642414245b50103fc010000d6b62210000000000e14fb7ccb762cc8007298989b07c1dc0392240287a9f12292feb20b03b0244276d1f52f313e7fcc5cf60610c2c79ccb53392da40209988e7bfa2c9045fcc70330fef96e99f42eed63770836b23611e4f2ddcd9c353c0cc16b819ab3d73f8c04054241424501017065674cda8688da03cf8f8d461768c91e1cf6f1579008f8b3e00f2194f67439d13cb1f934f510ec1acbe191a26ff7615aa43189b1c8ebed16fe593fd6fa3387bacf0b12bd525bd5535747f45d303d9a565aa527bb1aabfaf5d91737dbcc4b084acfe801d0d6f74c4a32795435202ee7d6439ea4b0b2d379e2fd1f3c9b54669f18d568f9d4f5411e6bab291245d8a59443d5e43949b42bcfc6cb8b77ab2a46e770cfabad080642414245b50103af000000d7b62210000000008c7671f47fd5889f70499d6b5579cddade2e1aaaf085ad6b07a3366c0d0db561bfff265e1b6287af885b2e7d940bf58af3db84f5c175630c09f27bf9f2b329025fe1625b6b631341b1ea2bf6040ae756c3dde44ebf68fb466207d4894c7d6f0e05424142450101405e253cde4a86b8246505da41e561c5f485831f38d345056f209c84d8bd9d06db55e5ff68dbd8a5606d76e3fcfe705f01e53d6c228c6e9cc6891abf37c96389ac9474f512b2f86042b0d94d85bf5ca246faf50e05c0777ec4637a35d546f3374ecfe8012d6e84fca70b4be30d2aaec4c0bb309bbd7c80203b0f16644b37d4fe3bf89df1312cf2ecf12ceb1b96fdb97646cf8b9ec7c58d8e957e928c88901e80a1b64d78080642414245b5010324030000d8b622100000000098a0153961b0ec1cad1d6a2325db6893ffa8b36a0033f34854e6b4f23dbfcc3cb6a02626029cda123186bc082e9043ad65a9f454bf899f56bf4708029aec96074f0805ac23e5a476ca0aae191c2294f7dd725774b45a517473c2174eb2f4390e0542414245010150a1fb68e5ef27d64ab7d53f4f2ec8d6aa86f36e518051a7c4cd046bc0f176047ac3f99f7d5006045d90fce0408aaaa9243e72f5159d035b195d72b9cc77c68679e941be78e930140dc2ee8637ef7b27804e87e21f23f3c7f5d2919cea9a87c652cfe80185ce399bdf5a3e5b34fbf820d91c072a0c537db6b2d536242924347ff968970a18cf891f7ae19a28b4db41580712995ff556cc069d4c1faa4804023a1324b8b3080642414245b501033f030000d9b622100000000038ffc3c895008c32fbbdb4d4575a94c6bbf226a511ca28c2d13bbfd21da96c115d054c71594e77e28bbb35011641a04476b74120a31440f41bc54a91feb90e0741f3b227cf381d5d4282d10a173bb4bcd13a4524ec551e68c39fbb247f50800a054241424501017a8e376b61c4dcaef6beccb8ecb2cca0b4c1f89881e21d0fe7af4ec3eac9143c23c2dfded339888f125a22ed9dbc069280160b2393605b77dcc76952ee98d080fb3b200cfba98ea0a85d5990c3a4ff91b09a3ccf731c93f1dcca1f09da808eb656cfe801fb5d0a5cb17c7f99b912229b1932e01d667a67cea2738a6545f2ddaa3b0725bafa586bc5d6eaba54ad729555fc06dce40018753375e1c88eb4b6a2a31c6e0d1e080642414245b5010377010000dab6221000000000ccd2ed68c2ee9e8f8336bc0026d371221f36841257c94a442b8ed36e47bcbd63f0d8a63f83626b5a3d049da24488f56ffba8d7244e5c359aac9788cbc4b35902cfd395ccb1f38d2e419a3f1645eeba4cc0d61a8ca971cf04bdf80ecae6ecf6000542414245010116de20ef5478918b3be203bd571e842e5a3d01fd1848efab289c75dff56e233a72a2e48ef2f8c17b4a3c3a04d78b5d1a3970d5e2bbb681e0c21012e0ef096f8b4cd2e7c18040998d3b013d151b59ff9dc15b6be71f9ce8948cadd3263e6b6c3e5acfe80123a66a1d505fca484d40ddba42da3d8b98219d1ccf57a9bc3b55cc0494f971889e6ab2ac65c816e3bc920aa363d82eca6d1416e34001042a555acb54facbe88e080642414245b501033c000000dbb62210000000000adb07f77b3dd9b3041b078d4d4f7e874f5401f70b09ef245ab4f4a36186c25d4de6183f39ea755dcfecec3ae76d01c436fc5c89b0205270f24391e886611a05a3cbb972989be9893dc2802669aa7a584e8ba4994052f78d818fb703a8a3f1070542414245010152174534acd15224075c11add178bce6bdc9499e1d5d80068c84c0e2ef96106d20c36c18ebc262843e8ab7d528a40b6dc69b1e455b29737204ad7656a859b88f62c8efca2e3af17f1ed6b60af5ccae38c9adf8ee7d12b7ef6759192429934eb35ecfe8011f9d91be4df54db45501a32c3b2dcf18f19f7c3bcc0427651f16215c7bc088fa78aa07316517537d5d24042c08a503d5e6414fc056cb3df81fe8e528ddcad09e080642414245b50103e3000000dcb6221000000000189dfa3cf57d80bda4a74fd3176cdf8e1dcf277a34cc891a9f9ba66dd22df95b6c95e37d06d1db3802980657960aa3e986334563533f04a5ba8e791eb0d36f039a6ef7dedf4711f7fd3533dbef76ca04369bb017598504bd4f6db76ca0b54507054241424501015605beb068f6e3d1979f2d1f9f6379c0f3b8ebcdc67961149a74b9d1963188128b057f3c08be5438c60fa438c07c5b93ddcb20b252ee27773d99b03ce2bb198e54b99eb52daeaf5f2cffa0bea66b0ef575756ebbefa4ecb3088b0f79402f820062cfe8014b28b3ab201d7d691ff9198dddb0ba6bba220a8a6d1ae6d74e83c86406cfe8ff9a3eeb2fcbc3971beb3ed80ec6291725d005297818280f7ee4709bd36eaf7258080642414245b5010306020000ddb6221000000000605e8ed5b1f243ed5a605d08bc2b2d1e8ef4890e9f5fad1bf755d9f193d01b686fc2473a0a53d2744fe9131fdd9d386622bc9c7f86741c3cbcd0d765ecb27b06fd25a1517976c8570f3106d0e0eeb770e010e400885731b1902b40faa5aa2c030542414245010146a39486f9c03757b1a832ed73ddd4a607c16e880d1b9f282959ac2f31e38d6921c67365139016642a8b0bc0baf230f0ed37f184c081548ab394ab76885086894d2e2b87a493a931c8b8d607c972d58f7a2add3190ae141903c3220fc9767bef66cfe8018a3c2dd09900e20c3c48414f452150e4ef421103f74382ffdfbdb5e047693942be4fa550f108717c884e9f61655da27c4a3e355e4f818aa7215d5c388bb57b61080642414245b5010134020000deb6221000000000b41b543af3470e7b1d03a28ab4861fd83e0feaf34bdc216e458bef29b068b5151381297726d8430bb9e742e03c6e0956dba135f546c204d03031eae561ddc30ebc4d48fb132521a8b3c01d6a7f345e78c42bdee1a7b6341753f6c5af6e2e180305424142450101c2023a0535984959aea6fd6688939493ce0462ec27822e6a3c6199fde8280614c023a6fe9e7aaf5aa56d4f43e0545a6e0f0de731f03a1581a139415c5f97968ed6d938d8b3cb79684ac21b1c2bbe182d0aa485656897d587273941be48c489396acfe8016fa26c346cb3cadae277f3a6449b4929a030c28893115fe43a7f6f60ca01e730f70a2ca6c664bfff8e0e6196ae74308a737348d6b491f7c9687b7a288c0cae44080642414245b5010390020000dfb6221000000000e8581d0cb84b3c43f69db807881827110f3f938272548aae8dc2620876508609c78e777c81fc89a160ea56c5c1f8d814a2600c887e947f74f4c2954ff50dad0608f2c272d73221aedb7aa62999d61d264c3e6f6197ee7ebc3f5b36e0c6cef60405424142450101e4dabb66e35c8c4dc86eb9fb4ed44a94f8d70ee4be7ea05b1a2bb17899d5f96b0434bb1c22b87c06ae7d27d993264721ed3dfe37994eff134dcb21b40fde308b235a2913ede119e47bb9e796a0a0b32a89f883276b1a00128af5a49db3342f616ecfe801603b7ff679a703ec6d72e5c6914d1ed485212f13940d05638a1963fec20ee15f8a268054cb9ca45b45a8297824f2c3a8c3657cdd91c61112323095e6f4037d6c080642414245b50103f4020000e0b62210000000009c209b1c3524c71c364354cac79fad87a84ccd53ed8bf2c33880f89610b85d6d8314ba51a16ac38e3608a00dcf09bcd0ee7319b0f23ec6262532ec880cfc3c05756205dba9a2757cf4f36ac0f8f5439769c5a6734e94c3def49bc9cb91ea160c05424142450101b28938fd3bbdc3ab974e733259c21a87ac6e70bed3bb94a3f950b69b5d2f780cad6c819d48fa3ee7df49a61ca4301e42516ce9ba56b1c881602e0edb9b81648484a884851d0f9377013dd5c5291f1da225a71b560164e51abc7a2fc8a04ef4e572cfe80125ddbb035891e5b3ee5a2133726878f5e2272f5b656c5444b373b067c7b18a2abca4338781296f17ebea996b0f4994ea0c14f30d0d0e7a094cb5b6e932f3562d080642414245b50103f3000000e1b6221000000000e2f3cc588e62892fd032aa6251509c41439a0d8e350a1584f21b950773abff79d8207d50343fcd9f9ecb75ff60095d817aaa194712ce4ef6824a581adb48b50075f2244a0de16beafc94a470700bf8785cbb6b1ceab6d1781342dfbfd81a810c05424142450101c062eebcf210a50cd2ae9fa9347025e4d2c060417b4f385263ba7590c3211561336a7e8af20341989564c6d6c300bd861fcc9f620b1538acae9ba07f2760a2876b0f2806f49257ca1760bdecce73f0256b6ed84d30914f332e67e96929fab66676cfe80134454d38dd1103b9511b058475b91cc2730e8a568239df3c2a28278a8aa137022ff02f502bd7c29cd27b1ed3f2a1614d5474593536c0b157ec042fd33e4c068e080642414245b50103e1020000e2b6221000000000ecdf2a2e22a89c3aab247963a8a1b7743be617cabe7e33c3aabcf6862e54175b25f381eb52be8f03e3a03d73e74977e2c63e36dd1374a0ee172c869d555a7c0fc01f2f8f78a1e2749f7dba1bc06b867036f090a41d40396565fec7ae30d4e907054241424501018091a1b7e7dc6d3515f289f9417ba1284268402f80bec723a480bcf82e020c207cba0fb00baf4902919b161cb371b70f94d8e806c2eb22620fdee9ac64ece28645d316bc120cb70aef23c97a5909073f896bfeee39377c2baedbc4e792f5b51d7acfe80163ca03f0614b5f9f051335129a774e3e43b313649e0abfb4cebdfeae4febb332f3f3434107784385907ef80432e53d49ec54e556d8c1e6ad92dc9ef24883b465080642414245b50103ef000000e3b6221000000000de2d9d72dd789dd680f90bbb4011bd790d2de00863f9e0b2c2cebb523df5735af1f47e29a9bbb378e4a00b011af8c9dc9e1622e38111aef10fb4da96dc4f9006edb052c210c7b163c0cccf147fabf4fe6703826b96bc09e8403305c03a98c40e05424142450101a2071ff5281faf21ca098a74f10901ec4cc649e90798b3cb9dc2bc6f796d422c34993c5d6a99ca9a80ecfb34d86c6abb7cdabe617962b59e82be244bff3d5a8e4b92c376f1ccb97db0f532c4f7beca4aa2c76f487a2dde4734b5f033b508c4b57ecfe8011837fb1c4775c590801e2b195f2dd64db7bf8cd7f38d66efc3c6881e48b69f445b874d27a8b3a1f8164bff16d8e8ee4b1f7ba65688aed995eba5384dc30795c2080642414245b501031c000000e4b6221000000000548b25fc29256aee4cc85af591bf62c5b5545454d70c7739da442177b07d620c22cc7ca301534fb3541788bd83b1bd58c164e6ea86d6c472d04bad3baecee80fd7251bfe90c4bf18ee4c1c692c8749a2e2d7cecc0bf845eccec466419bd2f90f05424142450101d6dd6c5708e2d599d7aca60e27d5c2d32909a638d241f6e76502a76cb53ef56e2b07e7decddbfb9a15eaf8d1358bd212eb612c39f7bdd3cd4ba72f92f2805f8e145660fe3cbb0b7dbe61932ed5d7babbdda19e7a80bc0bf46c698e8d2531bdd582cfe801b12c9a884fc1ed3f5b2e508c6fab170fe690f42ccfc2e668156d305ff4921cc89d06942d4fcc9459b3d1d5f21f13f9d4ca88238373321fa7aef90c4ae15bcde1080642414245b5010373010000e5b622100000000048f7868a4514ec9cf8a3dba467015a816cb04e1930fd7fe4bf69f240bbbb224e826b97b4461b3b6a5b70c384fba3d0d4aae48ffee2421eec226b110b35220500d2254088887d0dd066415b347218eae13f3ecc303fb8702e456b6ef798632a0d05424142450101c2245b40e3cc79e9165e3c97d076e4cf8b1ee58c471675d09bc83c216316d3561cfd02f8dad4429f147f510ecc86fb7b385ce3dd7928acec5e7420450f92398438eab63311387bcc1ac21ac6f2856b8f52106fae3aa81e718c27fa2cdc73608e86cfe801115ab82c5311d136f4b9407e238aeed493de658230988bd0f54e3793643d78c7da9f8a2085a74de682c60c9bd8cd66a21c518e238b2670e69582ab3c45454d6a080642414245b501039b020000e6b622100000000064283c710d6ba8071809684546c94999e565cbebcef88ba4e958fe26f8ee916adfac25f4a63765d5a0cfdb2d25a708a820da62b3e7fafef043a03148cd27be0a73162ff868cd72c750b7e4092250ba228196fbf43f15f6f968730bf04774c3020542414245010188f30b15df37a5dc3fd3bc247df6ede76810dee7244775c10043767fa387115c63de6327b8900fd540f28cddf0ac9f5886b44440d49400fd5ba7a592656b61860d23bc4d96164f8cc3f501ae18a0b0cd761c893ea688a5b757687106f9eef55e8acfe801071700a444bf279191a543455593ed6d223495c29deaced5d81f8fbb2a9508425851d8d5be12e81afd39f777e051d756d3390a6daaf736bd44abe31c05215d7a080642414245b5010354000000e7b6221000000000b089b740d3e55fa920917d4c8fee2c648be02edae482ccf2e12cf468897e604c71ad53e46ef3fbf7fb05be6d74b7951d179ad9b61bec4754bc493bd945cc2e03b3d775662df2479c6899889ad3d968fceb1d7e5c27db4505cb9fb5af9bc3d00005424142450101d8bf5fd14a4cd1f4f7addbdc237bad1df3cfe4e6dba7ad016137576584e50c295256ddb97b3aa87c0fd52671807863c7e23cbf7c64a0780f429e15f6cbf63a850380a5546283b0c4a82f26d6e0c52d6377619a403b17672aaa06aaaad8e04d218ecfe80113e8597de66a9aca50217783013edc1cf8626ba61ec4193aa47d10cb471324b8ecea1b18846c865d6e617e62a246d15bbdb1d43b2e3bebdf6c161834790ab730080642414245b50103bb000000e8b6221000000000e848df55a4d10f4da7d6f8d0047f08a14120021bf06767bfee2ae0a0e185fb1139dfc81eb9c5e634b16014c2a70b3ac45053df427374ab6ec2e6242eb03cbe0759298b43c1730b47dd75448078b6a6bd2381043062ef629e3cb10406f9c63307054241424501019a7b86e099ed253230191e7c2b46714546d5115f0fc40bae912fdb7e479e6c5db451a7dc2efb04861874dc5211bd335fb89ba102217f751e37988fc7a13ea1899e442f791079035d515d968debb6576330c238a39ab562bc9c17f07446cfb67792cfe801e173893395b6142257f01678c212a04ea13f04db5839f3f9f2667d17a574299793b6e4ff905ecda6f2a10705efd5e8414447bafff78f5488d3a90396144bccb5080642414245b50103e6000000e9b6221000000000b00509ef1c2415b2aa769e751b864547468556b0224c387431a274f18ba1d558746bc121102fd8c7356294283f51f9b0c523e573924fef630e10ccf3ae8a31027e9535a10f4e38bbeec074d2adb0a3d0d13fc977fa0675bbd817eb57febff2070542414245010152cbf89739eb6c9220c5819adf0ed5f9bd3c10b28100e1149f65f69417d05b14fcd8c23676cd21ab79e0fcefadb414a264708c89855e95544c23a3b0426e7d82463f190aa0a66cc752696e90a902054ff9a71f11c0c1417f7271d76c8b56c6a496cfe801797a27a68c4df7235b3edc89756875f0bfa6c3b1c4eed97be1fde1555707b23608f5b6e3b9c63600f874b3415456ad532372b186080328264f5271c34ddbece8080642414245b5010369010000eab62210000000003c9ae34311992a08d28bd049d0ec72540673023ec152713371c68b8b17ab9c6f0042c4052ae63778c4f5eb8ac014b2a34fd92e04528ad6cfaecaca7a3122070841c4abbb92c64a7b73b3b4050d50fe18a6920e3bb91ae6649380df763330700a05424142450101a6d4f38c9a43f024d1362b08d96ac7c25290e7e929d3084ed316acf72d25d224a08a5dd5e17a31fa8aa78960c1a37d59af05e4f9215555baffc2553f346d258263adb945b335ed451c0f43cb1b5e5c9cfe8281a8c683c6f6c8ddb8e27bef59029acfe801c765e82a6fe212e4d17750bd5ed8cbc39fbb9d0b0f27d8b24613b1d609e51eda762dfbd1a4daa641ff97485434d2f3791706061de9464642c45533ce09c0e5fe080642414245b50103ff000000ebb622100000000042db8733dbda3a42ed327b6c371106596ae9285963d9dab89f675f1f2334e818818d0de6a741303cda4141ae3cbec32da90ff565127e70e9ceb650c1dab93f022147eb04737561b4d60fcb3d7c5e7cdbc26f02574a2d0c793cc4d99577c29d07054241424501014cb64a66b74f195363f56b0e4184b1b8916a6eae955c677dbccf9aa7fa34fb5145ddaf1d1818dd3f7cd51b020985ed45da5be9a264f582a0ea88c76e6288fe890b8419b8432bbe28228978d3ab9470c7b0759ef9b389d0d6eda04cd139aece199ecfe8010f1de41363ea1fea5e09b48d2a494f8e38dd2200f6d78e026ff246bf7ac32fe41718e311f20c7dab030c0115ab3e922a830b205d3f12e0ee5ca02b3d578c4850080642414245b50103df010000ecb6221000000000261a083326840583c71c754dfa9fa51689f8425d1673f6d4b2016fe5c1d40922c990b385de97df8ab52f84473739c3c8711bbc8f67935b0f3ab14080c5857200a7057618ef591a4484046b1f965849c764d55770983282dabe269bfef057400705424142450101a4cc2870bce872d2f4c6996603d5791a14f22e08f88564c2ae0ebfc8a5f2c46d804cee016a8d1fe446ad3fa0edd98984b8a5cd3ebbaae7cfc9caacd7d1fc6e8921a5788e7ecce8728fd01a5ea6ad51609549546fc0fbfcc7de5657cd2ecd5e98a2cfe801e434dc60d3b49c444195bdf9350284504cdea6eee95417994dbaaef0882b14bb05db1a1bf448c531f077eed3594d5c13badeb49e2faa5a549251b27d78845975080642414245b50103a4020000edb62210000000009c6ce9ab574c2ecf7ca6321961f2564ce0a199b8806b2c17fedf1fe53e31213a1188033bbd6638bdb0ec275d64e58ae15d9072859ca6b08f1ac086a582d9820e1ddae3cefebdf7acdc8d220eff16d2d5387b088e792c31d8a007ade9ce3c830805424142450101b6c518ddafc859172ba76ffc48b0e1a9dd2b9454fecc14375403032ff0ef454abc3fdbde815b49ffe3ebcdd434dc5294110b243162b33366783ee163120424872f189517ef145f1b8a3f97975a09939391ede4665641080317c6d7a47bd34abaa6cfe8013fbb7cd7c21b4e5efe716772144d3ccdc2422bed5c8fae38e3f46440f01ba9c01162d9993c45780b0d5acdde417b181c62a71305a75dc00145a1974963f19e9b080642414245b501035c030000eeb62210000000003c535bcf5b1452fa39f87c9f083f4785d237809b8d7cb154a8924d5f18d57a2b78f858c950b6367932b9070b74a84b0d566a01d3cdca1e79b36c3104f286a103af595e5974fd8d9da858242884c1cc7b1392d815a85731916e2503c9dd18350b05424142450101f0e072b911cfe1b324e841f6005727031641a2d50462588f34da5ca5b0e75863a341507806aacb7c4fb9daded213f1adc33972538571d01190893200c461698507ce3d4ed400ef43d68a95c9f8ceb5feeed9c1f3a060829b9abf91ef1497aeebaacfe80181536de0994dafe115b68de1a09498fbd7087cc1244857843837c1c64683d9c46f6d1ff1a79562110d9f2658b96e15c7660ea98f3ced0d011bb433483c8be79c080642414245b5010326030000efb62210000000009e047f4fb85cde327423a77e027e0ee475065c2625a4e28672b2e1c1aaeded32a3deb421f86fd7126b827368523ef878b0f3b863af9c1703f3930949b9355d0ffe083627dcd1e8a5f022093023043f782274a606733ded330c8513bdfa20140f054241424501017cd1f51bc3e19e55915f7555c8fe9f3d41c45835be8a08e41809513658107406bdf5ba1b2a607896e54207cac5a8f29635da83fbc546ea61c2f61ca8d694218360d8a9637db2b53f1d608c99830a3bfbb800b397f1d65fffa47de6ac8730954baecfe8010efcfddb137d77e81d5aa0bbea5d6bd7e5819e225707bccd13058b6fd4cf4014b2cede2aa78bedc1686158e6dc388d168e1c64ce941819df3626dd34142016f3080642414245b50103d5020000f0b6221000000000902b3d511a457eb78f646347b10d9508ac9470553e9e06b24ebddf4e079d575223ce55f634cc0de979df1e375ec1a0829aad5bd12bef72d59a3124f405923e00cb3161335dae71fc3449fccfa6bd6963baf93385eafba1a85a938119402ee2020542414245010142539d2f15ebf81873dd34b367011db04b2d4ae83f47a2af0e7b3ad1f4e35a3cbfdcbab38a2bffcabf2a282e8a91989d0d68dd39766299257f3d1bea1fc478888f191a89498859330be3ef1239f696fec5948f6f2d1a1f9313c495b79e19d35fb2cfe8017f8e9fc54f964e66e49833ea6620a384eed16b683177d52939ff11ab35ad01f72207a568b27c866cb3fd0e811321ff34e1f4fa519597e8c79d50587f61b752fa080642414245b50101f0010000f1b62210000000007875f198922f52df48351bd3194e6db43fc40e52474064d0eaab826f6ef89d2cdcb03d73569cef288901661e914ba81396ee3128716f88f9e8786c87b6bfcf0afe2d3f8dff8d0bcbc06976955816ca696a5cbfe3b717e139e7a11fe5e819f60c054241424501014c5de79b3b98871c716107d898f4b231a032df1dd0d4e3aec2703d0ae622a1114dda6a6f2373a67779cf1ff9043890e7b865808431b02463fac63f01b2ea3d89926052f13e2b49b1ebc85b65a633d3eeeca434cd307faf4893612150cf8d2ba0b6cfe80132042b13c42d06ca28c8ad5813ea6025680d4413e9f6da126ae074d58c9e10d64d0287a1c135317bf5587f1e42e8422af0f0eda9dd48ad391655245e87fb54cd080642414245b501035b010000f2b62210000000009623aef339f17e69993045944b054648f368f2a51cb65123f2bd9be215a8a37c2acf10ea1f05a03c0d42ce1c2d74ad314dc903f4ec3608210eaae7df5e17fe01232f4465acb6e62010880856cab66e387dc56a6efb1c0b76a90f53434db9650a05424142450101d83c6b94533c03a47082c962f9763ce5f6bb46c4c3e3e9075119b2b08de1f5447b0921d33e10fe7f882393f8ddde9fc1e58b43254a70dde9ed6fe50f2c8dd381b799d0a3a773ac87af85813e06722b428cfb11bc849067fe11178a93ce23e34ebacfe801ef809831379e7ba592e69b4d29cfb10a3152ff9dcc52d4fa380f2fdbe57f05bcf87967fbe647f214308163eee48366fd9490304ae7543cb99083577c3678bb54080642414245b50103ed000000f3b6221000000000d8133d887a7409e3c1bc67d22193f3710f4db00f9f31af1bd162526976aad122f524cd275466e8729ca0ab5199019db2c8fdba01b94f99a293b7d1636b94ef08a1b7b81c46055eebbe39f884388e8fe55a2f481539d6c7fc945022bb6e1c860d05424142450101ace5dc32ed70a0f79dadbcac105b277b832dc943b446e67a7af9260fc7f61e15ce051ab8a33d3cfb64cbcce7e5bcbc7543a960396b381c7edc2054c799084a8e4257fc033f251cb1c81846a8da01caf36277fe2c37ef127b71519ab1c0815c24becfe801b125be0152e24ac9df7bc9cfb5c87411818ee40d4df9ea2739f96884e80651844d825162f6e7f7f717d9b79f271fa178f9de7dff2255c33a37ddd8c2e5bca263080642414245b501035e010000f4b622100000000064a40721331d9901e87e7b532833212d92af190e9c1c481a700efabcffc4711ad849b3cfda4359209e8334cabc3f3e61dccc71935e0924c99eaedc9db6b72b07dab111dcb5dd3ad6ca9a1f4f9a3810c83e4b157c6e809c4443bfe97ba8f2ce03054241424501017ce98024141edd263678828545b8d8b656987586285e1260800e6c5317e60e1490d1729f4a765ab6d97964e2beb03d9a74e4adc5a5cee4edaf60324781b75a87743d8430ef7e6078129e57609697c4d07de7e87199c3d348fd09cc14cc68cc01c2cfe801ca95d54c4e43933c93fc370178e759eb7c240c163dbccdfc952052d9b4c0c5b4f244febe8602ddbcc2b33f680080abca33813ec4035d0e7eafb99488da4819a2080642414245b50103ce000000f5b62210000000008ee906b7206b9833b304f1f2c1fce068c5a0885957249a24a3731142ce36f842d0ac55458511f8fa90cc5f2edfd47d99a67a6ce12484a379890818384b00c1081bb5b5dcbe7bcf53f75ef0eac129eea66b9fbd33b80b724e3b29a6341dc68e0f054241424501019e74f2f9ff08396e35a4e14fb05e4d2addc9f38633812ff63d109e5b3644886ba4dca9e5e58c03ef5643826d30e57f4450e909565b3fa2617cb1d92e7a3ea78b6717eb9d3a39ec47eaddfae120c085603ec453a866517ea9a88f7b3ec237eceac6cfe801dd394a78ea50b2db3f2649a4df6fcb61b115c311f5ebd6dc48a36652b3eb6d3dfd8e5eea9f1cc336781ebe9abb4b5b1ad6ef4a31fd6cafb94e216e725b284955080642414245b501039f000000f6b62210000000001a8ba258cdeb2ece2f7efb7e19e4a4d2cdcf86b191ab1e8c38a9892105171f4b1e27a9c2bc36fe2067b7a9572d3ccc5f695065ddab5f82eabad2d9c93920ec09232200357877fc62a9c5041cdf4bbd404181a87a60e1372310df5dd758d461050542414245010102ce48599baa048e9b6c1a37e826e44647689090e061966c4de1257249c7d44a330d8db153485234324aa3cca1ad2a638f21295d2fc146f8978e1e42808dc68a2197c83f77bc6770341f17d232dc80e86b5986d656d7989ddbf75b6887281cf7cacfe801a64dd14357621fbddf02a7213f4694978c9bc09cbcba034a5ed1e469ce5aebdc3495b616f664a92957533a9b6fec441ea42a5a745748fc2c576ff7cdd8448461080642414245b5010361030000f7b6221000000000160d5b5aa0ad0a17275dc8da529789e184b0712233da2f243cd9b2030341d7242e938140d717adcdf700196bc26fe672e36dd0df6c19f545529857a9bc3b0000067b8102d35096fb16548aa22ee83c7f2c4ae661d1b2cb2d7c6424aa8911260505424142450101cc945f968decc067cd06ffd5876b866e76e7c6e3d35935d22717dedfaec11c6a1d20483b59d8c0631dddd3d25f14ebe10d1c73a626a8855ab3feb822e914e88ea704ff8c672322aaf7573a40206041265fb62e80bb2c15a2d74a79977a3b84cececfe801639265d6cbfeb7d2d670b72ce2d6465d500ae2b1d2749b3ea12f3391412c453b96000e97659982f97dc8f16468887fd56e146dfe5969e2470f743e33ea2fcc4c080642414245b501037c030000f8b62210000000006cab07fb8b044d8226e7369f1ef624872838ea556338de3846d79d382657107a718bef023982f6822b874f8ae7f6ce8d56d7c23ab14bb15e5f51110c0534f306d376917afac92f2bccff80935139ad13c41b2d849811e0b4e60effc40e466e010542414245010168b1f4478499d3af9c4e5c1f2222409e82199bde722025d1eeb7e0457e05b22cb3b64237a0548fa29b0aa9c356f64ab9aecf490c1a5d81592e54f697444d768283e6e32031a1fea2988630a0a3a5c441d409fbd2c15357c28528fa06af7e4597d2cfe8011c760776232d30f51090dbe59bdb17576a7100cb127a4316ab21d2e9903b4f4b83f4d215db97b1051a4479e0d6a4b797b1f51e1c34d3dcdf9bb0dddfc1124089080642414245b501038a020000f9b622100000000060ef7db207a341d8ee218b658ad700821d559ab99758fe4c4c1b4d6a36379a404d314b26ff73779810159e9111a2f9ca3c2783bcaf4e6afb4c46fe40f5aaec09648cafe73807c611de9f6d22e45cff27b7e1e7b5fa9f65a41000fdb3470f1b0f0542414245010122df73bf135d852d900bfa879ac50d3ed136aab9d901796e63062c7929aff1072df7cfe862fd13550431fc68be7756a9f8183619061fe8f4fa1cf18a72be828caa47118b8cd2b92b699324222884b39204f6612bc0c74bf364163e3eec7ed3ecd6cfe801c63ebf79d2dc989e07585138811193530052c85c0a98a14e4ef11e8791a5a3105bb522b2ff099f39d74a523f4c77032dbc5ee867b3e6ad296f2a458f3004c9b0080642414245b5010134000000fab622100000000042c956540262f75c2529becea8b83e3572157a4dd7be06a911477ce5b6ceb96e1e179a6355b45372d675dfb34e08522eeb0bf4f8c36cadf301a0ead2c33b900f9eede7fb6925ab8fe73f58b1d75e3534dde4a4323fd76c22e240cf4e2df5f9000542414245010162e01f037f3956fd4cf840809708222201167fa3d7f5fd158b01216e91d2ad64ecfc52e5bfa34c765633143b905be1e10588f54e15ffcc45e796bddb23a9358ffb4c7942c9daebcb0b64e5804ee43c4139389446248204e26909962023ca074adacfe801199d8ef67cc1510338f394d43c1b900202742838ffc92605174109914a356dcbbbfec1ed96a107044dfaabf7c3dc761b07d23244242fc9b99149a5a7f1bb9663080642414245b5010349010000fbb622100000000064a0471939375d621a355bf19670c4c90d8bd733cea3f33cb82a712d6deb450c72d6b53a839c8a2bca06b19b53ddcd6e41911cc06d663db2ccc09f0fbda50b0a9d34583de422a2e3fe810cfbb35178716e1aacb679463f75d216eb42250ba20405424142450101641b1aafc43ee140059cfe6284e5a630efc4697941ac6a535586c120968d5c0d190613b6bb9d608d4d6880ceb3a59914a4da5cdd908a2775f511877e358c0b8b656fbfc9c76003b97c9f13a17b4b47bcadf1c4084e9a1b5de2af1ebe3ad09229decfe8012a9cd5e0a58d8139d433e1da741927c737777238db0868a1a8829b743ae45660b85b6807b9ebd8b6730c729f43dca67b715f81ea239d171f4e8302264c0868c7080642414245b501031d010000fcb622100000000028a5d05485c4a515c5283e24a39508316414793875835b56818fa4ec57e8e30c9d9c468186ad4c21f8937d1dc579bdb11f26eef44276b64261cf4a11e180f70abe9912889d8b527b9afeeb88fc67960df04c82f36aa11aecf3571a3ba86d650905424142450101ac2557baaae50bf0a1e3477b33596b737923f1498d8ef5a8ba02b24f63a4951b69b89bdf61d6e30aee5f0d6929705cb2c30c117fa38245f6a244c969e3aeeb8883b1cda6e62b075129daad4eed4555afe3a1be02837ec563034404adf0b88116e2cfe8015eadb1372f63c5cfba0cfdbb72e7d4669838268a2ce3337f848ee497f29ced79ff236b63eef41163e8ae01b01e67a94b40783e5c6df2dfa852151e507d5d9c28080642414245b501037a030000fdb6221000000000988871d5720aa981a7eb24011552e143f6538c48e6b81a65794ebe9fdcdb4b46fafc3d0ccdfb2439db01030795753f80d4c8a5f47bbdfd4551369a0773a43903e592a5e931b4b7af79a8db114928af3915d9f261d4f19a24381328d03e62ff0a054241424501012a0fccd4169c06374f8574e09ed7cbaebccef53cd249f786c76f06f7cb6e7e5bd0b995cddcb1d6c341c48e98694a169e3677e0141ee90064f653e95b6c575c8a645860352a70e531001738fb60195e97a93dd5520b97285d211a5ae9c05b59a2e6cfe801155dbccbd149902b02300160d30f4f312df7b5750927eea68e99d75468f3ae2641b2c70322dd457f9a02c61cf385d60c8ad77883ebc89dad40d54eeb3f002bb3080642414245b501033d000000feb62210000000004223a70f1e244b13ed1a1ebbe47f9d0e2064dbfea8246048194991406aa83e4b69c130e444a51ce2c4e0a3e770708328f8ad26604956b5b617b7f3101fe60d02d69ab70108115783c77af25e72c14285ebea177ac111dfc26b3b9a5f016e2606054241424501014cf936f006d7798d657643b43e9f62ffe25ebe3b447f63fc1db7b8286f7cfd728c466b4d55e836d690d069579af066a25c8ab9afdfb4d45734b405409bfdb2861f8e75653eca69341b5042b3bb75f955f0bd484e442ede4c7d67e92eb1a4f3c7eacfe801e4d7185160d76245b068ce97d9daf6b6c5c9d1aead349e4635a09b1baf2323389e5e11f31f03a03eee672fd3599196aee8da0938b74e4734055b06b3182e9256080642414245b5010382010000ffb622100000000026c977ac01293f6395d2f56bfc40f95722b3fe1518621d17d1f6ba1d5957615f9ce998564e23c792a676ca9124da45ae2ef5d6aa56962e2faf1c4c3276512f0145e490b4288a358c59de2ecb49fb48918ebbdcf4e0f7b3c60dd63fb0553965090542414245010110cf1d371edf49780f2bbcb86f706a0b10dc8e2665ce9faf4b76329e6e3d7d702877747878c50f47628777d9d7719cad824c786b91f3f791a71e62713da4358fa0ffb2919870c987156060748209459a4a18240d70c2f293d9026b57a72e38b2eecfe8017c104d7ca553a7e22aff9d13d0df20668b9850c08e3ebbede44036b9d0e61ba24dfae2b54731006236b8194030dd397b8fe526547906fdca7a29fb4c2af61b86080642414245b501039900000000b7221000000000a41582d13732b409df0f17f48ebf50ab81445d317832c6539976d7abfa5121266790c7653fc8e08387eff3e93557afd625116745c5b70ae3bcf170fac5c5b705aed5aefea856cbeba5a0d3bbf8f3085730f004a3f23404a2a9820f0b24703c0e05424142450101a4c515ea28973f54485454d398eeef99d32d88c4072e04c921471429c3f5461dbf5d964c435001f3577e62e024e6e235e201337aaa6d35a59f1db67fefdd9d8dd5b58761764ed5c2c024a9ccc8c350204b4ef6d97efa0474437f7b68a64c967af2cfe8015c52ca93ce11ddb030a1b22252129bc2f72228197b8886094a486e26e750bd59e287e3a1371d94621f08d884e1ea8b09e19fce2c921f96b9411e3c97ff0c4737080642414245b50101ab00000001b722100000000034d9b4755a93e5173c6d5596e937bcf86230b115850a1663265c5516fab04822e79f3da4a17836aae520525b14f761a4ba3c2c83d3db182fd90a84c081cbb20ea7482442b8c5993c7f10487dc5692225e69d9b1b3a03d695633d0b36c982270205424142450101cea11e81079fc10a2a5f54bfc621135f1ca1f3a6966b84fa933a8385b7c1c35a039fe4fbe01147cac7de5a7bbecd5e4b26784c9192ed07d8900e1e9226b88d8b833f17149015815fb0816cd3f605ac22ba591bc55fb59331862b1c0aab7e598bf6cfe801b0219a5a04a3eceb495f258af6fecbc400b3647a34086645b5239d6c36c9085157183f8f9cd58616d0b7d6e07381d59d95030cba1b4d3235c17aa9ab32c77289080642414245b50103ee02000002b72210000000006c7a1efff6154b75df3249c464a8ad6618dc3bf94d68590742be2f89b26fe273f71106284ccd6182ccd32a331b80dcd49cc69d4628fae11ac6d982fa0566690c60e5b7fc9389b45d75ff34fc76cf44d9bfb3a25de421a62a0ab86f3327fcfb0705424142450101ea201c80840279427e8dfd90bee0e8ffe1488bf174597b2486c385fb90271d761532d1b66f2dced2589f17ff23e8d8de0b41c7a9f4b841e374c86ba7b966758df647ab5b59e82b837399a8e4c37ae0f3bf6a569ae86b442144f4f124ed8ca392facfe8019b6837cb63e13fec7f2c454468a9da1f0831ecaa3cb372dee42a55bcb8e935db388320a7d1a0ce7827b23e117f52d67c28609bfbc2d34636ef050d715bbeb328080642414245b50103f102000003b7221000000000361800535b0dd1baab8d421b1ccee687b0d87c18ea46ba6ac23ed75dee97573dc1732197bf643ac35fe6885803a19ef1c5b71682d517ea3c60460235234dee09517d35cf5064aa9a79a96c1d59a9f07a18d31fabeec6b286f783d156684d1c0505424142450101d46171135ed8349094b18abe1d9a1d56609e800130f1a20ee25bbd55f485392446a2a2d1fc78830dbdfa984c05d26ab3dedbb4222308f7b0e817fd4d96c9768101b9ebfba3ee21f1ed5241ea512def797b3e574b1e23acc99597b84ea674ec31fecfe80151093b6346e427eab8fbeb5a8ac71f45a3eb7e4bcf7707d7bc6fcbf24ddbc001782a525b3386f54acfd7ddaeeed531b5052b614e1d80cc2468ded469e82a0636080642414245b501039c02000004b722100000000088b80498068e92582ee1742b27530e7c349d072503755d9d8c855c60cd7eec5f3f88920fc3482880144c4d0ab3d871e58f7209ad3a7fadf097d65eabb9089f0102596a3ebf59123902a9c76cc358c38ade048aca8936fc814ee0167d83d9ed0905424142450101fc29a0f5d0a9b928fbbd80c2b1a9d130f2b902b155b23a35730f9acd1fd34b22f230d9a2710dc143a46a91399a08f72e3a9035af9f62b7aa9a732c72ff1e2a8530eadcc498714eab28560c22d7056ddb01ce97c980824a0115623f4129c9520202d0e8010b2346d0c0d7bc4b19930947efea8f01cccca7648a2b4fa5dc6a743963ad21328713b461cd4c89f68643812851a7b138a257d6594e63bd0a5bfa4980deb5aca8080642414245b50103cf00000005b722100000000098940bda6633f56b890f2e4c44a306680ec4c02436e9b695d87b31bfb3032a40e6ba05872bfc9bb3f437c0d7e9165377380e34163f03695f0887eab74834c40ae19c7d8fdef3a8ec7ff1c2e4d30b3149770b62dc23a4ce5fcd73bd3cedfdb80905424142450101bae2b54bf3ff7a8a83830bf79ae931a4708fb74d53f670c822e3bc18d7fdd1614069d59e3e672f0d3c795b0685e476efef9503a0fc78ee5c17e994f7d56bca891f4e3659684f7bac7b64da9df60f07bddc965c4cb39f58c1d6891b4e6fd954cb06d0e8016070cd45369886b82a82d2590a72831e33b75539d749720270b41139945873c196c2e8b2f618b387b2465f213373a71919c67894ded8fcfd8e0d79e8293c9ef6080642414245b501010502000006b7221000000000e0144e23752805e2c2bb8b5a1fe6a03a00eaad91f7b3d740bfe912e59c703969b10ed98d5068948b38e56fb04b747fa0eb2ee9cd17b171146532262ed337120861b24e042615efe494d004183d14d7b09bd00d3e4b3d2a95000dd6228731a40b05424142450101b28a1caece098f9b8de0a75d5f4f677a9fc68c9fb35ad37cf3374caa5edd5e5a0f3671a93796ed1fffd3f859ebcc6ed38ebf005339e00c8dea4013a2f1df8888369474ed4ba1e9246b067d7e9dc6583cdf46653e8a73d90ede3fb91842c1b15a0ad0e801574ecdc3bcbc60a914abdec8f6830a9132a03256e37949a2687d9b6fdacf7c68c533bfc7b067eb162acffb914298018c461bb64eed368bd018e3dc97d03421d6080642414245b501014d03000007b72210000000006e5231059902b82b0b33d6db65650c9d4e8fef57a2851e2fc7ea0e4def88170ad0b4d99548016d98ee599f975441837f3c1697050f249e4c8d1e5eff0614fa027408e3a7b8b89d2235427c298c541478a05d5fc2a2bafe2586044ccdda5f4b0405424142450101281522e421fa0fe9f26189127ec803d444cd075abbdf8f258b186091a4237f12bd8676f4eedab8666b389ecfd1a5523d50606fb16a5129a8d170236dfd04ee8d7775f3e918ae8516e65d47ca47351930744f45f14734d4b2b8f0ccb5fc77fc620ed0e801ef181932b79ac6f0eadb2f56e79cad5056b5318dfb9a65d3d908a76ea06ce217e500af8ac6419d2e7ef91163880ca13ed44d9c2fa220245b14838a4619b8ee40080642414245b50103a002000008b7221000000000669ba4b6fd0a0c85fcd0ab9bcd93aeb018fdd37105985f806801ae5b835ea33c150428b568f6e8e6409d5e37e627fecb106931d27ef79d2026afd2d0b3098f057822fc53ed2b8b395ff5637a6d913f6d7b1f140c303c51185955cea0b680af0605424142450101d2e048fed3f33f22eb6ff2b616b2a71b9bad3692fd504c312fcf4627708ed923120a01ebb27a2cbb0664b095d4effa6220e283427d1883948172437357934a89c2cfa29ab54b468247e267c62c86b7e213cca015015680594e2bee9c279d5fa612d0e80116993629ca2fefb2ce51065bd0c3e9e6945452de41eca3024b97decf1be64b073dae80897dae8224d4a33fd26337157e9e9c439889f397aba7791565c12067b7080642414245b501036002000009b7221000000000b88a6953d70223dbf322bd34a2e494572f14a1365ac578d7fc6ff0f80c0cc834772885d09b7d0163509c52782480af6d1cd03aba06115385e748e43a53005e0e331b2c19163b68f139a426f36564bb1281b11cb54bc0e4c3491b13614074b20b054241424501010c75104edfd758e097c69aea19c8cf0f70034d4ecfce75d7215e9dd4a28b6a6a6c349e3d5eba1083dbb81a4772f7e1faa415dc5b7d15d936ad3c3c2cf150c384b4cef4cfe0d8f9d214f4e1c7c38bc770e66476cad44679389308ae06f204773b16d0e801771f7c2827d0c646046bc8b5ee42d8a0694c2915b2900a23a4cf8aba171131e82f0818b49b2aa4dae40c4ba7e83a4772f85132fd35fcd7941d3e3b1c55d7abb3080642414245b501013a0200000ab72210000000005aa44eda1321873c126e50d36249075a80f81f30194beb9c8eeb60f2871b134c3393a8da775b5616ffd0a5520f240f5a6c7641e1121e8f55163dd146d55eb00ed0c4dda5c3f84ba191589f4ae751a40d56b5ffdc13ef9a49e92d64bcdbdecd0b0542414245010100e8e05a2de18287a7a989a51974c9b488372b5858657425f184f657d92ba16ffefd5e8ef444dac2cdf997eff432145dd549acddecf658a091bbe581d57a1a813aa2137bf2e2322296d03cffc68021c7844f0eaf78687814c46fa251418bc2d71ad0e8013707403fc4367272bd8f075c7b33e109e56b779031e6b673b5d0c44ddad9806ac93716782d2387965b59352c2566f7cf28280691d5168513af17637a600f50ea080642414245b50103aa0000000bb722100000000012aa18b4355aaa8413807fb4f93cbf0502f2d9fc67c60ea19941809afd814f4a2c8d2fef97cbe2cb5b7f8b2e3707b175ed35e562b28f023ca48982de5f4e9205b8a524df76be19b76c54bddf40e3d552ea089a311c99279eb7c645997febff010542414245010174d6cc4035a177f84e20b009bd6c4fe72ec51bbfec243558d3f2a59197b5b171d884fbf702bde7922b9fdd8ff649ce84643b400d3abe5d0bc33ae8bb41ca8b8032ec24da0c3403c84ccc86a6731b24a23baee195471eb4f841cead0aeab4985e1ed0e8012a7345aa100952fc85771881630a0857a304f3cda41fca201bfd16ced268b248053f345103c18dc854fd777b7b40537e689d35f59fc0b567c3a126d67d6e040f080642414245b50103de0000000cb7221000000000ca77eb298dd57a167f6d227a555a92e887f70ea06a707cd49cb42b20fb9a2174a4ef0d15c3df87398cc7fb3f8afb2ea70ffc83d85c49c8d64655d38939083f05012fa40df570fdf1b36836a2e605096c76e1d909b61d28eedf3670f387cfe60e05424142450101240e49099e8cd657a631ea1ef49e2a1eb8a37cffdf7591387f4dd8b10ec9d8743782d1ae05f23fb9bc98f3ac4f040d5391ddd6c50225a5f9c25e23a7c6ad31804148441446c7d02b1f1f450b91385d53e084b7f5bec80df6c506188f32a60aac22d0e801ac5dd9da1e5e607c2b6cbac1a2e7d4c1a6eef0a950f7cb746ff092fa7ef471bae128662b0e1253b728b0511c9fd2a4967233b022c5710f1e44053e16b92ad7af080642414245b501031b0100000db72210000000004c2b7ec5b0b727bd8527919e5ebc47770cb57413530640ba2923dc3f1e75ff2dd652f3f1ae7aa5b076b1cd5280ec2aa9ce82286291c5d16c2a54efe4111d8808bafa94027b6cd0c5ad1ae8e9c80addb9c44c2ba19a9694ac6483070de97a240505424142450101967ecc4d06488e09be081779bdae9e6e26329e2a2471a0824e4d1c6a7158e91422b575dc95d81d00372b3960224a25c5e85ab91135d35ebbdaca35778c83ae88f8bd1ebc9960908d704e6530365204252929053d6133ae286d9a1654eac98ccf26d0e801be73cb78858aec12afdb130f33ffe26ab2170f8b621773d0959eac0495073d56f3344586a214d9f0e824d7c791664bdce7059d6d3a54a5d0c04e614be4c16a49080642414245b50103b40200000eb722100000000020d2cb78472992e4657288447e711436872bf3bc9ad61a43636deba68a1f4f0e46dcf4d15d7e46ab603b9b8f6f689729dd9a36eadf3e36946bfd16f674b2da0773005595fc17429169779f272e9a424d8dddb7d6ece3abc9da20f6f16759640205424142450101cae5ce370ebdc5dac28dbdc9f39537ce081830c9e8e1d843b6cb9437db86553889bce11bff93af6f60420157f77d0c08aa788d0f826f2dacd1b2567a9178848e1f13a49c00955d2169a9a63a796ba170dd10fbe169c8d692c9de14150fc5fbbb2ad0e80144caad493a1ea74c7658008ca38147e8a618489ac12da04510ead1c08abc6f0e05a9db1ab9d49d60e48df6338acddd2d406bfce89a7681875197b89f36113caf080642414245b50103fd0200000fb72210000000009a7f9713a12850bd9eafbc56a3a378c154e352fedb0af8e8305189b3971b6a73e3f80cf7b7f17ad38ee6528cf9d5818f4e5b2ea618339233dd53faebaae35c00d69536d5cebbf7e132f0104f480d857c7f30fc214b8446cbf18b9bc966d6eb040542414245010136a9aa6a107594db81919b52e5c3d3361c9d411c5fcaf7891e9bb6ebfcaa3463313a9006234d9b03259c3b70b0b5475728df96f4a2b2f5fe9bded4b3736ec583fa86b3020038a2e245cb8133d49da753fc24c611b9f252418b5308406c23a7a12ed0e80153d6b750ddb0570d6a53788031c5910632af1af817e97ccf578ece41e39c286d243d7712cdabbf7ae85fb6fca11f5e7350febf9a65240e402ad8dada4ac3b98e080642414245b501010200000010b722100000000064c55ced11680a1ede3d33795d7deb0ca7af98f1f91f18dfe5d896044a1b2b4667262c3ab54d1616a1698b9a042bef138ce43461878b891dc6b6da449c5747008b38bfd1ca343791ef0896d44b996c7c7137b293160642a32a90ddb32cd3be0f0542414245010138cf37a4eb4061ae0bb5930954649a57f62aa189016a2e68c88d5ad2a4a57376656514ac44646a5f04e61aaaee514b048a044fd4aba12dcbf94574d46e74518c0a49461e2af3000c926dd79ce18dc2ad91397ce14dd502004d660a1bef31d69832d0e8014ea91b2aa6fbe761de6d9060c3dbd8a4d277a5a60025aaf19c9b4ecd45ad212ec6d21479c2e7d0e918d1b3ed6300a451c5b7d6d2c00ced7a00571e8bfd6373f9080642414245b501037102000011b7221000000000d2c97a042670d2d9fa3d1ad2e0aa5cfe527ba526776fbd949da7bcb16e6a671cbb41199271d0d401b7aca4e7e5fefe55948d5a3982eb3e0426601d0ced7cd4048a772ddc6c66ed1b2b609760c58cdd64078987077047e406510954e4ed94770e05424142450101ba3cb46955fe1debcfd4c15490f6690fe7e2861985efe55187658ca6ffb0d022d1c0c10ffe7f001b46f15169b97f49368f41c40b49e24c5a769f61e3284c23842ee3655473313aa62172758d9490fff1e0c1fa5f2880e3546adaa0b674c8822d36d0e8016f2fe7800a76750936e21f52c16f5d39b7786c3ca4d0b0ba2d16e04e4fd38708b69c279e6e5a8fe1c6c157356698f3e00bc37f3dd4485920d8924cecedde6894080642414245b501037b01000012b722100000000044ef80895db540ae23ce2df24e3a72948fce6a46e1276e7a98b8006a9e96b16ba96b92f55e9ab1f69ea34eddc1907645f63abc906f2ddfa29109cf5776e6bd067fd6b78a9d0d4fe04b466c0d67dd149b66a150a720d9276f435337470f01520205424142450101a8364ac39ba0068ee1a1725ab477b24fec49ee649da6b31139d0a900ef75a06ad8af27f1c6deb9701ca06848bb7ac4c643a5f355b87b89e21bde8fbdc3d543808c70e9da67fca8e9a1ac5312ccd5ee8cf8ee69bc80b60c25f86aa86e3b3a6d9d3ad0e801588c1fd95b04d23a9fb55baa96391af3cdc04b16231f0fbe8dd3c3aa0cbbd5599d4c54fbeeb864a220443f79d6077be9df65a7cc7b1511e0f7853125774dc18a080642414245b501035100000013b7221000000000a0e5215740a561530fd0948a5855c72512775c392906e93f93329fbe8cc115029d4d8a6ed1ff0a694ca3d3ae16411f03d2531f5d0fa3a62b6dd5e79191c265046d03d94c6b8efa10f21ba0e7c4e3a13a0e982642b32a3bff4b3ca18d761bf90a05424142450101f877f7143b56b3c922c46bace69dc007b3d710ded77287cc0073e1116b5124720dd2a6de375aeb16dc2cf4fa35bfd3b79d91a3a1eb4244f840ba5db0efdbdf89fedcf192f12e839399874aa2bd813b2bc2bf19c6422e74f242b4b1466a97e2ec3ed0e801e2d13bc9a83ded5c72de0a87ee39df11d8a9e9d68623a985b0e062e1db425d38a2a0b892c164a7bc6567732c42ed478e9d96a43ccb1e06db56e0e61e2ab2340e080642414245b50101ea00000014b7221000000000260f4789130ca46659e59ac612ecf6276314f27a3e6ea0d27ddaf932c86f243ecc49b85c1d8a656ac411ece448089a0dc4b7ca5726259c4e3e642f6721490a0b9f1a28b01667ccbc753f618178b3685ed5b35e25c0aafffe8f44ded122cc7d07054241424501012204f255a609b72856be66b1d702b4c1b4ecc3882cc8a698b5b5e7f3a91e6a2a1c9694da5e8d67d3573aa37e977d024eb5482f90a6aed9899f39624e23d81c86610c34252a1b3426a00880919064d7e28497d489d3caa79f97ef49512ca6e6e842d0e80195bf2f2a3f94c7639d834675057386d420b786cfa24a8fa431d79c54e4b722a84d7b86e809e80a307cffd44e6dfd6c627cea0ef39306c5cd47518012880f0309080642414245b501031c03000015b722100000000074abed1bdab766c0e1a01daaa00e8933c7c372c4b2b4867f023b541218ccbc08bef115c8261063df10dc14c97a0a2bbcdab7902859b13dfe184d6cd8d1d17d0dd1bf46cabebf652ce3f8416f9027f9ab32b036f7afd1cd0f8f13f7a21ca22e09054241424501017453d77dcb953a4721e02b1c287c9e9e4c30498da853ae42a557b95454b2187e40de65a39c5b90a9ee17d9c1390b425d174ba04c53ba272ea289cd7b1d78158ddb30c20b86bc69665674b5dbb1483b4a1dcf66870a08b4e4a72b4dc0aa88e90f46d0e801a7d80118ce0f90c1c7e5614b6f64a3cd3ee069cb7f7a725a56978d42512fde0a97ab6d7578f0931e7ae1ee25f3f99bf28f27dcca84b7b8626fd5053a67ccb07f080642414245b501031f03000016b72210000000008a6fc5d3ff3839b11d8a7c9fc61ecb600b7099abcb27ae6d530c408bc00a86731e4489c7adf5d4aa6ecdc4832d53dbb085e172f502486fa7709f4ceae8e6400761e2ffb65279b1bf174e2053ed91eeb951654c1febe25790d124df6468842c0b054241424501010c49f7554be71ba4195dba14224950ffdaec17c92a1044346ffbd3db1eaddd5ddb3955d4c6bfa165857fcda344ca87906fb8850f669ccacbe26207e07c50078ae3c9f2d4ee8a60b6abd1a3cfa7461ced6823c13170cec62abd7d4e6377f64fe34ad0e8014157579cdd2172a6f1f9fd9287eb431b5bac42163e19ef30d37a7ad40df54e6f6da560029c25e9d0bf52d14693e351939d28e0d671610d7405a076cb0871f712080642414245b501032203000017b722100000000000923a4b7e9e2aa4456b72d06cede11b222a675d662ad74545407cba7dd5a741200c2dbee5ce46ab8cda66c7bface7d4999f4de7b429233c76cade06b764ce0a5dcf34eb69f13e41ab52e0188ed8a5e6b429bb737aba44489017666ed7d79706054241424501011c93f3fa940ca8d8128ee4dc4bedf1418e36c2b4965afcc6e57d786e634f87500e6a6e50d4eb33df7d5abf668b1b63fb926641f22bf6a849e74053e51dac5e833ade3ca78a142f4d9eab098b783f6e0a15d217e27af37d5ed02cf153307c38874ed0e801c41a53fbfed356b1f057702e89641063f87f2543055ae4280e79dc94fb0cc76c953cd3489161a5a96ac89b252e4b74fc205e8dc3591b0c59f0dc41d9b8797330080642414245b501030a01000018b72210000000001eee5b6fa617c482c0ca8c1f2a48a953b29324c0002719094944dbeccfb7a93e1ba4250b98f56157f25efed89d261d1f68b850c212ccde86c4983f25abff890b74960c1f7ff3a87d58f4d556266b065da9e6fb45e57351fd1ac1f45a5b5e320a0542414245010134a073cb3b43dd9391243082d73b5bedb56e7651381512cc5db0c101779334050413b414706b071250b31acae17f34931950a326b7606ab60847cbea51cbe4876c160ee31773e483de26ced0df0e3385464d7289e80f86e1e6b9af3cad8a466952d0e80163a7099492784b1e8d608e9bbc99e470ad9dc6aa20483c24dcdbb52ab835c63be95f795ecf31ff4a3fa4e508e000f30f6a2c7e0ec810d18ccfff8e70ad8a3608080642414245b501011c01000019b72210000000003a04f66c6637b40a1e4ff54a0fa9045c76a01be3da3adcb2b6b89778a932911ae74ba51e5b1774f1d60dbbe8bdaf95d45ff6140a4a488497bafb0b87b20f1c004e7c71ba20d74496e2782a059136e1d2156aeec8099135f0493182605a69af0205424142450101aaeff69e257ea39e17a36bd68fca4ea275c046d014e1a99b9e4c1c5dd9507128e9d71e4f63a1f131940745ffe22a62147d36f9d94c76a43cc0fa2321d25d598411f26084dbdc23525a2cbb8608dc12cef07497559e61d2f260dff89e896c359356d0e80165b086c739047201afa3c2068b6a429d349ffe9526928ce3cc8b69c021eb0d04f8f3397b2b256045dc4020b49b3240d74ffe94770dbfc6294bd39ff944671613080642414245b50101870000001ab7221000000000daac1e1f6ec9b067a6571b16ad9b2b8db8bb7b18c652f0e5ed4aa2051b287c1fc11a17f8da3cb327aedf3207916c57059f2dc31e900f2673d895506bb000e10e14189fc94bf54608743942ed9a6e0dba6f1aee47c4f59009edb8292ebe8b260405424142450101088cadeda85af8d28810c0a5ced7f0502bc124d89703e2052d3111f28522037efa57e53617626354e310025cd48501ddd9f3a62e403016934520d825701f95893f0bc65ef94ed9bc69ef4cafd9cda7d9bc7c0c070f47cd669c670a6cebc396ae5ad0e801e08fbdc3ce1219f30e5759a269c9abffa1e684ce8f30a128a6caf97daca4223217a01394aa9c8daac79abe1587229d524c1c0aecec85b767213c746124339553080642414245b50103c00000001bb7221000000000f802f07363f88e1be6c126016f85055c5ce2771b712cca9e176f2032159278033d4aa1dcae6fc3e0309ae0055341caf771ac77bff907db21ac0ef38906c6670c3f0a0fd877bed371c86de145a7c30bdc2708bd825feccc7f863298f34725dc0a05424142450101f4ec8e45cca77e9fc0affafeef38ff9186c999c89644060986190202c191b1565c39add38a596f67979e4ed122a4e2609b92035f53df741d809e604736cc2f809d319f829ffa62e02c7fc2a91c1fb4a21003fb3f01679691d0788e6e73714c765ed0e801d9249c7f9ffa024c8717d951bde9f8bcf0c64c9e5b6d8f8cb9b529f702c8016328d1f7c8fe22c2fe4eea860a7fa96bb4470fc7413229b94b7c273a2d7ff5e56d080642414245b50103380300001cb7221000000000be64048a757616e3b6a4242cea0dcf2d5b9c54c92d8aeeda2bcd1d39f4d3ce4944333f173d2083250e66c5a1422d6ee73c8d283be239badf95fc362debc4630eced62f944b8af1daca45b32a993c505bd6d2a96355f0ed6652f099154ef9530a05424142450101da6230ef20557627d9cdca228c2e98af4952831490486782377cdad9b7d2ef086756aab818b0801e1f6e52d9ac820dbe3789b6076b47a1240a68bfdb4d118c818152d6c0b305acf9d754e32ec54f221cfe8339af405d8eda5f169f864add83de62d0e801e60f736a5ee002e6dcd9dc7a67013d248a0e7f9a7d24812f55baa4ba9d99026c70af32fa71497a3d500d14ae73c985e45d839b2313e92e38a28d8f68d5b52bfb080642414245b50103380200001db722100000000028a9539ea0db53bacc277b53fbfe2c69f1b4afe31cb160fd34aa5b15768fcb527afffcf9100b43329ee214a1c8ddadffd98bc443fa50fbb1418a5e454faeaf0fd5bdca55fb723b67966f4485f42acd46acfc9bb8233fd1ef9947cd69a574a00005424142450101223ea984f82d4f45292ccbe02718364d5d502527c8faafddc8198abcd35fdb43680607cb46bb7fc8198e3556e0947518aff1485eb0aec5484863965a9c112f8d1fb029cdfc1860addb94da43f43e7c7ae8bdd7b13bd69b112dfc12691536c75f66d0e801c5d00aed603fc4057c6d0c457165677afa0f064a571805bbf89554f244120540633160647fc20cd349bdab08e8605d9af267f103da0da274966db9876331c652080642414245b501031c0100001eb72210000000001445f9f3aec557ddde665417d6291d1c12f23351485fae7e4749e9fa0b4f3a07fbf476eff7345d7ca34d83a5986e27f64543cd8e6442152582e0ab9a49bb7b091c713f54a49c3f895075049025dafdb401961348470b77448c6e564b26efd60e054241424501017814d973c30ddc78571cf961ae6364eff8d532bbe1b66f7baf68b17cbd4f4c383b577f7db1c2ae0da9f9011608e9a42b3cb77a55e44ba256bdb843db82b6218594e24293c29540696c2a097ca7154b97871b78fae410ae090e21eaf05b4d26fa6ad0e801603df8098e912b39361ee56072ac361d545db6d234acadc6fc49e533d9ea938f5c664ba25d2070eabb8a4df6e9f073ef4a267d23fc90b08e87271638c4a3dca3080642414245b50103ff0100001fb722100000000068ddebcfe02f60abcc87effbd4a0651a4386433faac398f1add930477ee0e418d049f8e890f1dfadbc9d7c16090cd50c145c4e7e6735b3f8371719949b97740291d545fb5fc9a06f06c5e5aa311cf7a5845fbbdc9d27e04a61f8e92e0d0d7a01054241424501012a44dcc63ead924f9c8e4fe4685a3db67b8ab87921209ecfde279db4c84f3806a41db3ba7dcb8bdfa790ace842596762a255ed9e350cd43728402fd938ce178faaf87538c3ed3a7e8147450a87748bc897b9f1cff7515c33cc26bba355e6021d6ed0e801d6fdc791e50c52b95e1f45b1a1d6ef1d5d22adc851cc634488adac2cd74f4bad26f971d43c0ca237c1841ba3db8ef35bce268862d2967297e706a189ff477929080642414245b501012a03000020b7221000000000e206d25b14b870c38763f4fd92eeebfdb6e44703c403a433673f63a58613c751660a118a7b9e2f40c4184f053f16bc7833e69ab1c0106d3aef92add3be77c407f46084c56b1f675051005f89938848d90ed0aab170ec3f1877edd256885055050542414245010190ff07ccf9e6f24944f1c7646a3f61565308bde9064a567569e244eb019ff01bfc5444a4a2fb9b4d17cb46752de31ac4c1e6f7922e0eb5b3a3b9e5390c2a218dd9adc6eaa22817e193848e465d859798c70eb747981a281a8aaed411243b2fa272d0e8012d8cff87316ce099594527342a7f946101a09029fa83a19cdc634c962fa687365e3465c8ee9c8c642fd70387a85bd1582a1a6f09d2ef0ab918b27e31067b80d5080642414245b50103c102000021b7221000000000daf32ceee5c16cd379cdc4e2bc5d7fe4bd829905ca7f655bff5b29ba03ff40671cae4f794c15215c3a36c70891d45d78a2a4ec86d70a1fe43894ba53a09d080433f9755d147befe3a33a3c5a5b346223824a51323f315059fb1ca458a4347d0b054241424501017a42754d04104874e4f0225238cf5e9587201e59fa6239d830aa2fc5efbb9718d82b6a6601e67203cba6cdc2263bdb6f9ca380dff3e143656de9690b9425508589e55f6568eb78bdfcd5f1c37f3540f33b58db6531d75eb5530f46138501c29376d0e80186627b21f9060d37d07a05970e2cba2e27504ae4ad25e464e74c8806967f2d3aacf4e1d4870b1da409c0d31641df3515e28993f3ba15e98043af362c267bebe2080642414245b50103dc01000022b7221000000000a86e764f889fc741d8925e76820768b7e41d479e73e17660b14eefe11a43a36f95d1f317611b2d61408133ea1b72cfbcafcaf2c6f53f9ad2b4ec57addc1fbd0b7ddd5cf1bfe229456abf4eb6bd5adfea3ebb29a0a642af3a09216ebb46c4e20d05424142450101fe0a4a6761d43ff5c26ba7934c494d4a9443ade1c4ea4660b375ff7e11c5c07c6f6171392b97a0aa06b66d79c366d5d8883c4cbf01ba11d6fe8c2d184a99678333616f7d68ab2612736735a356ff5f24aac026479848e85ac30c530b932f3f0a7ad0e801d4710583cd22c0e41ddaadf62ded5d565438bf629dd8546146a870b6caeee94b5919ffbf7d4ac67f798e796805b4dfd4c0ac55918eb54de50d09fec03a0f817b080642414245b50103b101000023b72210000000005aba67acfb02b22459692e9b193e5f1de4039c50b2318148e1f88a1f53b4c00cdd52c0b6ecebf3654c1a221049eea1493325d1cc771b343b5cc8b3eda21c7a0a401b9a07c2669118e1839c2bd8c646c079b009e4436b74b87b133440ccf4460705424142450101e80361a9efbfbdefb1d072af30f8720b36e6b1c97cfe8faad5968e5c4b80be3048cdcd991196c0b1dd6fdecb20865a804bf401d971041d1aef8ea1f49147168ee2738df99de6305e689f26be030f0aaa4ea1a29746f05aa7676c84aff97808087ed0e801d27a96ff4b48b9ded8af4bb23bfe5ea21b11cb2fcc587df94d60f695a0319de56c81e5e268cb53d8289aa5325a095760086a26c2bde764c69cfe067adf7c7801080642414245b501032501000024b72210000000004c050e6127e48a370d7cb29233218c433481dcf90a3d2a8347fef3d126a6ce766ab56f9d9ebb75d93a1f13dc9c1583b6286de9b082da6c18f78544826d8f99075da827a3a3c8c0d242c318f0b3f23a29941853d7734e0171b6de86fdef458e010542414245010158e5f897ba0f35a685b90ddf654a46c27232837fd1fce926e13f9f71a222bd5e9ded09a9d473daf68f2618dbc300c93940c9a76ba212d89d36bbd1161ab9db8f7e6dcf5ed6662dedcb01655653bbe36c2dc94ddfb2e89bb92586f4b1c35813c182d0e801383b2905911cf0c836a985907fc6fdb4c6c1a4b4aec789c89273e8fb6b8f955952b41dd7d822089e89f6e8d0fd1f2abecf86cb6985ad367602e435eeabd46f4d080642414245b501030900000025b7221000000000a2ee6bfa01b8260eb7ffebd5b56dc788d3aef34e93e1eea1ce50a040ab9e7668a3836d582123f5715a232b443e190c53528a9070b9e9c0ff2248df571c01ba0d2ab0ac460349bb6f3e0f56fb257e788df376a562b84264f2e4ef03b8d402240e05424142450101dcd5c62ec8c88518c7d9dd20544fd924a04ffaf32b2d7180feb9cf94b8eaf429d1aaccfefc27b753583873476d67b6f2cda24dfc84670b568e5642cf0d16988d5acdc4ab99b41c9848c72cf458100260cb4c6da8d40da591e969dd24fb1689d686d0e80100c61b0c95d0eb7677a6c97681068b978fbaf9d6ff27added76ab49b9260e3331a94ea3d9d1630fc5b723fd0f047d13c3c3f61289647f5e9c6307881d91feb60080642414245b50101d600000026b72210000000009419ea7796580fc00cfa7c0952a3fb468050dd49d0f21eb52c26f1dde196d55cbce4ddb9d942591366082eb8719ec39cdaa9616d6300c89aa9ea12927539320471f743769b44f018085c31195cdcab24448eefddd8c07a2a26d1dc00172e120d05424142450101fe11060538a950b58d2694aac56708c80a1b9407ddb7ae41e402cc0df39e7b1ec4de48e54535c2d10bcff1103cd56c237a11c0cd0d87294c9f7d6cf0f8666c80e104be85fddc24c7c33e41351da3d4d61dd1a80020a575374233a23333102ed68ad0e80155033a28538627432162d80e9f09eadeb9a4d345f1d3ee8cec09c7aefdc14ecfc8b2a82d1d6a379250c0104e5c872ce427d50e52b22b63e8f393dc22159922ac080642414245b501030d00000027b7221000000000c6ff94b651ab13c02ba5cdde599f958c039a2543cc4429c64de41c2c00093c4c78d3bfa36690e55fa3195ad3b24b97b100370ef14eb5a010ecedb956dfb2690b754f9fb23fd3267cfab8a5f24fc81e3bac59dd890b2bba0e980612de0b25ea0e05424142450101a610819470f3e0fd08e21c7a21c4e28908a97dd65cf274bfd3be32a922c54e1190e6ede1637cbbb8479e827d9acedfaa705ee0d7a4cd027c7a9ab68dd342a18df61c5db730dad2b5faf2498ebe96a76cb59e5cc3bc8321bcad7bcc07820afbd78ed0e801c2e67a0a192ec953fd315e1d456505d036d90abf39fffeea8551a55abddd148a7417c9ec96db777cdd7ac8abe383bad0ebf50cf5e85de57ec7f69588806e6601080642414245b501018d01000028b72210000000007c3449e8d47d69016e486a0b598523eba82ac004a98296a0762fb9a68f3bcb6ed7b7ea48316ae15ebafa3ecafca68be730543f9edf403a30a7dcb0d82755d4087a71ddc0567c63fa737b876821a320e307d4aeecd3f99e50d1a617e104e529000542414245010146dcddbb6e682028643b11ac14b69bd4d8b5c16b064438cc7a705cd7f113193492cc7fe533109faa4afff74e99e1eaed26d2f05add4470554217f056c58b2383fcf69f52992a71041d3cade511e3c4a453e498545dd415736b597f2fda767ecc92d0e8019dd8314b7be8f7e66cefa05088aab2fddd5ea0d1784b4984e619bd8ed2a7c457f9330c210dc22524c5207c394a0d9e704a807efdbb3f25d0524cbf6cf4905909080642414245b501015b03000029b7221000000000eab41f59f5f0edeaecab4e0e82b23cc43a7b405b8eccdb76660d37d52c56cb0953a18bc6fde3a06f43de9d1e500e7ff49c5b36c9bb78881817a04dc21960f70c60f6ec9416c84d0f75c90eec272893dada684173f85823213f70f8c70d86570005424142450101141e09f9f230f71a567148195479c6b5de774feb938497faa0f88e1d60fd556fe0973bd423774bee86b25e88e849ae758e71ff1b8a1d6da53afd9aaf29a71e8213665fb6dc1af15cc8d713295f8c195efbb4e0fe7093aa65e90eebdee1b89d8096d0e8011370d679676edfb10b0f7964f26fc230acd424431531efacfd917077aba2aaa1302d2706e87c6d6e8f3110d1eccd30cbf22e3961d8f440aad75469b0d179c4f7080642414245b50101700300002ab722100000000076ea3d63257d75191d132dcdc1ceb21ab1e5e214237a6de55e64fa3768030253e4f268d1050e41b0b75ca3ec16e418d6df924f458a096d5f42cc7ff666ad1b04852cde250dbe909b8b08d97dd13b21825dd6d1e520e7af5bc36b7950cefaaa0105424142450101e22a49c73e5383994a5649e188adf1237a66b10e3b23c54eff0694f015966a5e2c5ec762f3001d01b0db3bf800b580f36f2464e568033e8cc0e02a6c074a7881e92f262a3b08508f999d197debdf49fbc2b771352142cc879f3ee08480d677079ad0e8019939cb51de453a15e54c4cf53fe2fdd1f640be71a895bebed046859ea431777f1ffeae94c0f35b686824efd59dbd056412fc9015f8a901703786847e61ca9015080642414245b501014d0100002bb72210000000004273791931995cabd4cf2c133304eafddae86998a0209040c5756ab26ee2715efa25773a55101a21308239fd39260c100e2311e58486019bca7c41f9aba71d032d92c58f614afb2414da7dca64c2ef001122e9f02cc34877da81236934ee640805424142450101b2b0d28f3d3ac3c8b438ca8a837a6fddeed884605d2e904d2317a314db3c324830dd056dc9357ada164521bc809f8e2d589d12a468c7e4c118a4806bedf14580397324dff6a5aa9ca2ef9b555b947eb3a278c763bef0bd7a8608a66cbbe7689e9ed0e8016144120ddb327f8c8577095b7c9af723955a601401204a9646bb46d844b9060f7f2d39a5df60dd516ae94fe1f9b732143a86b7a27e49cfb54b81d09f15581fc8080642414245b50101070100002cb72210000000001c0db7ee94bd673b4dc42bab6246c601888ae0031956378e11b5ac3fa836b240645353365a443a48cc7374c466ae2b8e5b5b52920d88b251e77b59bc0ac0e60a5a228d30975b383cb4b4017a8124d5f3235b0cb29dabb5e13e0d66a56b9f240405424142450101d6f50447c3ea1789d74544cbcb7a7e81b78648965f9dbc7c75b7965d63ffa623ea7380b58755b0ae63ae846b9bf8830830163df2a5e159d2368a2fc5dc05d987f2380c3738917e8e3fef21701cdc32327d0df4415bb65e93a6abd17578cf117ea2d0e8014acf9f0a97515be1adcfd04de50f08f757846588410da4a36b55de4d096e03a1c203a09fe301a754bf399526e6dd4c351144e5fc7b212b56e22d21e1a2b00aa9080642414245b50101d80100002db722100000000068479a85bb19ae5b0dfd5e211998cd619e72c923102ec7cd7eb7c76e7bd4863aeefad5f06637d0caa902ee2668419a9cf4f3ccbb8baf772751e73efc02e1b505ff299e998fe66c1e0032f2a4c6c3ecaff6a390428ae85782e0b244516cb99e0505424142450101023e7d5303b7bd651cfc12b0f6546f7ab2eee0ff8281cb72a5de54c045d9105610baaf11f02a632893182b0e23dff75ff22fb9408a1bd642acd59b03f432c088fc99303cc8e43a77dfe2f0903ed4f60d925e27d38387235ca250d28437b850d5a6d0e801fdb3c581a21efd40bd5fed82581767884d42c3caea6525a6c8178b8f2420cb92344e4d7925dc0669821f15f93559a0abf3a9722e17a63628f5a1ba56e5b6d646080642414245b50101820000002eb7221000000000322c250a75460c0af4199d4720aa569b7c099b780c1d1e7c67d6e99b98c17f05d99f4906ea701de6f4456d6282a3dae2183741ba21e2e31a98cd716fe9969807f4033b92d9f3a00fae363877a41620923c5e60eceb6b41821e4776b8e56d790005424142450101b60ea66c0dc1cbd7dbb7afd385de64d0c056da1aa926409efb488ea063146844040af6e9d1f51d836e03536a407bdaff9e238f7135b482ddd05d0c75f2a9eb8cff55513babe6e81aa0aba6865ae497d4503e9b6ba8c78543de6b6ad5794ac438aad0e8013a057ee4ffe47a7e6e6464c0292ccb4b20a169e5233b91707bdf32a1a2b0eabf62d59ed1aa01e575cb81c9d862be3d3e236de40362eba5997c5d8edb3ccfb7fb080642414245b50101650300002fb722100000000008fa8e78c2285076486c70420f7e466911bc2fd8774a9386e229c992fac0d37d5a303a1e3c72af1b4a067371831597cf1b1db41614a19578dbeb20ae5b5a2c06d5b259510ef2da365d09c6fe1299ef2461d4288f32b02aa79c4d7768d1e12a0d054241424501011888e7ce9815781a1825c66395e50855a7e86fefcc1af975fc4d81d6afe2032a0b169582bd755c7c9bb08cd7305fabcc7cead926c008dfb0ea9172c88c5eae82d1e809cb9253e2ef3c7ae127e61d383ac9b4777fccd433c297df13b3fcc4d673aed0e801dbaa12602c8831345ec402b7c6258c303f66c8c0989cc24e44bbd4330500296e53c563a9fc01141ac28a3432b6c7db7091a3355cc8a9cec88af9c1f01216c7b0080642414245b50103a701000030b72210000000009a5c793491906c930770bd4b253203fbaf1e04241105355dbb675a2cc7b2d45049630e0f88b4e22b8dd6c87687db190da09e3b087d14acadde66559d4a29ab051dc656ac582ee1803f9fbfc8800a5e4ea3ad005a4166535d72ce235155ccbc0005424142450101e057db13b285a1000bbb21ab2d97aebf1a3fb53ac25eb6ce8af8d382bb8a4316889e9ccd3bef7de73208861a19bec67844c8dd716568e3bf20290c962ff2ae88670c795ba8b7b2efbffa29acb831b00e584a15cbbf8496e8c3fd968be7c52afbb2d0e801c2f789ec997758bbef933355c2d19103fc36f7e855aa558d3464cad687c68100ac86178a33e2bf9fc2ce8d3a6be51b07a545763d88b1e7a1896609086796a453080642414245b501019001000031b7221000000000188f6a9018a1ef56564655ebf5a7f15f76e1ef1a31e2763cfcb6d7c9d4ae6a243eb9bb32a5d13f9989ac7ad4cdefa7dd6f62591a6ee7144c9eb377da04998902f12bb4f5f5d53efaf45d5c00092f264cbed52f0d980b0004c52eeda2ec8d6d0b0542414245010148db0058b0fbb6fb4b5220488cd9db6342411ed792016accdfa6aa432f3ac31dfd92851772069191d1d144606da44ad53df8483432b3e62e5e28a4b623852b86b6afe91e69270d563bef6fb06f94019b3e180ff6b44a1f789d7050bd43dc8a93b6d0e80148204541d96d4fb5098a5f1657b067e91c245304b7b5550b42dfb2bdd5dce5184f23716c04896177a809444eea589511dfd2f219aa5e0c75d3a095e49994af48080642414245b501035a02000032b7221000000000c694c2a62547ff354887ae1b5506d9e28d649e39bce404dfd182baf12e118a50c17c1185165e85cabc39456005b31736e7dbcb5fc93a2bc38a00cb2a29cf2b06f16c24daa588fcaa23ef4ada4abacdf57f5117548522e7b03a910f20589f370e0542414245010192728d37cfb62d7605903334ea0e350accd3af63c2726382e9595083f31a7a6674c59dd37cebe7fc1e94a2fa7538e514588fbd226c9318650baaa777f127008f7edb6a4a86f04b8f48420b6dad40b7127fa4dffa07d5ecefaa70502565db4b31bad0e801f5b8abcc6cb4675a8bda752dd342a6761f742407d5694e1b026b480082c14b22b04443fb4f5f53a6350e6f03860d822e98b4bfa6828eee7f329a9b09e5ca44ed080642414245b501039801000033b7221000000000eee0bfdc9ea6a744dcc1877455fc34151c293da0d5e5f2cbdb36ae2599b98b3bafdefc36ff3efa6f38695e099ec1ce137d5a6562dc27fd7ab6f28429338af40439a1a5cdc04a5919e0e17045d0c75994c5dd32027e81a0c3607f55ec022ee70805424142450101047dab35bd45bbdda149ac1a20a60fda15b26e21eec84fe42e2597fc78f98b4211e5a4119541c6688603c7b402dadae9bfa3bc9a847d568e0cf89a898e1db68f9ef4d5f22b2f38be5154eb45a140ff35437abf86939137ea33f10fea2d4be381bed0e801738c5c51265f7b3b0867b5f0476ddf25b8f5b75d133ea0dd1f42edbc3acd055df96aa99eaeeed69b179f432ace81114c32b1cd53a0b93340996d9cd420a9a036080642414245b501031d01000034b72210000000001854df01d67760aa6d674b32d8eb39dd77d77ae281bca5d903017fb9377bdc13f51a96ebe0e0712782a8a999be68f527bfd673b2ee8ba5153b4d6269a2798e0184416a8d8f9d74e51120952115af66a9903aeaac6d7aaeb794658020468df70e05424142450101ecfbcc1dab9484ac71fcc9b11e80f6eafc22801b27a12b940bf64fdcd5f19f1194c43bf6c291731e0de29267bcb237acff4d4a239cafc3c67aa431df92dd4388584fa07a20ed44770ac3059411e629ec0617dd8176655ee0630f9f3c421330abc2d0e801b6bfda535935a50c5da3fefd5acf06a04c6d014d421674735070acff4d585da82796bf0b7593c570d43241e94883daa865ea2c934d28d00569949f2853c812ea080642414245b50103ef00000035b7221000000000caf2b2f2ba4e90d4c14fddffcc63ba8226bf450e6a080bfdc9245624e743587921beb9d6c7148bec61de51b736940fe8782cfe099e23a10be54178b6f0d63a017beaf48ffd67cb14901233b92d4ba3a0ff95a51ef8ef9b3225dfec1a03f3980505424142450101f00ce7eb9284e4aaec3a38cd7d5b8e6b22cf47a677e739561c6544f9570d9b09b97e16adfadcd9cb7a44b3bc26ec31c62b1fb2b9e33435ea4736ce8159a74c8c870edbac08281817db3d9fc34ec4b1b45e22217a97b858908b042465597a5464c6d0e801f26c11211a0cbf617da38f5d2b06ad71c060ed92d71370c570db46c84498a9c86cdc7bfff8c8b85ea7f7b85c88fdc8f93aff4c1f2d31139d96f3ac0b69726e1c080642414245b50103b502000036b72210000000002e1721aa1521901c7e57c720b18049b691bf223382e6ed1d77622b41f003092c20f23218536befaffaea2c1433e07864e27935e52cbd6587aceeec9084b9230ccf1af63c3c9e1c9c885bd64a752aa9d93b6954eea46d35ebf075d943ec7ad20905424142450101b642dc868cfd7ec390304e7e3d748bd8eb7125d55d00149b2aaf3d99fbf4141b9fe2752fcfb8eda69e0b45b960dae658ff2ae914fe0454e0a41fa64a14447c8fede4f79e6536602bcdd6a8d7ffd87a1c74f3aea803255e701ddfd96388e09a46cad0e801c4f08221097ba827a514d69bd3f0a718bdabd232fd9baf8ebae09f8598390842ec3949aa797016783c0945680a640e2b3ce21c0a8f056ae240512a1e5017a283080642414245b50103f602000037b72210000000006428fc35bf1f794c0c32d513d4a0455bc830919a0208a2474551a43b8760227f54fd922db4909f4abf9437f3cfb667a5d5d8b3a3912c97ebb5e9dd230b71600f5494273e63587fcb845959c9f0ead42931c1856403d052679f411aab1516de070542414245010192ec9f861c9710d98576993a929bfaad6e4deecbed80c9b6120f0cfff3695040e1eb3785bcaed5a83572167cda24bd3712741bcc36a74fe897dc618289f41a8eb97d0b3d0f641a338734ebf9e2f930eb799094d568a97fdc17419a19fb1cd3e5ced0e801a9ab03ebd022b0ded4953fa714ff92e7699109aa44b5f67bfe62714268ce7a90c6824040da3bb46b3b0c8c7fcb52efcabdd0a256f32907f4b0f0514164d59ef1080642414245b50103bd01000038b72210000000002e489e9c730a60cc6b879849845ae0b3b40c45e5f60c648e8053d1096a3a1c6976ff597e8894cc22e1ac32c096b627400275a21d969fe46e3ce93680a1a39606b2f20c389e00055fa3dcbc17deb18167d9326993a5bb798b5fd35f49a0e5a10b054241424501013635a0b25663e599e36bbc0aad36bef82430389788e9d5e0dfe384ec1e28fb6cdd9100123e945177e7f5cd6624c15d1d97bbb980a1e93b2a28c4c9879d10928f1a52222dbb08b6a0309cc04489764200e22766d688bf414b596d3914031605cad2d0e80106ae7d0777c12ae1e83173b6849b5a7841c889582aab18fbbde1d451297993ff82748428660bba2f4530f96eaa4046e33ff0220320dbd2ab476496fe9848536f080642414245b501037301000039b7221000000000a0a491555a05a3c9c23ba6fa44ea6a52770ca5a7822cceb27e4ea710dcb2060d26db3ca1dd94ad88efe3608192b805aa95cdcf09c19198ec0faa83bd862694099d4d39a0fc823ac2c51eec337e6ea51e92874ac5f6416ec5345d2592ce29950c05424142450101c8e20dec18383bdff75072310c2676fa1b51c1744a19dd25134864070e418f245221c924411385e739e0d4fc9576932439f47f65071a4358df4ed9169ae729866a93f3994a31be9ea31c0f3074933b6687193d87e2126c0261cbdc368f98f564d6d0e801aa07340fff5732c231525fe584987d129aeba78c423d492455b7e21711016ac813e63cd89645031fece896902a898d10aa7d50c111397fb03896898d92fd2f7e080642414245b501034d0100003ab72210000000003e7fdae893b8c04e43d76eee6665f5f7681f86616f7bed427d963334add43245c546120ae0104512f346924d730b7a5637d2b31dd2248a4c27e817af8e30e30a279eb19e796cbda495c559efbdd7a4af86ba0b14165774c619344c569ee64c0e054241424501011cf65fb2ff275e2da140c347943c6e316c1d7123e92521325c9e9188d77142791c8c8b764ee27428dcac480fb17975a6de66cac945d88d38627640f1281469817bd90364ddaf2ef7e439051f4976a57c6162488da8f28146476120f32a84f373dad0e8012a6a7b969e633f5f63b17d0063e9037d3fb0c6c8de2256bf9412e1f16aa74abd00f0d0c328a6092456ace799088adeff2d2044dbfa1b3db00e3089ed6d04ec60080642414245b50103a90200003bb7221000000000fc1a99bd49f878e23ffbf1f8c4cdce3da01ddb484502857d9f8213bd145cc67a23d37990e96e8454506e6e7cfbf55e34037ff3c36ed6c96457c9b6c6f114a205b88c22b8597f1f458ae7951ebf74345d7f20f458b142b68e660301ec8e79f703054241424501011445c5e843be6b10a8c8b74183ccebf286d6049602ab4d1b93137a0ff2ca793faf72d02efd42576ea25976a1ef3cc506e7d99fb7263eed06b9fa5560e4daac860bf2ca846e49df75ae064e077282fd9f317ebd375e71097eb09b8897207a6e20ded0e801f675b5a6fe4a86a34b0450c9759197bd653790d9b6718bdda460b9dd326337625cc7ae7d07483225b93ae99907d33bcd49871966c92a32fba1ec95918ba09be4080642414245b50103890000003cb722100000000092e4f3e3d18609c643d1c0fee8ffcb6381b1e44a7f8b531daf80fe266246e54093be9de7efb3641acf990865bcd7c282175b09ae98a5b0983a545f6be9cd6f0a41d31ddd7c9e2027fbab58535770b522e2e5e0fe78990a83b2421e157d0f510305424142450101761da9b0b4262a3a01286807e1de3f4f649b23fd516135dd92c69ca7edb1ba34876f4607a34a46e846dad2cfb805a1fd94dbc975bf9799e6bd2415393d2574842ad7c01bb2e16ddebc6d91970b7fb01a2a4357fe19b542d327f43334c10d4bd0e2d0e801211fb787e17cb60ba26c73a5fcdeb5e41ed80a936eca2d9d40aae040568476d37832acb458baea0b1d8dd4b9134bb0d2f53cbbeea4d6af25b677a9f3b010a6f0080642414245b50103b10000003db7221000000000dab07eaa907841a5cb6190a26811772dc363494d5c70919cb3ab7982ed6e7b767f4cd850e664fabb0a04b5ff36dbf05ae137bbb9cde292ad82b92b47ee83f5039f609cf746a085c8349e94816c55b62237aa3beeb534b3c943018600f7e2be0f054241424501012a93bcecb4c6e7d25b5fad5b740dae7d7b5a426acc575b512ffc74540b7d284245244124e7452e3f9d3f060bef511ce3c530fbae1be301c9be2a4eed0ea3968c91bb49f7cbf18a8b8598f9c1b2548334ce5eedfa3d05602690309037b389c9b1e6d0e801b5f27887db1c8124e1d0d10be4072ae914421a7698a22cd2bb4b91bc6427fb2cd68624e554fc1cec31450136095da7bf9436545c54f0cedd15bcbe02d5400548080642414245b50103b10000003eb7221000000000204151c47b5b0e37ef42d4ef3ed81fd75b276c3b8161c39e1703fb5e6fa1192ceca733d5c057d0ac49d3d0510413d13dfbf751416aae0df6b02f74b0830e64033a3ff77268aa6e1b96020a375e3c19d5882c45afa4e092e995c2f282839c540105424142450101e6423cf50a84eeedc36dd843714fe353f0af7d3e5ed02cfc4c3d3ea1609dc20397f20724de9468710d543f26d18bb820b7029c07d7bb23c388c8ac09c61af7876c9d020f5c7f359f7ab2fc99f0b15bf843083b610cc67621f03acf43c412a553ead0e801c19862f555381ee1ffe71ba323f5d2c2e5af7b453b4e233d2d1779de7a5ee2479543276492e884f130e244d34b7974224aeef79d0ba96789a0214d1f6127ca2d080642414245b50103e30100003fb7221000000000d6d1388c6f04cd8f04b55dc4d5096ba750c8f908b4b4e087145a848d86cb6362a98f4588ce52420240357010b27418a85f1871fbbb8ecc132ed3a727b577e70f620c0fd9c69d27a48a167e81af1c64efe492a0641cd19dd43da27ab35dc3660205424142450101ce63d91d56f51df776eef6abf5d7e5fde1b745e12633e661f87f730a5f53914ed4bdf2674fdc3cac69acae534ed65b493cff9f968ffff0524e61591329819a8ce56d6f15ae459967244747073e98d487bb6c2563b023211fc3774994f6e21f0ceed0e80174703dd713f45fe7cfa1edf6dbc94d377578b84f2ca25f9223fa79ccfec8f6a2d30333a27c4ab69a8b1f72404a822912e35713ea510803c23562a3e626f0e755080642414245b50103f302000040b722100000000092e538f258eb97ac42fed5907fe63dfbb9569d149c3697ee1fc3fc6272ccf428617ec612867da6be304d11591cbac189a8355389d0599259568e99fd043b4a07410b2c52b688ee7707f22c75420be161f042a8fd86479e751a0972646620320c054241424501019e7a3bdb31396433b74dc22d22e2ae4606c3c78a421140f0b78eacbcc67f123484261ba49746be8d95bf44facff8157179932cf59de2aadc7a72208e49fa708ac1a7518dce24c6f8e28b3fd6162ffdd54c65f5080759b8b738c49c8b88fa28fdf2d0e801f98386be01c92392c575cc18448261be2778d6e01a1829f38cac29827bbfa8cac0c72ab442b3cd90444d619f0b88e2ba5ba28abda00da19ea795b56754d73970080642414245b50101ac00000041b7221000000000a286c9e9ab499f6915194ccc89717f414cff353a07197d759c68e505c930046f11c8249e64bd60aba634ee165c8773bab988157fcb24aa079a18603c2296f8014fee05de65e4a562612619eab68bb10c472c571a0fbe4dacda0292c83c28da0d05424142450101eaac361c93e410287fdeb39f3ff072d561c088a2c1e7e8e501aef64e616c323b9617e631deae8ad30e7aae5e1bcd6ddac743af88a737012a3dfbb870949ae082fbcfa118d0d43c9917396691cffecbc290aadb81e2f951cc7a0316b3466f3754f6d0e801ea344128233029e866067a4ae93b90e300162efe7db5c7992ccba154b7957084aac3fb388b3d0f2bab4f123205e4c81bf1803d8c6a6fdca55eb7b60c3ec628e8080642414245b50103bd00000042b72210000000000040dff9a8641b494398d51430dd0b3464a6c66536c52467e0142a3b81469672e913e19b3e4f345bac9830fd70cb07ee94c5c7f0ec416bc3184b63c79992ea0c30e9f293b136b5cd91a725b419995b74c66ebbeb9cb14448848556cae9b5040b0542414245010180df039e6a310a1a04a01a3b9f50cc953efe73fa0779f799ab9fa1d72dc4b03236bfd5e0082ea5df6d0cae226666d6133c922ce0d8caeca85483017360ebee8dd58db4574b98598643feec022b99274e89a542813a4e34ab3d4989980e682f41fad0e801c48372844721c021bf027692db05b333e9587ff528e3142ef357aece689ee987d726e61ab723904a54f78e808af7a7c1dae8349ab38b93ff7f739e76f3855155080642414245b50103fd00000043b72210000000004838d525c49cdf17a588be636da3cfd56f62bafcb433af240845868604d4c81883c4e3c07d87e1c09108b3b4b4efebd1e7cb3d19fc29505a7c4e94c0b61a990da5b52b6507618aa8ff246b94e4468cd0b39508ad894b659c27d2f02e202e89090542414245010164cc675423d08bf904da6ed65598769b6a9488931c725361727b57b2df7a942c1c9cc7fd15f15167012890858e12d0a5a32b3dad6f031d0257713cea9ebacc84070bb810d17080d2bc7e3ec5174c9f2beaf43868a43642d5f6b64e7b5c7b27d3fed0e801b137b09a45d8c55829825f60107924aaa8325ef0b990677ec4aaf57c088237d75eda1014f600ac07a2cdbba6f06d56b81089baf3d9b9fe4d3aeb9dc39e016e6b080642414245b501039d01000044b7221000000000f0cbdb411cf953e66c7eeb48962e398fbd86a01be3b41d0a102d187707090b319a05a4fb5f3f79ebc912c9cebc9a6beecea29bc9511b4dbe8d00ce961d26a40c742cfea2a4b7a0882ddd58d0924515941b26b43ebb3f5ae0a071a0198f0046030542414245010136373fe1946f2f435156ce87dfdea8c6fc59e7d2e56c47f6f430c12875882a0154b09026025871d8a2a8659dcbea89b47bf366c49c5ec76ccfc778ea4a23828bbabea022be254aaffdb078e9948c080cc7dba4d9e8aa91b338e006961613b67302d1e801ff0e7f32250b02b8c96ed1668464f07c91c1464180e2aa17b554032560c7bec3cf606ca360468ef359dfb5e039702eb90c0f67bc613ef0d2550b6727310b7044080642414245b501032700000045b7221000000000ae954b807f6d367cdd123b7159e5f70fe727e5680febf383d9bd706594e57c68207aae22508bbb53c45b45f6faa1e8e1870bfd5c2fe141530f40534e23d9b60e6eb279dc720c7d3a8e4263a606a9dddece02d215241c321e5b604404c96b640a05424142450101e486d3f6001ecb6ece4ef10a86ff396d0447813143406bb1036c8fb94583f55011e4048835235e263504514a9e7191147253d19ed67f5f28bd8eeeb06a1ce682eeb99e1256b83c241b4c0783fd06e12414775545f79620edb0ccaa387d52c28206d1e80101031b5b3c76f55b8e41480d86f1598bc95591e851b3cdee4d8191b90ceb76b3519fdd7ed89d1f26feb7ede5ed2a886957ec513cfdb0ec6459bd2071378fb2d6080642414245b50103d501000046b722100000000034d8c94eaf9b7cda70e420498c389833ee9d4f1270af9e01e7b24af9dec504467f31dfe14a48f6b12f31cb35564ffc2873d7f3e590d7a462a76c6898b28c7b0ac6e52f595deb69cebba8bef1385546459d948f0b0851d4fcafc3947eb513cc0c05424142450101bc3f2baab9048492300e5dce828d1fba5ee2d80820b8c5fd69bfed82342fbd5d77dc11e828402a2fe458597990bcc1622105bbf1d5c143adfb843577e2be9686a8f3940d438a0cc206e8644c136e9ad1a329b167499eb6374a7c95dda06750840ad1e801346d1f753606f5c0078e31fd385e41a56e1fc55731f36531d1d0d18a3b4b5fc938bbcdb6192a6dc2ae5b619c10893c8c55ca68e3e54f65c24df7b92b0b348783080642414245b501011a02000047b72210000000009ce76ddaa5eec32f36f1313c0902f1512ccb69ab2e595a438e919353074b1e39e97e5040ffaa120220775d370ea5299160d4789877256c0057bfc2f7e64fa905c58c4ef2cc8b2a3dae843551e356445d6619910b7cd9344b3a3642c46cc9610a05424142450101be4ba09c02bcb0ba1b243ec7597a8b920486603b1295e6d6eace0d546bba0a60dce4b29d32e9c5b6919fbd837704d400dfd535ff1abcf6eeda195d9c3e3062860f3d661dc75fe05d31367dbbbac97269b929e293eb3a4e19dc6665969e24aedd0ed1e801c67a6e25463d55d31465967521e6492b83363b4caf98db13d9f5137866554539f46403433ff0a47b4752fa6688549fcd639f5e9a353d0fa24835a6ee7e48fe6d080642414245b501034402000048b722100000000034b2898c77cdc92430c1ca2a401f5880b99ee3f4049e61c77ca7a36a05fa3120535c2fdaf77663e8911d4b84fa8b2aa1b3174d481e964211d24bda8dc2b05b0e1f00e1ab50eaa9beb324f1e71212481be619f91783a115cabe6bcb3cabd4810e0542414245010190fb212d53a7a71f72196bf98d256ba33d5758d6cab936ac4dd32ce2102d3564ad8bba051481d9f389d3a4977ee79249e97e8e8fd8eaec6dde2cf2954022cd8468e4dc8ad09d1f27ee846571bfdff84df3b9ccb81dc5239434beffe5646dad7912d1e801c6bfe2681e628a61edeeed4fbdfe105430bcec58e85b80a7667daa6aee27f1244c2c5bc1225313fe6f7269125ddefa49023f00253577e3477ab797db89e3637f080642414245b50103fa01000049b7221000000000800bfd53f8258e22802fd642a10183a9cf4742c6ba8e117e798806d1e98829058d7905ee46aacb5d88782783f60b2d98dd1c9ec0fbcb1539e96765bb463b000ef944776443f71e20531a9fabab83d45a7a2fe63232ec96ed3a1825ae496e620a05424142450101c48c37a18d9f91b4954bd006d7fe96d13e7687850e238caf9a10325f1a1b6460c722ab60d4b5630c2379b08f060a65a8dfed07b136adfdad09d0db8d0ee9b583c1ec0f4c88728fad168c2045684662fadfa5553bd07003322a7a9c85c47601dd16d1e801f59437ead289f70dcb727b3bd6f17663f0817b615c2f2ce725af3199c119a94e4ebc9c63646a5a9d3c1c19ad2c45926fe59e06534ae1793766490532609282ca080642414245b50103580000004ab72210000000003a23a12105ea68728352642c0908c49caae72361e2378c78d349be0f8addb567a8970011f23a35c03d1149466858c60dc523ee36b801c2a334413be6085bfc002ce032a8a23e9123bb40d3d27eee9722400cd1c714c879c6111462bc50d5de0505424142450101601c8a6de00066fa370b22fffc95b9cf51a0d51f37c5d6aea14ccbca730e1f738c0a54c46776f1fc22638de20b4d70c48788c729a32a6b7538c3cf6307c8a281694320e6a01f30f446514b2ca558215fbca4ee3272efb41a8bbead9752f747f31ad1e801138347c408d659b3cdccec8ee85644401dcde4079e2528628043cdccaa0e525095cd9e11ae610a90715deccd44e9a8e88da58ad0a730c0d16a8f9a7b309e9ff1080642414245b50103d90000004bb7221000000000364249c0b7c35d2ceafa7a97dfd7fcf5ff5a987b9a638b1d471d6cf8bd6a265d76aed4a13eddd996d69b1cb4d59a060ddba8cf1d09f8979118133f23b4b5bc0c33e63baeed848c0fb7fac219d7bd1056d7d1e0116b99a66cb63ef717ef1f950d054241424501013cb580c8fc5c2a07d9dd4bca7668a12724e26177907f4d8906460f848e024a19e917648c9f2f6ebc62a2d14cb8ccc7f2b82d96569bbdd72682713d3d5d060086e88179c7fa042054a745ec843d9adbb092a07a3005e34c1b465b28fcfd6f3d3a1ed1e801ca920c15ce239b19e4ee419c7844c2bed71aab21900855d2c8622f2a3f6ca73ffc3f2751b1bb0f9ae0c67435f8fa6a0631e2053b8da8cab5037d7822a3bf211f080642414245b50101db0000004cb72210000000004274790c3fb8a9ca7584e561b77e73677a98580276ee39e19fa1f60c030561446e0840802ccb839ede6fd25d9656fb5489d47c14c8ba0e560c0adba2cf294005e8004d67b95c1b53c8f55255565773c60a1c7cf396351fdba71599ef081d3f0005424142450101b095c4791144599cc2ad8344bf427df953f5050ce003ed324698e94d958a525372e0914069216b5064d75361c3add30e20fe2b0e3b1210cfb14a471759b02a8dd3966c26b8c8e2f633e1c4f68f88c4caea317d43876dfd9bf48fa265c4e2365a22d1e80106862e8985dafd079f31b3ed7262c42aeb326657a05e8f58c90c7a8d867368aa566e19805851e7420ce206f784e23c7c9b74006f31d76a4b0cd841ca151f51b1080642414245b50103690100004db7221000000000e0384bbdf412012cf4458e6141d70f5bd640f032bc45a394974a5a8f9a0a4b7a969cedb23fac413be09f967ba6c2681f41c9f09cf43fb2e139be2b5c205dc8070432dabf0d596f3442bdbff4e760e13e7578961f61cf26cc1f3a2f9dde9f39020542414245010176bb87c8ce3fb8f45dd7a02d4cba7329847660a67ecf3c215205c46f973eb85115ac86b7f4d875a459b3b3bab1a33e00ae7224e7d704d775a0ade52c02618e85f9f937d6738fb04b0c9f6f74e09cb76745681ced2610c2909eda97426a428e6126d1e801437c3adeefc1aaf0c2abb426dd71249101749b9f918fb9e16bc63f44cb189ebe23df90ec66ddc405f144f1f23071cc028cfa43bbc5f496ba257f0ea8a9c54044080642414245b50101140100004eb72210000000006cbb942edc7ebebd37bb0d99377e31eedb50de02e1d69f20ef75833fcb445a287097c4a05f68f9cd1f29b7fb699197ae32cfd09f8c28786cdae8b05514319c0b1acc2067ea94eab8d987eec581f754dd09117323f503796a1f3911922604970c054241424501015206031626630b1b641ae85ad8f7afa437bae942e4d2b724c7c44b005e7d6e4df50cade0188c808df62e469fd46e1a1e8591815ae67390b4820b1f58d1dc378beb174790f0c74207d80e536a77fceb952ca95bfe155ad0d5bbcde86e9a8b0c062ad1e801be04852cca1cfa9231d275a46ece56603606095ff1079947af46c0ffa2e07a5c525d8bf144699255cb905e949741543231e3d5a7970abd885bdf300856fb7b7e080642414245b50103250200004fb7221000000000923f4bdfbfaf9b0d8659521a9e672f5ba1e322f1cd1ea5a6d2e55b70824a9a569042a8ed4d291ca70616a83b90eef6e18858baa26321eb8dc72d4bb202eab10f53d2d90f0d7ef6105a5f427c21a5445603da332821163aff75deddf0fa929a010542414245010192aaa7f34f1e13ac5ed64fe914c483740c34549175576ae70b258c0ae3bf48317ae501613491eb4b3a2d89140ba72605afe47d607763470f19349622e4c0938ba303126257bdf1ae4ebb7271ed652b263a79b79a8f5b1cca0d0566d8a3f917a82ed1e8011acac5956566ce7b94840af3d8c0344c08c9a9ac0486756d763ba21fbc0850297c2ed8e248ee492207d048940fb1ef9af7498e29c594645af1de903fd98ba0d5080642414245b501030303000050b7221000000000c65f2d98be1a447ee72949e6dacc4c4a7b0ee3d737e1718101733d49964ea5562efc4118aaef187f9a496153db99c3ec7d055a3e3083d346dff07402e1819a00246b38ac5e4b45204c5440a7897ad90ea99ff3ccbaf693b498c1bbb79c111c0705424142450101069741c5f35f46874f11eb0e90ac96ab1d544ec6bc16f0bf65f93ab95daa233c9d16e293c47fc615f4c2f4e88bd897e93f009cb3bc6f2a3d79ed34e726412e8b690cf2ad62ab9d68ffc7ba0284d27511c39bf3d6b29a8e48b54791369d03e68f32d1e8016f6fac921a8dd2198fc3732563a6430e1b0fc8de4098aba93b3637c17d4696f4c134b6133403deb59a25b2ba27b33dd55f165df571349ef0cf240d859fcc8d38080642414245b501033402000051b7221000000000585101a5ea098869cb07f8a0c2d87402c85f0f31de22859b9a8f23f06c56d5498eb1fc2cee4c13c69bdb7b504c9fd6489945a27dea84c34add7a4ed3bb1e1807d569bd70c25cd1d7a87dc7ecc39ff0809ff2e6f6aa32b371088598649880480605424142450101bcec303080fdc79fea636ca1e6619419fa307e23a24abe91570d4e450839b445f0b718ea566201c32ebf09faefe77c6c710968abeba9a99494df775c625c4480e2b2855d804f0094806d063fed684f311872392fe9ab1bdc1e7dd66056f6380636d1e8019b91c7ab3e172fc8c3ed09987715edada2880fff6b3b5a804e4acde355483f03a43e4f12a0526344ee35b4e6b099e0196e567879ba026a334520a615a702fa95080642414245b501016d02000052b72210000000000c02988e7a92bf1fdaf3e21cb99ea8da91bbe3fdf6b0150b4d23721b05f4645b809f7fdf9764cce5f7af7c470e25e557b7dd9de7300ce8cda78654827bfd6c08b4028292c423ab0070ffedc8b3833c9eababfa96ed1ad96676b2903befc8e00b05424142450101fc9acc534808e0f0779d7f6a583dc13ee4284824395ad883b6f1c3fe2de8091a8c1c96f981d871015e8441037acb36d1d26abee72afbcf99034e6a6d587537827703be36100bc7fd4525ec15277221cef0d362db4033bc198ab8d2851e6b7a163ad1e8012699ccdc20c4b763740657b141cac6c5ef71cf43250b031b92a202a018df8e40fb43d8004a6d01aa5959157c6cd6c1107eb9539806ca93fd3f6f18782df45b13080642414245b50103b901000053b722100000000052603caa775330a6cc55f26234dd68b4f98fd7a1a52f8b94a1bd728978dbdb34f8e0d04d6776164c92601cf19416e49d37ab0bd77b38769d56f4b074795d4e07fa801af1d5381446c3c8b6a28c669c6d11efa4455d73eb837fb3e8f679424309054241424501019a532e9c5bd5aad58899cc04c805941a5f006a04a9e5dbaba3ec138e07ae4d36d1e3a050bce6e2d45d13fa56775e4c0816ce33209320e90357019b333d1f6d82a166d5014b69f87c5631e7b7ed03f3434a7e7071d0da3ba59109969e0365e36d3ed1e8017433144490f6b4d37210580a46c5fae7a182c4cb033c2cf41655d61795c6ffdb239ea4c5b902d95e5714942b9873b1a5c922d206acc39d54e6d7ad1dcf5bd25f080642414245b501037600000054b7221000000000a08e65ae684a8d9f81945f01480644d714510c8df2b113d4bac14d35d34da452a7d894222205578aab9cbffd55bbcf15b6550cbc00d2d741f8e82eab5fdb950e17f5c483c2a2f684fccc9da8a24316a08c7ecdb3e8ffdcafb2cea2917dfc9a070542414245010102a6a93bac76962ada64cfa5803787844d19c88edd50232d674995719e828868987a91fa7c7f63cb7f90a6135c675ba5bf1944e582a0ce4f32be15278c46278c75fbeeda8943ffc60be9e8b1cd182d944e3451045874700d180d74f1d6735b7142d1e801ec7249a5db56b4136940b45535e2b133ca84abfbfa61a7e7627ff996c9a8bf2aa4ff9e699f043fb2bf50cd52f7d09c09df1187d4fe2f983bdc842f0ee099af9e080642414245b501030801000055b722100000000046e00ab2a5967a3b0b3eea0d58d57699fbbff47a52bf0a2669b2f96e9cc27774216c81e1417f2ecf080913be80288cb0c5a27017008ffaa48b2147dfa2094e07a7739107d16b02a918882e5aff1cbd23255aa6146c63fed1af3252983a13ca07054241424501016c1637c084e823b4276eaabb2cd0adfea041638663ca05d2e41cbbb5514f1f7e3af89846626e38fba9b8079031caebb0c99f1694a782875627be3b6734c73783d3692a6ef10174908f0b4464069aacb5ade8a16a5fe8ecb68ba283f914863c0646d1e8011f44ca683969cbaa460c9e86f60e86e2d0a35c7abfa39339375ce3126ba06c6d8e7ab5a6a0b8b151fc01651c04a4b1b5376863fdb2942d2f53f0d7c01ab07a8f080642414245b501012103000056b7221000000000ca9a5d9e4127778ac2d6d1ccf11bb73fa08ba286ee13ece08ddd128183228f3d5b6c98dec4c7bf764757ae34cd9f88c9def6c9f099ce1c76a4f2e43bbfbaf60f6676a8a59899bf615b65906661ac1c90ce37d59afdfa60fe27eba95a9963f00905424142450101b6cd15bb98f6bc11948aa26e607dab9507ee082f103b513932cf1837aaa2cc01dca6a9399d14c19759c49cf1199a5e36980ec25fab6e70776fbb6b2a93962784af1890e73ce2e44a62a6cef94d4ab55b0f7305c2a48bb41095491f999feac0584ad1e801e569b478541b2a7aa44cf10c6a43d2ac8b742b7839af92266b338588cb26024fcefa4e30f34811990b42224d86139f6df1fb09faa4e3a0a6818a24f346c48a13080642414245b50103f901000057b72210000000006027e8ca76cc6ef552ac69968ae900148d579f6afdb22f9f0d075d35ea92a021ae928b369fd98bb435868b0678baafc5bfb01e262dd6d6995c1f142b688f8f0bcd5c44bb9e16e61d452915980354376d9fc87d2c5ccef7b1eb32696c0f90bd0f0542414245010172c143de403e024305129e2e2209b8941a3deefe0d49124579f4231af79e5a1dda695a03ded37433cb6593e50b93f2956e925fb6d72f5b05cddd26d913582b8b71c468fba8cd52fae1a406a97050e904357d15c745e9dadc348e8fbfd7a5ed8e4ed1e801dd68a6bcb67a9c8a24ce4bb8a23a9577a16358088011e6028e30947ce4fd442f0e067cf6d81934c46c7930bb590ecee4f4a570e30b0b6c5add4c8dbe42fb02f4080642414245b50103f700000058b7221000000000fe5a3c2b7affac548051eb56ad2d3df25716d608e96c04b919d901cb116232090a24eab6b3a1b18ce179aecf112e872280223af7b1f585d8632a90b3d3e8fe01df3f59d86bae67037e014c6d4850fd6f135c1b0c267f1492f018b05401f55c0b05424142450101ca299edcb5ad481cd92f46834b793a5133c66c65c0d6db5025644c646f5dfb0aae58b9016fba0300e77dcb8b39074676c7f6d1df7fc8a3c1bcacef1535282788f4d9a49a9af18d3abcdd8460a6f8814f3a6569117dfbdf85ca1f11ed927b531452d1e801feb2499677c63dee85e1fdc1956c3d19f5a3670051b48dad00d9b4d2738b311c8bdca434549ce0994b6f7ceb89630479d8aef1bc6a8062b1de0373e48816f6db080642414245b501036101000059b72210000000009ea6ede1b28f452bb10e4751ad1aea2c5f75975f6d72e4f4f12db593ea1dd95eb5ff2809bd412aa9bba8faac53f089acc1d367da784c47f5b17ff5cd061e2602ede26ce89194c425b9064f991acf12ac7178eb255583320fc096102be935390a0542414245010100d4e400390952cdc0930f8f9594b481528622021993d9fa354ee7860255f839d6d6a1602a310a87ecf0112925d123c4dc576eb90026baf88beae282a3710888d86ef92a94a02e639fdfab947a8141db24bab632dfb2527917b9853ccc27ba2256d1e8015c81d4a6d2661a53c16cb114674f1187c9414c794c82f25bdb92648e51cd281b81847fb32fa4f6e527418b98a794fb36b5c7a744378b233251f41ca7460c8007080642414245b50103720200005ab7221000000000d8fbd024be1685b6901c6b2cfb9a2a473a52255718167cb8d6a8d3d76955d306082211be190f5818f65ef271abf3ad8a924964c6fd8600b7caf429b1a10cb20d3aaab3c5a2d2eb142dca11624a79ef53e83470d9597fc636f4fbb9c43925840305424142450101d80f0b3636a53b00d9352dcc28094d4d2d477105155263df837adb1616e53e74b8e3c2aae0536274cd912e0c7b75d4e7fcae833f1bdc78d2dabb8b3dca2bf089c560341a4ccba96eb3464071650623008721b5682acf5e91718dec787a8539bf5ad1e801e72d9d90c5cfb47a9d214bdba993c123e3b2cd35ef52f1b5cd40f36149271bdbc7fa3a223327151d974b7e553b70785a36efec8019df205fefadedeb81a9a45f080642414245b50103c50100005bb7221000000000e28e1da9f1fdae5f440349231bafcb644dd9f56781bc989069140f97f45b714057dfbbc92cbf7fedd396d723e3c0fd8a365ad619c1ee9165a4a6b3e6ab94b30f1b5b2e412c2aa25da32f12b304dd09c1d62a79d09c268c31c552aba2ee7d0a0805424142450101e69a4e2321e3c059a70f0934de56eb522f214d9b8c6918c95c35b66a82d46c069bdc69d81126e46bda05e48bccac8d5ce8e28c09e7d861e0a593234544b3f388cdc794e4ab76bc9142bf54019eab1b8347f38cb5c7796d42da4cdb9abfe2a4f65ed1e80161c835022702f54568890120452154690629a16afabb13953d174f1511c733de3d1c48b4556e5eec203aaa93748afe27b7d315c83596d32d4bf10871a8f464f9080642414245b50101360300005cb7221000000000461642789e470e2f4fc5d6fb01e299bf2ddff6f0b7ee3900ae3457313541bc6e65f3cc7cdca2b891a182e3133257ec5eeeb34f890a730e73e06aa0ab221f460e694af12d7f1c8b244acf95d26152cff5e916a1fa1a36602d8f8c333c402be70605424142450101ce3c3f724661fabbf520566a0649f9caf52b9f41556dcb877fad3a3276576f706375e0d70290a4f9ebcf89ec9b3044e9c9c67bd9c994c5ce0b264cd475e8e0807ce81a0fda4741a4c83f99ecddae200e8cd55630634808aaee623a5f9a673f8a62d1e801c1d2e7ec128834a3d18fc20d61a6856d96ff765cda2143113eafa3f6ed6b68b5ac515f32330bb594df7642c6ee95fd600b5eed94163a79dfa1972d5dfc1d5975080642414245b50103a20200005db7221000000000f4c57e9ee13e873d9bb8f25aa56c922ed6bece7257317157a4f992d50ceaaf341e0df860749731695be1ea8bff52307a46890fd0637657fa10301ee11a9380004655e793c9c14cb843fa3cab348a99a376157446f035a00d3400961d2759370f05424142450101025ef806214a046cb329065ece1b779b1b34de9d144aa42ca860f0b893641676322a20996af115ae7476401592b0eac7481eb0a8ddfb9f459840a2d563dec18288cc83097596a64d7aac84f271d5a431089d0b5d9500553e63609dd0a0c3a41766d1e8016e5cbfe89a64eb719a1e0199c4de2daf6b0fa77cae8352d975a9dbaba74ef5277d79e62199c52570e909a47345b8cc4a3a183b577db3894469c5ef24dad67ee9080642414245b50103560100005eb72210000000007e5a2cfc9b458f7518bd242dc5c2798f29eaa8103d8560460b9793506b338c63c1bf368b3b2afa29c7435ab73b6053244e1db58eb85f409d8a80b809e14cb50e6e82048b0a0d418569c6c6ac466486970a2aa11e494901991c6f179229b3ac0105424142450101525d8d79685b2c8a34e5624e1abc6fb9ae2cd318b6003262a44cf6538701922ccd496fa27a3bbc57281ef2cc68dcbf8f0079309c8982ac19e5ed963eb338e3816b810397cb447acad2960932146f914f6ab2c0a5fffa8a874fffc1b4f0433d5f6ad1e8011a6d63d968955bbc2051d86fa565bc9fabe3e938be1622efa0bb72c5cbedaf5e7af63bd8bccf843ec667de3d232e3368354ef514a516fc3427894ddbe731226e080642414245b50101ea0000005fb7221000000000f27573fe5f29d55129d41a144131d3bceaa469438e85962e91482dfdff815319b8fb126cdae01a2c352cbd7806c4f66827a470098da0bf268c44ab9277b63400bae62a4ed49d64adfa1d9d59308e429356fd7b3f8113e067b419c2abd87e4c05054241424501016c273c316445e3b6e68823dbf4482f54f8caacb60c5857ecb4d319d1283bfd6ad54e2b5d925ceabf62ba2d100b0222d299e041421a246e73f4d2e2aa8787fd8cbd6f8b1fec4f4e41ec0766585369e0cf7809c79f8469e6586d42b9ec7d438c146ed1e801d067473b5d9f41375de4152f3235b24ad000122df57b83fc11326318e3348c708a109a2be8d36eb7e9a1dd4d34e1515b56523c890f28b60760924fef192be74d080642414245b50103a202000060b722100000000048a84d33a6335ed03b2df88c258b1ccc84a033c4951f69e2b2d96f969a099e24c970a989e749718c0745e73d7d6b156361fd63c35a652832aca8f86bb09ace0a77e55f813794a7b31faf627d01b3c1e8e7e86c6069c4c989c7e656fa18fccd0105424142450101843bc39fa959ab7b628c8e8eeee931dc6091aaa43f06d8e4bc756bb388ccd64b8c9e404f7020ae172314a9414ecf4b9cb0f544fb4a161bb92b22f5318e6e5d873605d3057cd80b5e79327b94d2c616f16787447292fe726e68db90fac2845abc72d1e801afd3150e2fd11265f06e4c67a289e2eedad00786017558aca3224645e4f683b9bb725174bc7b74f08a08af45abbb32d1d126ba625000f01e8c2e695d16c746cd080642414245b501038501000061b72210000000002036ab41f364b41cb97bc194d83748b9a3b83e1a4e764d997a0e223f9f75e9789a3641f5ca8c005472b9dfdc5af4123f62bacd082d19c1b7a8e1882e61084f052bdafc3109d29bdafeec38eb4b9fe8a058c48cc96ee3de91db91df7c04576a0c0542414245010186ded43e70c55b11c52fe5f7532e3b33a4c2457a27979a36ad49ba87f3ed2a538dc5bfecc70bb7abc288568a6e3f154e62e6bcb21bd04b58338d78877bad118bff5016a1b8ad511b0725f3efc7855e4b744b023bd61ed96733adae06845340db76d1e801fd07f738db96d8518e64ab619fa036b4bc4e23c49ae781c63e513dbd576db5a461e08db54af9e06465b2044f1280c8aeb3797c20bb194d488a34b6ccce994cc7080642414245b501030700000062b7221000000000e6486cd88e9418ed5ebffd213def37dccf5d81c08e16c18379cb0e20b4f19533d397bdfd5f3822797765a543af39a801f4619c6384d3fd8e0f480541fbff01016e6f65f745b09d92eb5218904fc7f90a523f7d7204d3bc60258ec61667fe3e0a05424142450101f856cca2f2995f8f5f9824c0930be61a604d8cbfffea72d26bdd7a373ab8ef020a89b53a5911de282dadc9af905b79a0a1ef04bd2851a7ed5ff95132921fa68147484bc7c765ddff35cd0af630ba6e439fd2d2d66afcafe29e9a3d64f33fb8b67ad1e801eb386f314f2f8730880a7d5269d37a39f202a1cab6ea7dfee178b3157f759bd509db46b2ab67f6f8e7d1935fec04896037d8dc1667644043126ae887b1cc4728080642414245b50103a302000063b7221000000000c6b1adb3d429648d9a11794dd0ab3f4cb9820f09c0c5af6420600b6d04ce04566036425498acd7369397663ee14a148b8c3606337c3a2f4e6f203668e54adb0d2b936222e7edeb15855bc696c46efb63f34218bc06548a63a1ffccc77b8de1020542414245010166b5760f306d437d12b404f8c5a89372910d7c961d190ffff79599d1b44d1a10743a5d7995a04db80e0537046dbe9689c77621e2d90f552f2b65c42f7b33bb8ef54105007bfb21bec12042419ab178ce30aba6b89d2641f8142a5cbf2620f33a7ed1e80183943c5f0c579c88e3001a0da362d97b470670053336d1bccdc37e6bd0786e6dc563d88f1dafd21d0db64ae296644208d620640404a98b384fdc6d42e488adc7080642414245b501037600000064b7221000000000e6276fd9fe8fb3deb054d7024003bf8bb1cbe894d73b7206332e0fb8cf0369266026c36581bd0e50b435161e3aeafbc8b292a546eef8a9925dccd0af45e83b07fe6a5a12c9628cbe4f776f6fa57d494f7e0a6d8a24546b955f26b22f79903d0405424142450101e036d683c522fe254546a4360d819bbea9fb6467a3767d952de72f4510884961083aa43e69d87568e86d54feadf7da2c8c6dfb9c689ee3b794283a6dda4fb18b176f87144f67d06a77ff438f8bc77c3bf3b8522f3356c48b6bec85361c60f16582d1e801a08f04c4ba2cd633755f078bc5fc188ace241f15fb037963d5f222943c36d9244c3abcd09fa9b62cfc548397ce401ae92fcc58b931cef17fd52b401084cf2562080642414245b50101e702000065b7221000000000ba007db5f5865fd96311f761f2fef1454629e2a542054caaefa691396db3966728685acf7142ccdffeabe8390a124dcd10d84f6ba21a367db5951c6700bfa60ed87a1c859ca140fffa8a15c30134c6759404f3508e8e15a6d32e2901b231ac07054241424501014afe39582922600fd33c05cff0b28118abd1a4fcb1175b60914e0fce995a5b644067553f7dfb61da26d5982e4cc5941574727fb8b380c1b205f6b670285c4b88e51490df3c4424cbc886652974f241e94dead287a1e83ee575a585c12d1b7aa786d1e80134351f4c56aec5fcc438de18242508db11504908badd697f5f0c86bd1df442acabbbc33ef84f31b4357d514c012d941b6f26257dfe409fc14f34e985c1f73c87080642414245b50103c302000066b7221000000000c62f607f68b4facc86eed9a482b56bd80950108e837237feeb5f920d3249796dbdaa4bff167416d10c1fdfc28d8a7723a45562888978bf0cbdaeac6e31cf9203b073ae048d94ae451e3e3bbb4ff86f12ad007159a4ee699dffdaad8f578c1e070542414245010190982bdbbd6c7bcd0be2386c3a2bb86d457a9c043463cc6c11a769d046df3e3bf1bb268536fa13db22cf314bd2dc9543d7555a9c3c143d04ae3ae2778ba8e48681a69cb8273523118d83bf06aba2a35bd9952df9d39589ac00103538ebd503f68ad1e801942a907e487c5876a7ee36c6b70a241e1f959bab2f8b4ef2e19ee776dcffd61cf05add0d31da9661602a43ebd955c2b2073c3248f6c0d45d12b773e9735671b4080642414245b501030c02000067b7221000000000c61130442b8883dea9e5d2e0899d17e8bf8f135999f923ec0d203deb9afef41a2907a21450ff0dae96b67b4e3b0e2cafedc3c4cd63e4bfd22debf0c68cfa8108168930c11f2855cc8a686903403c2814b78ea53f260479e6a7073ad1003bf0070542414245010192df4958abcca7726ccba930fceba2eb9d1f49a1ff28a603502ae0066a5c090b2a9654d1bdc7211dd73156fa9c0f829d5785099185820ea7cef77e266d7f91812ba97d24597b7bb1ced9e59dda072eb7fdd242652d0d8c76078a1548da19e2448ed1e801098bd870c41f74a56069c1ca30ab9274f945349bf1d9694b338010e4b9b80a279d81721340c82ada17e8dd955c710dfb5ae2d744599dfe48ed2567259f98f672080642414245b50103b200000068b72210000000008a6fb5f8aa209c8cfa48ec50c00ec3b0402a1bc7d0038958cdb100bd6695c11f2a4c1b0a719c1fe8238ab51d8fc73adfbe63223b4e20117f81de37a0574a130b42dd74251f19c2894fd47a7f00f033c4002f0fd1a7b969640339ef2323302c080542414245010188cbac36513f62787aacd40f9059c9df31aecf0e562c6873d5e9424295aa2b42da4516f4d64071d61a8623f73128638d8bb8f695233305c83ad7c3ab430e05863be79521de1ebf4621e85b44e41b7c94e2294d40084be2d294a3b680b5eccd3592d1e80106c3eaecf4ebd6d32da2be06a3acc40ac3fcef4b9d00ca6c258699b62e36063c5fb7638a8f10e7538bd6ba0654a371e1588942dc28ec3f12652a5090412bc45e080642414245b501014c01000069b7221000000000143fffeaf15e79a86edc85e51bbe21411c61a27d60c0bb18246f0c84ca3b6a6fc3ab8d67e389f91484f41f5ec1acc54a53b4a0d3e1d4061e7ba80b12a9494c01b75d67c89b96b1fc0c6c545dd12ad8b4abf1a3350931740ee7ec68f6f66b6c0a0542414245010194944d263bb7e396ef86d545076b0867925725331bf36404f645380da4816c620419109304b35534a45741d70c22538144ed9228dc1c2060c0ab120d7a32de8ced0f6f838b4b54c92ff0e2a44cb514d0ef57a654b9c242e81e87911402014bb296d1e801dfa9efb1b27c7c39e30f55b4f0e545375da9b478329ba7fefd9bbd78e2a8213bbaa97613e599ea7703073178c00e5dc452a1987110a9b687a0e127a37a2b563e080642414245b501035b0300006ab7221000000000e8f28263fb48544d5cb356e79bef8a8645cf956ba26ee44da0712f9b0bb860101ba5a0a109d0be69068da66002db5d26c45d48e2e1b41317ce889123df326f03c5e6ec92b3048164d976a29e8f1b5a8f464252b8ca0c11e836ac12afe1b2c90205424142450101f8625e46a4074c823d36bf642f9a9ab3f753e794506c5fd17a59766fc64416097c6cb975fd5c68bbfae5fa20c4991b5d9386649c28ef208e7f4a8b76721a5281b05b3a2b29d6a9e8a5371bc0da0b8752f550c13a6276d64f9b115d0a311ccc959ad1e801497db4b761bb11ea359102e34c757cbda9e9e37af5d2e9d9c06ba62922ab006fc368bb6687eeadab7287778c75eb94a95cdda2ea6048c6bb50637dc5a66c6750080642414245b501032e0300006bb72210000000003e4e09ee42eeaf037d9ec6b6e9a256e76fac27114b8e9bd72f893f81315cb07785ce6fc6040d6eb02e27c1713355b6279d610205255bfe03dc184dadefc1a601358e1ea4fb9d4904da43b9c22e9e7b248300f92c66ca213b07f62d7eebb819070542414245010144e7a41844fce882089176788e792bc91673209379ced58a979e56646569545692f8005911fad297234c5eb15be5f4fd0c9755db4c46bbff616a4284ba92fe86de1ceadef481b65513d233ad283dc0a73512a4ff51b351b62f534e3edf9842de9ed1e8018e404e041fb44a880a50e2f9422a505b1926eebf49f7ed4021615035e2fc1294de4df12734d2c4d106370d36317fa1b150229b53f002b07bc44f90f239bd0a62080642414245b50101750100006cb7221000000000feb2fc555e5c1cc548f584df8a066100efd93b3554fa695724daf94788f20104bcbc6ca0be6846338faebdd8c33773b8731429c922cb9c29e2325af9c64f64014c1b43b6848e05e5d2c3e6bf0449ad6e0e1ffffb07ef69f3528a1464ddb6cb02054241424501017e765180d1cb42d3856ac68392294db777519d2fa4a22a1b5372f234992c034bcfc3b3c1204da8e46c66f7e41f1b879d4022d42f1ed5512ce57c720ec6fea38d2b143b959677b256d40f9af60f7897f03e52f53c71e6b6cf75a4e520995bdf57a2d1e8019f4a80d95247b6dea17b54a1033c49296967f84d60ab61ed97900c508e3cef9bbb504a0ca43a14e6ce44bf2d73ee110343d76d82b157c3f864fa9820a55aa0da080642414245b50103e20100006db7221000000000245c97da745a6211d05fc77980714c6990e465ad7869753bf81d92679fa0e022f8e7e940c4b3597fc288fc282f26b36093745d8051c1dc92a66a151ee0358e0d164831065f5ec158887cedada472f97cea903709362ec2eabd3036b2b207be0505424142450101f0e89e096af4b76f8b5ab3e1fe06d6e76ce02cbc6658b37413af0a4d14692231d4421cd1bb4944ec2ddd6e66c04d7fa735cf09ab9ba7636a7a7186875a11df814f8a9c971cb6dae350ceaf3d8b172954f37297f41fcf37ceea8ee1b1717018a7a6d1e801ce75762be59a08442957b15212699db2c8dd44b7143a45a87e15a34f52cc9aa05b0b35da40a7009865468c80725ffe4d1a69e44f329961ab394f176bf2473d5b080642414245b50101fd0200006eb7221000000000ae79feb1d7c439f3345a965df922eaf58438d6fbec3718b803a778cbd67eb737cc99a31276bb4629432de1f0e5ceb54fc529884ade81820e42472caa1dd43b041c1e95b113abe62875fac4075dbefc815ab1f3081758d48b50261a35179d620005424142450101ae6e220999929a7169ccbd75f4b0f5faecccab8497f6d44293bc3fd1643d9c5f8c9da3e5937b907873d73651bde139b9def9a085863732f2009306b98e86bd8c76d97cbab7117bd21227027909e40906b58bb112339180f66f3be566ef46246faad1e801ee7bd25746f4a3d3813c38eba6700e856ef807f120dc14aa8af5603dc19b47a8a0d7727d9dc0fa526bed8f9ca73747c0693f5fe31d3d2a1e3f29b054089f6ccb080642414245b50103710300006fb722100000000008db62d57ee74d4c21930f52c3c969fafee0cb7d9caeff5be1fb786892db795c9fa252a672b05fbd1c613f4378d35a566e9365d40f711d4c7633bf46c371eb0e2eeddfd88c332722a0a8b13f220ac86d5f4838ab0bcb661189e51bfed503fc0905424142450101cababd5330a8cf99168575712aca1b191adea6e8769697e44e7b083ff430f9765548c6a63ecedfffa9b65b5578e9d41c24a2d34b5e6da21da3c3a995072de780019294d5171a1a2e8fe43315162a1f5b33a30949197105e02d94f9c3d2ebe83baed1e8010c8f4aba74b8085bbf75b2b4bc2333bd850828c72c41f1b3c90a0bee7cdf8e4267d10f21fffb1fbc22b5e26b3c9f831b8600e66ee3fbf421043fff3d70053b33080642414245b501034b02000070b7221000000000e20c46a1106b5e6d61217d786b571def3c80ad56d02df741a933e2f0d5860e2822e7b3d81f8264f30fed17be40ca74a0176fb9c7892b716355386ae1e50dfa030a0a2c0f39c50700c6c73e8f34564eef37e6c1279ff7f9f7793087f0c9f9860a0542414245010120e41472bc5685bcd88668d2863834a88a42cf11e78c6b0893d16ecf65f09430091a214178cd06d62419b83dcff2078833cd0d85775f06101562bce9e2e8ad8a7978be14107981f8bd98d074e2e9fc8fb8bf188c65986108923288cfac34c0edb2d1e801c7d577178fe1c0c8f4a1e0ee00aab00a8ab216f82e95d364027114dcdd560509828e6957932e3e372b7bc4c91a7b7910c39eb2bdf4a9b6f8d63f8a659d894808080642414245b501010602000071b72210000000008c72500e1f60e5c589529818f885cfc69a71a1f33fae34b923bfe5e705ef533cfc899b1cf77b694038a35612d8a7e441f5d7b471e958680203e2257f8b286c0a0d75c09b49211cbb2fe1756432dcc29e403468c31fc9974d3e52a0fcf4db0e0f05424142450101641126020177056099e538672e2a78082f7fcb173e16a6791f918bec1f86657668353a8b8a5a01d5e7dc74a6fcc8d4b8ad03537d08a265ff602d29eba597d2870bf1342a4ac02389d7f1c0921448071911ba6f4e0980ffa5543859cd6ede7bc3b6d1e801a543a4bc006a90d85246bb85069813d8041f698e20c3982ef90777aa1e6edc13ac3bb97ad4c547e48c5150b99a35c00768db546bb76c5ca0bca83ee5e0f7d6e4080642414245b501032a03000072b72210000000006c2bba866c8dc598cbc71a3b483672338a1d8963e61d9dc6dc508a2c065e5d1d781f63197b1abaf0be5a818a88bfd92bfcc4ee5370e282c631e3c810b2378d01b9a5d2c8616948d327c74c46c5d80f27f510203f5a4d4fa4518788664c22330d054241424501015a29ca447ec4937e98fa8d73ae203f6446fcd4fd08e05ab42d381d3f0671f31a371b0be6e2e143f07be4e0141ce04ed038ff7a3aae3bc76573e81a6390370482227d9cae65153ff399281828e31c5dbdce8ca1991996112a833a83bdad42b6d0bad1e801d4485c017a35c3696ec27957fbe080e1a4d664056661740e0c3aa36e48f71c2bc47a10a025dbf1ba2a805bdcb2c3074a6557aa33aa07f9740e48873d30aae968080642414245b501037c01000073b722100000000030149631d5fcfc1c04d1309b9d61032144edc18529d59783b2736364f7d29e1372ef66dde6f8aefc14535d5a606346a4aec5a78db9851f2d0848fa5e230edc0524d69793fb78a7984fba217d02c5a0c32c64504e2208f707b94dcc0f5b29c80a05424142450101505e44c6c4791c6f28438a3feabe764699a1c12f07e1e806fbd8f279b22b68680c34c9982e2da1a4b24008c3600b58006d2db1c67013637039be1912152e1d8b523146350f86dd86761bf03d7e495f0c56235cd43a02f0d24f215a7cac346177bed1e8017cd153a33f540651cc98c343b59bdde67c1c975234f43c78b39ab4a951afee431be29656c8678feaba954cdce9a3c8c8b8eb1c2d5b87bddf93240f8d7e1d3243080642414245b501010802000074b7221000000000ca5d2dc66ba6ba7d530eb409685666ad895bc556e91e70ba0b3b133e2de36569dc2aa903842ea92340aa2938fc9890eb4af76d8931fcfbf9c51737a8de0e0e06845d528a39c1de16625fdd1a8f4efefb450233d039828c632ad002a330ef4b0305424142450101ec87229c863b940cdce60f8bd6f125ee45d802e53900d4c78cc88952eb14525067c938674d14278d523c8da6a7ab24466af5dffd8598931c18b723dc1311c3807aac99c629977e045fcbd063f38412ce0c4698f9a3d9e4b48110ac25338e3427c2d1e80139eb47816677079eb6bca48fbc905775d8bc70576cb99e33dd831523b2c55c57bd6eb68f0a8d61873fbc01f1c8373213e59f3c29bc073eebbe337458b8d3aa17080642414245b501019200000075b7221000000000bc157e72b7f3b72d676bf1f1681ee867876501e6f0e667a0f596a362c145b61a0a31977dc47a33abc5e4659f109dd7cb3a949a5f306fcd9add358e81117d8508baec8439cc889f7e207ebfcae85d6679f62b217ad0374c89ace3fe44171cff0205424142450101ee4c827e583c43543591c02aef4bd0fad6fd60a7f4545882eaa57f47f3008b738d708f4fb9e16466e36cdb4e04f21f4c8c9f2088b6605326270e1810db7e068daffdcc519e191cd71ed6c5f9d90014373b02d6c03513b573f5bf5c37b4b73a5cc6d1e8010da45c135a336c1b8b18c9c3b202ab2262b090836c8cf6c327b45b68a87fdd98b16efa2238d9ae3633f70721c6fb49855ca12bfe47fab2933a0bac67f055b0bc080642414245b50103b002000076b7221000000000a0b2952526debd9a678676f1b1c442ba47117dc6b0fbffae61646dcdfa2dca1b4c9d5d2cfc1a52557c5fc1e9697668428f082edbbd5ad457cbcf54c24d9e210eb6fdccf68d4736f8357f975cb89204dee563b4692bd650071707f8efc9afba05054241424501019cbf73375b27bec2091ddf35e70b1195f9011f2bf33374092bb3e9c370c6776b95c944b9a19effb2c7d6f175082c1f22278172e5387a41f6af186d3b93c8738f93a412868332959029dee81caa4dfda348bb8767b20829cd7943f29c22a6607fcad1e8013a598d2965f147662ade5efc418f85ad30a7313869607429d54914f4c9b31ad1e7ccbf09c9f06fe1f924485d87d90bcdfbcb04ae63bd5a79ce26848b35f4be5d080642414245b50103e400000077b72210000000009eb1cb6eaff62186430709bf52e7369e50bc1255abd0e39bf9e41156ba8c5339869760161314df7690f2e94695ae676ff015097b4ff6c86f1d2efa42a7af10002952ac3a40367e38e5dba1a79e1cbf53d7e01511c4565eb4af838bc9c5c839020542414245010134c7ed3038862f99378df8d26818f7b9b54fbb8e7aa2379b682a5e51accaba5c111253041700415a39af90b2a3eda173c7d7707c90c906ceffc830fcbcff74810b002aa9cb0210c996896da479918ceebb2d1434c339d5289e2baae171ca01f3ced1e801e90345b847d4f62fa80257ea02e438f9aa0b1d3702b2109f79bca1a6c48cb37836e80d666f6ad62eda2249040cab6800c745eb13c3a62eb21068e6295db1569f080642414245b501031803000078b7221000000000f43f621d11d1de04dd078d004b117e2faf946409a0ade6ed9a28a73e87339c1f812cf2e84ad40a36296d7f2bcebcf814da7eff0a74f44e8879c993b46c1a67087b73913c9c767566349783f9155219458c8c727f477c19a4da6ea64819061d0105424142450101703964f11e87119abe8259438fa4610045da6be6377a7d891e5a76e2c27196782c7f896fe1b6f0bfdde502d8b43674be1c047ff52df29e00310b7a8a095b498cd5d2c8122d892d64a41900e313726a0195bd1e3e895a1b5ba983986e150b59add2d1e801cfa480ade830bccb2b210201b297879a75a34d16bdefb8d2307abce2f23008c96135392f61ac8d03029e289cb2b767bac9bb07eb7ca27f5898a65e891a28959a080642414245b501035201000079b7221000000000bc87d23cb8ec5aac83444561e5884a62bf4aa950fc0b693653f6910100818212152f57603314ec75d47af40bad4f9ad8541f2793321b056ce1a14469de57cb0d19f6a0837541fd88159d278fa2ef4dee9efde47365ed90d27ff79c9df323690805424142450101088e4b72f65ae4539cf68fdc58e7568a04b97233a419c0cdecffa9e4697d934479a55a64a1859d870218e49f65ab99de20300d87b101e08f79ccbcf794c5988b8ac0b73f4f1ad4c63d0b4cb440230648cc12bb17e92374b17a521579e0d53e01d6d1e801dbb4a1e66eec62c7ca61a80da7e1991a00f2b3dcb677d306340323ea08aac7f19e2a6a9635081765e343fc4afd1ceb6955c988eb442ae6532e602c80e83ecbbf080642414245b50103560100007ab7221000000000f28362d4b0d970466485cd43cb92c7c553ff1c29e2c15eef17968397ec770228b8a7180f132ef5267755f638cfd62ed90c7cb2b83cda50efc04e7328f6199d0149fc59c367168a1ca3d6a51050ccc1ec77a80a338f15ea63d45839f602a78d0f05424142450101d69484297e1a81ee2d18766f3625ff9a6ccbd0c0d5fc701a3f768a7c94ab8a15243046fc6a058b0578743d6e4101798eb9917e2901fab54513372b50571558814a064ce408b5af431b3f99570a21e717586be93093a8f32b30e204706d51080cdad1e8019fa73730c67a935e5229c65fd77d523b949c4833c4f8cc4c7e75fff26d2958313d078a98286350b6dce89d320d79ad53bb141e9855a38804971fc2a795cf52c6080642414245b50101d90000007bb72210000000006e5aedd0b452f3a533cd8ebf4a493647f6215ba960166f601a2b98f02e020850d24e5104623898e13e5a017b548a457fedeec28a5fd258c331049e14ee69d809a79a051a1b92eb43b9209ca72daa0c974f53bcaf5a312fd06bbc7aeed843aa0e0542414245010106a75060dab4f471ed21ed28db6160cadf7ff4d44484312d249837eb58d9fc78818f38cd04bd3eabb722f932770cb16525101fdad058dc44aeeef8cef60bfc8787bf11436ca7e7a4294617daa85584dcbc2e36b2359560566e9d579f517ce813ded1e80169a4b1d247bd2902e630275820d28edf79f9ae60ca3e7ecfb2da38f83debfcfe2c06bccea155070e3da4ee2cd4b5377680054e1088d493bb1761177b4bb59ca6080642414245b50101a80100007cb7221000000000f27a48d033024f80b125809ed04d9b315a1cd7a60071d9c44a9af0b8486048323bbab1ccef2f86dbb0518781ca14a72d2799dae75da3a308bb1e8a3022b0d40a52aff5c8c5a9a93eaf3b5942e24f7e3daee0c7725006bb681e43e1e72a01450305424142450101f2533ab8f472b9a8815d7f280a0156fae6a0dce53d766d4ba6a7730d6ab1fa54a31b39e659e7e05d2f4b6bd2440682e8cbd239b30de187007340fb2d9f06328c055d1fad8afdab1d3ccac6f5bf03916dba82ee396bfcdd59ace49b80b75a53d2e2d1e80108c5d042355922a4506dd02b337a9c311d5057574e2b8bffc5e2da5857790e9b4abe5296736dcb4fc1a1bc12de5f16a38dbee0e9daa1bf6b7d4b59d3c7ed81ac080642414245b50103a60200007db72210000000003aa42ec74900d836d824f08121900f57780214bc04cfcbc2e13c66636c708106af220a6bf4b6d204eee33abe24dc66d9041c7cc10f7495ce775e9f4c1e9bfa09c6544fa79f988d3a60759ed5cb2d38469f492cb2df57795e150cc00d9056ea080542414245010188340a913aef3ad1704789053bab820e5b63e934c3f8dde32ebfd186d8258500a29d210c4547e1625c2f67783102ffdc7ccd8e4c787f4c3febbed4ed6a0b628d1b75bca6fa3cf128baa6a902511c3e12a6fc5d56a1f17976baf35e494b8ba35be6d1e80103b35d51a2d7f663a0518d998042f6b513e24aeff8043526ee47d21119ae856ac4b19f9450a96b30ee72d372b75569cdcb5b0fb04bf6f15133eb53b828414667080642414245b501032e0100007eb7221000000000ac17a6c163649e1c461b874df5f39b7038072570c0a94e621d6dc9341180a14f7640d76a9a292ebf845dd490b5e578798484488641752192538134bd6ffe4f0aad46d1bf61777d55e1014032f170846b6e14feb1dce2ec6a01cab4539736ed0c054241424501012cbf49b15a47c51706010e3b7585245db1e681856b215fe68db926b8fcbf8061065a943cee8042a72afaa1f854b6a188ac863ea546578848fe5aca82be86ab88735cf33da34e39191f5e5a2d23414b241f61522d0778b2fbe989b36005647dccead1e80193939b2bdcaa6d3d7aa87c7b86fd50d893a769932e83ae44dc9b560a7be9cc7a0ed4559a293c0a77d6f92ab288187712da0c6a03b9dad9227b74d3a04f9fbce9080642414245b50103810300007fb722100000000002c53f9fd479c159c9fc74d1405be9351d0e4b76f53f6b7388a25911a2f3be57f356bd51e1234cef64524c784366126788f2a9ba96aa7cf9639cf2d840dd4a087b26cc1eb2921dea5f2f628ca8972f171b84d0281244a99db39b1c576d18e10505424142450101807f50e5e1cbfd36337d8b385848855022f4a94425037d92ff0f673306b3d7005eab5c11b0b65f3079a5116243ad27aaddec926f21c3ca7efd6c7834e232738a233aeabab937a2e5fdaad7206e2b1064b75ac289d636ebb78e97a57e8f5ef597eed1e8012b2e9bebf4179ca25cfbff9bde0e7edbdc1d3fb4f45c1ad1775f452efab48fcaa10d5bdf256b712d7103e02241272161e43f1eb6fa5d867f7f78e9be51587557080642414245b501035002000080b722100000000056404ddacb069ef8ebe65ba4be1af1107115c7c4003b47adc5898b299c1d1d26b8217b52a477cde9b39dbc4f58fa606bf360cd9aa54a976fd256ce123a18740b206270ffac27ddb164d857832f77d675e5109d7129135b19789df87b9504c30a0542414245010170ae03b486a18968e854a69756896b1b622ea042f131eb90bf7480d5fac94f4d46728c1bf67cafd746602a6ce10e9380e12f4428e30e1e8e8dc50d5ce6730e8bafbcd8331361df39ea75fb1ee21c73ba6288cdc5ad6c88818da634aa4b5aeb0ff2d1e801060dceb76f36b79ad29cb2fa032088b4c6a370441912585bf49af3f5b6a72fe1b7c99ae6342160004c85f1e691226adb647175acd19da3c470cd243fd9a41460080642414245b501032900000081b7221000000000b24f306d94b789d263e0aba9428803d3281304d4eaad44480c40a8c71613a2197d14f0f36324f71952afee7c998e6d8b3d170d6a363a17cef8fd463306664b0d4953e6e54aeba6c8bafc71add0d4a7561c8003c3262d98447126c63d9546500b054241424501011e8589d98d33dc9d7bef6da847992dae836836cf02000c537bd0656bea56c8698acd51a035a6a8183fc5072b419a9c08f27021cd7f7a5c96b9c5d97cd9baf1868f550f3986aba7a613a7a0ed0e16647dacc1cbd4d7e9fc767519a670291ffcebf6d1e80118f104b7ab99e22d93e84b9c41d4672ae39db3197e9809993f1408c44575ffc13ec895835b14f422fe61eca2eb6faeb5e539ae50c42f65ec2600497a5dd23ecb080642414245b501031102000082b72210000000002686b1fdae1e0b05bb4f6f6d736e5739bf8677345d61862a05471f0802ee6778fa8e81ae93c89a3fd70fe16c8fedb6de6e774fe4b465a87ac414f54287f47305a8a101ef2c9469ab0354e30218491cff8620ffbfb38d8493090601c26ac1690b05424142450101a8c0a2aca1bcf374899d2b1dffa063c7fb3b5b9fd7cec83f43f2e1e1eb63834188519ad28a72df1a21830f9b0a296e43fbb0a541cbf3bce13079c112c3924984b790410999a6ddbc0cdb181614db86df45650277bc4383e7966592549db22516fad1e80176f4e95dae5b803c59ccde1f61f0b2397bf0fb272bb4a9941b9bcf388f830a5368371231c219e89df0427a2c0ff55d586506d11c6f5ae05e34f3b3d55374d065080642414245b501039502000083b722100000000026784d8d7c3228db514751db86ae45895c3b0e2ddcd2bff824a9c7c36bcd511e518bc885917436c5bba3d88815db1af7e09ed16d2ab91e7df20be01f4ba95f0a6c69f2324ca130e9c7343809a18dc4a66f2ae2168f113f36681f7dcab4b2a0090542414245010166065c298f5193995cbf826ae47fb248754cfe1a84c97b39f3efeab4e1da8d70dd9951a98c052d47295d969e11052f86904c0e2090635574413b8a303fd11f8b232bed6713e12e13845d036f73bdd9497475d680995a01f3e6d88cb1c76d9facfed1e801be4a452b331a6d2f8ed585aeabe90e62ba71a0f99064a3b1b4d3f768d794086134c36bdb9b3723caa964787c65379738d515be9966aa99056ea623c1f47f7a3d080642414245b50103d400000084b72210000000002c0cd9c2ad6144da9952eac2037cf4bfd161b14a75c9ed0da5db3e0efe610a5761e5d322aca1be06d873d6c02531f586828b6c20c679f03a6f147c9f89dc1004cca4acc525e99b44856ffb07f68d7850bffbb31b47cb54a09119764af5e7af06054241424501014232de35effe2c2b6b704a0ac311cffdd6ef7e7c14169b4cf9444eaf17a90e75d581dadde53b874673a3a730ea8d2692062a40cfa7fd3939e9f972b092906083f15d01fc8df9dc1344d4fb45077d5bb92b337a89bf29199868fba3c3fee32d8302d2e80107ba08a4bb7887c13a15b80d6ea0116283b5ceebb33d04e7e1cb9bd0442059ebcb87a9d7157f0d83e35e1bb30db7168d66485a5a28eb50a895a7368990f225a8080642414245b501037200000085b722100000000038d979b2688252dba537f1aaa47f958ce64e6a6ec9c4d2f108a216fee8b99d4765a482d37a0192b47d6824aafe710a577ecddf143f0706df701a599fcf075d0805810a20e2fdb715db5e2dc04df2d05fad4dcd05ff830b4311b6c8da0da1390b05424142450101420d8c338f9e59cd92ca442e37da695c38ea8d2d764b8ca444c9645900d4a03d64a38515332c86cd18ff51627407c53a601f148b96988115da180fb65d43c58f53b951af1d46b2e062d716ebe7eb0e3f68bab05e8f0527dcf0e4149ab60ddd2406d2e801cf262749d03dbb84a495547b10331ef4514b9fa25387116bafcea802e1497fe5404d16a6cd732f0322d2f57c26599156d32d5c955c82569fc4b2ab71cf48feb5080642414245b501033d01000086b722100000000030f258e3e4e12550dd86c7e0065800aed16293c51c0a649537b4604fe38a0f5b1786fa4d4d5cd5c89cb2e6d7ea6d87cde7900db5a82144f31d7ca58142d9720c2a410c612760ca3c5d0ad4055ce3fdb2fad668ccb9c962e8f4381e8923fdec0105424142450101f66d9a260a945274e6e63df393498cf0bd5a21fe047794375531ed7cf2823b6240bcb6e8bef42ac0d092443d94ec5d858e6cc377576ea0f171a75380557eba8da4f77b87fcbb222d7fccbcdbce5112bc216d10385bc51e0b47184d5a83d08ed50ad2e801b3cf5e89c2ff7f7c10a4e98595e4accdefc1391fa6de64218008e53e02605be3ca67bc29ba6ef1f019403955b217a4df0b0843b33e8fa2534fe02c0de1a4e772080642414245b501033e00000087b72210000000000ae8ea1b44fe4ec4b8c775df42762d162a22d12af69651a1ebeafeea2d6e9871ef2e66195a00a2cbccb7798b4670ca0e8aa586a91078eb293fde3c4aee1a780babe3a2f3e3a08885edbca6ac83c5fea863229cfe09df2e2c878e634e9f65d10a05424142450101a40592c20c3510900f3f53b2a13c242f01011d49fc054b5ef644448b7b5d2438581fa31b0b0ae56bd0ce37b6f7e7cd48c95e471055606e540972a0372f858a8f5ecfd8161b028375a128f1dfb857af284ed619bed3085554fd6300a18c39f7740ed2e801051aaedd1791a682335b03530b15a6321a3b77777ee355371a41892e785a1141838f09e7b65cbc021a7518c9bb71487a539ce60fc54479a77bda5f79ea01d7fd080642414245b50103c200000088b7221000000000d43905f13a244ef2171413a6f9ce226df22aa497c520b48bfb7e89c745095772eea42bd83788f4fcfeb96bd23cd09625179a82f53f5fc6851c3f0ba3de91050d21105cd62ba9e8c3e5f866e5c7ff72e1bcbdea3d4640b9f730d751cd27effe0805424142450101c0e6856cfecb3714eb97b63d4f52486d8034f12c3ecabef8c96f254b97c6a641e242185664734ba84b7a0e385d9968ce33183eb6aa8d11fefc28c43e12713f8d02fd49d5fd3c7e47aa0ef3caf74125adfee87346b642da54ecf7215288f42af412d2e801da1a55d46bf1a481d30dac966a38153ad74af94aecd965d102deb11c3374a131ee999e9217db878fa6c85e440e560e7740a5d100bb413caf43372b572dd893ab080642414245b501017801000089b7221000000000b687d4ed4a816ee144a468ddee5ca1d07bec1bcee32c7eca4400cee6a6627d232d2bc51e4ac3677a47c0ea4601ff3c289b44a345b4444c6def9c84fb5b48cd0fd10ce747564e284528f7d110b8d74a6a6fad00a15fd0a9a9b2963053bcde5701054241424501012efceb8da410e8937d56e9a05b14688cced0f3f97b66d95f66402f52bf0a965fbd7e2c20b8530d0f479aa167426a1572326bd323aef5e416f3602589c4e6c08984fb503e74ede0fcf955f3f9ef80e1c8c09932c46a709f1fc9d17f66ca23c91616d2e80129eb02d4c4f5a1f90888958028d713ae1d9047d3923a7bd27588fda44ecacfc2b8c71be3e48bd9020d8d4aa1dbb6e3594009e2a29f60d9209e3979f4d2d9c514080642414245b50103730300008ab722100000000000f4e1ff63407f2e0f63b7fb0a10934dd1cce364e617a416d008c8f52280406cd4c25a829e6e01c2bbcdc0bf9aa983a245350bbb7179f34b2c77cf3877566d0820e669c576d50aec0813a5362e772dcbf2269fc3caf098466b056e8a9deca40705424142450101ee911995580036e29404163971461b3e4cca45c02a4ec4db592fa97602674915ac128c75d5ae62196154736007fbb64b6ec25abc7ce8bcee3a1bd551d09ba28a7aa2fdc675268ef2874ec1c2c281fb52cd9ef2c31abb916fe1c8c311e6d8ddd91ad2e801ccf31a47137ead615f0c49eb11f3289a37442bb900639e1d690ad359395cb56c68e925365d7edb7533ac4e0b253aa3c47537d115633852564fcadf8c299356a4080642414245b50101ad0100008bb7221000000000c00697ef2621aaa729d5c3fdcdb1b43a82e1e059f1cf6f5af65e52d4378fe27e65e48d6fea80942be05228e873e448ad069aa79d76fb83235d2cc703d8a6550ce0e083fcc22a79d09a36cb5f4c45f45db01683f36915b83ff3552c507469d800054241424501013c511e0a61b9acc3cb4d76316ebac1b22d69b8ad16f5f9aa863c45ea1bc2a919891b8c34cdf1ef19026c785fcce1afa42510c9cc8d8e20125a4ad888af53c689c654b5fa76222a48a95445ee481cea3e02c0d272b3edf772c940ab55d8507a8b1ed2e8018bee86467c441c613d9e367e4cac1a9bf0d38f21c933436a7d1341b707364a922b196a1262bae7ee4329b31e708e14aebba56a7b06ffe1795da5971e49d3b792080642414245b50103ce0100008cb722100000000042220ec0934b98552041142805bde8b4cf2c34e1f77bdc49b9d3908e738481158fed2b544bcc7ccd6940e2198e47f95274594ac3399e0a6b097537d9c7b6620f9dec072ac2b1fe598db786bb007dc7a54ddd59e785ee22fcad30617597d61203054241424501017a703f137c13b87a9d5e7f4e328bd892a4312a815ed68c63e479a676971ef40d534df0b9415c289402a2346dd3172e3dabbbf5ea96ba7291d3c682e35aafca8c196e7b775766319df633c7a21ce8a7aa1282de6a2ea0da0431d179a602f4635522d2e80111075a47aa5553b7f4b5d43d78047271d86e910a127cba28ef628caac85ce67aff4c7b169fb9471e7d8c25561a0b1637b35c74a6ed58a64fefe82b6776e62ca0080642414245b50103270100008db7221000000000c2cc27a012b2430b51c7e556e8086891e3c3650100aa608b4c53b20cda3f421df960b02404637767dfb19be66992f91e1a1b615940f26998c839e669cdb9ae08847338f71470b8d07139f09839009467acb43836c7e583332139c4b330b08601054241424501011276f9c7d575a6d7f1fdc64a397afcbd1ad785a60fd1068c6772f3371f37ee70c4b6681ca1b11ce94f397398ec18ef9ddd02a7147a7a7efca9b778843cbfd582ca407271736690f1a5344da67129b16020aca9883b9cec27061dc39419e27ddc26d2e80103a9f4664ba313aef56145f8ce54c1e21d097dae37abaeccdd5772b6934ec4fd9b4ecada5afec263f3c6ed207b60f6f527ea8827dcab6116010ad70515b0df17080642414245b501016f0300008eb7221000000000ba0f69f1660e8f1aad446fb160ef1891f325fe6baf3011d1ec59de5d0c776468bff45afe290bb8e7eb12841ee4f45e707b2fdb9414a63caed8e3731c7216c0084da77266d8a36714edefabcded75694d4dd02c164c77f4b5e4cb640e9721c80c05424142450101cc67b6e580414e32948615f82a41a8ba87f108100b74a20975cbd570d759620f48c5649940104e7ab63523a89bdcfa008dc8acb70dbad6cdb9fad9dd7263c08420d6fcd1b5667069a75c2640f871f754c70decce6d063c7fa7a7126ac269cbbf2ad2e801b258a61614a76f24dfe7efddcdbc82d93e191702064da5276de984299ca4c6a246f0cb31efb73972deeea4617ee29ecec47a5da24695ded9ea190582d89e1e4f080642414245b50103840100008fb722100000000034cb1932f29ab4bcbecca854cc0f0bb5a0d748049a3292819e50a3237c482938317fb146bf36b51b15889e286ecbee0f0fc95ebfe71df0cfd02dcf0186f05d0598a2ed3cac2ca58cf5ba5e256718c641ac785e358a9ee2a6df317119958d9200054241424501019cac60c72cb9afd27c4daa7f4cf835bea1c875738ef64bf2079662260caa89048ec5a0b6992950bc79d578e4dde90aedfe6b469c5f3e76b29a9859bb276f778414aea3b4b2d95cf51ad7a4a0845b15a78d69e2a3fe716e0f12e0c049522fa2212ed2e801272a9abc6f616b949981a5e783ffe1bf0f88383623bfe26c49445c33a572a62394b94dfcdd0b876583f8f461a1fa2f369ebb9db913f30351afece24ada639fa6080642414245b50103f801000090b7221000000000e0d0c7a613a2612d1ab920c528ae8eed3ced0ce961e3dbea2bdb8fe965426457315d575757f1fd72e09e0a19cbd2502e4a9f66e9b964fe259d88835ffebefe0a2e6b4249d5df74843f386e71f141210e85b1e38685da4ba72f72ad6181aeae0e054241424501019ee335d05ce6c3e694f0a4bdc862945153c20d9bcb68f3222fbddff2c5b9e12688d20b6764848462f02d2b70f3cf71c12ec688cbe00624361ebbc3ffd5b20282fb565cd0ada554e9adb096de149b9e8582f1b11264ff1a27732ff1bbb9030e4332d2e8014c81cd7dbe23ed6b2be49b5da7386c16d0c6190c86dd5aef91c8a28a0a8deea0db194ba2bd2b4388c205a0b6c802b4461c84f4fd9cd5439feb019808cae2a32b080642414245b501016102000091b7221000000000f0b58af8347a0703a8cfad56cb31e2f71ffcbce8e2866f8494e8da5135d98049c573f66cf8cda72ef5ada52a3c3be6dc46246996a844b10f925dc0c5acb0ff0294944f53387b74761a2f579a0e587f121ed9fd68da52e55d9d8b0be0c7115a0605424142450101341fb494588ae8db520d17294455591b6c0ab28cf43c0114b9966734d1462957d3086e0c07d6157200bedbb125b83d50cfb4325bafec5b92bbde8c9c45d6048f62abe855b06e011e8a66f7892985d7b4d654a12c1b987a8a3ac714170286bd3736d2e80122087e900d44fdd958c531305299d44fd78d1b26cec5167072b14345b5fa625b6a5081c11c6bc0d7ee23cfcce71bcc0821a3b87aa9ea54201f1f5b7ad89e7ab7080642414245b501034803000092b722100000000052f1b7f53f02102d703b6c7549627cb7aedc818da80f95a96d74bc17cc9d46348e7162d7b0dd04e6470e8d93b6202cd88087bf53aea1b0bc0d97433ccf016d0e7ba01a0f39c8eca4b8d5d2bd878817de9d34447cf0e5cae54e4fd1787effa80b05424142450101fe3b1871d6b407c4a10c7c3d93c1f8721bb9aca4732a2668ace5263aedadcc4b4addc997e5255c857eb64045da6b25262bb1b807ade9bf58d04c307b7e06fe824403954dbf17af076657a0db33cad4d31a0ff98e4fc640b29ac0443e13b5be3a3ad2e801c09e9da9794f8827735b310049abc406fee28a8f063cc864d374f37c801da7ace5817f58bfec521e4375d2244f6fec20686aa04b9a90d09b2f3babecbf090bf9080642414245b501032001000093b7221000000000e24dbd946b3b40f7bfa49564dcae6abf6dc0e19e99f97a0c59175b19b0b32102a53067d2d9d58f44575310db1bc6fbf6b9587c7c5cd8abf2a3524003341cc50173458269c0ec6f5249133c54d62a5759590eb0b39c1a81094859f810a8de5a0205424142450101f4c1da64276c2aca9e751e8efe488d8cd3615d5ac230125301376d94e2f4302dc71c70587313f91df5125c2cbf3946dd2f6c0d546ea23844c241795ebd7e45815de1a03ea52d9a6f1b19ec81420aadbc858c9616a3ef2455f241da3f07cfa4443ed2e801e5614bc157fabf0529808b41ce26e7ecb503c74abf46de93ee56de91483dafb66f7ead0c23263548a44eedd9ee2c02882fc62365b9e4c5a84c228020766654bd080642414245b501019e00000094b72210000000005e93acc607f857ebc83816187efd5ee41698aa7959939c31f19d04859268172895354de33938b3ef35f9aefaf5a9c41e86220b4a7a24e0ef235a0bbf77661a008defc2a9323a643d933c6a91093092a68669d715bbcc0cb217fface9d9286f00054241424501015a099df78bff16ec54daf1a2b0ba2147403de3364c32eb288689d350631d4c3ca9aac7ce6f88a72fc1716306dd21875da1ab8bc58179d6393c1265fdcb54fa84d56cf70a0f0b2dc1c84f6fdb4dff65b0ce3cc1c8d560d346889831871f91d2dc42d2e801434813f6ec4434f573ebc938fc15aa52edf55171cec83ee9826fcd2c4afdf8541fd985256fb7839e851636759fbb11048d9244c0a1c98c8b638e97e13cda958f080642414245b501031301000095b7221000000000e870f976d89300907d3d5bc00d9628fcc0c7df50be60cdf7c6d0b9cd1c9e736b45d60e0c0d440aa653ad7653f5a2f88e29044c06909232f059a45462631e1705259ecd095dff2c99946283cdb0da0eebecbe0b256adbe05f7c7a006cd653380a054241424501010aef2466ce9abc127a01ae6e7b1035323a406652526ef8da01ffe7a10f81c33e728a8ba2a9eceea9382e135fdd9eedee0c8f073093470f3ccb8f1112a016c48a137be2f5a73843e6a93bc9401ca641247f3a849d86057e0cc33ce4cf04e52f3f46d2e8010a0e591c7bf42efbefb613151828ebb6bc23b30d82aa7a8c198a386d57268e4b9733fb24e2f4fab820540ab61d9a55edf5914208be7181f5499cb6a5af774b5c080642414245b501030e01000096b72210000000003e1d6c6f23ac15f44f6e0ed3575f56c738cdaf31ee7ad827d512b9daec0d4f10d80ecf73cf4c3edc2e059c4e45cdee37535c3b980c07b2dcdc242addc5f34c02526243f159870e47a8c40f3c6406d099036a3363c6c579c10cadd959482b7f0805424142450101f060e36d5306f2720b2892fec0b3c6ba7ed1fb81d5137f7a1341cb6c0dc5852b9650016188c28b40a81dc8884de3b5030ac1999b58bcefe89c0d807d2fee2e80135597abd36f63f55325cd2c308a605c193a399eb11b1e3757091e13ed50702c4ad2e801348683ae7f038facaea6c44b46d580c6e3b7bc960c1efa6e195cd7f6d4672779b14fc0f28857f69417127c2d6881f10e827cc2f7a7e6838f3623cb3946f5a68e0c0642414245b501037d00000097b72210000000006cf2a0a71d25917fa9b5e7cf79be40d71e9035d9ad64b1b72668d1b2bbca7847946a570a54de759ca07d3b74e50062b51f9ee80dc5df47ffdf598b87600c0c0e4bb53b9c983a26be2dbf33bdbea11878709a16a26abc2d5d0b7d128651efcb0704424142450e33020001110ec434cc142de3e83f6c5d32526fdb84efeb826c7441298ae84621479a717ddd2601000000000000009a4419c9fa76e2a85014f2604f3acbe29d052d7f8ebe31373f213cfeb3633f3901000000000000005004ada038dbeeb636a58c61857a2b2f64a467b929128d118889a390a81739400100000000000000b681933791fbbedbb24bbcb2eaa48411c7c27711875b942537fa2d247c51122e0100000000000000240a8579b9c6cfce8e9ffa06e49a0df2767744abed4a3b3458b01fe92d44b4540100000000000000ae8ef962e1fb879eeeae9f78e8d57fbe6c0fc2a5b404c93a65f5dd8c982b51230100000000000000863a39f3310812dcfd9725d1ab3cece6dc763e7a43eb5ca7c54f7ed5ba1ec3190100000000000000f44df689a3d5ace5b6a109e35ac63d1e62b52e3a1a582ce0fa0e220fc8b671570100000000000000124f6cb9120882340f4289c1e4ee09fcad3ed8930ec634feda536628bcb4fd55010000000000000080e41ac5dc4ac06088d9c560d193b6f7b7ac4d86e0e9bb517c21df9453efcf4f0100000000000000487888f977bf72f61ddfb9ca54897c22fdd73acea696eb009b5401dd7101130f01000000000000002ccd992d7a9f1650e3fcd531b307d9651c104df6d5facd2cbab688d3fc334c440100000000000000d043cceb4e7a4daaff5be2f8fa11cd425af051654571a146f254afc04ffde42a0100000000000000ca204674c4aa968ae4cbe115db05e478b6ee03533038eaa9cfb95ba1b265653f0100000000000000361fab81129e1e3ac7e7c9b4aea10a218991b824c6dddc55414a8770e84f7e6101000000000000003652dc7e781bf25e8bb4b1d25de1bd9e9a7fd67a8ec75b479ae370ed9bfb2c5a01000000000000001873d87e3d21ad568f5cdb255f9870015002606921c6a3a905ce146abd77a2340100000000000000fe585a6b3636068a9009b18083cd6242c78da6f5f57dc0a7571b65a36d354c2901000000000000004ca1067f6f4f8fc7c9432818d0225242e98796be516fcb04ee272799a66dd23a0100000000000000c441ccc085cca761a998a310a1f074495af3e839f8c71de12615f22a7ea1073501000000000000008e747ea3ea05bfccecc9595e9cefe7fdeab4a39fc7e847f56b04135fc4a012710100000000000000fa5557d061b27db6b1bce99a95c830029fc60ac3430d55af30e94edb3a77a25101000000000000001ad6ddc62c61923290e127bfc7708c944c8d6a012805b14c70156109d83986400100000000000000285c8ef3c7445f4ff0ce912cffb2e521044aebb62620963df624201c5e4dc7700100000000000000947ff774965431ec727966c6dd539cd70d15e9181b192ad47b74b1c85745b8480100000000000000e479230ab2921c9ede58fe6211d8fc8e8a3ed3ac8065a7dc6ac50ed63763607a01000000000000000ed017fc562bf08519533de0279f2f7588ec4ea382d48a63edf1df2c807fbd1f0100000000000000d8d8296101fc3a99c56bd85c4078c2e389c071f731e18cf6be7c00aa9f4e2e6b0100000000000000845f63a15c8afdf9737eae1b91666268894eaa6996079ac7297490544137e80101000000000000007c3c62b725f20653adaa44243e4b3d8485c66c310e57178a3d3940f8f780043a010000000000000070e65e0e42d42db5509f35fd338047df8c7a6879ddaf62465c3947b929eda6210100000000000000e415160504f666dadd30265cfcf02b9d10136ddcd69958d9174da69329f5f8490100000000000000da8f660f8f4b90fff45ff6fbaa4df167a0f94baa1daac048346c50698fd285690100000000000000de6317d82c9feea35a44cc73086d6e73d0c7287728bd16c63933f913b7f27a11010000000000000028db15599a7fec3c93c26f62014fd30c214358c4443dc6bd515ef09a113070760100000000000000141f7c5f1baab0cdc3e2b10fa7380084c25f20252d96578d81700414f9eb7b7f010000000000000020b32e50928e8148a1b3d137e6cacc80f2a1ee5a3730e8880bdb0cd5d6b07f630100000000000000a406fa3390513c131099c9ea276472163db611174ca44e3b5c3e52aa3b47f94e010000000000000094c8b55eb88a7e3764116702ef768950c3e59336623aca8260ff44291ff9f66101000000000000003e7c3c81fac56951ff05ff47ffb49285750f6f55619ef8d0e11a388d92020f550100000000000000f840d40d3f38ea72cc1598f7e2012808584c0b90ce7546cdb82250f6b049972a0100000000000000962c3b7413c205f80b072ba3d8b9a828b10e42be559fbc07e9c597ea72cbe56d0100000000000000f2a70fe0286602a06633c7aed149fa7a7bd542c97bd6ae90cb1a941e684518760100000000000000b6cc496a16c32cca3993d385b995f38a6ba1df00baa71632dbe8f750ecb9b2300100000000000000d625a53dae5c0f3fa7af221c68c96fdaa1a498f941e10a1751a93378c4f4d04a0100000000000000d236b78d518725dc6332642273c5f6375be4494ae1e8e6e8b42bd1a19af14c4501000000000000006464efe71ac4e34c699a088b19ea08801d9e394ec50d56c7cddcc3dcec103d4101000000000000009c6f977636e4989f241a12728b5d2635effc4fcacddb1d2cbb45a739504c827601000000000000000652cc6a94406c8d2bd9c9f9f09768125cad75244c0f495373af649b0c82295301000000000000009455588e090f0f98a3a77dbe74d336e08abe7a1b6e30c1ea6ba7d73687ce721201000000000000004ad8792f5556e979d62743cb45b3098c23c003000131eff8b41de0dceb8e053a0100000000000000e0888da4a5a14218fc2c38b8d18d21a4acbd0a912f39cbe8276be83305f1797a01000000000000000678705af1f738381114b82ee5b8a14757515185f69afa15b02f40ba9adfe4530100000000000000ee4343f61d67fe213365a0495087acbea7b000429f2fbb80af14d32d3244bd4201000000000000006844faca3ef4bb5a7ba212d2a2914ab99eba84f93bcf37abf62db3a9f19fe0050100000000000000145ec81be8208098bcefaafbd43e887c772685225a803c19730d25580814bf640100000000000000d0146ca7b1e2653d0127dbc6b46398f6421cedbfe73fcc796af22317c6aca71d0100000000000000aabd30681e440dd836c894b44d5af8e890cd99599899bc0195125e742fbe184b0100000000000000ee768be474ba6200704b6bb75d6a0526263047da6fbdcb9344ccb35268361603010000000000000082f72bb1cc00b5f96c95de38d2698ba9c64a7ccb4d58a077538ce1ccb55efb570100000000000000a0674ddb6f130eae680532c6ff0b05a317cb3840c8906c3a326f26ce8544f82e0100000000000000ac2bf96630b117699e7fd6c6c717890510e00fb6db3f8a0a5c461a9309ccb26801000000000000005afb98d27d68e67f6e8eb81c6d3375425a5dc323bfac122963eeb580cbde5f430100000000000000f614bac5b188d32a7041ae91c9decb77f68d21c8ca76e06ad9205eb838056714010000000000000068bf63365fd8ca42dde5e1adbfb98817f88f205e17c1a38e07f8620fa19eb909010000000000000098dda42a2ae6079331599ec904ddc18cf94fee85ff96e1fd3e7b6953db8fc70901000000000000005869c7315c16568345efb6241e1c0e9ca9d279c8c7b398d32ae7c7323a93092a01000000000000007a043f552a7cdc9de8641b15b0369b677badda49d8082fb71c7eea3344159e390100000000000000a2b759bd4c7dea8909c86f2f570eff3a3b56e71d50ec8893eb66abc945a7212b01000000000000004e9fb9675233d8de1b78adc3f27ec87e2cb1bb838354172d1fd9c93b318e582301000000000000004a7a434d9bc1105ae7df912b7d64c32ff70ed5e531f464447b7d3188c06736700100000000000000b04c669e6deec9b1f47db449905e1bfdfea3bbbff5d4b4fb9c85dbfcc5a0546d0100000000000000baa5986e9b5b50de5febb4ac70500028e71046fd84c8083c0f62ff6fe31d1e0e01000000000000009ccba303b66ee95ad28150a987a672273809107589fe2f7d29665fc943ca62660100000000000000c28524b5af98c5c4f13ea78f14ea81e91df1afdd4500333f9423a80ff331de0a0100000000000000e83c8269c658801c3ac9014fc5a3c8309d1769a8dfdfa272ebedecb1a2a1e4290100000000000000ae05ab8b2f6db5296f2766087549efc6169b3499dde3d6374f7c772085f65d2e01000000000000002083ee72d69233ce625e5f1c3629c331bd6c83385c73722e846752018c64e4580100000000000000586418c4ad73c32f19381e4c32d8f2d082671af513b0a2b528dcb24a4230281d0100000000000000863f88d41e8128af4882262a1d55cfac41539436f280b567385da2cfdda040260100000000000000e077c9be8e48279abeb236ba07e011b082f2bd09528aae59180c7eab0619e60e01000000000000002ad69fcc5342b7b7e11595eba5a00d20ba35d6f081cfe395bfa1dcce4a290a0a0100000000000000900733f798d51d2dc54005ba53eeb9c3d63909d4f7e7ed32dce66b7259552f6e01000000000000002c605db889f693e3f6f832512bf4d6ddd8570ad3c71db7a9b57bd5689b44a73601000000000000009aa7c8266d57e70714e5757cd2e187e287c63afe88ca3a4dd42b3f403737f6760100000000000000705af57ca737e78669fc625f5fc98b16d120ba871b488ec79a9d12a1ed72f2390100000000000000183be67e3f80e946e21d9a53db35eb2d9d53b227de1d371e61ecf0f7b5e6b87201000000000000000e2162fc65a7a09a88b48781078143933638f7f40c98048b0a9b87694e34d7130100000000000000ac72ebbb90dcc166bfba1257e42b8b58574704ceae439f2c19ac085b61d40a6201000000000000003a04567e101ebb28329de2600817729f6f3c17c5e5789f036a52ad49b99ddb23010000000000000088dc2749fd6af775f8d9f3a1dcfe548d60037cdb8ec1db422165cacf9c96970501000000000000004af471b98c45055a8487a042c44567060000e421e7908d4ee86169d4f36d4e0601000000000000004221e0d01a623d31f3f7afb312e0ae6158db5edf68319a8b6ff98d623b0b1b03010000000000000068bc9e681587d349c5ac904a296ab9a4481c7de5d6b585621c5ed2670bfebb5f010000000000000016d1f436ee6064b05ea10ff906a7b6baf9cae49431489a8afbefae088bdfca1d01000000000000009231b7f5d1f17d7dd8791204288f265060d3469292edee45533267a7a3976b780100000000000000aabeff206862500ce4d4d54491712448e3823ab909919cd759937759a086a10801000000000000007e4ea4d47bf58ce719af34f30ac79ad8515ff9cfd2574abe5309a21ef0b17d5c01000000000000004c230bfd3b60ba5597597b154fd95829385acf1bd740607b9ee07262c994106a0100000000000000f6b74d3139ee7543b6821c342b061af75b609c2728c9eef724066fd35abdfe5f010000000000000018aa6738aae287470270a2213f8644d31150682adff4cf3b7b2da19c473ac8390100000000000000fc81c16f4d0dc4b27d1e0dc381ef15781f8e0ee41fbe0586bd9afe15ed13173c0100000000000000249f2c086a0c22a7cd3189282875e2398b6dc28a2b9a8a0ac9eb3a53de73b43b0100000000000000b266ee40cfa61107e9ec12f162cd72c6518af1df74002c2004c5a98b476947130100000000000000aa42949770f022aa18616f18f02917b25ea0182b6aa062ab117f9268183d9d3d0100000000000000886edfab02c7ab5f4f62f26edbf5dda9868c8a413a5c379b1b63d72a2121cd36010000000000000076cc698d58e5e4938fde51916c8c320e0c3f71822b4664254fbfce6cd4dfc03c0100000000000000ee4b50b0921a9e1f4e5f22cefe6bb00d6a4357c8570636a39b26e7344b3f66240100000000000000e844261de8b691e753a7ac7132c821469d83f25ea8e323ec1cec4dd478a7e6530100000000000000925864a0fb6058ba04b0d1425df158e708ba37cfd877f126796e2bd7970c1d4f0100000000000000ceb4d80222ff1340bbf953176e38bf2a8d40a4c6bca020beacbcac1f1ebe7702010000000000000060606066af08f9fb9f9afa87fb07ad0baab01233516511c5e6947ab8f3524c7a0100000000000000ce3ece9acf75ed3be5d3558b5cbe3b5703ec326647668458e82e42cdb5a948010100000000000000661e5553fb5d358cbddb07dbe15fcf7826a27e2c30847b9548d0d8f8f8048d7d0100000000000000248769c8acc11f3a3dede5b15035433729ded2ad8af740f2e7d3340d9d40ef0d01000000000000001e97263763dc6b55363fa51306709f7e0243af1910630253381a8592de97f00401000000000000006c5a12a06624fc5084d48b6eeea4b0ba00f87cf57986fc6110124c44e7e9542901000000000000003a1a122ffc962253073ab844867308704f6d92ab8f6e318e734f3b18b7e3ba3301000000000000006040a8414acddd0b30dcb1fbe083cd72889055353bb9f64c6a4875b861a4295e0100000000000000e06380c0e44c490844857db7ce6269e52272daf3db8c9c98f38b24c79c1eaf560100000000000000667ae5fddf7c7a58681fd75da15d319dda30921d6c659db6d0bf982830cfa06c010000000000000036b1387f3674856f0fb22a876ef12bdf2ec21fdcc849f200acc609e5f33279470100000000000000ec2f04aa8bd2ce4717e5747c506be32db5db1cda05e3875fba658505ad2e373501000000000000008e9fbb58a96118053914ff5c36348f8bc82d7e0051e8183315a4746d77d5af000100000000000000f419d56e18e14258c7eb351d1951a0c091194aae5c55ab1ac4cbaf795ecff17f010000000000000092dcfaa7fc0a16bb4a2e197db3179d207663971a4ec18c5a78594d579d02fd7301000000000000002ab8ea26eb951dfa831de87f69aa6608b4de76df2914cbcddb163beb5ce21b7b01000000000000002c2a0dd67d2550d4231d79efee8891dcc1cc103c007935aa49e6a2157c11fd2a0100000000000000f8106e64f3900bdcefc9dee82dcb471571bc421ca0b0b60ae2d9fc0f482328630100000000000000d88f551dbc4416e3ee46cd89c683eb51054fc9242037ff19a52af87b30402d2c010000000000000056091588f3ee5b9fe15d20fc336d68541a08e823e20d691a41613ce2c154196201000000000000006e2fc806b2626507a907566aebc2a6fb1039a533df8e7a8c9d9a287f3e7bfc3d0100000000000000e45c8c0d6a5aded1d938d8efdc41e5ab6b289c605cc6c19c3d7a12b384ffc81a0100000000000000a0fe04881e8cb6865fda23d69a35ce7f28574bdad4fe3b85a9925f7aebc6620e01000000000000002a11c0724e4fb460dc5513aa71488c58d8cbe2b5af25a62a4363e51d23fbb60701000000000000004e263edd08343be113e4234765496cf31e3bb0068d66e4782209171df67b2c3f0100000000000000265680af1a1392be7d96ba0fd2e32b5fcc11f2c394bae83dd49a29e4bba585000100000000000000c284219802cb0e56397b42e6434eae1cc49b1f67b0d05b2f13ef175c8e0aee5a0100000000000000cec30d319a93c00b7044b2f11165f3a5679d82ff4db6cb2783fefa42b236b8220100000000000000a88f2dfe51d3fa0370ca543711ee20e76ac172768abc40759ee95d0f99bf364c0100000000000000a88a466b1f2b4b7b6c497f85d1d1c3e8c13517217f2f2cd00c8ce300406d311a010000000000000064326ea393fc2fb9fd9102b08971b19ddbc878d77d0f53978be7f3ab87ed826c0100000000000000b4e7de600ce05dff305c9e1ead59c7dcd2b27f05b71c71387583e886504fc453010000000000000086899c0ce665034c2d701b5429944f115e3c74ae858ff9e7355dbddf27a4e70e01000000000000000e71bed03fe04edd6d1e28e7bcf8cbbff881734408b49f7c3e87e0aec6489f4401000000000000009a64638ddd657bedde38872091a93d700c6a29facdabec86f916c51266375f570100000000000000187d6e92ae950ca3e83ea4b5def1553313c9f732e865113c24a5dd1075ce2f7e01000000000000006432baca82539fea5029b23f67c912ccd9a652fe58c95bde1646d21e2b02fc6201000000000000004607caa83c3a777629226b92ecfd37cfbf53fd266e6cea36042b5366cb7676270100000000000000a4cf6a4ed9ce95da9df37128939a4bd909771b726e5901be83669fd30ceb9f1c0100000000000000c6003d828b9b0c61a99e86053843c22cbd9fdd734219f449e6200f3602c04a7701000000000000009ee91399c765031a73adf234280853022bdafa455fc539ca3920838855b11659010000000000000070f27022b5b79b964b7084d664d1fb56899b3c00295962bd8e7f330b55ecbd3901000000000000000062420d348b1b6663b61c0078995d5c0ce88659271b202b9d15c6597cbf760d0100000000000000f61ff7a1acb727894f81bb4e328a7f64ddbca4fe5477b60a72fe491536f97b550100000000000000341699a2305af27ee4e032aae91b4779218a2be3173500502d6644dddd1e653801000000000000001c0927ab80ed3e549a848e77c86fd30b448d4d188e0de7bd66370c05569d19790100000000000000444488d1b3e45c7aa8e35cf71dc80b41639412dcaed88f71743811c40abadd61010000000000000040639e72e9c4806d47f4bb7519ee7ff613ac108b17d3be775d72a173f0fc691601000000000000004c8ae5be1fefb1169156bdad8365d33f6bbd91b77a3e3897cbe0d5c4d7f7e3620100000000000000284b3a6ef1ead33f5fac01a077dddc179190daec59fe99586b99c0e35340ca72010000000000000092ef51762ec663f4b099fe6a72a74107d3844913df78f0b80d626fc4e20b1e0c010000000000000008ff6a7d89b6e1936b595c141ffbda304e6d40e36ac264d411615faf8049ee110100000000000000ecab81c08817d3365ffb7e7f3a7d847a701947a1db288bd2177868251da5a56b010000000000000038ddd57c995be34d71d938d2d1cb188a0e8b827da6d23ccd08ecd936850e9e520100000000000000149dd20740e21748698d0d48fcfe3e97e3ec1de3fef8730d5ce5f2fc110590220100000000000000542b657db2973a25f1f076f7b15ad6cdccff6e9687e7a4b5c0129bc77c847c26010000000000000046659edf4ece89d3ba4e4539a2a4466334f4107f099c1a9fbd8bfba22de09d7a0100000000000000ec9afce67bf970d2eec61a3f309f52c860732b69b156205775af8eef041872720100000000000000045fe14e3b9bfef6b174bb0624f9ca0f897e4cf6168ea5952124c15b5b2597500100000000000000a8c43807924e0b4efdf713a557ac34056545de254fa3745fa6cab97df913c46e01000000000000001afa773e53d051ca10aaa2e7d76b01beaa90c8c2d6d4a9c36d6dace0fcbb88230100000000000000f40475fa28c0b67b56058df22178c5baab25a28d153dc3277e0dbac6c094b25401000000000000002ef10a06d765808d2d44b2034edee5654fb27218398f0e79731b318678ac07110100000000000000500b779c0cf43579b8927d8ab50ea09f3d540ef36911b7a7ae99e2fbf4e137570100000000000000ca1f7eb42dabd6595813c3b7404cab648a2467fa9e31752949e07090b286a36b01000000000000007e80e157841d2bd8452224800a02e3c4b561a27f1731f0125f1291c359b4d4130100000000000000b0d74c7b304195803ba0b25627cba2c79293acff064d41b010a32b6d3ce1060501000000000000008cce5eb3755f181c945b8de1c6a3bcb7454557ab4f2d8d3cb9f7d137c2e50b570100000000000000fc320d8d5e575097cfffdab4641c3feba3bc195d7ec01121e2729ade54fbdd0801000000000000002a8cb7211931f3477b5731b23c2d973280b64b509ff16096da574f0648c9275201000000000000001a5d6fa531d4845ce8e8b33fe5e37f7f6c25593f92edba3ed34689d15bd8db0d0100000000000000ec4b66140f558efc29047fa4cadc629c72756a4d70727bc00b7461b74baad96d0100000000000000c4a4b433dd005332b2ff4af2534cae3ebc0a696340d8add1a7399e95bb0ffd7301000000000000009c1233cf95166c9255e9a6c5e0e927b8663b1b8a5a179b6bca90aaa1608705520100000000000000100f8e7d113d41416cd80214599a75f85b3b19e96cd2da7039c471de2006a64101000000000000009c1b9343afd827544ee40e6f45af9f75d1d4488dd9f479510ee8cf47c188f92e01000000000000006cdaa7dad71fe0ac92388b99fe1dbdc5425b39eeaf06a022ea8d8976720d914c0100000000000000c870afa85f2324fb5d56a923fb5a481e606bf27a3660a674ecac23d81be6cd2a0100000000000000e293330fcbd554eee59e0561b517231bde9f1bd1c64cf8530525f997eae2690901000000000000008a40b5186b72111670500b8cc2794555e4c67408a703f89984e957e9dd9d532b0100000000000000e2898e69209f2ba56c07e0c3f037fb20a97e5980ab3bf5f19c840b8f5d14641c0100000000000000a44074d35bfb2169d9f8bdae3caf80bcbe66f8bbb20dac30c29393f2eaf4967d0100000000000000062b28feb8046eff2268a58ade1d5c30db94ac566c33ff5a99a60541703431570100000000000000a62c0a8c58b06086dbd00a76e81ce32688c54f510a13dc5a287bff4303f41b4001000000000000002aefd1ba05d4f7c13ead2b20b4d868e8d787b8fda35b182040a2aa9a8a163c2a0100000000000000beaacb072ff2347efd4169fc7993ae758ceb56ea6f6897cf9a25ff71cd41cb02010000000000000012541487fb284062dc65881d5a326f3c963924b68ff36efb87efa1520c816d060100000000000000a82f7ab0942f27c701a45054359b3658662c80c0c8b1ccafe8271c5d3489ee0d010000000000000034a21abcc93598500f0d1304dbb4df783cd4faa9ab063e4caae4dfdc09899c570100000000000000a47830f3a4a794fba6e4ab624dada5b5630a82f1d61eb6dc406e01319c1a2c2501000000000000001840550329e05c67e05813a375fbba9a2ec82d1934388ad0292bfac2b45bc1030100000000000000ae2f6e59604ff1c1492face76604bddc0c48209485eca50f8d82cf67c727e37201000000000000005af885b0d311bd032ff14efbbf42adfae2a8ff2088f1e2259394bb03409dc4330100000000000000d6d45addae162f1bb235d16821fcf1136568b644721367bd3dad6a9ba9dba6660100000000000000c248107abc3ab176f7952a645a89b3cbe1303b3af53b184a32360cdaca6c400701000000000000009e51e504c5eb7a27f36c064c1eb1373d66a4e11397ed215dc8909db7fe523d5e0100000000000000da5f327512e71306ffe1905a70e12a259d0f0ef611dc7f56be8f0ca30940033e01000000000000000e6bdcaea0c05306fe727b5593d281bfa2eb7ff3aeb4bbd965af01396807d6100100000000000000f69d3157e535952e2155d65b90f2059bcadeb8a7b898e0443dc2f719e5f0841a0100000000000000d678254fe840948c178fb1f90f58fe093d1aaeb45b4919089f52c7faf222f5000100000000000000eede1bef0c43d1266c31905705140c0aaf136887ae4446bbc94cd5440d1970660100000000000000868dcd842b9e0304ac20a855f4374fedddad8781be75eabdf4f1f3d638593d1b010000000000000066c9290ae33d9153a0635c8b00685f11f9a89dbb3e9e907f8bf56c7b1d797e670100000000000000bc7567b86703c8aaf553fbb6add4de6c65ace7cf1816e31db07c6b4056ee7a580100000000000000e0c2d0e86ba6e4484856ec5a68dcce77175b15ed87d436bfc5abb5a01f38c01801000000000000008e7d9341619a28dd6a2733adef62b13f8cee0733de46494549fc38ae52f02d6d0100000000000000c43d0c516a2377bff6712585e03422e226f99014c9565da7d61bfddcc60ba2230100000000000000c6b1190ae760998a1286b3e6b9dda502a335c56f868f442b971fb891f361f75501000000000000003c611bcf0d259f8b57865f3250a1a09ddf562089bc322eee54be703ad422fb340100000000000000dc0e6a184863bce89bfee06c3c046a7cccf040d0d235865c6f0c56a75790244e0100000000000000500f6ef080819e095e751b2d13e5980446df95cc3997438f3b5ffdfa6ea0ec5a0100000000000000ba94bfa261dfe0b32d2697c88f97b9b1a3e603d69f916023dd3afa9d8f3715260100000000000000d4d43a66ffc7bb3c0a543a75fa1ba8c1b25eb338c2165bd0ad9aab3ef367fc17010000000000000060a92cd57e6ccdb5667307b0a0c9daa782758ece051899c00a7cf0578c1f131d0100000000000000d064fcab2c3f2b380af1b6ad48f31c17df5ef9ff46ffb7bdbc1908c7af5ad26a01000000000000000244515309e58b54210661c190fc86d5b4568eb4e85c578318729b50cbdbbe190100000000000000c4d7221426175dcdca951d8e377fe9c314972a9667550d2980d184dc7713244601000000000000002e2473679e557a5ab4b5f53f3775458dca1ecb9780a69a12d491e06e7b9c4d7d0100000000000000666758dc7096f360265acd0446e0e68d4d2c6787ac5d2a5d5ca76ded97805f2f0100000000000000ea4a297a0d3ee9491ac96d774dff2da3c675b3d7e4dbb9fa4e76c047b093880101000000000000005aecc7d9d96f48094caee4d395c8891fdc90529b74d835dae0d52123e5d2386b01000000000000005e092b5458ab949391f3323be3b09b90f02c9f2cf57c99e61bf5111a293c245f0100000000000000285f4d091e0564aded8101aa98e417e47008261ff729ef856003c798fe62725201000000000000000abdcf937b0f368b81ed8e41a7060a9d7917e6339c48b6abc6d8517b00620f7e0100000000000000f25c49e92258e740a9a79f938d3ba8249438b07884ee48a4973bd15e6fc52e1901000000000000006233f94f49a8fde5ca1b30460fabf5f1537bf4eb800caef619fe1c5a0572353401000000000000001845051fe44100fab50a4621b65b7c189083c3701b8366e0d853e139f60a45470100000000000000a41bbd69c636b6c12fc0512d2b016808851770a22c97337acc5fc8240bb82c0b01000000000000006ac55a703d9f7b20d49bd2387ee9fd6f1c6447a3401f793ac7be2064f3b6f464010000000000000092061d3be62cbed8f429d95dc1eac9d9bea8e23c12499426704ca64ecc28675201000000000000002c44ca2a77214e408a8ebbc6ea0e31cc4c01f1e9477afc11d9903a47777b345801000000000000000c34bc418f0ce9002c060ddf2f2b4b6942b1163f4dacd5608c0bb6d2159af135010000000000000092a908b3b5f108df9401ff0c6213ebdf5eac412295be6fbcae76a9320f12231401000000000000001cbf8a564c0beda112e06ce28226c1775b350a506fcc9133f1c87594a655e02e010000000000000030ad59b9fc56fe3d392dfcab57fc9b874707c1fb3d59ee78463f8d74042ac3040100000000000000361601767f088b9ffd46588d7049d46d33f1acaa4ea4561219c4709510c1cf10010000000000000074111e0c15602d7fe0c6eeb12065b07c83d890b94029c8815db2443a01b0eb2701000000000000005ceb546da5983b4c431d196e5bd32aa4803dfb513b792066f83546f63dc7534d01000000000000004a16b874d8591d53c896cff1d280d5c4e5197ebd894804efd727e4bb37bde7060100000000000000141bd9a074f86b6b6851e89bab648df0e45972c23acc7ccd9d5bec4adbea3a190100000000000000365850afeaa8058c89017ceea5cc3dd218f4955d2a4567f8a964ef389078c54d0100000000000000608e3ec03a52c67c8b0f637cfac7345fa1c2b4b61d09e50bc7b79a2644ad053001000000000000005a7d2d2e52a9a7f01b1ef3b252202b4c96af120e867141675c0820fbb67738700100000000000000baaf82c13c70e24dd09199d2fabe38d3712470ad7c8235bb9c47df20870d301c01000000000000002c64e7353c81c2b4ee434edc4cb8ee6d7a484371540875c546f6c9b42f6dc2300100000000000000c2a1109372991310a96d78b8da314163f9385399aeea368ed7e7c37f201893340100000000000000040b8138245dea143b0e2cb70875ea60ee74ad3df50b916db931db5e583d5b0f0100000000000000f6d9021798d518b38d24a74035499871973f7e0e67a3bc5ba37c5d4893e1d9480100000000000000ce87603414deb87155f1df0a5bce17ab2fcf11302407b96b7b73ea33169a69670100000000000000f045461d722b94b6c0928af47a0d8ed3b78d646b2f4f754b1c90196465d7f92b01000000000000005ea61dfeccf577b54216c24d8580984b5a948fc2e0cb15cf75d3ec89ef00470f01000000000000004eefad32d3c3dd9b713031d2d3412b110a5997283e12e1573f9523bc479e77220100000000000000949ab1dab46139ac9df94d74a3a91b263a98da335b9127a2fc7369476bca3e41010000000000000080cf2f4b8c6f71647d087c97a83df3f3eb230bbaa5bf86299a2d3c8ff2f2664701000000000000004a85979f5a5af886d6808c63ca7f266f7a55bbf652e039b72f1fdaa9c90deb4501000000000000005878e7553def1229480d89260e485b7f3a5280d16b9c1f1163151dfe472e247c01000000000000000869d1174ea5bfe5b56385c981fc3d8b58424426b8a826f44bc7fb6e2aeead7d0100000000000000d876abe03feb4a8bcfdefda30228ed6820b1a1fcc5954148b0ab36c88fd1ef2f0100000000000000167fcf03f83b30dbddcdfb8e4854b03645d77a3be1268d674e65ff2085615e0701000000000000004e3ab53a4117874a7fc193a4e51a4b50efb808cc0f26a3c3616cecf4974079660100000000000000502537a2d009d0bc18752420e46bcf3ca17091bc04458ab50188240361ef633a01000000000000004e3ab9c976dd36a1fde5ba6c85e91f24b0e0f1a4101b2abd92f8eb6953593243010000000000000072febf0d8454db0a0c9287d7b22f739ba0154b6319111cb5104130557b6fc3670100000000000000e036c3ff765103769a69a0015caee3b83fdccadce2a269a3df376d0dd4001d4e0100000000000000e64945c884e8e1f126c36899bd7540a6866ddc58aae678cd0415e240dd3610220100000000000000744488e84dbbc73174838a2fe49f231ee82448487e5e52c5268f9e659be8e95f0100000000000000e6dfae0c3ed3754930c79a74e58d09583dae0bec35363be7c1bab1c3f25bcd480100000000000000408fc6ba61cad770bfd6555973b079db6ad49880fe3335c4561eba5ee649372a01000000000000001031987c29702b10623bcc65912a080416d2101ac6e204a2075d9a66dea5752601000000000000001c85862b279bcf4a725b66e8fdbf4150e253a52affacd77f2b52400a2209fb430100000000000000c648a1f7aef735b686df2634b86d91c94428cab340393aea3a5494d02cb57a4f0100000000000000181ad1bad2d424abe7f915a584f39a6953a87fafc1ac0b9942ea2c64406d4d510100000000000000a0668c4c43e71d99ad863e6f10e887b2a1f2bf32778d1d4d6eab57f28ba5425d01000000000000006a3f450101fe066e048ef87e288d88e0635b3677ea222f17ebbe87d4fb92575301000000000000003847ea56b040d92b53331bd3db1cd353b1001bb6678bf29cb12414eb5ed03178010000000000000092049218a50b3a59059599fa127bc3719546531c80ecb68d8ca8f5a0671c915a01000000000000002e1c9f1141ac38310b771a3b1477ec4456a02000abf09c4d0fe505cbd8cc0e2f0100000000000000acb79942c9051fe2047ba6a2f0c73ac71bfa033ff1b76d84332361573dbe8a1a0100000000000000848667087ecc48b5e6ce7a424eb24090c6b597cf121591e3d648bbde53393e79010000000000000048f1908f2c135d1c47c7dc79f63d6c4154a56ebbb46b039db014d8b444cfc1380100000000000000f01fba66537030d59b1e78a3246fed5a25c5322734ff422a9609e78eb0b6bb230100000000000000000ea8eddba18d4da471cc88fa5ebf59aa7e0cdbbcc7ab13643e2f9d2a1e120701000000000000004ce0e117bccbd3903add7cfecf3546010867b1a5fe6ba3ebd189e4cf9586a224010000000000000042f67b46c75b06dbebb389f8cf054f7c82187a85ed234f3920b919a161046e2401000000000000000a5aa5bb5d80bbf7ffad004fe82a73d66db8edad93ff6ce0d8df78d3e0e6fe1c0100000000000000acef4f49f13788a7356755512b74656cdf11fa80d7fe7223e250a7478d2a8d3a0100000000000000b4acebdb8857e0261b2229a1c6848cbf75001ab181e0504826d6ef04ca04e92101000000000000008cf03fcba2760c34855ce798becfa293b37a78b79820144da5ecb526f633cb60010000000000000082c55569a2c6861ffa4f116b34d3c6e12b5fb075f048e7d6ce35ceacfa40a93c0100000000000000da124054c707782430358583f3fd98aa1d4dd90968e8b4f3d8683ea3f27aeb1f0100000000000000f2bafbef8d77c9facf5a04d9c079d847bf793bbb1b659b4ea4c7679557007d7d010000000000000092ba571f52063897a993dc9818bf50cf039213409b550794ec32151fa089d31b0100000000000000da088ee3658a5e9587f0e2e7327bce5f20d5cc98cd986a5bb817d9b9a587567f0100000000000000dea6ca7823dcb0837f8533d8da27fd1a1411b1b48df4a8706b2f1201baf23b330100000000000000366e377084ded70d1065bcfa6e0629d9df8b01def4ff4620432dbd1ce70a6a55010000000000000098755d5f5fe724fa2f0116ddb92c0d9cfac4da3eeeb51e3ecf0356cb2966a71401000000000000007642f0c1c4d20c54338f0e3fce9b66576b392cf5ad165368c9924e1773c8f0280100000000000000ac198b700e0e627e2642654d791a9102e19bded7085332a1b76f543f0d94a1700100000000000000380ecd43a50457d037678d57a799cd411c1f56265ef38e0f777790e7ade53e7301000000000000000a42164a9749a911c34ff4a8ebdfc5d004de3fd7f06891fe5c882d2b56c2e6340100000000000000c2ebf281e96ead69e96276de04407f5d89a6626b5cf25bc97eab4d35b1b50a580100000000000000b64acae010e2e85df0d36ce79d919a14c43f0dbaba232ee96ed1e5708ceda94b01000000000000005ab857faaed9e6c7aed2d200aa81404e546160140fd0e5f314df885f6cb2c31c01000000000000000c7f753c4216d22f5e9a56f145f2dce74230a875c77c15ebb57bbc672a84bc5a010000000000000076f35f08d6050caca1bc42f1bd55d6d300580caf671f4a3b3bfa14b23b8019400100000000000000e8a3d82b64794602d009b8aa44a922a6c4aeb9c19c36720026b9b010e4880a420100000000000000442b8aea3303213b7f5e5b592ee0ad9a2043c34b62b8bbeb61958eddfb70912e0100000000000000c6daea6e872ae2760484c874d425703ce31f238748821e5fa7689e529d8931380100000000000000a2d5d4f254b43ef338ae46aeb6c96293c9e80b6508a4f9e3534ebc3786c3137b0100000000000000d00d343b5b85e47a6494a75fe67e3524841029026739ff98f7614b269c7e10440100000000000000e434239ced7867561ec1c186d12742bcacc9e69a19f6677540ab902f0e450a66010000000000000050a9d209313d30fc0decd461fffd3f46248364ab3a7e34932ec3b1e4ed720334010000000000000058ef7a28606a9d21571e962f3ef8b8adbf9d3acc41517bc9313985759ff1933b010000000000000016e5bb9cd97e2054f662f70325ed8440637c299e77736d3c31795e1225746c5e0100000000000000e03b6d54dd7017cd4ebcc6642ab9c23c62e2d846e610c7ee2e2d3cd71337b55e0100000000000000e6801825bed5f4cce30bde6abb75623a845bb633ff2e14a6d696a85e92690e490100000000000000f6c65ba1b18a790cc704f8e3a1bf3177ff51e7eb0081df2cb69173bf44a4f71c0100000000000000fe84d61179f6f25c0f3b84b4867b3dd0989aa22a2d9bb709ef67a8095008414201000000000000003231e6a8c5c5e47509eeb593347ec53cad6f05a0b30938af3861075488f2b03f01000000000000000e78888c098cf42dfe016c9ee1cfeab499eb765ad64cac48b28892a0a41ac20f01000000000000004c8d2e09ef8bb646de84fc38205dd2055f7ae3a56ec41b9d8d3b45a15f8b69200100000000000000d896c042b016100c440efe8a5ada9d0fe30f4eec18ba67992bbd1d5e513d147f01000000000000005a2e0cace9c852e709ab431b7a2ce9bfab90fddb0676af571452b8373756f63e0100000000000000de32e1c268482c89301b141116a85c438b6ce208513c71a12fd81777f144033701000000000000006211919c6d1b11584fcd6f3f3fef54d0d72f23ea821685ba235675bce10504290100000000000000e21d26c4a79725351bdc30e3825afaf4ec037fc82890c9eff8eab6c5dbe42f0b01000000000000009e25cf7dc9db334d01aa6215a0a9e27468a5bd09ad742bfe68b302c3975c31510100000000000000eeae3f5142c5fb5b16632f97c3885fce76a7969df551a3daa26c0690c610716701000000000000005adb6c8cced26392d5bf6151b5118a54c8c83d4a6b3bc26c2a623eeb3b2ad3190100000000000000ac91d2d8a78bb7d759742bd920741ed92c02115c7218d39642dea17086de3d3a0100000000000000d263e15b1310b82d6ab02f9f77b307fa234ea4a139b1135739602ec3c97e3a6f0100000000000000c20ac319e9cc300bed0ac95df0fd200751ce194400a66775ba8b68523b3f895e01000000000000008890f167e5966fb28d63bf02377a8b064b2466930fd947e54adaf5bbe6019c540100000000000000f6e92ca7e76a3ead20727d91199658a84ac784f496f078a71da0ab09e16a6c1a0100000000000000f65d2e46fa483be5f4d052b2f03563fdf7621ddcdedec753a4ef9508782e106f01000000000000004ca7ddc4814c4452f6d1c98371c6d2ba26ccf94841249edb7fd120fed311262b0100000000000000380a3f04ad89f508b389352994ac011d1845187c479281320ff77f4352ec0f200100000000000000a2c51949aef85bd6d86dc1d699c453fd89f19e171c580a43244b37ac32db775801000000000000008cfe7abfb01580f1d3a9633b11b9229b37f979e440073d885ecccdb32eb64e4d0100000000000000cae3883e0f4806a85ebdf81669fb03f002d699c95f5cf6299d678ec16edb5f6301000000000000001e2d439fee3ab0966bcfe0569144e399d8c1e431528b04a9a103a0d5d76bcd5b01000000000000008cef7f6ca0d96d48c03771e33900c28541bc7d5ee15689e697cd160aff14797001000000000000008c63ab0ca01ed8b358bb5eaa2582a0866fbbb68d170a03f2ec98e9eeffccf85a0100000000000000dc8fc9b4d3b7bb540e9f12c606e4b0d784dbf8542db625f56e686a2d2fcfcc25010000000000000040fbfdbe5c13e3a46a731b8191f92cf01734f42cf7293fd56d664a732c14ec5601000000000000000ef24b61e18983aa285f70ff8e361397b82a15a41c6742541ce7b92da9f8931101000000000000007854ee17d89a8b19b9756fbb60653d788f6d261fad13a56933e0640c674a3b3e0100000000000000e4a3a09d9e9b63295de7b3cb2284d87af269ee20b02de0b66fcddaddededd0070100000000000000e2b114eb3572e899237c85d50df9e64d4f29901220e9de1d7a8e04d96d2ef24b0100000000000000a42e70c4530076b877035b43c2e41672dbb02b95bf1755e1f808e3cbe23369630100000000000000d8df9dac3ccd81b28470ca51de56a4ce7c637a0ffe4b73a826cf7fd33b7820040100000000000000fcff2cb776b86e8a3aeea98f908c3195e4ce5db676d36bc3a7af3608698ef0690100000000000000b684476641cb59f5820a10f788f088080a953b907f990df22cc7943dc3a46d3f0100000000000000c0a1d3aa25d5150e5c38220dde8f6049268270e41242d623989357654078362c01000000000000001431ee16d3a18ea78d6d22801c6a25fe304172dc5febbe550026a1c0929864500100000000000000fafbb4dff5111e01f4ec36533c57a92f691edc68cf3e86a2a51ff16113728b290100000000000000a01d73a031bb8f4aba71d271be16a28896c85cfb9e2dda4fa3b44936207a2b290100000000000000eab3d83726de7218ba112c82a45c033830114b31870c16d0a49d9070621e012a01000000000000006c1eed8b9700d423e00bf57ddb3af32d21d8f0a104dc650e871ff044fd754708010000000000000004ad9d2a4b586292d19f935a31d89b90df9c99c74592adf2d29e64a5e79ab0120100000000000000e6dd828a602eb81c7522b98e53ceee9e22d0cb5a6f7cb0e3b00a7006f0e0770201000000000000006c7642774bd973c6114e6f0c3558c8f166cc9c8e980ec1d7ec3cc0276426c77b01000000000000001c3d79dc60c89a9873f2c5379ee71f4f298165e07109d4d47bef6d993a084e1a01000000000000001287be7a45554529c73b9243a51a201100a37a2c2509a9fa3acaa4dc04b5093c0100000000000000325f3840c5a9642addc7b6648fdcc21495999e5c00b368420b23e8173f5b9d570100000000000000820409942da35bb5ab23f20f62db3f47c5f87492b13f030734b7e7fbcb05312f010000000000000088ae601d5eca42f99686b6d19b3889fa79e32551d065f1cd650badb2fdc9450a01000000000000002eafead8f602b2ebe286db17142e60a41962e071346b4f1064fcc70f2e19552e0100000000000000de53a86b00a4690bb292c096b97dd222fa82e28a833bb5969aa9ad4a273d31630100000000000000f0efe1c1c20dcc2667571aa3375be67362e7f28a49bbd9195f4b5d71e99fc1650100000000000000545b0afc9dd9b56d8da46d35f40723548fc4c13033912da4fa1da2e41265047c0100000000000000904d79ac4481c639afe07fa078bacba2d3a890b3f8e133cbbf744e7fc5297558010000000000000036e5fcdf6e878fee49aedc20f21dd56bb41ffc148d32c34ade94e6fe40a947250100000000000000bc615a1317f7bcb567f078dcf0fb173bc02efaaaab4751c1d50c45b9d1db891f01000000000000001c808827ef3bf3e9de1e02128c8dfad7c13bca0a657b416dfb2352b48b02db3b0100000000000000ccdf3a11292657bec36bca11a4d5be787cefd82cac4440d0682b5cbe52af6c5e0100000000000000904c675598403fcaa16f8f3da9ffd6fa995046782c87590a7c715f2cd590543101000000000000008cb90a65a771000542944e389890ad0abbd2e2abae26dff9c389dbdb20f525440100000000000000b6ebe42548778f3fbb2fb24dab7aef418391da6fb9a07dbf8b889e338ca825010100000000000000fab692e1a2e6397aaaff6fc06302935eb2dedc68917fab400c049df1ce790e2d010000000000000022aff5aa2b5c2f1b1ae4ae3855f3ead541c2f0c9093a946178e127dbec92a04801000000000000008056a44b805cb78adca43e77724ea222b5cf6c25d6b314d38ba7c169576b0e1b010000000000000096169e83fd02bc9ed28c6c832a3240e0e01bffd7f8e5db4f2b85521077212a620100000000000000c848b06bb3577ba4fbf042fde0779bb9a7eeb2978fcde090dc5733ed36043e2c0100000000000000faa30132a968c6ad5db9938a74cfc8f2a878349fdce5776577bdeb3db7a96a13010000000000000026b3a3fb30b16dd2a07b9b6887998c789f2e0b2b50a4e26b792d16d9410215790100000000000000e68f1824cfe5dcc175d3796ab3ab85442bdb76f9210ecc09e01db47291f7e5110100000000000000b84d61ad8579f5930af27feb2c48e63831bd2929ef86c3145e1bb065da6b1c4f0100000000000000f4ca3b31c69d7a198e30c876e1bbb904ca8c5b038e5a811667d13e391e13207f0100000000000000fe922a6bcbb95646bd14a97d96c31cf7a4bc599b1ca7a53f39eb603db40d5e6901000000000000008ec82c3594fa26941692a378722e80fe77a45b83bd1c6adaf06ad48bff228423010000000000000032143326782c6df4957093bbd84260679d1373bb0a62bf12db89e2a7c6969d710100000000000000a0cd6c7a7119abdf54904eb287334be70f7d9ed0b14eb82e4b453efdb7899519010000000000000092ea0b49900eb565e46e7fe6c88d93858a5908a48e13c5e073183df24054bf44010000000000000044479354ecf5fd25d70f527143ac61b7824743dd51426d1eb89964e05489af02010000000000000016a58215c2c1c4a19db6a79ed12df45d1bd036ea02ca7fb81798fc28e80d054501000000000000007a9b8439f7407ffcb77f97f7bd878edefcef5c6c780bf4a49a5b4ecd9fe850190100000000000000e238e399e8aa6247525fe6e10f77ffe34e93465f6e2febe695ea74f90e3ec32d0100000000000000b45bbe08cc25b9b0e98a490833440b7932e4086201c82cc950376eb77a6ad94e010000000000000050b8e2a374c29f6a14cd27f72c3467663915b006599f220c40672bd080a0217401000000000000008ab52131785bc4db73448c72fd6bb6f55f6ef370e9293489e124d0eaa3215230010000000000000092aa82fc527b300d0365a62edab188ed0816aec9a68071f88d7d9fd78efbfd52010000000000000028bcc25f41f0efe2db27838a212f5f7abad9542345b3502ceb91226ef95d0e080100000000000000b2370f9da2faebcebb91db0f4acf2171e4667e1e88f6a011c1248df5e19d85310100000000000000e25536d123bb812892da8dca4cbe099a8b1c86ba9aa702cb5b95743935ab9d1601000000000000005467c16e538a310001c285235bf0ac5fe3a9caf9d82738451a9dc188c892cd5d0100000000000000f6b6e74ef97f7d14f4493ff14c9f0f6a76cec7a4f713d48fec5d008aaa830d4c010000000000000004e09dba366325cea9b0b8df63e0a00a917b53ce34aff64a8d1c3e76a15fe34b01000000000000005ec8bc4e1b32dd9e65c029e27494ea67fde94ff891b068f6f01a0ae33b17312c0100000000000000569605f7c34797a11605cec3b4950f8f5c690db51b24bbe366a1259105fbd1120100000000000000e80c0983f6e5a56461e1312af164e23f0bce9da93e1339df16b99e45a335434a01000000000000002afa459c5fb5a2e43b8c6162674b968cfede7f551acbffd9de8ce753643290400100000000000000c6ba3592ef9d1e79b36a3cb707372fd1e0c0994887600205f2265d370341c71b0100000000000000501a9f8a14e0f544bfa2dfa5793cd7f1549d45d213ac99a09f1dafd821cee6330100000000000000a4af5fed768cd4433ee90c534f86cc4a458795fa7bcc54b0f3ed8cab36cda46f0100000000000000048f25ba12ff8795ec8dc6a7ebda399cc18453accfdba1e58b111bd3c3db322701000000000000001628bdd8cc64a774215982d34698cc24cbf79a71c526e1253259c75c3ab8273e0100000000000000f86a955e179e71bf054104adf25dcfe05d6b4844c14a70b71818c6e0b1dfd81c010000000000000012a54ebefa1cbff0e621b59449bc24d94881d988c2439178c91ce7ffa374a2430100000000000000b2d01a3feedc72f78e575c8e77306f2f337f33a7d8a5a7d28d27dc58849a631e0100000000000000466e7c8220cbdbfbd1acf470b839b2c41f428ae619698a8bb640f10f0d9ebb6f010000000000000008427554054a0e5a7ce541a69a3b63c4734661eb39e303e3fed502435e5a73730100000000000000fc6000725023c95d259e5e405737c39a2965d6289843d3448fbe06a5165ac7390100000000000000789c23ffdcaa9fead87226e570e0ccacd11ef3659a0c7b5d960517d86797bf0b01000000000000005cdc38b8b8fcecdd85ed9c6ae897c0c6212d8fc55fc06ed61f503c157688831601000000000000000a790d1f76d6fafe4cf76f5086e16b4c0102e1f05bd92844034b342a69c2303801000000000000005404349feb5559d19fab0a2f924c4cc70dfb9cb28fc954322f184bee5026e10e010000000000000064ef00a9076e78d72baaa85d81238c5c02efafbfd43a0c07d8e57c19ef10b34401000000000000003e39bd391328e498ddc44d51f5ec01017790c2a0334210fce8239ee47487dc1a01000000000000004cc73eb33d62c0ec3e7f886195bcded7857aedb9ac161226bef22b0317582e63010000000000000004b263ecddcc9db4186f90e8b65bdceedcc5f786fb72698779f4d523a7d28b430100000000000000fe8fe762d7b321d00ca277b8c4aa433bbc8001c7d1d7a2846929c42e0dd6483b01000000000000003e6f47144b40eed6ebcca435d33d00d4609bdf3e7af96270c65e858094b309150100000000000000a0fdd556e5d6c1f8ced14627bdf01fdb4234118e85703d24274a265c45f92b4b0100000000000000522263467c287a18428c4a5a92f37d273699c9ebb0bdc21f5e9a49eee434dd360100000000000000c8073eef80dc2bbcea972a83302442249e5d2a5213e65297c60d56f4e9be86330100000000000000fc04c8e12ae94b0e3889ca36e2b4a09c32f9c6b7bfac999f56e5f2dc72badd1d010000000000000096c09ca3191f61bad04d1bfe86d042857d3df82ad457ea844caec645758c4d220100000000000000a04f6b14597d61c7600128326a1d75205fd239ade0c3b25cf359ee731c80c85201000000000000004e0a418c4c371c6e99b76781644eb38a63789b5d7c5263f949751e9714fa7c35010000000000000044f2675a02133f10999d3f5542ee2a6de9f29a95bbd0ffce8dd47adb1f87c7260100000000000000f4071d8ef295841a0bc13deb8799e23dea815e376eaa23c86f65a5083d9adb650100000000000000fa4086b525cf61d35736e736110859250992c443420778a90cadd8e8c88d8205010000000000000016dd18d2e8f3a507321ce87ca52145f9526c0fe35f7c868d95fba2b986f84169010000000000000040eec19a099c9ee6ff24e3eabd0a7bcf36e0e0130b4718d50106344044c0d6270100000000000000de220179c5aae1e95b3ccf3a989ca36ac76061bde0cf710670a6910d26564902010000000000000090c3500722dd6123a02fc74476a10b866118e9930e9c73d6e1d48cdb6d57e43201000000000000004246cd1eddf8b9255734a8503025d3d0a30995d71fb5645b1b16be777c78e901010000000000000070c83248401cd7b8780895793b6a0e568802f297a3bce0af7d51eb653d23304f01000000000000000854a35b9b536c8f18af98c90d6e8e639f855eb29bd76cf5105414c21a1ceb6f0100000000000000ae7c51f4c49c174b2400d057d61a1f43a8e13a6850abcfb079a909d136fe374d0100000000000000ca5b9515daf1925b3e200d73957dbc4aafe71160d33b5635ea57641eef8bfd7901000000000000001a43d2bff1c184ae4e83348c8b373df9dba72d90741f3e3c664ba6a2cc96e9460100000000000000225ddc65b656709973da9546280bb4b9921bda577344d3078f29a2a8d463131101000000000000001645c1277cd42f9d98b76f5b54b9c8e33550d31727f24e5c5c50acb1089f68600100000000000000b6e834b1c53a32fb3f8713bbc96bf812c6e208232c2d6f094805e8fdeb0c876d0100000000000000b60af9ed41b27a8e5667ed9c45cbe12bdccece1dbdcc096c0763c0f5046b7e1a0100000000000000f2118bc805ab00459fbbbf94668a8793c8fc142956a91a538b55797633728b4e0100000000000000e2d0e3cf521829f443aae687d4533647fbace9e641f5fe677a2c4bc3d7d3b142010000000000000034dcf88929109b300a7d0cb74fcf0ab261b1a273e09f3d9cde025e8a2b10d62001000000000000005eae7ec92e82f6fd8bf2b1f720a696d0256b35724406cc1c16fe4c071448311701000000000000000e28b34ec63d7e1e4dcf7a5136b63bced930bc9e02d416fd5aee66d248eaa2020100000000000000ce49383c47b78ad330a3e5899850092ed6c99012b59d57307de5168d0d75b423010000000000000098280d11d9c4bcdf35884c994da018af07701df1a3ac3da0fb8bfcc95b7aa05701000000000000005eab887073ebf35904cfc25021f4f15442d6f334488dee6a02b9117698f0d538010000000000000078dcc07cc7a2f8cce0384b4e14f9518d3f771960abea998292bb6a52f6bb88560100000000000000c8387a2ccdb4ec7443fd3cee75e8e559990acf2db6c54a88cd99f45fac1a66660100000000000000327e66f794b3379ce2ac64024813e987661fd4567c9f067d32438be273911f620100000000000000d25555083cf20fedd9b5aca0f26447b57bbc2788ccdcbf0af2efb0b010f0ef26010000000000000082693a174b02d3e8b8d6cd1574b5c782dc98edd92134854adc083ecb36c829250100000000000000fc18fad14af9aa7bdbc62e1fff9ed7078d63d1137a3c1e670bd3a3cf5a93d3720100000000000000b4765627822d1702513ff5648aa320cda597f54505757d630e96d66aa1f3c73401000000000000008a2c2b3a835311965eae34810e418fbd1bb858fb8840a850bf036fb7eeafdf7b01000000000000007abd57972d56585451d75240342fe7195758d6022e0120fb2cdbc9792bd2ec270100000000000000a2d8db3a4f3fb0687052b930bcefffc672c01a65538ee7b33ce9dab822bca1780100000000000000ba73d9161f8469269ece11dcfb9081eedb02df28643417622e258bf2a424a6460100000000000000084b56e7c7fbdd6b45d2db9e3e922a969b28318b35ff26692d88beca3a749b260100000000000000ecbd30a0be3dc9d3f8f4b29af83968bd76d0c62bea36505a114cff593e0b8b4d0100000000000000bec516da47d1cb28351ecaf868365c664aab9076816f1c536a24be5892426a150100000000000000688a95e2be5dd66a92c6b401a64dead0b35f90f517fab9673dcec192e1fa207001000000000000007cb3bd528db15070308159636ad9ca12c1378fa7f8adb5206ebee98c0986065b0100000000000000d8da588746cdbf28af1a52046bad4d5374c656a7111c4969094929548945df71010000000000000074c38c9e1867a292166ced2f98282cde342789a3b5d13c5f76f15998eba3e8510100000000000000485f591ac71f970ef4610ab2fd5f034fcddd9584d0213e46f3161611d141512f01000000000000003020c45aa46e4f75fd5fc42d966f1b99bdf1104a667722d91568f063bb2fd511010000000000000028c912c4d4ec34a97f6416c64424bf80494332248e6a7bb65784a6221f3fdc2b01000000000000000a3c03895ec10aa735b1e2eccb283242afe8ea6f17a675815112ce842ebfba0c0100000000000000e45e549d0f5e96b9c5b320e37828c80629fe7130e5ce695dccd3438dea5ae650010000000000000072f45f59c55d6206a31a1bc2361e096731b34a8745d0882ca2e77694b650134a01000000000000005405d5e7f3457fff778bad5558ace408e563e388edd901f3af12990afa7e8a0c01000000000000004e717a4d2c84dd7b159a91bfa677ff9dc57bc7fd3cd661523c8140a7680a27290100000000000000b280b7f37e7cc9a50d4a52e0b7fedcbcd3cae22ec16ed4b4b75ad460efc227160100000000000000282509d0ebf9a77532c60bbf28ba1509de4a0ccee5ee2f58ff267963b4e11c600100000000000000b85015643739ee03dba7b523f32db23cd192333ca1c2dc134be5b67c306f104d01000000000000004e8cf6fd4c64d23457b08b38b3a72a24279593b2aec4aec3294c67c92f4d801a01000000000000005c3a3673e42f0560adb44ee5b7be7948f8964eae967f80ec506c0147cc11ab020100000000000000d6451fc5d7809e480c3dd53853789b86678b890f4bea62024194b14a745370090100000000000000c0a1f13309be3124c2357c00f6a5545b843c70749fa8ece832dc77bbb7fb630601000000000000004c3b4d7a82e669e759b0cd7a6d6dfef7b94a00283be725f25b263d1b4d9a7f5301000000000000000c714d39ea91dc8f07a7b925a03895a148022b8f117d19fa2f29479189fe423a0100000000000000f697d527f96e9f3839e3502704ed69b500b765f6a64ce6dde88af24db12f14120100000000000000de63bd0d9626943917c561a484e93462de4c05d233bc4c1fd7c8b6833d29353a01000000000000001a4e01ffeb9f145ee8a17dcff9bef42b02b5c9b047e05742ac85de33639cb06c01000000000000002ad227e1e2b705e448702ad1d8744ba85d7d5756952b19976d3abe60d60fc70e0100000000000000907b44da2b726af53076263cde755c2723774fdc25ea52f46769d8f357ca9f01010000000000000078d57cef73882a2d361a28b7fd2ca671b3f726ddb22627d4b915217834b7f2390100000000000000082f296935a2a606bec1f195e6b8456f07cbf7b27e1e557b3887e1c46e4bc37d0100000000000000f8549151ec9c6dabfb6d844ba74728b04a8ff079d00e55f5ee55bc3b70604e0401000000000000001a12a4f27227ba6a04b5804f339b0ac61510b6c15abc85e3bd479d6536fffa7c0100000000000000baede105d1ba83fd42159a80f1c0969bd4cd6ee77700ebfa1aa3283a9d274a47010000000000000032e2a6ae50a3524184557db89a681b1de81e490b4066b4fd67364984da49931f01000000000000008ea9246c435f67a71e9708e1481478b762011e0965cb3ecc405155b4d2543078010000000000000048baca83f5efb426424d78dde70340fd86117f01108c84f4595deaddb22593420100000000000000d0d353277195e01a4236de8fdfe59b442f17af9028dd3f44967dcb17c2e01c5d010000000000000006978ce2bdeb4b02ac7d76a3086ef1bd09e3b9857e950083b50389a48871f3690100000000000000a6d6c761b15a3aa636f5254f6a838ab668421ba56e31430282b33a118ac9ff1e0100000000000000745f2d27e596483e2ebe6ee5750b0479f3c0d355af3498c570549a29b8ac730601000000000000007260ab80632d40fc1d7eeafeeeb99dd747a0f4800de34c2e82578786008c5248010000000000000064ba39c577fc90f2d2976ae979a7205a4918f4c8db532081f06bda711ffc3721010000000000000080ae71b506da12f8a73992372864dc19b64df51f1437fcb247c215a47a759220010000000000000032df009616a2b4d4f6bc9111f2ac419abf4890c99d6def4ce575f82fe7dd163501000000000000005a378e104d9df02d1dce1f9a344f3d4b6f9f088bd8904d0eefe08734a98b42710100000000000000b64c6d920385362820b9f7f9abe817a4bf64224c857fb65825376471d8c95e62010000000000000082b8b2c123aaccd04d02db43724f51bdb437265e59832714a2c2748ac83f2049010000000000000026a2889142114c9b4a07fc913e87a0e5283209088b6ba8cc0148a84f94f2cf4c010000000000000084ec16701285c3c5edc5ee9ae8172215873ec71af3abd53ed9d407c2bc029b3f01000000000000009c2328407d05c9c294c6f207f7c8829f1de10c13a77ae0707dba0dd9f40f8f6001000000000000005c450084e0a0ae90f805bb2e856bb5b46a43cbeda05a7c1e2cc09265e1791a500100000000000000e21ecb8f6b42f003d0d9d3496bb5dc1d57dc74e496acf2c4a3735940cd54231b01000000000000006aa3cfb637cdd3b960d2578823da2b6dac1083495e421c6c32c9e17ec0c905250100000000000000a25f7e57318e03aa832486e574815234404b22d1548f35cf77ffe26573da1f0d0100000000000000227bd022f3a23819559cefc5a323fe8347a3b75f74de5c7930db82cb7fecc8400100000000000000b4af11fd00750c08b33c22eb7445aa0d0fdd877bf701f0755c436d7907c1c27401000000000000002aee8f940e7da1699a6deb601858ec4aca5d597c50ecad1c3402dc97da339a0e0100000000000000d4738db14d68af6357fe1d33fe05635287c20b35a55a6df8018141ae26af9308010000000000000026ab3e54f192d9f8ddf020571780d10d92a0460284fd7b9ae94e74c926c2112b01000000000000008eb2e37eee463c14fe7855c6133aec6244ea3457ffb9b8e0f3a6ef5784548036010000000000000094d6f27dde80e42a7f95295a0f62492eec7be0b70478fe5f30f4c29e280cf92d0100000000000000d27d11dd2c3ab8a3e15a00507904b9fa81c0182176208f622543309788c68f050100000000000000f64491339e6b78127580800529b6ce624217adc82ce10f56d5c6911962e3135f01000000000000002808e5a217f5aa78b344d154002fefdfaf686ebfdee6b7deddfd1411b6a06d3d0100000000000000602f60398fba25dc09db2b4d8843ef9d96e0c60a01ab42ad976fceaca42195700100000000000000a0fc4378807ecdc482fe8d93dceebf78cc5d627c03f9412ce1c1d87d7b44090b01000000000000007008909e788793c04e57158251d1485884f3bb98741d889ebffca8ed9644141b010000000000000072dbf42dd0d9ec7a221912ba34b585d7fc6a0fa240d719332bc372180b3b636d01000000000000008a5716f1c004e49a930e1f0de54439f0a32145cf2565727dfafc11a024503659010000000000000030139a5482c8907486506234bbe2eed7359b6b7c0630468d46dbf2118fc5bd340100000000000000165d26b408a69bf67c63a945a49058f53250882485046f71375f9e3128aa9e52010000000000000060cf7d800df60f707be3b735805e13cd4a7eede0ac2ecbd7033a872b46403f2c01000000000000001872a4fa55daefa13b0b4e4f218cad38d08d7d39d42e47f7df5eaaaae02f0a370100000000000000bc800344f0bf1a71721fca1cbd7ccc47dc009987bd3524a883ebc1e925071f610100000000000000741b61fb1574f9989b1fcbfe4bdf72134ed5699983228a2cf27ca11d76c990160100000000000000ca2e3f3b6f3cf94b81b3bd4c55c730e41d8df97e97bd8084cf1cd24e68719401010000000000000060b1145a4451816b6d3ccc173fbc25b6c7b287fd270758152b92b30ae62eb7520100000000000000483e62e86514eee828c7144e945b7364598a01df7867f7f637cca09e702eb02201000000000000006aee8ec61df9edad0c5903feaf88f6297ea64efd82a52c692c5e937e744faa010100000000000000040d3137e8d771c19de798bd681da04262e8185db315bfe393cdb5cfaa79fe170100000000000000864b7adae776289119fd4da00567efe67846ede7f576955aa4ae889981276a680100000000000000ec52884a19e99c0df416f81fb211ef85aff4b0cbc220d9ff6cd34afe35bfc8790100000000000000b42d7f267a4e469af84c38da01808f135bd6853a7bd92f6cb44e7fcb952cba3d0100000000000000ac220a7798ad88df760c16cf8d18a42683c98528e854daefc7ee32bae85d646101000000000000003404a58d5e89fc3bbe715dd5921f5f0180059e74d9ae6196fabead7746cbc55a0100000000000000380702aac808412a0b886294725efbf4fc033b7bbb85d3b24c2876cc9e05d212010000000000000082af68c2e6079cea3474b2e9d76796b70b2acb3a0ce9fcc98503080f25e629230100000000000000345d63333cd7a9a17653f94374476eecf5eba6b1e4cc1c6e9ed215ba55e5cd000100000000000000960789a11e8dc46636aa94789c0985f9b8fe7fcea34abd0e56f92180c0ec773e0100000000000000a65039da8396aa3e559367c50a309f12e8747e4d16245ec6000d19a0af26f9470100000000000000680e8993ada64e3423fe61be3e70aa728ee73725fae78fa5bf8c57e8e1c54e44010000000000000052019b0e030fac4f642c31526c6308bf9501a297c9b6193a82d6915fb708ff7c010000000000000036b72e06ad058a7708948b66346a0350f8e61fa8c58b0e925600aa9bf1476025010000000000000068b06ac165f434922b8bcf48deb490e43b0d5497a2caa64654a1c673da36b83f01000000000000002654b04f0eeeb6c03a28fb4647305162a4e1112f631b66293ba16b563b971d42010000000000000052283740793ef867c77327902030e4f6c0deec494b5f4db2d680452880ba11040100000000000000785545f9357cc0a5e100f2ee283dbf8f5f9d4967b07aad4bca6ee866312430390100000000000000d60eeac98c4a07a3f08e3ed451c61174b367a787f720a6b7478178209e28334e0100000000000000843d6ad78e2665ef2b86f659fc0f12af29b11bc7f138e45f013e9afdc728624d01000000000000002e9d04dc6cc5425a6ba5e0f6d8b88623849a669542d42435c7c843aae0bdb7250100000000000000e6a422c3c7eb6cd9165fee529afd2209fab7f39b1ae491dc3d5c1320ccae1c680100000000000000c00470ddb02e84a7bc7500d90418dd5d133f4f8184ede2e15cfb43b881cb0434010000000000000092ef07386f9bdee89cc4cf8dcd1aa942134b21ace1bc2f121c261fb57388d278010000000000000026126b727ca3d359ad9aa85e71508ca3fb6a6fbe1416cec946bac3819fba9a590100000000000000d61549c459c94ab938a68b3d96ce644c8672effe9916ad3435324727eb18967c0100000000000000d6df798a261d38a33360d44a4d53e7a686562dec34fa12b7e5db309b736ca642010000000000000076f3274425ab752e21ddc0714e28e9651d4206841b736aac6db37aa0d0d4c937010000000000000098ffae7440754e3de87fca995a95250a59ea23cf595dfa1e53b1367ce20b455401000000000000001a089db458c31ac827e683803163d520ee1b08ed721dcd45b02c3d85855c8c2c0100000000000000267812fa98f9e8e92421e2cf5fc5de6a9423cb81f3b9242d57eac782b3cd09700100000000000000e4fae18a7a4c4f5196539556b85a450a0c1ecae4ef28bc52d1ff9cf3374c946f01000000000000001283692388ad47b5a1ed8b0d791c4f86a36e4422ef3857c912d68f5d425f34730100000000000000eae8a9a4316fe7ce6cb8f624a6615ea281f02082a5be9d40593db1048b234a1601000000000000008eaf59711472c3ef85e130855a4e34e0e3f2b8852af88ab561e9a49f206156280100000000000000dc6956dd35ff772cec5f85ce4aee9e6e6a4b9242495077d77df3e3c48bb26e50010000000000000080d7f21b01bbe9c1c6d89a7275a660af2c01570e22e9ca538b212a9577421d11010000000000000062c124e506bb86a27ceec1d2dc59e15802f14312b81e84f8998735ca13ee9a730100000000000000667ab5e18370d52b66a8c57cf9104d5732b22de108fd38eb14f8b7da2a29ec0c010000000000000032483a2e28748ea5b9d6b87256ae783ecbbdc3bab669542fc642d2c8a6e15d380100000000000000727f2cc64c33ac6bf5b433538f35548a996e80ca8fb8b1de84626882c7b6e1660100000000000000be210d84cead5ae1c0ebbefbfc0b35f33310d6aeecf4308529b86c2cf82c0f5f010000000000000058070bcbd7f8f06c03f9b7aaadc8678f182aa4b3672f3eab877525324f35774401000000000000008ced7755d2a8b17b2d9d157c1b9ab5b0ed63b25b9377d21230d346c7066c39520100000000000000caa319312d929a03892d06e2a524621cd9fbaef1f86d325501b4f1108e65a96e01000000000000004cc8f3fdf153ac8200029d319b8fea21e9b483bea1ada7cb9bd9265da69cb9350100000000000000346e685f148b3adfaed81980e47a61844871dfd86ce1d4f84a7b36998c66712b0100000000000000c497e8cc7d05d62aa5f59e9699962c395433b9ad38bbb84748f90c7d9f1f373301000000000000000ae2ca012bc830761e5afc8e1d81d62c6d160962524fdc3cff021ec49f34e123010000000000000018f128a7e5736dea14b3cde003c82a6ac8c922d2ef21b7e02e1aedc841033e3f0100000000000000a875111a42b538e568016c84c89f4107e37e5643d9a307aad904a7bad7fee76e010000000000000012082ef825e367087ea396f92c97868c24aea7ac2435c8acace02be51068b9760100000000000000a62c3f742cf861feabffd3f949f64703932d3f49aff5214e7ce5f7bb6966374501000000000000006a0e1ac35bef24f8e229f76ecf852ce3fcc87a884e8770c38e2677447e3c8773010000000000000004b06530357d34a7c37df9bee937d879dfe32d5506d1eac3fec50de7bb44dc5a01000000000000007afb0c23bd2d29c347a9b435a6db5b56e035a3e021f3fc9dcfcc47e52f3f0e1701000000000000009eac82acfe866c0d456b51e909c6b83dda9ea53d89b71945c3af1539f7b367740100000000000000dc514d2b1231ab63823ae49ff21fe0c250eed9b4d2e3e83d9cd5072b5f994719010000000000000078ab99c3d9ea7f380ebfe7db64dbc72ec4ec0516b143c2f66f8b06943cb49175010000000000000060bf5700590c273827ccd0dd56915f377fa2ff366ba72bd81942908b7f2a742d0100000000000000a013268290f12c0417675510df18855b1f1a923f0b513f7e7ddc1e763e27cf0a0100000000000000fe0446048559fda5d5d172dbe2ec70e54a5c3080394d055b0b15badf80b6af1501000000000000006ce1cc45c84f9394951284b950417355ff4a093946649a1ed9935cc650d3457e0100000000000000a253dd11fc5c488265b2a938adeff374961efab3789e0d9e637b566a317d71390100000000000000b0d22d2a52036e2e23ccf11b48ee7636021e88a63160754c8e161276e2bd9d520100000000000000744a5456dc956cb654fa881f1ae233b3fa4b245d357826acb8874669726a9a250100000000000000d8971835a1c90022cd05c0f259fc6407970d8fb59fc3ef0c515e07be6d4892390100000000000000aa22f189a8994b03920c2bf1d15d0699bb548c81c52809751cee4ccc794f5e0a010000000000000098f80bca2432755fc52885f1d8023db386ac20b33fe47dd516df4d9318f8ba6f0100000000000000a434f5d00ed6fdcebe637e8fdeefb417f9fa17c37970d1cb13e0ea4ac82e2d4d01000000000000009ee276b61fbded6deab6125d7b348e03c4b3aaffa7840190ed1cbcc4a07ee93101000000000000006ec37117872c92130592d0d0a6929d21e3411790f02e20cd313f09ff2cc5866b01000000000000004e4fe940a0fade721c4c3841d8d88788b100bc0964b65c53f1fc9de3f55d416801000000000000007e0ce7ca3586ea27861b81e7334e5dde29293682fa860c367b5a47e538e3760f0100000000000000549467ec60e011b6bc97752245e56aa4abb389ad87f13b914746bf88c3c93f0f010000000000000084a19d04d5544e82fb8f9f928af37029d73c82da314f437ca62339890b878e4a01000000000000002eff374cd044c1a795a453673fd050ac67510592d7e051ac432f9f1dc1080f6c0100000000000000d05a7baf5e5fe42f96c92c6243a6fe99cf7d5a2f314ea6c4672e5f28e23cd3220100000000000000b857af823b2fd4d88db51d82bbaf4d69b624e1edd3f4fc87afc632c34353971901000000000000001013f3ffe121ff83fd6c2c1f10175b3ad011d9e57c41e85926b1033277ecad5801000000000000003c68c21b23ad1e95d92b93146ddfae139e2ee2bc2c7a8b437c41e20b8a11d9280100000000000000ea8e718c93e3fcb04bbe362c8ed92207599394e6e06aac3145a3dbd6e068f04c0100000000000000e094c83c28dfb858b2cfda0bf6ac10b079e0c35b875365158201a60800d5ae6101000000000000009a14a73f6d4b1c9896ad4a10169452a5dfff45aebb946ea701a5ed94d23c470e01000000000000009e69adce8d1e72ba462dd7e226f1b34b91b038d63573cbfd2963948673045b7e0100000000000000045cebbcdc5e50d0a344d6e01b0a7d180c7fc14d8d007de35db199b31c026f2301000000000000006c1895ede65311cf2f00b5c57378fcc48a95a614d4aee03ea5fe0abf27d8d5580100000000000000003c355f55fe52ddd78f410db36294273478aa852a75c8eebffd3efa0ab2ca5001000000000000003ef792db76744a93838feb7da8b7f665689e6db4dc8a510578804a49320bfd6c01000000000000008a24331a5b0a512762ead3fdba639afeaf649b9d7ba5a939b502bae8d709640b0100000000000000b0951f5af1811973fa55ff157a38b0995e5700eea61ccff01c33096543b1b053010000000000000086e37259e697af7b4028a1cb533d906758ccbe3397c121010e5a094cbd10231201000000000000000ce0da1b140a5d9983ea7245ddc70a90a004d4aef4c5c056cd5fe3f271099a7501000000000000004017c942a56376173f9ab39d8ea985e21b703c3350280cdfb9e495795bbb995b0100000000000000c4fcf817d2c8c0c0c7adadfadb4ea60b6da2913587f69641ca43325516d864520100000000000000183a6a2a9ea985d9096c322b2ff3db841cbb158e4737965f5a98d3a53d0c551e01000000000000009e5d3be1837102ef421cf29a2c0ddbae40b42ad3e090307443f8d4af4da603750100000000000000eea459214eef62024f3fd784c5bdeb42e844e39bd272a92917d12fa66b62394801000000000000009a8f1dc9de51f2a09dada8a66c489135472c9d514cc9c9d17e8dbcd89a307c3a01000000000000000e4c162c806a25c08db000ed62e044cc8ca078f61352f0cc5c83f70cb514c15e0100000000000000100d6b2fc74287c9d030678b8ef8576c642d8ce76b9b6ce366c7ea5ad1bc4d7501000000000000006cc18e610d5c76a068185aa7f300215e2fa46ff5f1bb58307dc8f0df0b49112901000000000000003c26e68e3c41050e6603b62482ac1e6852f1076795ab0a9ec9d87bb4e76f1e210100000000000000568b52d575a8fec7299159b9d534b3268e50a59d3052fcbea17956d8c3e53f2d010000000000000008fac1a954a0e7a7702a7c90de2b1089ff57865e3181fb6e9ff7a1c34fd0f2650100000000000000b63e767df6a931fa235e41cc7bc47f939fb11dfa4e25698e443023bd150a437b0100000000000000d85c008dba8dcf0f67cae0e58ccd7e09aeda4d16e78ebfc94c0de3364bcb9e4201000000000000004e7f5c7b96db92ac862298a99bd047b0ca3f8356459c7e42bda56a2d80146f0b0100000000000000a6cfb4d5fb89fc4d3cf2ff522b7fd4267ccbe8ab89546bae641d1a583923b05e01000000000000000ed511c927dfe3a5d2ea0d858f16a008db97532b7a311fa82885fbcdd96e51160100000000000000ca1fd3a7d39bdfe8d47a70d6b1c82d19ccf792482181aade0e8d2903ee83a5130100000000000000cea92756fea9002431fe31980ac9fdd87f4139f22bf7436b182a0dd03e762c4401000000000000003c1a2bf3bc6b6c09a130633141343b91e3d84a9ee3572a00f5ce2a1bad23af7201000000000000003e7f4634849980d7f3ac6804336df6758ad393816afafa7c4932371c1d8797260100000000000000225b2615b66b0581bd26b9d2aa025900e83dde43819b4d94050de7c90d5aeb710100000000000000ee80790df6b6c2d1c29a053a364a5bab2536c77ec7c07080d5d6d1755a16e171010000000000000014fbfe3cfef628559b53f5cef7a3439af56553151254d5d9e8d07e85f10bef42010000000000000020a5bb4207395b9f205e128f899c3b37d265ff90531eedbf42170121029c392e01000000000000006e7b420de20353a979ff79c79672b543e70d70081eac397b039dc39fddb81e500100000000000000262510163ecdc49a4c74ec24e9a330d36f6ce31747090a05c4a264fe72ac3b220100000000000000bc45808b65d5f14bed3ffc08df2c647507893528e24e9470fac50962b60a3e5301000000000000006cc53152825109cab7a5e657c955cadfc846a338285027affe175e6e89d5c7560100000000000000e2ea16b3bfb7647e0bb6f5a8c01c0462662485630e4ceb1193ba0e17c52d5f2f010000000000000058af2d747b43f08004665cee97001fbdac2d7f8f3a90ba67d57de144d1be3621010000000000000050e17a1e724d93c69103ab8656f7f38022a83394a1b817100992205e0534927301000000000000003ab369c42e4d12dcc2b478b838aed3c279bc7c0043f6398c315c9f2d3c47055e0100000000000000a204e47fab483977b269636c625a6d568e6066d6a1dfcf06e8b20864e342987f0100000000000000a2fde5e21a95cf7fb0a6d25e2d1909492aebea34a623572b46ca9262d44aac2101000000000000009acd340a31e012f94cc096eb81b69913480341aabb48cb7a2451ca9e5bce3c51010000000000000026e8b6bb0f086aaf10d543e45ecc77511b08c7a5f61ac760b6321bc961cbb86f01000000000000005c17a2edbe07f27251213b7f4db4f4e4ccceb01b5f957a296ea89b83cd6b6d460100000000000000f2441030ec7780e75e43490ffe6a2918beb750128114c2a54f7af73eb015852f010000000000000024b9dcf73084abbacbffd70baf520f1edfe5e08877dc6dec17670be166d2820d01000000000000000c2ca9ea7ad31c8c17d4ad472d7520dc29b38fec6806b95d6f13c5f431a2fd0e010000000000000000b9289b629edbcb7691131095127580893d36bf86391a3497fa0642c58eff2e0100000000000000dabe3557e532052159d0eb6aed80464eff5ea60a7d4d11ea6f3bbd93a406fd7401000000000000005ce86e8ab8446889e7794b716d39f72772671300c4f286247c9b94b3971f3a6e01000000000000004cb6d487b23b0ff1c1dfe94c87adaf45ef8cc0b60b3506bf72284b8504d7ad7f010000000000000008e78bcca3d3e2ca7f2813f465a76cf048f628556415860d98c8997d10c4ab5801000000000000008658f3d380c23e829c50082e5393448d014928bb3c7432b1dcae95566f32fa0b01000000000000008ea53537ec9986bf2df5bcbb81af619d3301489bb12953949cc4d62d44b77a080100000000000000fe20cbd8720f46d2c16756fb7cf5942981e933366885167c3905d3213dbcdf6f01000000000000006c15f28efaac1e24c96bbbbece09d237532c1436ed316744249aa55700d5e4370100000000000000242be63827b1ed32640e6465378afbc28a6c83b54013b0b79cf671c7830b36510100000000000000e0dd1b9898a07d1e916784188bdf246ca60638be485f3d1a8407ae39355d644a0100000000000000642bac715fd971189538c56c5b2d81af2a5b86ca9512aae04490ef9a80c0736001000000000000009aec4750f312da2c844b78f64e6da58d6a682d4b7d3ab8ed0bdf3dec3efef06b0100000000000000765548435b4a773a2aad522351915ee05798f3a5873a52931df7caf8b615ea1c01000000000000001cdd4e72f7ce330b5f458d1988a481a6a518f81f98c6e2ee6e3cd14e247d19520100000000000000586b3b1fa70eb13fcaba8b11f82835084416ba431613f235af469cf144abcd1a0100000000000000503ec36c254ec34d47788344256e9feadf0336386b742992f38b21ed62a0677301000000000000002c1ee1c5edd89c3699d7e81bd67caab2351702c68a313ba1a1b1530025e14400010000000000000018bce81817ec3f23e2d5f45944dbf6c9e72569390fa8b002e560291bba469f7b0100000000000000e2e55d6ad3fe7564b653e1687684978568da1df5c07f89d3ef6fef6bacc3b41c010000000000000024be8c31c6082f993e6091302892a1d5e678ae890daa1eeefb002737799d512a0100000000000000147e76962a08ed7feda65a5559464c39cd32f64a7f5d2879b22d06120e01634b0100000000000000526322af40924e6aa848968ffd4cd1f64b6932a6af827e09d6087ea2a23b8e4401000000000000004e5103b8b919c61b36c3162a9c8d47c9a78a2f1bb849a9eb518bc2338a3e0f51010000000000000020494dfaa45734759c4aa32982246b29ea6e49f22e0ab07e8760372dec30fe7b01000000000000007a2d4c9c30b5ebcbe2ef461947979e4a70a2d94c3988e16670b1b51598af7f190100000000000000d8aacc82937ac337a930403e3dc610bd4ff7d241a0a4140044812e2c0b9ba16c010000000000000006924daf9bf6f8ba2317e0fd39d4cfc568409c67b2f0b584db8ce27e8e0bfa340100000000000000c23b317e2ac29311d399211cdbbbf116012f9577212cf68ead5301176574f467010000000000000060728068ea6f3d0f3622d47ca6ba9e54e49742d83c728c6c1f3c3db925c12d350100000000000000b0a42ec890cd378d66733e45c739319aa6817fb717f9c882f6646116c67c113e010000000000000066e62a41d32cf015685e4316f2c5d22026c358abbd815b590af151fc8f65256a01000000000000003cb5fcccbe5ac7fb6d86626c29fc5960a6ce0476d8cfb51e8338cc2b619c497d0100000000000000a098655547e92c7ac123facdd4b3cae305e330f764903eac5abe9af962c0ca050100000000000000a84e5174d0f7fa67088df644df104c96f66812907348626646e1d5de9ff8976d0100000000000000927bddfdb9c270ed770b442243ba58804de2a383b51198ac17afb48d4fca602e01000000000000005003dcea465f6654fc042d1d2e44e9b3491df630b39769e33df13977f081e175010000000000000062da836ff1c0f853f1e31c2cc30bcde8b0ea955eadfefad24fa6d2b4421447520100000000000000f0ed29463a12cca8b1bf44755b7fc4b271611be8322d125f6d6657acbf9866010100000000000000f672856259fab6a08e26157d63e00a8af36793f579358d2549fff83e845c607e0100000000000000ca6ce2b47137af4e77b2c050dd0fd5009dcd0da8ba2de762c20826d0a939307c01000000000000005861cdec6edbde92a2035ffa83f87b17acf09262ea38cf4729ba5a8071ebd24b0100000000000000fe9a0a40f1de6f902e0dc05b033a066eb3e82dbcd8b3542336e3119facf3332501000000000000000e43c4763bbd1595aa61e839d16f29b82bf65e942750eae7dda38e9546ff201e0100000000000000be223791af645d5d3ca38db90e6b59bf0632bbed9e3a3940a54bf80b112c4e1e01000000000000003458516691992e93fc2e1af1f445bb9f5a20883f2780ef854e77d2c1a94e57070100000000000000da6c5070748ea0655df06b4cd7b3670b182514cb2d1d0551225c3a7d5c8d62290100000000000000e2f27f21c4d6c72e3542c85e6acb1250107af219dcc1ec272f74e378ea5b6f280100000000000000fa11dea7f82cf0f0f478a16571cfff58242a1a766f510240a67dedd35a8ee82b010000000000000024e2216cc3180349a56347d56e164d38959b7e35cffd69eced79fc9b8caaf06e01000000000000005664b72ea779c079691d0e6135b1f2022e8c634c014b5b937b90f60e4e08042d01000000000000001e48cb51cbf5a72eb204d54a4689e849243adbff41d54d5f2dbe5d93a0e1cc350100000000000000ca6b5b26ee50944997b2061b81120b0ae368c5e32bc65cf8efdb47fb85bba3700100000000000000c01e3dcf36740aa070991e472d0d82d7f1904e76a049b957a6caf1564e46a32e01000000000000002e8e24430b51f6d5f8dac6024f87e451813ed4db1b3cb6b3e612ab4bdab8f267010000000000000086b1555953c5f9f44fcb3a2f0dfc8ca8331e02dee7116484884e0ac7ef2230770100000000000000009d14aa7f46f47424f9925f605599767e56f962960aa896008c9683cbc3401e010000000000000040506a20667b254d811877385dfe5663399f75dbe3e5a57f289b344b6fb3ef170100000000000000b02c947b1a2bc4d773ab0e28044e8293a875d7b8870e4d607500fcbfe649cf2d01000000000000002c52d329744a9d2458e129fc50094550facd4aeef04a717cd5c452774128cb2901000000000000008c35da616e607d118115aef27079b5702f9f496ca21f96d682d614928446b8440100000000000000daa83cfbc4d25a2336289f3261e6a4901030ed6b8dd063b824fa787a3f68126b01000000000000006e97825a19bcf21e0e299dad9a0594fac9d40bbc95c375eac3cd3824b3e7c62301000000000000001ebe00d3e01f16514c89161a99881d53753813cf680ddb3e3c6bc577829064110100000000000000d0c63d9905fefedf8cded1dc7c758793d257c74656fac1c2ea134d2c8306cb6f0100000000000000b0e486d3f6c244145d23032e222f807db5f3c553dba509dac3ab9ff4f37543760100000000000000e0dde9387f1c3fcae0bb2529e0cf3eeaeaa05e244be364a3bc316c9580d6e93f0100000000000000583e56856e4256b5b3cf4a254312d6dd09279a529d238ac9235d603ebcf0bf38010000000000000018ea5e9458304122e280ac00a656c32f68fe24f92fb51da1183949cc5045241c0100000000000000789b7f450bcbeaf7b6353015413c24a5feac5bfb72372b7085d2232146c9311301000000000000000c03563780cfbde0d9184f984939a14daa87754e9e5f74a23da0d015172ea35701000000000000003aebb093fe4e3d0bd38e9b040a6be249a10f680d4afee82111da0cce9596de05010000000000000062f15b56b585aca4194e0646c4f1003f1ec573c9563dd8409ee3b9e06de18a5201000000000000001284399e30f2a03778b05ab4c104570c30d929344d9785cbb412ea09668d7e4f0100000000000000eed0a44cce8ad788c707e7bc6454365652e7733133cf814e5d51b2ff95d553630100000000000000ea538b55ebf258d78fd7988d5a1c843c9f52ec8a519a977a9c835fd872346510010000000000000092b1ef5e4cf3ec9e8b011e3295349b53577943ffab41f1222b2091758b9cc15801000000000000008853f09259ccdb3d1210fceea0d14bcde576201f38f0fb7ae94930d4a65baa040100000000000000ca8079469e665cca6ac1dd47cdb0bdb56bacbd031813f710ea82e6e14a88b94801000000000000000061bd8f3b3ef9e9f391b0462ca53eb93fff4f3d58fff22e9f84061a53aa236d0100000000000000a086680178e3769f1d50fe93858b70fecf29743f7bbc87b11501ed64bb087b37010000000000000004f431a123a6d3061a18f9553340169faddbe7c5040bb490b4a064b1e0fa927401000000000000005a3d41d3610332f2f571541f984fe6fc38da4251f65a3d81276ed2452e4b1a100100000000000000b0f8da2a4b24053cd5999ad51d0240688269f87aa7ccba7714f46a027977f7080100000000000000bcb2b4974bcee94202bfa32a193ab42facfaf3d3e615c7d901e66d1db11ce6720100000000000000c66d20874fb3e00d049eb98daaf2aa549adea852f3adb625f905518c09c2ea1b0100000000000000705a9cb74d5adcac0f4fdd165d627e10d3aa2ff85fcafc33f2c5315454bd61090100000000000000ae3b8fe38c68695e5442fe037c7d32aa8466b10d500a5194a5708084647bf7380100000000000000de73d7b248f71d461a03af4cad901c83d5959f3e0b039dc987798cce072956590100000000000000a0b18475b63b6ac2080302eaeaf6284c78f69079a4246b9f15ad41691310d71401000000000000008621a861e5500d932d88ac38bbc44b3e5f674b0f97ff0691a9afa25a6863a32a0100000000000000c4089e51c06f4653ba21e1d7ef818b038c009df54cf37e0992d6584fa65fa92401000000000000006e94e70c8e78b8589c175e400a77f92544ded52fae33252482c902cdd20c9e0e0100000000000000e642c2865d5e506e56af1962d29ac53960c84d3bb39ea88a5c3664594313ea780100000000000000f2f4985003d63b08f627b236beb8e744d584ef3dbf5c82804b2473258bcf522701000000000000008a5d4c0de40939c7d94f99684174fab5e0a2ab8de50ff648a7a0b7b98749391e01000000000000005e480e3ab14cab90ac2d02aa586ebf827f06a4751c38b87e3e3860c06a16b80a0100000000000000a611a7e5eddc98ae160057f937968c8ac1f79a83aef44f680ef264c4f88c931601000000000000003a38fb488088adb8b7ea111068484073ce8a181cc6b76737080734cda341842d0100000000000000dac0f653da931dce514482b31b67bd3a7678aa3f8a5a173a87355aaacc4977700100000000000000fe3855e100f68bd31587a9565119777daa2b19e411fa16d9f41c86c55a947f7c0100000000000000e82aff6645589ca5801dcc40ef37a7ece665950cfd48cbd26723d7a2625ce67a0100000000000000801eeae66544febd9632341c6ef3aabed279740b6b28eeb1d1303db8ad0f566f0100000000000000f8f230459692d95fad1c21557d279d5d0bad1a3d43744f8312fd51b339f81b03010000000000000040e2c0bda3d579af7aa0c75a37cda7f8e6d580fc57df98c2e46c682b16ddb92a0100000000000000266916c0513f46eddd8b74fce853a00389345bd7f8e97783d83cbcc9d23fb266010000000000000060821cc864972a7f113473f9e0071b64c69584e77cc078cba367aba13efdfc580100000000000000f460f9d60c5456f75825a685b229aa6e887b5f19df3a7bb24073e09180b8b66f010000000000000044a2fd326ef1e0ca462550f91c247c613baa409868fc6592aa39ad478c85317f010000000000000036d0f298717ce466ec596541acbf95e460fb8c1b40d5e50980e047df4191786b01000000000000001c796f4302a0d1f0a32d1fb9162b6627ca7097d7a3a1b30adbf4ef21579ad36d0100000000000000b893761395022e6f6b8fdfa59f894bf51dac1cac6c4f52c6b8338a1dc19689300100000000000000a4f4f89481b6ee8d5ce2f50f9af9c49d1626966683a01f3e73c358576127381401000000000000005ae9062275202e049d0d4b8d7701ab0b3b70799c3f89d79fb25e79d10c33f92c0100000000000000dca27aec167e0fbb3b984e209d6a253fd1d0a9e4c19092bebed233c7452d530d0100000000000000403f4f38bc9d1481ade01182beb7e4b8b77a8ba08c1800b92b878512607ec2090100000000000000eee9adef21f4cf375f086327411732b51607179bef18acabaf37d4f92f3dc00d0100000000000000a066fc4292f0ac7db890846fbc944d62f13b9edbc0e6c1eb92e8ee916b94f61b0100000000000000641147da6ce96c1cfb49abc2f3445ba4dd8e00e90721e19ee6ba99c1dfb1836b0100000000000000ca4a99507a5f4391876a1a0be7c26716a17e9f65a96679f2c8b2228bdc29661601000000000000003c09c5079e597b9b4abe38fc0f67677609cc936eb0dbfbf03061da4bac95b53901000000000000003cc3e4c20c232b2ab4f442d8bfae67a34730b69f01b0bd2033d20b5b7e670d6f0100000000000000d4db4b7b7ab59840347e5fcea8f1cf0524089fdd64a349fe5b8f5aff6ad99d7e01000000000000006c2b5d66b96f0c473fa442e5626842993528592791e62875260cfa928c96fd410100000000000000bef4f75529d960a44d2878fae1824d0903d5052fa53f75358d03b0cd2f550d60010000000000000092b4b6988ed74ccf189bd2e16868543b853d38e4ca82c63afcd3f3ad656d0e1301000000000000009a75a1a445f93eab93aa0f3f328ddd64d5c25c32788289c42301fdf1ff8aff4c010000000000000042be82ddfb8da6ee77760664bc9c0dfe6a5b7bfa58dbe148b965284514d0715601000000000000000e8d5114e90194b8e11db6414e52c5f9c3c99a886c478263ab17e040e1689f6b01000000000000006864c42f3cbb918799d7626c63725fd96cda1134944ffdf2925dd1c73aaece1b01000000000000008cdd8b5db78d01220b9b747813806f33d6b1d8c19d9fa3fb82098a3f8d53c80f0100000000000000765aa204bc165c6f9595b2cf359a1794f42ee6bd273118a38dd7a45dff33406d010000000000000050478b2d5c6272ba095f2d3316f19aaa06ff22f948d727999bb32feaa873017601000000000000007476496c52a5cc5cb0d1475412135699072772af64302a5a4cbc384ff2b2ad2201000000000000006c6a5941354b9419623fd51e0747f8f467f31b6f4b1e38b465107f02cecbbe3b0100000000000000de09c9e8d3e9c785c9f51c407a17e14fbd6c3c3187dbd85ae23c23e88dee413f0100000000000000d6501868e1cf90df4f8bd798e6940f4121f0e986c899f5539138faf8ff50fe0d0100000000000000e28b8124bfb689408bc656ed979b101069c42adb4329a14f972c39a2a3e9784201000000000000009459d084cafd40dea98075177523fd137a8e6f35ab1068dbb420e616ac176a2701000000000000001262742c54cd31a33891c1650618ea0163c1dee7fc38b716d4ab05f487369d78010000000000000074cfdb0c8499cfe04a51d0cd85ac6ba602436e8cb00b77fb2c1ca01efdee565e0100000000000000e88db1672f41855dc8fb8089b8506989ab40a1f8566f4d564e9f82138cd5b56001000000000000005c223c2fe106334557847ca756505e801f3f798f3537a116f2890577be420d4d010000000000000048839721a668d391498431b5747d7935d55104a2eba9489077abd8777763dd46010000000000000086cb3131c5406660f6adbb0125dfed1efcf8b348126898732fe9e89df5aaa90901000000000000009256c366f4a4070d2905aace2d57295c36d1082a179b17c483f42e944851cb60010000000000000024e8ec732707e61646a06a013d1657f6a35819bada07714f7b57462a03246b070100000000000000323bb7505e17594b4bdbbc7df6a205843f1f7145aa1c40810b2bdae58a95da0f0100000000000000cc179fd48870861bf5cac6ede454991d4f354f986bcf36a3aafcf9041d6b22660100000000000000268a98a0b0182f23a1f51e5adf1e56274d10a977744e8d0fd0b6e10910ce1c48010000000000000040a09307c5938cbd5f5838199f5da3d56052449b745ea13986b5292e290da33201000000000000008eeb878fe73763486242835e2d2b3c65a1a78bbccaee2ce658fed20b793c02350100000000000000344ef75e232ec97983125b4cfd8dfc4ddb568ed357332adf06f209d0183c86790100000000000000ac1fbbd6bffd92d23bc663ffc22f2d056f04534f6fbffe6fdcc2418d88c17800010000000000000054e139e51d28832bb556dac1f9cc6bf8cbb0c729ffc0a2d1d1724c72edd6e62201000000000000001edf51c6e6bc501a13351031b28451af2a98085f614ade4f2b27113aa4473c3b0100000000000000023518b184a5c21ab39941bbe6019b68ab6f2cccf27a4caf0099b8d4cdbb920e0100000000000000e2c23fa5e675899b0de4ca207be07b8d6a436101c5cf4c023db0624d56a46c7b0100000000000000d03a71fb9750afc4e9d30f612b70cbe2e617816dbfef42e5c1ccb946099be154010000000000000082a7bebeda9f9133fbd5c9af9b470e1362eed16a603c28a209df52743262015601000000000000007e3d9d762911807fe95f9a79e45faccb182e612c98cae93e0d419f8af768982a01000000000000005cc4dde005b2344a81d2b52f427c60a1b447fe5fb50f0cdf59e4aba4f281911e0100000000000000e420d5946c2d0d4d99cac1e224b2cea6295490ee3b6a9803cd28cb03b9aff57c0100000000000000b8af11e910e884a06701a7f157684ee2d78dc205bf3d330cbe361586db733346010000000000000036c4bf964f566aa45d6a4581e2b56c64923944dceb6f9ca332a5b9faa374a1790100000000000000205e0711fb2e06eb53f561854a722e0aff2a8e3c82616ecd2c6b78cc56327e580100000000000000daa0ca1a484715341fedc1f74ce6f2d62764eef5a95a5fbf3225db0ba79b076601000000000000007a8c92d925792e50eba4c4d4ac50b8302ceb21967cd44e94a3b0a8f1f6e837710100000000000000a2751b4ac6489a8985a70ed6b3437e0649c909f3d6effce47f0a20ecc33daa2601000000000000004c0395b7d2f32dd6162ca1e2b117e8833b26cb181cfc4b6c997a8b73455f1135010000000000000032e994c3f4e677a6d4df3d65ca980595264dac5140f52b1968748216c92c75550100000000000000c6e3275fb29aa69fdbd35aa9b3c3161875f3946016e575aa2a95c9cc6ad575710100000000000000aef5d56e0a88f892ebb22a48771a10c72eaa3886e4609310cf613c08d8b74c20010000000000000018e2ffa37c2b51670e849d1ba370badc5d5abe3678c59d46e0bffdaa4ad49c28010000000000000072cf291be9f6b9330459b406d50a21ef4d13e68a645f93b80422be6b9d875a080100000000000000c2ffc531b154837795bdd573a00b6b27e177a872f311353de1640060d6c3c8570100000000000000422517e03ab4255a0870a7fc588ab1655061c7322065a1fca053d82be3d1cd660100000000000000eaabe802ed0951368cb8379e36daa67e6571aa3c365c850c124fd9567c4c9106010000000000000002cfcc84716cbd61fe5b3b4011701326a7bb865f73c99c2736fbe41a264d30130100000000000000aea9dc5a53103f390c8584cf892bb854f64ad845b382abb3220c2be0a70e492401000000000000002c2a070ca5392bec7c3f56b2ad808d2a4fc3fd8e857f0ee2d6496aabcc970a5e0100000000000000f4d8f4014d1bd8343aff7f5f1d4366ae6985885032ec8d6cb198a054d085360001000000000000007acfbfe9d17dcc8bb5cc4ce3b62c4149d51ba5ff74293ad82ead4e3d5c278f21010000000000000092d47173f0a1a3d8db3ac255c2dcdb9df605501a7fe33d78d504c4cece2ad12f010000000000000036487c3f8eead755b47b123b845418d12e16b590aa24338a885185809b85a02001000000000000005458c0189a88a6f5807d6aabfe020ee80115394a1b67c33bad109aa647fe3a3a0100000000000000946b15252e2d51056c52ac8024b47513bb9d08e406f86a7b7d4778028c4fbf770100000000000000aa1f85bf11a5565f36a54f17dffbc62fddfea81f4209796c120aa5bcbbfee25a0100000000000000464e26e781ecaf929baa74714f82d03a533bbfc07c95037032a46459f07d5a2901000000000000003867a37432ab13fcd7f9c7bc872c23e0e0e9cc20bc01b6783e3202a396cab370010000000000000092cca5d3fdc514fcff6906eee877af979b006474a4eda8cbffdd861dbbd33b02010000000000000020aab3fd9115b4f4385883164cd41e282f9c9d547a46f9610aea615d99de2656010000000000000098378caf3d7a44614084d31da9ce1d96f2f7aaef3989ce583dddee4d04b8252d0100000000000000980b477be76f9319f3ef5bb6f0de390d75f362890dd6124a0685ab06f4158b14010000000000000098347031396336c66a994bcb21b8be5906714403cfde0dd94615c082cf42ec6d0100000000000000700fb979b0f33bbc8a9a4ed3fb3474ed2b89cfbb7fa7d1c6bacf6d2f1b76025901000000000000007e33ec70a5a0f0f5b9b1a0587deb97d390b5d6bd8315ec4c69ff8599d8821c3501000000000000004af566dc40a9f1b9698d4d28977db99590560e378444a9592c519c3c7e0f00350100000000000000e6d63bfbb03afa14d919f48fe3a3ee4255e8c30f32b6ea6281ce7152ad694c430100000000000000c88d1e967b3871fb89de8fa6fc32569694e67d54ad30a92e3e062f203e35e21b05424142450101025277f4913f77cb7b7ed7e205dacb33f62bb6991cfa879340fabb34d316e541808cfe5e155c5b3b5fb414d2aafa523399283b6e776b40222468f50339e78c8b3958beaefd9bd93dad27993452034828b4c8774b5188a13c3b65a46cbfa8f3e14ed2e8013f6cd2a01a95598dde5ccf2ca16238433e5d12a1ac1556eba820399434b20d4d5c7ef888105497efec4c64386e65fd0741780a63a290190903cc1fe215c9996f080642414245b50103ad00000098b722100000000078298dc6a451c6a65939662c4f04a353d6de27f3e5d1b02f68d9485455d02c56d42ad34e29b0de0bab1d9aedc759994119173a65052dae40eec7843e685141015517bc8b59a92d5e0da73a261ed88865eab6ad773064e70808b7f82e8d05a801054241424501012a498eb4955a0d77df2a51e7acec8177ec396764816f0d590ae8b1946a6b227ffcc7b145952ceffbf03fd67e99537c50303444560e0a4f4b423472bd71eb6589325c652ba82fe1ff52b2812b7ab28254551c2edc21b13be324dc6655911fb83652d2e801267f11ca69ade83f8b5ed91f7594dd9a400d2d6c6b110a3cadb19143d67b91522ed770e6cceac3f2b9eea934d24ec380d9bbbb91032bea4a50799ce92f27c40f080642414245b501038d01000099b7221000000000741d888014ff4ecf147f48610245d238b8bc6f70c122907fbacd00a8e2fed538984f047b59577926b42fa0ed6fd35e6a9f9c96bbb9308689c0ea926311acd10399f60e0dcfc77329bad8971ec46cc4117991a4840a199a7fb9b7ba259e58f90e05424142450101a4822b7547cc9e9d2854f0376d62b4cedd17b8fe803b2dbb07105e5ab5bb5a60ed23dcf84637b59e5377257f99e3787a1a61fbef7cdf75e6a28ce6703a7f9f883d68547c36a6520deb53cf9245c8e2f2b3181daa5da965afa10258bb9308c88456d2e8016306debe74e0c424ad36fa0cd219c0f031981199ce2eb51165aa4c9c33dcaed31e06bb574d957a35dbe990dab4882c4f7e42dde356a61fbcd4d97fe662f8ff65080642414245b50103ad0200009ab7221000000000c4381c9c42b98a3d8e1fbc18d0e04ad2a0e68a054d1255cf9fb1f88e0b638179209610bd40d949cdcda91b2813333dd27bc283a96ca37b94ed2ab8c75cdbd40a39e6d3801933807ef144a753a6f080cdcb324ed86eb86a68f4a4cfd65eb7b40c0542414245010108efaf4abf02d62adffc59afa968777cfbe4fa2cc4fc844f2cca21ef79a92d0f778179172ee1666fd7159ddc6afa73d6592f70f3828d7e6b000cf89f69ba8a8b1f1de7d022cd7ffbeedd9e27707d66133c15adea1a7da919bddfbc0209612adc5ad2e80100571144663228e6cdf9ef9b80e48519cc93b1c7a93f7a9b79f910b3fa72635d7d0b93fd19cf69a337134a8eb227d036189e84b998743c81c290871ce3c53077080642414245b50103d30100009bb722100000000086a0127524204497c15fdc6043d32e113e82923aaaba30c7e3614eca31eb5f2b55a35e9b9fc4a140660f84087f8ee1a73f2212816d5d9b8462a8e0627dbcdf05c3aa350b27233d900ab44a3b7cbb69614d5c8c09e53e0c6b5f1434109502110b054241424501014ab0603e4b0d2afa8be5fbbd77960db43076f5b198ab712fef09e4ae86b4590bbcde34423d154f2d668a42473d32b496f80d17223f4c3b463d6a14cf9cd8c284a6eec73214fde59a23f0173fa5a27e0ed4d489224112d8ca0a3e8a038281bdc85ed2e801468cefd7635d81690761622de0d0186249b65a358194b3c1c119202f8eba3af55290454486beb3dfd8eb400914d4675d77a50a4af083d06a4d03b262bc56f75d080642414245b50101c40200009cb7221000000000f09218616d6d5f8d76a3a4f05efb7b662a07081f0787c5728f8b5cf7c6baba07b3111278d872972fb534ddea7a15db4f0e7d4ffea25d8137433c4a0336947d0848a0c54b0374c2419c189f252902c2930450b54229d665c8788cdd414809760e0542414245010122bfc25984265f8737cb44fc3bb01d8a804b97be8f136f9528dae4f9f80dd6769404a59a5f5aec4e8f5ba01e8352a20b489c024a9eb247af12fb1046803b5b858be2e82ee92ee4528b52c1d492fba18403dbb420d9348579d8a011dac741dbb762d2e8014768e15facec67bf0d8ee81347985adf520a565d356de120af3e048d650df49eaefdc0debdc4228a6cf062072d9e593e8bdd6fb104cf209a03b09165b7fe6e1c080642414245b50103b70200009db7221000000000e0502677f2295d4afd0f3f830eef5ff86acd6da3f654f0ed16ecb0c134d9b421a63404f673c3c651a979f38c9a9b55bdb84e6f5f17d96322ce587fdedc86cf0ee51428925535bf023b6871877e3419a7f2dadcfcde224a85e1d051e9f07e3301054241424501018caae332e41620e2e65ae0014fc8468a1c39ae6fad74fe0992a1ce53b0d63b0c8e3ee38af9ad54baa527e30b501727ffc170c5d3a93cb6174536cef64eb4f2887fa91ec604b5e574ae7ee69699a0d3c2f9e899cc70eaa76d931fe4775f01cd2366d2e801c97cca4721e424b3712b333b894da678f23122e52e8cd63208b518e39f846532d062e4ef77f5e6fb68e6eb2dc6446bc6df99c541fee1bbe1d654b999609af230080642414245b50103910100009eb7221000000000080e2c5fbf85fae3974b8125d89e4521258528f512df816345abb5d94b633311f8d81016d6301d1806714497f3e708e8e24fb48acc1d79c53f897d5e22d1590cb0313891b1ca1c1ace5b2917d39e1d5148034e28f15d2dce1c9ab968d3b5ba0805424142450101f47ef86e942c32b11a31d17f1a788deebd3b66e10774d556682a5cdcc6841778a5637400c7fc1bf704979a0f606d1b23898b10d59a9d727f10a39a588eaf0c8b8ee2561067745e6aeefda16b2ea6e1a0bf26a38af1c08d4ef48573b7ded3d4896ad2e801f892d1304cd030489472b8835e24b3aa8b782b1438d5fee797ae23e2c0a7d8f1447694a027e7d99b8327a871d75a6d704bc64f7ecf9eb1d81bb8f36a22c61ae5080642414245b50103480100009fb7221000000000ec47b01916a45aca20c9d4ca46e633a84c8586c618d3c904b0f3a8969b2fe97819cab64937b762cd27f8e0a87e7e2efb79aa3ccd2d0a13a943604284bca5e2096fed4ce1395c84d2498fd3c18085cce4a449a2d3c6c62aafa725ed7b21bd6307054241424501010a4bf25a7fe130c528939a5286661f7196f3f9e42f0c07bc7ca859067b5f1c5780eefc71ac9a5310490c0b92a49cfcd0034badf6ad5df5b6021c3e3156f17b817f8ef65e0390885ec5df05b2004f41b13b8a90f91b07a8459e40b8f8bdc4bef56ed2e8011d812f3882ee83e6eca4bc7977ffd225d979f19debe997ff2e1708357797aa8fae53f7263912023915e9a98a69051c636bdf36b6c1bb965d484a1b37e903fe4e080642414245b501012e020000a0b722100000000004c8b9ed0f914d39694d9c8b7fbf063f65e7b67730e0d1ed958c1f03733ce34df2efae319668ff2e5509fdcba005160be80204d6dda434243ce3ead3df3cb102989092c55bf4a99a8cb65555a181a7f9a258d5934d3f8fe6f2216f9f4b1ab6010542414245010110034fa7e432e76fb22463bcaafc162beb0148afcc292b2d74117c83492f383646c94bf469952824615b8e41721f432964f79cafd962d658e9bb204a42307284636b3d97942f8f53f15718891f1a89f9c7998c1c04fe907ed79e5a8798178b8c72d2e801d9b25b8c03221bf3abbe43365eea62a1d032b7e3aada79828971d2a86daec28b2f6f8263341f0bf685c3cbe7adfc5feece6ba8b46ea4e56e5b6b311f491b09f6080642414245b501014b030000a1b7221000000000fa6a76d745eaea23b9f074cc0086cde7f1ddfec014e8dc6bd4f189493d55d502c4f548b8385bea5d6c43c2682c583fdd74be3ccf83b4b1ebfec1f018221b6b0fb746e6bbe578ab933bbf95d4288e27fe527012f22eb724abc2e3ee91f75241090542414245010134b2cd0c70b14e7a01349db462f0d0ba901f946300ac3e4cf2341ef7cb88b23aa9664cacf60eafe8dcdee273c5530476d21dd05f08fc17953b131b135f87598bf280ee49d1209307e0a7d16a3bc426c3a05808bea59cb046a9527453e9d3a9f876d2e80107012e230adced16ae8437cc8f42a7a629c786162194fa2270bade610db2348ce7d390adcd018ecd3282824abf609537fc68565e9685b6e6cc59e7a93bb020a8080642414245b5010349030000a2b72210000000005e3b1273a744fce83d4849450d1eb73da72c3f927bea5b7bac15b7605e7a224081f0b867ed58089907594e5410444bc7ff7c2562eea6a68e2bbdd3a066d2500144d5378fdf7d4bc25b0e21da2241cc29b49d8f31f78a24093e003b9377c84b0a054241424501010af83dad6172436338619212aa764c41ea28e0431049cc87b6f1d5a870a0c77ee35aae30c663a71d6c822d7337ddbd2aec98fbd045394379e3fa8fff49b816865933dcfbedeec23707611012e990c808fcc0c5b2461cedfcd421f9981a7eeb647ad2e801506e50bf20682c016d0250403a306463eba96069a3bc90994d7736b889b5c01a9e5453a570d672b2efe6be0b296dc80ab7812e5d20083ab60f53bffd557ffbcb080642414245b5010311000000a3b7221000000000285207a5692f97cbd209830a437e99abc5c7bf31a7e2eda1fbb4c24c9553151e924ee9927b64dca5820100281981c0aad89ee17b7d4ad2c25325cd1d17352700a88d862d97786b8749a01aecc4e48cdb43c25803059a241e1ca4750a7319f70105424142450101bad4cf4a34640a5facf7144540646627f0a9a21a8b25d1019a235775fad0c54b81198acec624fa42c1954a0027835b3f043925ced28aeb26058bd0786488bf8edc25711050dd5f287d9e37212b4b8fa7767c519db551f6ef52d957ba83c734a57ed2e8016edb729fe2bbbd2d427fdb3b52e724eca68a8762e93cbef29e8b807d4c08fdb2158e138aa4f77441756caf71126fdfe3fbf587b8805ad12500c7c0784cd2d77c080642414245b5010141030000a4b7221000000000e6561b8e2ec46688466ee5cb40ce4e07552500b5e28bfd158a929030f7ba2e4b7710bc60bf8ac865202961d99b4a7fb17cd4d23ec4989c08c29e4048e115fb0c3cabb296d45dd6b11a93d7b0db2ba211b5e8ab94b90c24f959603f4be1803507054241424501013e585633cff2e95627d94015eaea935b75b0bf33a1317ab824fc1ba16d5ab93ddee9688b041375e6703dd8f51796853ed93abcb0ee969fe0d9bcb55cb714bc844ef12f8b501dcdbc14c655ad0c9e39967918c5eaa3cd6384cb0003598e0920d082d2e801fe4eb8b5670b37dcf00617d9261d754a11b404a1ca1c86fcbd2050aa84fb37a13219dbcea40eea75bd7021d9097bfb56b2e349d64d9ebc2a893eba93c4858714080642414245b5010366030000a5b722100000000034770f37edb1cadcf017563f621ae3414f2fa28c1e8fb2a17f8803e8268ca861ede622c07b1c008c474d2b17e8497cb9837c634cccb32509088a3560ca1a0e0d7849e28fa3e361f03dfe75352837a044a2c5ed954807d5a322376b8694b2560105424142450101def8aee7d9b34a667041a22f427df26617570cee51cabda0e5c6d451b3af7969d23012979043d7aa7d41df07d9360ad0fa097989dc7a69f892071ea21e5a5382339894e02cc405db7d8e3ee0532c901554296780c7911e81bfa563093921947c86d2e801c8bdf4d1b1db898069b18c2e71a300dabcc379a78d354f62402c7590329e4db107edd53d2ed10f33b30adee1b5aac1dde6471826d180df6b071750de205a8b71080642414245b50103e2010000a6b722100000000052ca3ca32f34d0b7c247182453b980d3c9beecd412da4efc585e666bf6d39d74f8f8da5a718a8bd98cea9bed91a99ba2f0bb0b1f5e5a719dfe92ce1108547503d6740422d140e212e6bb147de5b5acfd4867dcaa0ca21ecacae29be3e1481f0a05424142450101e6dca01c23a88d6c7199d5e86c29dcb9c526e4acc1e5ac82c60eca6431013c37b43b64a8b0238c78e0a156337528a741069139826de7513f5c8c0d2ecc943281255dbbb6136bae3aa76ce337b2f4c6cfe2bf58bb5f350f53e80a875b3a1fa6128ad2e80104efd3a5d3f8c2a92a0f3e39074e3ac015d2f2119044dbf6499ed3802b3d2082221f970f4845079192455ca5f5ea2c2410633546ff4508f3ebc74be19e63e23c080642414245b50103c5020000a7b722100000000074f67385af94aa51c06dc903732256577a0d1511b7e03d0288e82fec8a17a701a810014a10de4c4af8460f9dec69e450be9d1a261bee7a95f2edbf96432b2b0f636e87d4afbf3e359ce24c1f8bfbdd3c13b9d4a728da8660d92d01a6ba7b530405424142450101c8f4c961faa7cc820750ab25521f5c471e31a3aea58323310e2b263d09f7dd6be0c03df6111afd28e756286c77e762e1504e9914f494c9ab07bf8d6e38328384b7f52f8ed6bfedba845baa68dddd17561fed6388a73cce65ffed671c19f20d968ed2e80102f3156d36c3392a60ce13c0b56901676a1287c9e4d3a1afcba23781f142104fb71fb404cd1766c67b1822db6330a3c49e3aa4704509cba8e5b5d43ff40c2aef080642414245b50103bb020000a8b722100000000060cf860c3d97d11a94cf67bdc7b512b42dab7ac7a6ad862aaf0c39a9d2592d1c9c85bedcda5a67a7cfa25a630c78c51bb99caa60ebf92df3d66707290535ce081c16f285936529c733e6708ab3481fbf82765c78294690cd86acff188a5af70105424142450101c8f827c855441a06896ff69a3f3d65730cdcc283856d43aef422aa78b737be1b5d6b8e4cae2a0d00331d93f0a4dbc0ce97bcf2251ac1af0d88d02fa8b63b018ad065f599e8909c4a8513695e2a10799171dc331445d5dca9c2980c24f883a9b992d2e8018b0dd84bd13961af7e57407ce39f019a14fb1d5e64d1badc71273f1ec777e7428f31e2c44aab08235b8f13785255797e70833898b2124bbf9cfddffeb2bf0dfb080642414245b501035e000000a9b72210000000004eaeb5d47361ade47375f87fd7bcc22996ef55fdd54d4e29d83e5042c652aa48450b77039f9ad5230f7bdd9e42bf480470468b6de92381edc4f2f1a786171e04f376f53223cae991f9dd44452d453b32111a2bf36eb3df616907b7bced3aa30d054241424501016621fabea737551f810999f263244d001b5788c4984c8efacbe00ed52caab618ec2d542295119d0d326be72800e59cfb1efc093238c86c183202f065e503f78aa164534d09ea9b45cf726b21ea70c75640ce02c59627bda603e6e7b7dbf6784c96d2e8017452f0654cc895614e3289083abefb51576fde2daffa11a964a167ee4408dd1d02a30d3f1b4a6d11f791aa8ff74c03004b90ffcbd46082a17e2b2f53ef6fdcd7080642414245b50103ac020000aab7221000000000f4da073779db66dd84d0a90f351f47f76c4bbcc638cdb66cbb1ec73a9d0fcc0522de95014d73a4195d47d7028e77f668e8ff48c67fd1ff0a6d95cb0565c50907bd2eef00f2678dca6f078d664f2a61e86d693795bb07527be7b5ecc633a644080542414245010104d78721b40253b637bdaf09b5b8cbefa7cb148bc0678dc5afed49478ead5676ded40a7e103ce32ff0a39a50428ce5cacfc7165edb33ba3a444ec32fb75164852a4f87ca19b7f8fd699cf6ab12b4c22e79b72cdbff0dfbf2815aaa24ef7617b09ad2e8011f459b4b5436ceff423f17c1ab1cbbd70e6ab8baece38e553ad94edf1863512d8ca0e1c1507abebaca614a766a77ad6a724782fffb576fc3c1986468a2e10bb7080642414245b501031a030000abb7221000000000805dc25ace17798d6b6c3f71c10c47e82c28f386025841a375d1afaf2757f474f4624c4263bd7efee01b50b5e717b89fca591ace3e8a2c0a649ad551a8e5d406e9f833afb50c4f96e8c0b78f1db93322920f4261677a64f08c728dce386dfc0a054241424501013e83c34d286b22461907ecd509cccf9ec1372c68ee06236f8c463ff4b0e9bf38fb3d329c46c2d9cc0eb12c9ea5fc57016b0bfbd858f5ded600619e197b392e89d8a3ea0faddd28117825a5776f17630b72b058473545878f1397edf67ad177c29ed2e801c494d9565d52fab05d9e898449c806399c702036f47ec35bcff6fbcc74ee1af6c91e9bde08cc0a9a38a1b829e61bfc211ab76f6840112de5580203b1e9ed76e0080642414245b5010335020000acb722100000000012a1fdac657195e90cc8334b00b764ef741bb4cd505638b2c4ba6a1c669161439939711fb5e9510fb36bff7dfa27f6b65ea2f8339eecb75f4b64eabcfa2def06cc1aceab710265867a61ec22acc0ba2c1254bb0c7ebb910635a41e2d7262490805424142450101563d4d2bc6e702b12d47b1271a91d524ae8126cb30e17158ac4d4f6798b7c65da37a09c9bcf782d1300beb63de932e03ceffe6f32f30fa90977870e955ffaf8c1ee37f08f6d77037f375394df6d7feaa3132f4984d29520a03d6d98f9ec3e9e3a2d2e80145cb5fe1ae2afedbb4c00cd7eb3191a80c4ba87cfe0407d1cb2a5bf929a5d7e6730125a30b8c030a6658b0085f64a9cdc0718885e833f267653e2b93dcdce2f6080642414245b50103cf000000adb7221000000000ece1ea2915634828788dd1a4fe39e520cd098aa80513beb38a77761e3578fd59202e15f838a8d9119eaf6353597aadc223a39c47f802d1d53abdcf311c22da037f203030d5ac42b4cf1171d325f96114101abc7eee22f6c5181abf9b6f2bc70605424142450101b8744d820f60c8d8de405bc9415ad54c264e673ca7078e275bf51ab99eb3e85d9b651024f127641d5ff0d47037037b71b6876ca79e68c34ba1fe624af67f4f86741cb9543a3cf21c9423b85508d90a066cbe8f254e7de72d7766b91b48c41e33a6d2e8012c0c2d018399b41f2e4839b0a689b3ab73afe918d8efc15d812436ff8bb9b987934d8981c44d8a7984f45af81b475521496b63aa4a2cf5c730cd8c8e0f5771f1080642414245b5010358010000aeb7221000000000da3fca66463dc5459e927ffb1c7cf1e617ddbe51ca496767ea37fd0c72ba3a2972e8c3826ef37494d7ce024cd60e0b2b8d782567785fbe93e0966f9c5efb5c089b1a50010ee577bdf73c082507b30749671b0423fcb841b3fca93bbf24de000705424142450101801ade33eef5a35533aae857b45d894eee8e6cbd7c8e5e8cd103901187ab2f51371425946f3c0b6c88a4f8e8fd998d7277bdf12bbfdba8fd83f3a916b995df801aba2cf922d4d134bc5e077a73a3fd2041d20b6755bb8ffd203df0a6249cb326aad2e801a7cae622b5c7acdbf001a5f0e999603d1f121bb463df5d56fa447137841dda21ff4c0892ff2941445a218077aa37772f334f6d2e86670b3bc3f16659e6db9da8080642414245b50103e3020000afb7221000000000342fc57ce1eecb998387719f99994ea549e92f4186e7f6040db69fdc24e98977a5cbc6bda6fe9ba72486e625f4d19cc2c5b7bc9c23fe7cc88d487410e3dbed06526ce8f71c8bda569e3fdf687319e9030c34621e68e098b9dd2a696f8727290f054241424501018c19804be27327005fb9f224165f4843d0c5ea5950fcc5545df9b5f1e5a9ea56405e9439f6fd5e2e30b1277193c25cdd53cb4dd4500136c08f6386ce4f7f9c8610d257e39a854ebb4fbebe80fef5ad44584592bd7f38e7d7d3373b2bbae7ee66aed2e8017209966a6e9ba77e414509d52a3640bf53fb2608b88bf838880d7925ae9392f8ef9cd983b7a0817a2a0ca5a91c6ba5564f7612975e1bd52082a0a37668b77525080642414245b501019d000000b0b72210000000005e34df835257ee527f5ccf259791880211de5824aa4c785a0f74e26382e02b7c515a799d10414552c51b187fa2a5e3102d09cbc5cdd858ba3d8e0d60dda11400bd251b4ae534cf270e47871ad017265208975e331f299dbb2654240188185a05054241424501010c5a59d56923a8cbc17f5612c578a81a69461604a2f664744ee1729e4bf35f6da30752e1f271744582a2c153e5629bb86c5eee7ccde73a2cedea167dfe626c8a335e9b54363e7611b7eb987d502482eb4515cac27553e68245bbea76dfd9e2dfb2d2e801b4e54d174307705d0f99a8ab0c59a95dcb69f00236810c2048acad85ad9d5e81ef9b0b7371969ce4d49309c7a733df5eddfe1fd18f776e4bf65504b12e37abce080642414245b50103b6000000b1b7221000000000e203d5e66997a3d7908385f450ce7a778f6880bbe9670eab0f9bcd7707843f4169bd949a1c0c2ce3aa51056d6d59766a021c3c7810f086421b3daaf9e9fe5a09d4ba0cb409804b9aa723f3610f14c04c5cd52842853e98c7baa1e38a9885cf07054241424501019444395e2057616cc7013aa733a5e060b9295c7bf4d5201301b94bd9a6b9be657c7ef5bd9083e3ea8eba68cdd3e6882f9392de01e9f9adde6b921eedfb9ac28ebb9878b6707dd21cfbb97b4ee2513f71746915d1581dcd8bbd8c6a72a55956c9b6d2e8016d5d59fcd654544b46e270310259a9fb4283517de5bc5fcc407cf67ba14b77801002403e65db6efa360ac3ab15ccd824b4b330dbf795aab43382b9453a89a4a3080642414245b5010112010000b2b72210000000006a02e2f1502466518daad2dc4c2f6a6c1f79809c211f916cfdd3854f9747985059877d14d4f206a4aee65633140f27fedbc77b41fab6d26c85c7be9224bcc104217dd09db1d82c8f23552fb0a41565c69f948b4d067cdcb3d18bb06222719009054241424501013868c2ade392396285cbcadc79e88aa2af922fa0cedea44d47670f7917a03260bc1aabd2b92476ed4336d3409854cb7506b7fd0813e1501404bd0ea23a1be2840692fc34bcbff5a5d7ecfbfb663a290758692a519d62577f0041d771e626d966bad2e8016f87f60743b7a06f3aa611c4460f39ed26f1faec79239600a36ee6a94ce2a0dbf8f414c9d1e45cf639094710a31e0e16adcaf61d961f168b5863b635f8d771c6080642414245b501032b020000b3b7221000000000b86af3e80b0e9e7c83e70303107d587babe092378e74fecee0e89eb008ceda69c5dd2f8df94653b8f1c429eb15c27a39f8f19fbc24e3601fd240538624c47a0ecc139d50f80e2b3c4f86fe3c29bd2d4047b61e2d9c4c8d21d582ad1f62da08010542414245010104bb2263999fbdb2a2c83e09d590926c4055194f22565581bd80a2d25e35ea59e1e51b46b3cfed1e962b479302da79c21752d54e580a7752b47b4bde6d94ad896ca9d30a29ff5525ab33dc6996ba3d5da54f6aa5f83029f2bb52fc6fc8d5b12dbed2e801a55261f74455d97592d3395451ba1b5f03d6771c2d62557b300ad8877923e9d05c7c97a84c9c4364129f9bf7925a3bb30e934a325ca4568185acd8545e9b5b6c080642414245b501032d020000b4b7221000000000ba3f38fc908729a4884869a96824c713c8fbe725767494dff940c663e8144a1196b6bea079d1901db7798b854af96d760b0f7204c2efecac500c6f3bc9b33006ba073be31b3bdb73be4d18f67306163c343523e840a81a07cfa3e411b11f6a02054241424501012809f770504640d75b340600a60208f73bb802c26a1ef8df574c3c6f4ae01325b87aeda1c7b2db30a74e69b457df3878ee71a12c362c7cb8259d8c02a32d8f8f4b5f3ba9a233bc3e2c9d6f265d554753a395226a6ac2c68eb56e5b9adcf17c18c2d2e801585f25de2198895a95598093d946a9eed715af1e94b6d9d38f5501ef7dc42e989098f7ed4a79fdbebd808b847b49cc6275903da739ceb02cbcd4d07a8cc01ee2080642414245b5010128020000b5b72210000000003836e2c0381cc11abbdb43c8ea4e6216436fcb9e366e6e52f4ee8aea1f3c2e5d22a843f45813a9801ad86706c870c9ae66a922c996fc87ba3908b4df1029360ca4d2be02ddec2bc7a930d3d4a344d1f9624b836b20b13971b18df2cbe866740505424142450101c6e99e6d1b1f7f53a3ace731528d1978b50def492c80b483276b277f832dad1591ea0727c7b961a7bb1fc2df5c24ecab1b142e3013b79268b67eb948087ced8f0dc744a72eff97947bde9257245a87783348170d00898b2c19d6d32361deb2a2c6d2e801dce67de753dec52490bb85df8fce1157baa4a7b4a9d6f0084e2dff7d6393dffdabeb83e31afb041127f19e480d3d2b4a8570770fab325808d74848dfccda6fb0080642414245b5010390010000b6b7221000000000622062c5225276252d2121f133e096f5073620f0013cebd2c1994981c693662cc6bb82180979a0b6ab62c66bf0cb2317bb98b41f585aba826a5302ddd65d7701b6aad38e60b5a9f530da193567e387d27af3466134fb3bde7a56402278f72003054241424501018c9daaeb2bcdbebf94cd00401e5070389f3380b953c33b14de2937109ad8395eb515bdaa748952d1da06cc22f6c41c7125c003462556ca4bb95603bef09243845df9a27c0037d0132a008756367a097017f3eb021936858b5f449d24037c24d1cad2e80195cee807def606664141c58a5c8a55bf69a8e2d6100422469edd9bfd6d65f2812d276e32cfcb127c70aa381780df72a4825cc9c52fdb816ae2a8907c696fdaaf080642414245b5010325030000b7b72210000000002013ae3d5b371ceb0048eb29e255936651ec224f2b8bb0bcebc38055e010173921f407d592b2e2491066cbff1a93349f54e419fd96246d16babc354c639a260c691cb25fbd3ad0c853d594113172219d3c5491113046c9f8892743a091c1e80105424142450101121a217ed53f3459b7a7862c54496ac4d2ed7e58b8708d2075794ddd8cbad55ad41f888c77b5413e6b70b965a0c900ead8364d78b4be205a2401f31679d7348fce07e007ba410cf602ae64a3681333618044ffd27a472304fcb84e4ecc42efe3ced2e8018541e4a1c29969ded48287b301b0adee16a717cea029b69ac21ffc3d3b9a30b8ec0d534660b82b72338f5dc5c9fbf925fb626f46d563e54e53a5d66cd1b0f253080642414245b501034d010000b8b7221000000000de481d070699e01e39cef7ade13c131fe3e1daeb9380f095f213cd74f4e455053fba9b2e741e32706dbcea4773c93b4be19f419755ecdb1fbc31d0f3ef14a30a69e21ae5890adfaeff12186f24d8276666b1191e6d4e391eb1e43e60f24f9e00054241424501019a6a68acf781ed2dc5e9f07270b7b2cefb6f78b9e288d5f3ea1092fa773b5a246e847bcd55380f454afa259dc42dd347c9b20208a09b6307cf78d7a3433e3485c7c666d1d49f9b9caaa920b4bdf061799a023d4e63af97ef1209aba0f7a09e22d2d2e80192b8e931c8b700e2a3eb959a9b3e45ff284cd7909d321366f8b64400d5194eb3f2728fe709bc5599890db2a5c7e62961304159870702caaf16d408a729849d98080642414245b5010192000000b9b72210000000005a1763eab338da176e11c8ac5a3a4dcd6c6fe63d1cd6d04cd3363fc29a675b6dc0c51475b9aac668d1f8bbf679ad34134b5575186744b9da30062f7f196ca6027e0a92f6e07be23dfe84ccb9f47892fa787d54ae971a0288a4578781f86adf0c05424142450101eee1782a4c7578b0eeb5517072c6b34e9e241b19dbcb5e5ad9aa5a9bf61820294c0447a76013a5a625cafd475a691748544aaf0a69a8caf33559dc612d64b087b4e8e1ee6f9e81d606fbf72e25854a35cddb228060b5559b4faac402055fa177d6d2e801adb05f6647badb2602fcf50d04a4ab5646242769ebd2c7b4168d3ff574b02f24854dde8dbdd8db6bc75f286ac33f820cc9484f9cf517344446f63d938218a222080642414245b50103d9000000bab7221000000000cab263b46cd911a0d31c03751a80ff2d55d8e15d5f4037bb127e0ce21f10ff2c752bb24c36afdc24e0d5f43d68d0a44f89eec31dde06a76ffe74b240cdf3770527540b4fecc50d36848f5b68c1edccfc51cc81685bc80d4809f27cd0cc6812010542414245010186c2e7da3917cbcabd400f3a06fccc6c71ea81a75eb88a51b49fef7773afb8056317b507a01649b9d6e741d8b895bb26a67c877fe5ed0fd1a5f5b08197a3d080ffdf6f1d2cd156ca7506d226c76a5c01175ec21104185a552af527b002a4781fdad2e8019fdc04c0868f7364209fc8712a52cca2c4b824bcf296e5a6a984fa5e387806bcf364cd6e825c71797b96daa739e72ced37899d251202822e614d27c2af4ed3ac080642414245b50101e0000000bbb7221000000000b29a8eaa196e0a76020d42208f099438127c633209ce78e873b2a383dfd8fa208077d2466a5791097b33d896f11d14a1673e6bf6e58a9481c263df19ed11370dea73bbe19d3ca97ad4bd088dbfc072f558369f284caec8ab53499ec088eabe0505424142450101ca6fec74420d9fd734c3eaf8a64977af2d89200dcf41d26060d035c70da94a48024df3cafc96aa691c0fc8a346fe5bd23f91ff74b156ae93aab6ce40740552891aa268a5771829523c96e780927f9c1d448e5f9b334c6c810fa33adbc10d661bded2e801002762265e44f66a16e086884049fa616abeb3933182758e683229cb080772c331f5c0b7c2c5be8a800665a45379c2e031b2f22ba88f436db76e1456f9177bcc080642414245b501015f030000bcb722100000000058ae17eac0e15afddd61cc86b7b9808b975c24c845a26afbc3bcb3063aa0610d4b0a26660d0a77b36ac2a15fca8f74cff041ba70233d829c93dfdb78df8ff10aa721fafb242d19486b7e5bb2075fa3797b79dcd74a2306a2b931185ebc82a60105424142450101440126732bc4e260603c5d2a6a299e92a337927402889a068c3f72284f1d32206e65328cbe824ef329abfb54b6257876ebbcae7218b43b52e25795ea255ea0872e12e3cb65a6098c1de20537db86a2fddf5ad358a580dc60e769e15fb4897dd1e2d2e801b4d28b5efef64c3bb9d9948bb63c2e0eac5042d614114f6b257b32d9129d6bcf33480bf88947a7170ae8f6dba7ad0337e2cb7f3f91c1d274aec14b588acdd43b080642414245b50103de020000bdb72210000000006a44d1a2648f85a454e57376e7893fb61704a7273501a4299a7670e58d31a94a53ba6895c10ed94071613974aca6306ef6f67a4166e673225f2a40e518a2de056044f8feb6d4a5b7f86d5c19c15b88793087b49ebcec04cf3e9708dbd3e6c20105424142450101aa2ec12914d8d9ce14e64672640f0716d734010e572c80eaa8a31b7b7a6a1a515a918e9dfdddf6c9676fde5b0c2c230352040f1353d1445a66de2f144465978ad73aaef3ef9ce835b228c7a0b820ac05d57b68afeca330cfb3683e0fcef79faee6d2e8019f1ab51b0724d33defc6e612c7826a8a831b37148466df897f6865c762cef365b95cf893f5f80eb5b193e666a72792a54ed8c5f3588c047b9847d50c2ba1f21d080642414245b5010173000000beb7221000000000aee8e1c11690f5bde461feacaf405af7edd763e0e551f5ec68381fbc8e47be12dbcd5f13d898f33e2acea7b59d2f4b0366edee8c39b40b9b516ac090cc2931002067187499dd37ee75c0f623392a2988d65505b345a444e18a7d232157fa850605424142450101ce9273219124585896fa1ba82e9188b811040213157109747aa0c68c5bd74617e4751bac8c752c34ded6d39156472e1097682301a814b65b7734e2c54e9daf8966280f0740e223e80cd1edacd80a5db579615bc31ff51f9e633bd51fd549e1e8ead2e8013f658c690cfb0129d12b7a1b4f89a9440a3536462590abbf164c4e170bce29ccf8bb5b0e0cc5da226e69f2dd3addab36e5f906ce1f1cfcd3d019a87ebdb67e48080642414245b5010396000000bfb7221000000000ee2864b668e7575c1720ef1996cc752c564ad27a2cc2420d0965c6e63c20a267f7aa97a38fc11e977ebb756cb6e7b66468798c15a2bd68833006ab21c1955a03aff39354bdd5e04677f8cac56ce2881040c9df8870e1b9b9125aa3a972aff9080542414245010122a1ed3d8c4dc1f26e00fa2b1d09a6a0b86a2140d1905f597aa0519032891402046b551fa635b62c201de69a7ea3fb0bae0e8cc39290f6975aa2e16efe4b2184788ba5598cf624e3e43d3a3336190a66252d5a6739ad1c73fdcda3f431a34fd5eed2e801d2fd9e2f8da85c331ccffd115c9f02095e3f95e361d1a20da93847ea2e610693e8f059644dce7eb814615dff0f81b95dffccd6c871c29c7545692fcbb55f074d080642414245b50103fc000000c0b7221000000000f29a46329151aba1064f9ff302583d892239fd901f06bb0da805b01c638e13460c87b9ccdcf20f28075793157cb7d47ee63d3951c888189e0af2d520d29da4047a0fb692e8cab4a00b96f4e996fcd0d8c672843ee572acc75cf2553ed56f690005424142450101c85ef8f1b38fb7e020e8b1d8a88f037b791ad9821572920ef578fd098c20d050661399ca0f93ea994cd64bc18228c55e387ba7cff65a3a9fba198e1516c00387f5856e809be201a2714a9b81f5e7a9f9c0522747810615dd5bb8158e51444faef2d2e8017c509cbf65d5ecd8340f1f68bd41aaa3c8ebc005b14f5228ff2dbca0131a3d9e95e77a954d694532c4d36ba08049b932a9da12569baded72f30320183a8b0be4080642414245b501031f010000c1b722100000000088d098975c87d46e92ed1639d51212bd8e7f95ba8ecc520249f2eeb38f85d9055e04e73573f4ee10450235f05c8be19b3f8051a9f4a5470851fc048b02308d043787457752e27a136ca3cc3c60969172267337814918d3032f9d92b4aa77810c05424142450101fc565fc3c4a5e8bffdb2274a0ed6098431e59e423e355a7ab6ad17842ade200898b951cd615241d55a0a11dfbe4c6c6518fd6a6742efbd53aa2c565a927c8885cf9e5f223019bd0c42269c8c002cee47bc604b2a171997431c2b77af49e138a6f6d2e801e1c2fcfdd1d4e7fb7ce4d84d30bb71e15b73b540eec8b12f691558578bdd82721e0b5ecee384a4aa46c382e2fd2fe51820415aa9b10cced634dc86f5fc70f32d080642414245b50101b7000000c2b7221000000000ee27b0d38e3316a4dabdeb138ba4253d543ac81076394ef8ac835ca077b20d587fdfc67cdac4ac953b831f16542d130cbcd5fd30101a9a6fe99a6bf31c2a520ec5523c43638ca8854d01a28481228599470f1422e549070115894854368ae70605424142450101641a000fd3e7b621c9ba820b123ba252e57634f610253782e728f289e3bfff6adbca2af1d6a1d923bd307fe9f78848bd4da62af21d2081e6aa80138b443ff18fea1080fadd5c1b8b0a8de251a6e5e039448fdbfe7e9bef0b4a2aa26296a4d6a9fad2e80154b5227c8ade63693ffe845e74cb0ea12ed220d3978c9d19795abf61bd9ab08dcc59e05486e4649ae6b7e952c9c6ce379b2473189a4904e97c1db8b47f0296e8080642414245b5010115010000c3b722100000000094c74e8543fdcd05e4f044ccb38ef635f5c110571bd1d825929e023c179cd546160cb801aff33f2101be93bfaa92245991a23a56c1467566ad8e5f776f0b730cc66785021fbedb5fb6645993a5fc5146a63915d5abe605605fdffcf2641c3b0b054241424501013ad3061403d4b5afe9bedadd7177e8856e7de0bd69cdda4514ab504f218c9612c5a48dd6847d54be0b16f340792f3a7457276607d83b6a3efabc9715e1fdf087d3b93d735284735016970f19af61d52493e1ed71f1cf6d342d381b38bc17c5fefed2e801097309a490aa1c6ada24b50ee47300f2767d205d9576df75367b905dee9724160a9f2e319bdd462efb31f408380cd4fcf5b750e9e7401441530d7c5765961912080642414245b501036d010000c4b7221000000000ca15828c5aa08e561e29cea672e341c383bcbf7bb35c23ef8962911dab08c936c9cee007c0256935b03f11eeb35dbd0ad56911279eb317536eba941c07ba5c0d2789640b3c46e9aff6101ebf8d78c9d5f97b99ef1c1a4472a8e3498c9a9b2804054241424501018c3a1cdb066c643c451defd1b6c2103d1f7838dbbc161ec1682b3cde7904f95304e5b4a4a3b473da395252fe60bda25501c0160e796898d5c6364fd73b2c058a255563c404d380672d942079df26a2ae66c6e732308b8d0aab1ddcadfd12be7702d3e80100256b823afa179d7cc16d3dbbb7233a7405877fe13fd7b7a2cee6b7f301ef08752f7398519f0c453f4482599a03c28a1861c09b88bde5cb0af3b7c3a17dd59a080642414245b501035e000000c5b7221000000000da6916789d2387eb8e562f49d6eb0132bac50eb76f4a12891d6a9706b74de1680308b4a8cf9845ee0be328214939856c479d5d47824f78907b845978238cc80e3d852a4ac8343caf01282716af34a96bef8a2e9752bfee4e64efad9760ad0d0205424142450101b0a765d236727490cc8b43ffd4c5f7cbd28372cc24d87e5461437b3ba2ccf83784784e4811fecf613345a99d2570be288d414024ca349f2f0aa9b29d4b3496852dc992f3e6e184ba6632e95ff70097d3657a93e988a9a74f60c9e68d9c4c3b0f06d3e801067ac13a3f91a758f549c127f6bd00d624f36fc2f4bbff6c1babaacbd7b76d979074b29a65af28ec2205fa16ca163f9ca7f8ae0402721df0987cec6b01848b49080642414245b501037b000000c6b7221000000000aea0418da03e14317314acfb84664e10ff9f2f022ecea52c7c7ff927efa6e95776b62f0271a9fdd98a16b568671b98ad1e9864d01e903fd3f8e5ba924c235d0606592e4c6fd8180fd4c841f4841d848c84bac633f6a2ea82de3560c76fd21d0f05424142450101648c0d0ecc237a01e35ae5e47e51d2504496cf25ad58eaaefb864d0567b75277ca36582d782cf1657cfdf50910a1a73f56b9c31e5003bb843bc95b80627b008f9cfd22c0fbe433c6020cabccf09ead1e4e35ecae6f72caa86888686b29ca34230ad3e80129f52ef07aab39b173a2c9eefe87e049093b3dfbd7857e551257d6dbdf37038da0368ebe3ee33a03e3f3334b977116a63ca0101b9817e75255e83cb067cdf8e6080642414245b50103f2010000c7b7221000000000989292d49d7447107a7e3a1c9328e152f1e9e2e250c0474745431255bb294474f61211a46469ec870f9b8ae89980c8fa6ab0ec4eec834c01b6f9fd08af9d0b0fa4c3269a0906c16369268d06918e8aba8c07163630e5e27ef2b44322d41a920e054241424501019a0aae526526947efde2d8dc85826ca2812b38a32b0336703a3601280b0a3e25d229d6f9c9de3914b305450972a9b50cfc7ac13ed6dcf8369bab288a86855e8276d9b42078c17b42adf6b2edb49ddd6d98b4a449a09f262f3346cc2503300fc90ed3e80176572bee530497d304edc5831f9f45b16a817ff84969befe9b98bb71926a87e03804d9fccef5c2101b393bc6216d80c77df4559e972a33e2c74d2f127d4c1528080642414245b501011e010000c8b72210000000003a1109c3f87cfc8a0c8080e7279cd444ec67210556561b5f630b9e9cf014e97e8b6075ade0294b094636c771c3821b3460bd21c46727083516a320e6a72aef035b2484532ef0f3bd9a08ee226eef574f21c9522cd815e7c6bb667c766d8f270505424142450101560c0c6b42562e54e1737aeb93eea71b2c8fd71337ec4d52416b472a570aff1a516116c9a8ff5c9b9df1eb0ff3e0da1e5ffebbbcddbe3491cdb919792d77d186df87a1698c3f7aa8c243db8a5155f05cd3ead1c9e03fa2a44fd5b83e169d9b4c12d3e801e6f816b8e83cbf60e002c18461ac55d10b26c868ca0a70c427c5dd78749be0090e08701ca1c6c64d7cf09239d140a7bef29a155006a5df249ddb49f98ce33df6080642414245b5010158000000c9b722100000000030d81801fc4568b1cf32d9faea58f0e3c395ad9cacb6b6237f1478f14520cc2f10d0a4d72e1a51540c247943daeda59974ca17c0dfa5d9b36503a10899d454032069148f17e9bba62e9a16ebbb37170e9833abe0f78eb66a96895491700f360a05424142450101eef592e647a9094db7d67bf3555cac5a0e0ab92098f212c80ff9c4c3d8a01b6d8e289911e05776d052e849ba5560d795eef00ca656f5d4458723211a4315858f4c58741f4b306bef40188d8f7205c24d806d0501572818d697c180131b4e3f0216d3e80149903fb21e8b4369fe947b5660551975fc23e0752e1ee906293352c1bab75260641f370dd5a10f71cf9db448d52ebd8c4ccb3c78dce513740dd4d011702d83ba080642414245b50101e5020000cab72210000000001e8fb3de7e604cbee574a40434553f572c5d6b48eff8e32c1d03f01421170573104d40d3cd51e5c416c1f9919bbaaad4b312025f9f5105926eea0516b93f650e8bd8066152ecd05d1dccb508a7882827ae31974ae4a6bb5802903fdc6767de0d05424142450101be99545b549bcda2f7462ba044436056a9cfde81be79097409809ca4f571ba668a3fed702cd933044016d2836d6e20d1568f4b3ab0a8132e1b5845a9867a608bf693e6c02060801bf7fa3a306c2608c9e53259d40e49cbd7d5723d740de8cd8e1ad3e8016eff04ec09e274b171a4f88bb1eba577211818ba7e28458bb509b2c4e7ccbb5456a5aef06bb52da3a85a1daab27f657bfa7c3f74c81a9dcd286129b8c42b44fb080642414245b5010322030000cbb7221000000000a8e19d6f427ab2ab229e9013b8800da2bc999dff7980b2e2736964a8441a9e3060cdad0123f3b96f8165c1e38cd916e21de74d675b52ca0a7757026020c6a80982e3a604907d9cd15d3e62c67e4e1dbbc1f7f34424ab76447054cde4a26c0c0a0542414245010128e970459ff4925b86699869b4c59399b41e832debea67d0df933bbe2b03857d77b61c93f806fccbfd8ba962a7bd6daaf763d4513fdfdac9884af8b16f2d3d83a247557adb44eae73ea8c46552d006b3e6609d6843efacb5fd4fe11efdb20e281ed3e8016880d77ec6ecbd76a0b550095bb1bfad0fa9410d74136d0be3a3587ed9f99d663b76256ea4f0fb3d37b80a491d2a51814f9edbd9b6df9c90e389459dd99939b4080642414245b5010162010000ccb72210000000007ce5db51f954e1af5cc0da6a285057b2ec76029401b3c42758c24ec09c51a11bbdad07366fe14b9105232bc78f5fbbdca9bd086a566cc2767f90c2f26204ac08886ef65c6cff915baf78c3601458bed6db7e28f43ce9bf395bf6552ff5a4f60d05424142450101e0b5aeb44d2ce9bc81655d6d3a5b75cb0c862f86ab91b3d6b3133b6e7f891a652f74b307662a41c5c2672eccc63a92c95123bada242848054d0fa6e2ee5c5f85283c3e95238b8957075f2300430761fc0011ed4614ecf9d8b7ea16bc844b555122d3e801a789c74e0fb27aa219481765cd5d55fab828535be0225b76ac478873aefa0e230f0b85a1120a7a03182ab2f172c172f1a8535f1aada9ee594efdb198ad2a604c080642414245b5010332030000cdb722100000000042b808810c71dedc5d7a30095e94b1bb3938e3f780c60a1896958ae430452e4ef02a895f5b5865c080ac9cd6125ad4988b05138d4d5515c0eeae6c966893aa0aaed0386e1fd2577764a18ee291d0c8003f1f9670b273b74e13d61140c23a0d0605424142450101124de42ef4d03b698e0fd4479c555bfb78d625f909159c96d82a3316db5b5f5ae71e4ea223e95293ef5e0ac1526db17fbaba8af71d30f424946275b9a913a98fd23af034a9bfc55121a7e88d6f7a9b5d994fd13fcff1e1bde9b99a1df427b9b926d3e80196cf6fc8fd245eee765030065609275e7528c44489becdaa4cccaa04b34de73c5a00285d90091e5900e96a3913412126b8f93da57121ab05a54cd39e13b77775080642414245b50103ca000000ceb72210000000000a8b8359747ea44b0bc47a7e96036c7a7b1894a95ef9733c48d068ad4727ec170e32aebebcd285997f4c88504776321541722c6078a123ea211749c17cc76804726e412c445d60080d34b82835c790475faf332ffa94a32a5ebc6df5ae70980805424142450101d64e7c1067e4beec820c4f091286ae9d53920c3b2f7e5a4db1dc03a7cecd426921d1d3df749329d627e49ecbb6a1490912574a93c067d0cc556ae03076fb858f9d9a237ef8f9ac790991565e5b3296dc7dd906945f26ebbe6ea9ef2f6675a4712ad3e8011b4e7e79e21cd4726ee34ee2f25df0f284c6af74f7100cccaa7cf5d4dafd072e785d367c07e10b40f17a6094e15b0bfbc1c8fad7fe4f4f32664195af8fe7377a080642414245b50103ae010000cfb7221000000000645d756a23cd1d8b4bf31cae5e062f70fe4b2e49771279a67b2f9da52d8f3272e6cf18f3a4356c1061ce240e7fc9152fa99c0114b95c796c49fa7ee305c2ed0c8e4e6076e989f0e9e6039ea69bf5e53ff76a36f399d2ae246e59c05c3079f102054241424501011c45b03670ad8a70051c579d5927006b45495159c2a6e899718bd6613a2b496b20e63e66317918b2e4898f7f4054d334768245df87e960c732a1ff1b52dce1876e8526173957d7a5e05256bdb85db20b3b9fad2ad357742fc05df660e6646d5b2ed3e8019a4c68e79ea5f68177f13cc61c4aac30f2944dd2004c495fe3d6b56c826f8603d5322496246458a38b1067c8bdafafa5b8c5e60462b7fbca426824b0f51249ca080642414245b5010340000000d0b7221000000000c022ce4fb150311c619dd3b46e4412d8ee33549291d5ade4e636f6b3095039615b7813e8570cee6609a3446c882f3cc42af906c1aaafe565a109ced6a9ef770f4d4f8a9272fe6443f618c676305525455e4e0dc6b62015d558734304ebf6f30e05424142450101a4acfecf7fa651fdb4b1cb018a2185e72f745a36aa05e6691e0198467fc7282d765e0c4a91d709f11053d77bb22f7111f2f9e298f566f27bd611bbbe048fcd8334da059165814509c9fd8c016e10e92b83ab4fdafb6f3d770efce6580e49a14a32d3e80199c17eccb041b49a6639ad7ed441ba856cbc5756d0b947b08a56380a3caf28ca19d96b0973cbf14c93a271f6361d6bb080df357634686dc44e19504967d89f55080642414245b50103f3010000d1b7221000000000fc18046e8dc4aa0b31c91d6d10018897229989901f0a2807ed01843435f97c045df451404041a85014024e346811f6650310606b315ae0c6a3dad486c8806d0c11179cf377eb0f80b807d0a36d2d5ef945e2c408a8f90ca4006d041780a8df040542414245010102e9070d4ef55c4e675163295a47c84d13feb93845f98eb9e594ec763d39346656f47c969a66a15d74ea87c8bb13fa25f2d2b4ed83992e18a51a83747101918b4e1cec02dd33992392469bc7eb427f5ef072bfa33cc01478abc5e4c17efe995236d3e801ffb3bcef0997926e39bb22849019db54c8b3e522adc884030d7af2efaa8e12202a1391d30c002e8ceb6bf93322a39446fd42ffcbd6ab584389fe2f93f29f6c89080642414245b5010387000000d2b7221000000000066cc496b343fa90d577fd1e865274e7a5a65ac0be6a54c8194c7b4b2a67c5694b253918e19fce07de06521bccb172b7d183be54931927736f335bd47bd39008007026e2561c5f404ea562eb3ba23c9e2607fb35ec991324de01dadffa7abb020542414245010100906e6fa328ca8e7bd2ac9f0e7eee3477195b586075f1862ff95b3c01a80e5a2c4bf30913b3f19ec3774be539f4e928c7337e5a5db5e63d9dab4f99f566278137fe3d08cf3a65b6f3ebe9189f8b4424bef2b1b20ec8405367553503559467e13ad3e8018dbf0f526ba9a1eadee108be0acfde2cffba3a2466e0a6557d472b7383ce0fffa392cf6bbae0fd369a9933d8527b5fa0cad410c80fb06a1b49b2d70994907105080642414245b50103eb020000d3b7221000000000fae3a20289e9fb159d7a4ba5902f235cf607faceef7914b23870c7b07f317d17121443bc86ee19ffac1aaca71d851c417e8bc974b274422b994f2e4c16269508a11268885417e417108d202ca3ab45536f5b0c59a7426e9b1c12a319898ffc090542414245010122ee16e2d9182555ae144ac090583f979f6ea179f884bf6c039126f82916b21f905eca2295e8989ad2e09dab629d31eb0f5348f7ec068bb977f6a0173a4f228669b3f4f33f67e33f33b6a5a3ac59412a90f570e6e29bf9b97a973d53d3c6b83f3ed3e8018a208b14b0ddd115b2f0dae9026f97a624b21707f48a8f08d697fd8128f91b60516276a6e8c7c1390fed5dc95f053b787b8abcc5677192f1edc0527b7eab9ae9080642414245b5010343000000d4b722100000000024b20d854f5880f5cdb1ac1887bb89bc10767bafc9b6bf96e343ae804b3aa47cf6cb9b3c47e66f9819ce7e19650880158bedcdee0878319b79e840f570c4b600d5e2ca1fd015e220614a15857fcca952a7ea88fc6a450a07df19cc993b21d90c054241424501017a9c7a0f3774b4d8779571764bca7cab4c1d8f2ecac2c4c14d905aa19905270012e882c0d62f64dd17ff927d1359ef699bc4ffe725f30fddd8fc9dcf93c9df830001c3edf580c2a449b34a626a7024f8bcd6268b79e0165f9b318d5ef59681c742d3e80183f7f23dd0e31d74148f7b2af30ef52cfb59fc893237b2852c090786a3dadd87f7d94af013c3dab053c6001055e1fd2ed875a3daba854c556bc6f39193948976080642414245b5010335030000d5b7221000000000c25f772c9e9ad43e75808ee4bbdde5b5e72770bf6c6fc6ebd8cc71be07ff6048e189a2d449828ed2379ffa9f8fee02683f945253446b6b52f8143d7baa9024029afc3a6c3b1ff4f70c3b3152bf5068c9aa9c4e9945bf698845af56814a4f7d0b0542414245010190f2832c4deb45db872c96f9b7c48bc3a74acf4215f1525fcf95945c5a20e608f471bad0c2e0bc1e0c4ac3023880a39638fc050117426679fca12e1660e2c182153839ffc17539d9ccb16b6966693611ffe8f85b86e1ac0a20dce1101a6ead5046d3e8013cae42b15ea0971244f3c20fc74c62895ccc7412141706ee7b890de210cd25f2d8a3554948a7ee38e486787a37524e162383f79238a5b950e4c344621dd86a42080642414245b5010370010000d6b7221000000000b07762e5c96e03fd876f2976a04b5af3b60612bc3e4f672734cd793ef15b8f3b15b241f642c83bb426219b580ecfbe12716589cdf9b4f42c787ef604c138c70c79c12bf3e09382c1c578b6c35bb5e69cdd6fb8adcf15b8a717d6bf5ec0fa250b054241424501018e99e7c56ead4bba38e4b21c0946a23f304fecafc588ba58d2ba5ac9c244e436b1f29149fba98ec04a7bca72c3ed08c2de7770e861cb9759ff5e4a7aac02d68b9dc97090f206f40fc5e9532b615127bf67cbf08372a4d935e00080e4cd47b1da4ad3e801393428757dd230d2f1b078c09d62484d7fae3c03c6b7071b2fc0b6c3408cc1ae95389c5391ad5a828184bb5e15a505099418f21c301e8d7dab938de817fa2d9a080642414245b5010364010000d7b7221000000000f4eade7ff3f5d7fb705aa35304557617998c90ec1a0fe15b3472d47c05c0b67f62ee0166db931edba58ab7039e40f3738b2f51a61d286f8f798f57f8b59357045866f6fddd208d0552166f4bb7627e2fe4a8cfc278ee1ce1968b02cc380e720105424142450101aa50e709da4e6423f7d225759d321b364b96e6f5b8835c241fbfd7bb166b7c013a7b9c64704c166d3ab4af544d62ccbfbd02cb91d358583210f3c7a31b52cd84fd6c65bc00c44d423e71974b6998f3f158c55eb1191ce71260590de044ab04cc4ed3e801c0f5e6c6c85d93d378df9f759a9b28a36d60ce7d2d11065c2df3cda507de281a75cd2a4d5b1d545115a208108162bb45c29e44edfaee9fc79bf6ba2ea42a7499080642414245b5010354030000d8b7221000000000ec84b00764d05acb778b21dc394c47387035296d9e8305e4ec6702c77dcf6123d98474f8a4730b6f1ae83589300edb22ffdb1c5fcf742a985a6649b4104b100e2094719077766b895db4d9b3fb9dd3e0165fd28bddf39caa8585218f5ddf7e06054241424501012e18fb7ce7f0297ce4783321885addb586188052b9fb05dd506dbbbd6a067d5ba6b5a0b668c6bf8d72685386b06b294fbd76778e932b1d8a133fad1c54cb4a803520eb135d0fb35e5d18e2949612e7214314a01f0f337c02676bd2cfea7776e852d3e80170addd50f1b716696a534f7ab34b43033a9ff79aaf74c5b47f89ebb06675ea5636acf690091fd708083245cdf19609354d8615dc9d833b7f4a361afd318d745c080642414245b50103ae000000d9b7221000000000846e5a7238ba27123981a5f7b536147f5e9edba79f37d5465732c6283a70f0446d6d73ffa40856f8c0437e4488b5f2ca5d60c37fda86ee645e512a6cfd11d3072cec5578de3b9da8b69c6b3ff3f0235d9f1509ecaf138e726b54a9486bb3ae0205424142450101902197ae57e35d3caf980c339e23be4f7841e98ef4fe99047df059ab6cc7ec17038703ed008c0f8a84c87b62fe81c0a2e1aa9c8b98629d55474d240d2a92c48f997fd6591713e8553814f2ccde33b51777f07e93c76b1c31622ab0e360ec95d256d3e801f973c375e295334408206d5a7e9c97dbcda289226771386d81690bd4cc1e159e386a6df34bf0dc8d613345812337223c1a5eb4ae05b912805ef03ebbd5796e62080642414245b5010376010000dab7221000000000f0ac8bfddbef3cff4fcbb424443752f1729a684976db1e47e6b5c48b8b20166c50991f66a5f0fac2045cd254a51a181e28e35e7457c204ea64799260e36d6809776283162fa67b3e03abb239daa5404e996477a62263edd5c9e1314f1c386408054241424501016c4625b358dc8f3dfbb194e59557c4db7947550fa1e681e05f582f9bbd235262476f758461993b76897cd595f4b65f8c0f96828899fef7c1ecb6bd48ad469685d7e0be5c5842d92dfdb0fbf4690f00ffe99bdbebd908c9a203783f1e013542735ad3e8012418b4ee579bc17a098b22ef37b60f8fd6943ac46b2555f9c495cc97799ee1e40897bf103ea52dd411018359c48297827ee66084fde99891cb628e5df3c7913b080642414245b5010321000000dbb72210000000008c5c2d15584b119963c7aacae3fa17d001c370a726a3c735a3cafd47516e7e0bda6d8a08791d650b543d0a899eabdd91ab22c96e407f2ed86ffc41f97757160601dbd4d952125ab6a5a2859b48827d1ff09213476c86add36a29febb6967120605424142450101c6672e10f58d1aa5a30f4aab3e101ce4099684c9637c281b3650c36bd0593715929ebd8ce7c009a42eccd321351f33733dfd04221a2cba75bbf65b5f7fc6af825e47f6ac76ec960ad8798cb3f7e27908cb6c8821048023b7e96b5ff53cf083325ed3e801b11cf63c950662788d0b70384ffab1a98567d28e7e99a77119b09ad781a589893f0c218b411d0d11f93c5da0597c34b39bde2f160c38f52489f51f98ebca9916080642414245b5010337020000dcb7221000000000f8859117512efd2833c165f41aa831bd14ca36dd0ed1e5643e89b3aaf259f71f7893f53cb404b1234c9c3a724836e436be769f8255cd84f973034ef19a69310706077949bc6264398ee133d5bac7ea8e7b842e0a8cab9c9e2de303c8545b92060542414245010170e69c25f8b46854c3b27e05a738e97ac9976a762b185218957826056948720129db2c9f440c7330739c2c531b48450ed8302875cdfaa3ba3453ca6313c53c81644d752d87c2b3886dcc9051aed13ea220ea0151f80824a39f97517b5475ffa562d3e8013916f560d8f15f3b5d8a803cfb7f652897b65de62d577ac2c454f618b02d1ef1d39ae159a4640d86d3cc0cafc3d1e6804169ca987875849a884871bd32c75974080642414245b5010117000000ddb7221000000000fc2c06e90db5a810ed154d24cf365c1b456ece1af32167cea03dd2fc037b2a027abf67c39584dbceee201dc52f93158d47b0ab5f3d7b0f7bced1ea33694ae70a3a654981dacebc4cd353e8ca26bab08c454dad8d97fa4decbe2b2f7d955ef50a0542414245010188724894c438ac23a06b5c91df8388f07c16914b7466b5cf47d0a95336e057531493e8e3d51d7015baaebf99b8f90c9750769c1a4ed551a948e3c4b84a768f8a6fed4417418d7e145d1936896e6fd75514513d24fb1134a960ceeb26c726162c66d3e8010f6873abf5f6e88e7cd758b334200b19494be0ce64ecb3667bc32f7f6a55e834b6a1661c532e32d5a90f68b809f4f1b210ae38b7797cb94e31d03feaa52444a6080642414245b5010169020000deb72210000000004452bc2ca88b444e166ec37a09810d4405720a4622b24a1914af90cdce081774407f469a21593c6173009399ed03431f6671d2f53337a4212ab1bac4acf45202bd42ad995ca5727046f2cee22cfd8ad974309322cecf226bd0547e14d505150e054241424501014293fd239dedeb8c0f725ed69e9b447f0f609b2c71b60804da009a5292b98b6248c892dbf96cf23372187a7796d0436dabd44b61de4347e29df44fb04b3863807198ffc4daa965505e7edd469a9c4b71592d8efbdc46a71a88c4fd80e3203dc86ad3e80151bccdc71868afc92e2db2550f1423729a827364ef17ab4ad8b89ecee127039dc8e09f51015f2b259a0b17cca142395a3a1065ef8fc0a74c269c897e01388798080642414245b501035f020000dfb7221000000000664660d1d5326f9d621f256b3994a3895bb6bc8234932e57995900c5bd5dac3591f8304da1a5ea683e3be23eb17e733cd4fd4453fc95a4ae2eb78de575840f082cd47599c4ea3fe8cab0c75c88d8eb728adce1684fd5c57add6228b69c87510005424142450101887194bcb07f277638b910cd53eca700cf10b6bbc83e5515bc26f9e509cba36cbad3c9bc486ad273e330a2a7416fceefec11f643cf49eee7107ac37e6879138f2b5891fe4b30868f0912768b6ed445a70ab6cc37d29478561db5818fcde6916e6ed3e8010405419ca2449540771cc823ff6fd06fec19190c0aa8733d795011c18ee0be30dcef50eb0efc670419a6c680a7d4690c404df84bb281c51ed28cdd6c1fbcce39080642414245b501036b030000e0b7221000000000568649bb465c58ab84bef5c49eca035c6c855000f7cef7460dd1bf25699a5d55c087ed4d67c1690f3c10b1a52aac187fd99228865843289e160948f66295330440f80f09b712088ef01537d7672cd6395d381ecabb3575e8621c10d2fb019a02054241424501014cc013d41628fc63b0913323d428564630080ef2b70bd223d13c1d44f2e139127d92cdc1f4f1e7a23300f160709b05eabe3df0d679bdec618fe671431010ea8bdb0c4925db10198497416c9ca6458ec72473d792591f8b9af00ec321fc13f3a972d3e801dba1159b824ac75787581f57469145ac914c81182adc430e776b83c05c4fc9f632359a3d7853f1aa8c73a6c6885749080905b98fa512bd48ff39986f4e6dca7a080642414245b5010133000000e1b722100000000058e20cf83cea2c4f2969dc6bcd85dfe0a06198ffe79c22e028a9a69237a67952f33923207ca7d4b775f3836b402989f2865da72cf4485ab6d065893f730f4d097997397f714567647cc69ef0d8527e6ef2a1c5b57e1257b365ab59af46fc6e010542414245010168d824d1239e3c0e738ea86c34f30e19dc30c1c26cc73fd691a43237b3c6c27aa9c731ccd8597d6d39d34bee18a83fa87e86e6c09022c5e555bf9c4deba87b877bc78630c6ec198e7d2d3eeb5d0db7b63b0d39d537e443b2279c31329436174376d3e801f6ac212a26f3d24e0887770c3a2b3ef0cdf7b9d84e63c4b895272fc106845882506cfb03deaf5b62442c15b94681b13d7a7062ad57882f050c5634fcc6a4d4cd080642414245b50103be010000e2b722100000000026df95487112cbf77259572ce344a81a575eeabf91e55ab08d6b01c51f415b70c02be53df096d0e9ceba78655a58d2d82b2dcbce6a3302251d696195d7041107ec4a4c5a26d38d83afe7112a4980d50821a9884c1cc6e41b95737c26ef31e7030542414245010198a6b9ac02a59351e1b28f4cfd5347cff6561a506de2a45ee933f5720fa81e4fdb00bed81893113c2b45ab263e3d158fc1acf6592a65b7388beace2a1697b6823c8111b2e7c7e20bf6883b73011c3f1d2c4d9b11ac7bb0eaf9b79b69e90341dd7ad3e80166eafb7daba28fab15aa0eaf9b07365eedd133a14a0135a8d5d5eea614320456a5ab227f2b8e067876e65321da8bc52cd81d70b97057b040c6367a4a270e479e080642414245b50101a2010000e3b7221000000000ac665b5318012f9ad7b10e43c2ef81aa7d31701e40962595ee908c8db9c21b4e09810562a15fe9754fae9017eb942f1e5a965fcea054c939558696a48eb2c70ac4e7666a29cbfd54d2ffd2b2a723ee38b5c200fb93477cebe5e53e9bfcccba05054241424501012e5b82995cb979f0527d2686a0f8e94fd1110695354336077becb7f97641b15ca07675fa890c27084cf7b5ff7ff8d7b283fe84e3cc549d6908b30f283724798e859c4d47bbc66281d744e9a94dded3b965ba9f0b7b231d3ad2ab6187dba80c3f7ed3e801d809d4fb7788df8fedaf5cfaf9d64d1aa2751ebfc0dd5288facd9a27968dc43a0962086ee4eea129496b1a38f45f4905538da07a7e6c9e8f89b4603a756e2c16080642414245b501031c010000e4b7221000000000d851fc66d98de859e32f450e0d953931759b9fc4bc57fe140b313e6c350af61180c1269566672d761a1f9d0a4a61dad10a08579cb87376de00a887d97092d409af96ed4659e03e0bc401f53897ab2d8aa719d20e590a6932f2b864fae26f4c02054241424501014e4ff1a15abd4d47b52bb3a800abd11af6eaf66edf5797aa67785328f1b4a1141c87b1f9ab76b686a25e3aca03fdc0dcac410a41a4bbc6eb1ceb7eeeb295748541412e138d589011a3632c7f421c4e1bdb40dbba1b1d11484736997c729db4c782d3e801172772e2bc29333695864e99fe04695b27e81b740115a6eba4a405952805df4c8a636dc30c1d94a28952325592761ee7ab77b0bf451068f973963c8552843568080642414245b501030d000000e5b72210000000005418e6dfff511ffcbf47b2ce6a35255ec1f3ee86a600917d855774dd18b2e2095ef9f7e0dfee2464d08bb8184152bf643e74ab44bce03d2d1db1399633ede1084261730f7ac66d99e5aa642303b20ee72ef48900779f1c79e56a36aae9a2640d054241424501010277c1102acf230eb3894f275d7ba1ea24fabac07b6ca17d37119b9e79dd2c39ebdf2ef9a7c7b5fa87309068c91b8ea659d155a18b5ec5a6ed16d9720f0e30846381a23ec83304866e053c641e6a3a1eab5e4130691ba9765bc7c128b40bf7e686d3e80155e23e8865611ad0521fc54b16ce5bef1606f9b2dd1c0812eb14bd2cf1c00a22a8dad58b95c958294fcd99fd4343e3127c7f82352dc4a2d18b101d95a433785e080642414245b5010117020000e6b72210000000002a7ef2daf2552443eb3c3ba4117ab951495cf6ba0876059d0abb213e5b0041320bec35634de2cf28f93d55164777638a5fbf787b5b89cfeca44e44522cec2b0bc20ec83c3ce4787e2afb796013864af95df7c13837fc3409fc268f82da67910505424142450101546330415c1374a1312665459d061161dc82c78752ab3a04a51ccdf4a87aee4a99d8e9fa1e7f07d27c1252e785afccfacd7de9f4daf11ae360b4c4bfb2a8648fe6fed9da4760e1812c9a146c90ec0a4dfc6ac020c0d32ad45a3d4ad5703234598ad3e801b4b5f0b2792e0abff05f9357f39174244fe47b62d4dad3e9c4d14eadced453d9257cb31f8f016f4d38cef00a901e4b5f6bb6494afa883c9925a0fa2ddd159ba7080642414245b5010169030000e7b7221000000000d2174320bbb292f4945ab4f8e3a9190548fca145715b6ec9f11b44a2cb08e9534ef215d60fde80d62e882a653c9398ac3757122ba294f88da596125b14003704e51f8303b92a87d954386ea4bc7dd7ab25b87524c0b6eed90ef0733e5dedce08054241424501013602555fecb4e9c648584960a8b0652f139ecfaef1aede8e90a2147cc3e5cd6df269b5763e10cfde633c35685654f499fc1fb1993301a39ea51c3f2ce591aa894d21bc0e1bbe72a657b64804d20dc1caebb48cd862f78a494e068b1a018583778ed3e801f4aef500a112accc553e3c5b708f857160927d82bad34c95459330d2c055f85266e8742c5a20d26a13b0144cc28b18fc40134c86f8454df637f80f7e5361fbd8080642414245b5010330020000e8b722100000000014a2cea2f79257374f0d2931bc3bdf609dd4169cef55af00e2e81fe15d678f2e71edaa1623d6228fac8b5e36bb19765f546f89222b738247c5e9068ac439ee098939cf7c6ce77a4fa8a9b7506f58fc0f85269487cecd5fac6c2fe7332fc65f0f054241424501016e161720fc50e184c7357f1c0a899a80fe5bf20de872b74f64dc6d5b33610d0b8d632d1d30d59088ba6ab3243cdbe2933dcdfa42d607c97fda4f80c4a7338d81cad4a4ef3c5ea20454922673872f96b0fbb72952b0a65b5fa29084bc263727f992d3e80176f607819994e1b2fa3f6beed7d462e4be3f060f34b959435b218f77ecd587c8fa015a00f60bf1434ec125462fb3de9e082c11026435998acf6626bbae518b23080642414245b501032f020000e9b7221000000000ec1157842935ba2a677e94e29feaf052572b76de449b072e9e257afca5bf264be6423a30bf9a8a8e7b9e22b2dcf1a3ab88e19a85cfc530d9fcd0497e0d3aff0bf8de2bca1fffc04ab4120dde9698dc2d11b2bcc1191734b90c8e9b1722a2660205424142450101b80d58e1ffb961a79b27c117a8c93d7509694fcfb9f06ef5b1f6cc55fb6e1f1b10d9fc77e5103cb8615c09ebbe9d1dd50629726c70ca1f7b57732fc92629168501d4d35f1cfea8b7e02b53610432ca82f8ba0a58b1edde6d4da1032228dcb84b96d3e801d05b475c9adac2558f41e43ef33fdfa413fce85992f7b38d702997c21024d4c5562604902d86b761ce6c75bddb7dc7372d69663d1105f27b8278300959a1a822080642414245b5010357010000eab7221000000000d85b5d0665a709f0e8b50aac6e22649540800d45fed5333f3ed03ca00ed53c1f0743cfe3b3c3cc4b8fdbf16c59b4db8d5523fd21b43ed8fb034b3564dac4a602ffb378dfac32fd8a4c5bb1de1f0753fa2f161cd842e3a073183dfb37ece67e0605424142450101586a20d5e30d8ee8c57ae8c13a46fd80577f5ed2f27dcb9c9fcaf47f005a831709a824e445f76338172f88b209c877c4f5cfddf1468b976d9acf5cb7beba7f82b3d4e48023eb53ea0e85eeb62f2f73e7a6c974c4914e973208614fc6194d96489ad3e80179accf23dc6fc52b903e08925415e7a0e49db3f145ec6a422a03e61fa98449dd87f532d3c4ac43561b41dada5b1f05371619d0240756869be28cbed456885ebe080642414245b50101bf000000ebb7221000000000dc4b9a8625e38dd99fa6c430bd8e81dd700c11cbfd9eb069ead068dd07ad761155130a4140a1618f61c43de7f6e487c90bb8089ba8674158d625cb123ad79804550946cb6d2b05ca69e25f34d99907c790b579e814ad5a5c98e424996d114b0d054241424501016ecad2c1d1aefa2c4e092da48769c72cb0146ef0b7fd01819a35445974221569caa4e516308785b76c050c2dd7ecaf049351e32a4bd2f1ec0056b10c1c54138e35188c1c594029fd08cdb1c9ec52538ac64913a9d4f2e23cf30516a4604c66c49ed3e801542f8c75472e1efca965977850a5bb28edd1e162f2eb767369f98beefbcde8fd72db52352aedc4b49cb8f3f2a99b30950d36af20c86a0ddc099a289a23ee582d080642414245b501034a030000ecb722100000000046d1df345b52c13ce9003afd730f71d7b63366e3dadf00b6db0a76e6b2b9f44cd8dfae9f49c7f384175d1f26a4245a6ea332b67053b5a1740f0bf1f0d824b60e808b205817551be1f29e83e0bd492f84f8d7f7d6feb5c175643ff8906b83d8080542414245010118258c6d9c3560fefb39fb468a8b7b85cb5960edbcf40cd5eead67415be38f00b3d8572484208b43f4e82836cb0a99dc498ad702ffb7519e46cc99e360c8bf89235121068e70d9e0e7398309dff5de4c2fb803617b62cfa22fa93ba0328ed78ea2d3e80111b2a204aec752d0da8635407cdc9b79e628cb6c6e6f12259654c946b228b5ccaad4b90d87494ccb6bf9576eba6cdda116d9ae6523773a04a4ab9a74d9b14d8d080642414245b501030e030000edb72210000000003c3c2d09882ff42ce198e2b686614239766d3d2e620aa17a036fd684d85f56185caec9d20377268d5bc28f397bc30a91b35a867a65a2a4e9706a31beb926a9017e60db3409eab997703f6b3c1641af82232ee97f41e44117a1cf0cd199172d0f0542414245010182cbd9b66f3a22bae314934b500e7b3fb92a57e3d5d92fd68f026b77b1c5d9299953bf0714e74d98b74ee377e6e24870eb16888b6f8d4015ae6744fa668e9d87bc1d40965397ce22218779a48314bf751e63b1eab9f73780f1e2a04e45cfb26ea6d3e801797324ad91262f1a61431bd40ec447d49b19ec2299a8201ea1aa74c87da1d43b35c1085c16fc69bf6fe5d2105867a36a738e48516e23b2262fd930dee718249f080642414245b5010370030000eeb72210000000009e6f8584cd2f892e67a2ea391c4dc235ae9f17e6bbea22ed2f98c1290155ee31398e6eccdaaaee1e86ba03d618f18ae3b62d7e699bd8c0d791d20cdb579cae030eb33af07d9f6c87f30f77a28a070950ed79540486ad58dc375d79b05f03f509054241424501010aa3a47d5415488d35194591f6d9b0097a75c8f21d3def817fa1c835e1da644a9e45db3b42b864c898497185253f33f9746786306688c2e6fec186bf12ceb382ccdf3a72eb8e5fa838d128bbd4febbe49b9e401f2d10ba28b57a0af7d881885aaad3e8018a4f2995d6be057c61162bf9847e969ab55f8bc40962839527c648d0de2b7372233cbbc0f4fca5a65a05b9e8a2032618159ab5a7a169a3c7efd688c9d003169e080642414245b50101c8010000efb7221000000000a01baec269f316f68e4062424b7fafc7aa133045ee242fa50218289a2b26565ec47a2006e759bdf9b12a863945fea520303b8a4a25429dc9bd995317e15068055dadb7ba93831ec3031f1729328a58189a6d6a2516adb7c48a1c62d9095dd20c0542414245010154c6c507ecd2cc27925e96f97e467d3e2cfe5fea0c4535dcbbf0a29522c5655298caad2d8dc6fbaca44f9d0a557b5c6ce7ba1246812fa515bbcfa938714ba88c26971b06da5e6f9df0a688a9da121d5ea05c29addfcf82624ea408427b4f3e06aed3e80124bff7eff5c35be3768b5d22e765004d333bdec1e875e432a93e2582867c82ad556c8ca76f7675d9a248f6ea8b8854e47e15ff4af5d8b0bf34ca32de5acc88ef080642414245b50103c1020000f0b7221000000000ce0e7d74f0b3145156c0e303a2f691458fac860fab981b49c550488fe5271a04039be330d2b0364852586d8eed2486a79861b6982a7fc515eb518cc29ba3990ed24751bf5e1fe4246d6310052d995dd8f385abfb1f589b4ecf7fd770fe5bd30e05424142450101da0c0c137bd1d744accf2ca25e382ef7c2d3207e2885014834fa1712cf986a5fb38f652b9ba7ae0ec030f54b6fe8309f216e8cd7f1a99f271157d52bb11e6985e165aa7a39e84e46326782e6ede2c43e6715c50125bf86105c4232c158f8dba4b2d3e801db65a6d73fb3bb6deb5f9dd70597ba612196455cec3416a10153b03b3c267f121ab6e330ccce12de8c88a0a3a2b63352f97c5b64f2ddb0856158a9e155963382080642414245b50103bf010000f1b7221000000000a82159f26d595759e1f0d3d68a2d99844685c1d9cd8670f8816583b032db4a7e8ce4c828bd64d45f637f67620b389e3ea531f6b5e8576ec1493efca7b7e1340272e6a90444034c89b768aae38fc2c99c530f3aeab742dae555f90599c6e3120f054241424501010abc3d5e2603073f71a078715e2507cfc1a73c80749cb13496d1dd5a79b00a3851623bdde3ddb01a533da22fb54c1345dc9f020e720f6002328fecb986243082f3e2124e19bbcd2a35898ebb495b14da7e39e260509b87141209ec93dcb28809b6d3e8011cb0b77ef07dda5cee265ec3ae5ccd107f56ac9014ad43eb8e046c1e24473560c1cb4338e6c59f8a5783c365f8d82b1c387f6beead57a82f7090bb2088c8f220080642414245b5010378010000f2b72210000000001cc40bcd811b31c282caf8b98dfa2b512521610e8289a079806ccd3332deae08dc37c79c1bd61a17c60d5f7f1daa02cfeb36ea5a855dd6c8a35abdc732ac560883c6dd71a5eb43fbb8ec504466b83d3a52e359ffa3da92f39b18f424bb5c3b0005424142450101d2caa7e7f8c2b73055f49b84602275a4299437e722ae0ca0b4c6a3719268af3fdac41c307f0dd185b039121994ccbc4cf70b14464d12ab55d7657f8b0bf4568eca640ea7b28da7d7202ecf3cf6db8985e0892875698835c6fecb7003b443af89bad3e8013f37552f32fbab7170fb2cc56ca6ada8c270e2c9760e59512065aa2d125123ec3a53655ca39fd04245c416926f6fe646f5cf1140dd9be23332e5e9ea4750e91f080642414245b50103f5010000f3b7221000000000c0a06c4cedac09910aa02ca117206757009f990527953a84ff233dd13137081855536b7f4a7a535b1723d8444b77a6e1cca290627d0e18bbf2ae4013f3741e07c77a9c4ecee821342fb4b8a718140b55ecb67bb3c9490833b0abd59c95b9500005424142450101e2d7c0dce0c74817c3acc5f18774dbd992bdf299f5a17488ee39adf8a71cf9514c72b55bc66a0f76d11388caff9b378a4883d818e01597818d3b9007d6a84a844a96e7b95f5db9a1f9722eb9a70e7adfe85cf46b616d255f8d8b018b8575703dbed3e801ee00a7335129aa7f57f341372eab201a01153b9a08ee9ad06d459b07eed528404a3b28211d602f7ac38189a700c5e38b007d4c7a5908956753f29c45d9a954a6080642414245b501032f010000f4b7221000000000a214de86c29191c878b02a86f79d1f89094cb840301a97b180682762dca6030cf12fbcbed241e15b88ae18f8999244ab9920bae3eae4659f232f95d9da346209fa1f2d18ac65c29fb9fd20f7b77905261f90892187e11311f6a4683be65b89020542414245010156df982d355cdf0b2a157e67f120a13270e56f4c238afdced94d905eca5b5c04edfdec0f8c2c29ccc561b2ce7e13d1051e6b04667aafe90bd12df39b900abd84f7d758e22fea7fdcf9190af5f437ded707c3ce563a3a8f71e430263eb589e54fc2d3e801b7b930117471294a34886e9f1e71ddf2a01362d9e8bb75f08b3be2fc803056151be0f56cea7c7ffebbb9a3a0384aaa375312aaeb9b20aae64fbdce517fbe5b03080642414245b5010179020000f5b722100000000064f77729d027e02daf20e32379ef0079c217420ac0b9e0fb4df6b5639e3f6125324e2822ced27a419178128e263dad56429bc128a1ff049fbe15c015638cb500ee0f27ab7293729277eb12d324fdbde21fa7e72ba00ad3f2f68b535340b75408054241424501013a86bbfef16ad046951edc2923c84ead1ab7953b4ea8b749ceeb72f7f8eff0574ec1312f49b1cde53a667d8f4f9fe629d505a32588f74c558ce0bd4a66e36f873c28b197a3ce835049d95e25ebf683e4a157bebc27abaad5db382f6d617aded6c6d3e801c2f9ee5c5e22a90b64c14a2c7d983782253fc741d851e7c293f09bc4b54a335c1bfd39bc59a635cb3b1284f8c68c2b9649101030a5a2ea13899ea5a5900f5049080642414245b5010110020000f6b72210000000005003bb29d302c0a45400ffb6582c209008b6dbe917c2c3b8b68868537f3d01796e58babca9bcfc88e47905aee201e6b90d8a75c86f0e54faa097e08c9dbf570af2c520e826f265298691dd4c527ff07a391afd6d59c5389a5361b83202ea150605424142450101b2c29edfa5eec12bf048f6ff3f5d8ba5b39a1362e97a0716cab4cd4abc013e00b9ee1a678a79a05ab81af8cee9fa23dbc0035731cc46aa28c991cf9e3b48bf8ccb1dd295668c31482635c35de46ba2daa66aa3bef25a057dc2f4a03dcebc8b1ccad3e80197a37d845be24088a65038a46c2d9f43c78df6e92fea6d3a06e9e0778994eb5ef539826a4d097ac2e78703d7fd5edde5f450bd66fd5f45d091d4511dd2f6b254080642414245b50103c9010000f7b722100000000056e7b213e00e2977fa66d1b2a79e2ce93dd887068cecdcc04b1a8d425752d00ab04759541ad8ce09a1888c43f187ce3be0a394e34bae8629e8108339990b300084607a9ba9ad82aca5397327d8ba6a7e5cfbcd33772c5133eebd85d63292ee0a05424142450101a6207365c425b7bfe8e16ad92a9a8195378c4d746b1c8070d6ecacaaaabfc647cd6129bcd3fd29d7595802d3ba1471777c22cfc655247c29faffb4050c0db28df8d9656aaf3ac12aa12c23a539f47a44ab795e9865edf0959517cc5ed0436c22ced3e801089daff0aee7fe90122830e7ec8061d5bd2ec4e044ee136457249dca80779349f4cc383475373084c9d27be776fcadafd22fd69108ed9dd3db564d0ec06e90c7080642414245b5010382030000f8b72210000000004e72ec0f94e8d8701132b8dd6af1f492e37da7ce718dd350e189f86a2018fc4176d828f28f0a8153b76399907b55df1c49bf360243470853eaf2b3c70d6099075474ab54731885b89ef4933aad749aaf9caca55d611952fb5d4d0a74167c4e0805424142450101baa9b426a6f8d068a6ff3d41ff72dc08cf5a6f1351e82b8557d421b1ca4fd021d6a71846987b182572f6706d5f5ca9ff02076c6ebaeccac3caf3a415c0398b8934cd5eba88230534c5d562b3b79ffceb7295fe0c19788306c486c31e403e1ce4d2d3e801e647485b241162272a98db86e349a632c7119c4a9cd419ad3be4d8f6661b0fb421ce93187a08e582787e03c478a5a7b53a033e789b5296b70e01447c3b2803c4080642414245b50103f0000000f9b72210000000008640a5dfca6ed3d141089177565283cc4bb84f5bf90c6cd001696beb304e476f76d68a6f522126a9aa2c74f0623424fd8d4378e09505b816d4064238c047e806430e2fdd7d838f0bf73522df9ded8d847d0bb2c5474c7928ac7e129d1929a00105424142450101b0c420d7a091e08f292596eab39d900bcbb9ccbb9c10ca5b1237b6b1497912247afcb08333fde43c5d6d83713b012c03cf0c35b55300ab8be77bfc76f0727e8197cc5a12fe5f7a24bd3a0d3340f2ce500f717a505a169d6b39cccaec21b1cee1d6d3e801629b70faa5b049c29bcf1f393ac1993a1102169567cd97c9d2df554d151f632d48842a3c92232950707ae5c93d030145d386e7de15c18cdcad18cec09e94a582080642414245b501036d010000fab722100000000016970f80b9edc5a4984fe7c99d6dbbfba191a6d5ad1062685ae4a0b6e988cd68b1788970f14aaa88368075317e2df771fc3a1b4d2193ef898a827cbb50c7d8028e1c72a0280c13513c806208012ff5dc2f86f581f60c3d5d49d3c7ca3771a20b054241424501011e0f4753a3f27a4e8732c17c3f76901429c40d1abf5f8d943b994476be1d8d4c5f1b215d1568ffecabb5ac5694879ebdb454f46373193a86014975da39a4d38d664c835a24bb3cc2d5c62a562c621d2ac01247bb9f480b85e489bf0250910153dad3e801d65f10af1e4b8bdd68031fecb24b76beb4c7d5617d4b7b215112343e2fcd669880dcb3e82cab590c3672a1e79834f6f7bd1db171b01ee33250123c2d3dbba26c080642414245b5010367010000fbb7221000000000982c39e1a6bf6b089b6a0f64d7428fe4ac921d9302cfad96e9d04165f90c29200b7d85d990648ef52435a0c850ebb2263a9463035b4f8a49e1d4fc8b97458307edddd40e16f152433483494755f98d1606fafe87b863cd2e9902a962672ed709054241424501015a49d4153463363821abf40eec957864b0449f225cf45166c006e47cd524130c9f405e25dc94841192ac0dc0d5b70c6162d442a270e66bd43fdbfc5694f5de8148a3ac1ab2ce802b63df9c46f4194279e4834e3997f8ea9d934c8572e9f4bde5ded3e801ade8f32a506b3d59ef4f6476c4b95b4ee89e4908237e7c861567ca28b1d98130e91215a77619d43b83b239bc4fec348f7b0f62ff3af330860de04c7d4b0c49c4080642414245b501037b000000fcb7221000000000f6758041f80f25aa5b6a35fb4f6b46ba98507001e46ee01641204e012cbf861e5ba397ef9f2166b2cf3f4c7783354b18d9916713c63a129e510dcbdfddde910b70c87cebb30f71ac7f9ef59a063fbc8f350ea5b4a078de40053c43989e5b10040542414245010106c0cfe5113d7dfe04705eede5aea8af9a504474e814a5b2bf313ef468662e7054f815d0c6a5924cdd039e42e54e651dcdc55424e2240f93e3ef549ace8bd38b69f62f2684f57e789165f16b00f378240f04d96f2773192868959cc0b1ef00d7e2d3e801baba39ee1dc32ea82cfaaa1ce668f2b4f134c0b6c1ad09d82dd90b26f9a690cd84f12fb5847736218f6749081dab8a8a630f8d8992034e707fe0859683102089080642414245b5010174020000fdb722100000000022cf0c62072a4ed70eb7575ac05887d2fcabc33dce1407be9aa3541655c1b331d0350aecc6d05dfd5c59d8eae74f531bedc16f30c8a515cac64e309c73eff005e78d7ef6fdfe6e916946639d1cefc08b8a221afda47e082eb4ab305fe78fdd0d05424142450101ac5a67d2e9d18075abcbba20493fe3a20dc572412aeb586cac71c16d3fd91e0ce676f26b30b3918619ea68386568a5e8e9a0a80f26d820cb50bac629efcd688018152144d7a574de8a95726371f943a9e43c1b3f0a23b102ddc5abe9734080a7e6d3e801e0ba05f17ce346b2b960d628d1e0daf4a33ea641a29d794814d4b9bad40ebca4f979642637d88aba523fbe89d8d154df7cfad5a21cba7db981e4c097e8b02cd4080642414245b50103e1000000feb7221000000000eef6a78937e445479fca1e5b0b281122cb411f299c1513508bce3615545aa84eddccec493290935fac5879f3e89d7c215a7b48dd05b1e8f3e64cae4c47374b0ed31968d13742c7766a6fe98015422b02e6c8565fc493b03c505c658ad897fa0a05424142450101522c89f7d73b9243bde593a15689feb14a63160cf1a064ab0b36070533f23b480ca9cc07d29caf115bda95c1e47173ecba58fc7aa296ea1befe55bd252182987eb39fda99913ce1e50d24be234df720868e92ec6934736e9a669cc43b0ce9ec0ead3e8013159fa5f020a99b1eda96bc0f231a8d374edf352bc0e2ae00cd56583d64431d3d8d15d13079039d4796839400a17362ae95413f8acb343d6929b452f8dd898d1080642414245b5010319030000ffb722100000000066cbcfb5336252bdc39bfa3a1e82ecd7c9f82fb6b2977e633a19e2eadaf1726722da65d6b6fd22a7eef4eb76ff3e5e724d49192edde9c7371fb9c9cd3fb0850cee6ce0cc1b09d22bd74f23bc5fd0c78d9e01ac16c513d99fed832f7b01756a0905424142450101d683224990629b5173f7b13cece543327d639f5d3ec7b680c3a1133ef0cf3700eb404088decbe062f1dc724921de3ea2758e00b6b98ec34e2177d697749eb08c68f459637a9a01be0b740db12425a037a5062f3308ebc4a391005e915532cbb8eed3e801211011b34ae9dbae2d2cd7ee7f69f092695c80a1e522d95565fde5fc4e852c36ae8c366d4acd37b8b52c203d0ebf3b0705ae61abc1717103b6fa122e3af2979c080642414245b501030500000000b822100000000040d632c8324e32e96084e8556627fa83f601b4677583e7ec4419292d7603215d14468b314d2dff0d5da2118b3fadaef6ebbe9da13b47a209fefd5254c29d7c0aeb792101af9dd4229a9bff7ae367fa6ec2d2f9fc2e6e66c2e609400bfbdc8b0c05424142450101322d01c46474215470a20a3c6b10d7bdc5a6cd4252412025e67e1210d2e8d35d2b3051604c7c679b78d7692b774c521c0889a69b56c349ded46f3afd1c71db85a7b3734e0469e9aea6a993acc695f61818b06ae6ebd870ab8fbe1c70038375c1f2d3e801bdef0393b4d6118e0b6c152faf7705f56ae7d0b9fb5a532e77751d0d5ebc16f79c4a585d160b2d4d4663ad7f3c069bcc7f873a68e2b104b64c2ea601936d3c75080642414245b50103f000000001b822100000000084c869e10ebffe8a0a4415bc12e1fc906d82b1c834728010f2fe3cb8434dfd7e5951652fe758ef0cb619659296eebdcea0027d9270d6371035007a82fa34720ad09c1cff46d1d0df17ca7d39387af984368cb6f0a08cb430b390a7e6f991e109054241424501016cbefcc14b4569cc5ba8e50091723d0bcf4b1a9cabbff8e92148f31e19db590c2740eea4f3a79b1fc1d06b2d174d11e633cb5c9ded323a46fba2927f7174ef8efb2ebfb7107a0b60455df5313b342cd164d7438e917f3d18b2ef4d401d5c3cebf6d3e801998b27901cee2c85cbbea87cd44f959d5be40183bf61b34c1d898e5af7b66e2dee9de6dad0c0ce4f45d99ab519bf4aef0d80b05409fd749659958d99db895d64080642414245b50103a800000002b8221000000000ecabdbef8e54b2343e176dcc786c160bd93811c6f0c1248715f6b5a1ebd88d7af502ce76a5f9b44787342bff51053048fdf1e42af63d2101a076d6d502654901a8359415c51f78153879779ec52fa8ee2f2a7945cb1f726e145fb6591be1b50c05424142450101e0c1c2192754afa464e6ffbb7f5338465a33335e5680d65cbae74d5603a8f511868ac6d419b55b43be80fa4afe883048a3f4182d5da3c164b2c44d03b2d3438e7267542024b7681bf6337f2b928c80e9909a25922b03b097a9db18ce65152216fad3e801e0fce841a38b69d7efb8fda6dbfd4905c939747791601f10761ad9a0ffae45bf4240fdbee897cb32d95e1beccefc087e53ce78bcf70a07730b8ccc98948f3e81080642414245b501015e02000003b82210000000003c3c26ff83e7e82ba6972fecc8be868390acce49af1a633a283fbd9d9d4a98767296bbd6b25447f6a60d0d837a7b59940b73693f1481029a99ffc2244a4a910d674fc349768b82b4e53b9030dfcc83263273d3c9266506f55a0a2c82e6e53600054241424501011ca6d8803b4897182a708e84e57c50ed3f62e200c18dbf07b00f4a6248b72600dc89fb8a98c9974a3fe35e365197bdce089ab8e635a397656302570bdbe93a83d725c3a0312edd40e5565e22730c215adaadd25ddae8205f77a07dc4d262003dfed3e8011f5d92112ca9bef685c7fea24bffd294a1944f267402e5dd413ddb0bd92aa2453ae2d04a6a79450757ebbdc7cab302543c7eb6c3047d784448fb0d7b637234a3080642414245b501036d03000004b822100000000054e3c9f05c19a5dd2554134ed91b83ec37c4b03b1cfe3d4c546557653a26fa69fc07330705ba1560a884149d7fb9c9de6ef4c9d19cedc804f13e0aa6a8d1d20968005043951cd23436dcfda7304b0394fd117ee05ee988a4d8310ecf03d66b0f054241424501014e69360da64a79c24499120855fcd5aac4bdff1ffb574ef1cf3f647beb864475bbc8d94f06927cb25ab11eaa54ba0c8f5ed3e9f3b1919e2aa268353156b6718f9bb2752a871d5491c6a9574aaaf1a8b7baf42745ec9f6ec4478a5f7a2c7a6d2402d4e801ce134f515c3a762e9d76e070db5e81d3cc321c5e446206b424abd8882637a623f96d9a08edcd925e3c7ebc29e14f2a9edd7b3e1bb1c50955e07343c8a16b8fae080642414245b50101a500000005b8221000000000eee6fb6d8369ba124937f9c3c11f6135445cb98ffd161638297c48c914d75752a8682076cd9e86ace1e3aa61ced4afe7cec85531a0d37375f7267397a1a1d30d5de386d14148f07b750c12f324217584c91c215562cda00a61e5a9633996d00c05424142450101808c774a8b558ddce1948a0e6c45426c6fe4c7a13b5ce1348e81b4d65811926a62adc00a33313ccabf16643746673e0c3509e3b23364cff30991ea4e6965d98778a145e3300f2f448574d690a7837e0908bec317cdd3149928fbfe5b8511cb2506d4e801626bae4b10b479d22cf34beb02dd03714560fea68a05f703ba8406df65fc23bd61c30649dc3f81c9ed5d9e9f999dc5e4504e4d506290498a704ae64537842cad080642414245b501032502000006b822100000000052f54c451c9f6a7a225768efd0005f195ac0b17d371d3a4b3b3c7c69228d973070e922c6a45fb4fef1323cff820cfb3f9b7d15dac775b1e2493aa2c026341408ca5ba2df2b7d9529270c47472e17bad3670619e2254c96dfb64064848698bd00054241424501011e9d57cd98c5af86a85e925e7b3b65200deb095fdad4449507f916d769b3a34d72e46638eb152d5fcc3b8f0a3ac1f1d725eee2e637ac333f9370df733ddc578651cce16eb82e491944446243b1f487c8aa9a4a2db0c6fd0020cf337c104d1bd40ad4e80147538a743256528d963566ad3fadaef2a0cb3c364800df63329cbc32aa2809306939cba2c03c6d18a88a4f8f0ef653ec57b9d5a05969ef10d37bbe2512da75fd080642414245b50103af01000007b82210000000001e8d2e6461c4be93206951ac3d4aac27d45290d19f25035d9f11033aeea5791109916e86c5a885bd6892cac0855b0ef1595e4665f395fc4b65d3af4c30879c0e96840ba78087773853b8edf98f4cd787fb56115c887247916b3dddfbc85f42020542414245010148176f41e492268f759d1ec30c6356c4944e5e4f4c012eac6fc05d0c98e7467446c7f7c1ba568926bc95e7eaa08e87b0d7627340ea1b41c8904c172816e0a683409f18806e66c5c8856704cc5324e3a210392daedbaf0b9e8df80885cfb9aa980ed4e801368c65a8c9e87c05917800b8e4e60c65678c5996cad65f94d996be85b8aaac47ab3fb74738b849e5928ced0b5a963b5b29774af59b88b3b4517c086a2fbbbc0f080642414245b501036c01000008b82210000000001a5ba16cc384487797a0155611ff1b888c97f9865fba7103652b1cf504cf921c489068af3cbecbf4d9c892bb3a43f2162d31fa8299a4ac4a78b0f4a05f09c90d2dfca6a50737992ff3f9cd4f84dfa04239804d1ae83c3049e2cbf24571b0ca0b05424142450101dcb7a710783ef661956b29576e9858c13d2e41258c96ed3f2e5edebba371766c5d58739a560265ca15cac1af5dafff5622d5d221a882e1971c1f552ca78c908180e4af3e3bd2ae45fd303825f169edc019dfda0ceb4d0d85389bd33e420a668512d4e8017692071e58fe8af93f689a870af1b9ece0f9de21f17e75c9fe176bf34be1bb3119a4f0dd028ca01b317d8062448cfdd8abe09e1c879ec0720310baf119380cc8080642414245b50103e400000009b82210000000004e32cdb9b4172bf460d89d59c8f7b1da9e71221cc6c04c8d860d4bd5ae431458ff4c09009bc405c6fdf561e6a75ec4893dea424b36cb74876f5195ee2847d508de081379c7eb6cb6c96a67def6681bcce289a4cc289ad374365965d9b6519d000542414245010172e5fa4f1c13165d8b5920cfdd1a3884c16df6a5b28ed095f6a724bcecb50d6ffc3222a5af32da64107a39bf2e8e6bbecb6a2e9d15cf35e42aeb0544de52ed8d4e2a555f13097536dbf32ac4abb9bb9aa67ee8d7f06e501e67bc03e54facb12916d4e801b0f032ce517fce1ea26af71be16ca74b18bfc9a2d9100073da64c34dc21559b9b02ec73afc20564e189d7b1769df01adcd57eebbacf17ff3ec1c87b887c504ce080642414245b50103660200000ab8221000000000a4273207e1876e1b7e6cb457e34e7fcc9c0fdcd04a6eb607c614af3c68dedd1389c5d82957164b6235770fe493e42105e5ee9017d8d8d8fed1e97b1a1705280fd707e233ad18fd45acb4f344de549581a0d7f4eff52ab64a14d7ee91cbe2920c054241424501014679b27803bf98567ecee76707924cfd8a48804463729bdf33b8511ae3903b2fa2e29c4064e30d88261a2d8721aabf9b48d8e1391543771f1c78dbf43805d28113ba0a4fc71e11c8478c2f592c081b844465af7f139e867f299dfd178242b8e91ad4e801482f7fac26caefb0e18f191968307d39aebc4042d31a499ae6edebe05c66e4371367ac137b42de3248142b972bcbbe22224d2e91a4fe54d31b14f6607b34c51f080642414245b50103830300000bb8221000000000b4305c4dc1b3d60a07b5b5ae2b6e3bb8b79427afbf451d5acb2cf4667209cf2579d69fec45da01ae1b1742cfaf0e9f86c04b5f89d2e43da6de0d05d282f13a09fb563e1bfeb64e15581fc59eecb2d6611442c7306f09db42f3628440907c8e0005424142450101f06dccb50423b18feaa2f5430a8d1eb977d11e752ef9c9232e343b52273144793f519043a13bcb1bda83775293d5b2e6acc6c31fd1095d9ab92f39bb31159a8b51e8a6327eee941418d9939fb0eb75c52666694bfd11f41f60377cb45488e7be1ed4e80135d870340a2f1975a3f392aaf2140ce9ebaa707b8c05c0404a4b8dd95eba65c81569217e081df355d68137528c74b2948d50369cd962f517fd4af45b064e2ecf080642414245b50103010100000cb8221000000000be517fe393edc44c1f9466911236e31312d737dc05f3737ad475cbf11e92ba2ba6b19300df048058aa5f26c99c69dea7f1178a7ceb9786ac982bc9f7483b480f0fc746ccef41c7e5e654c9020b62ae05b644d6674694f0682f000718771bb70a05424142450101feecc3d5ad1c159247032e890a70f0df0892d995c2a04fac24e8767910116d2fd1579f19f46a7dd8fc704cb4f5eccb8a83503019b8087367a9f42854cc50068258905d25dbd27fee422007e3122bc25e45f20f20eca32777d976482b75378cd522d4e8010188b9f30b025f4ff831c3896c96babc07f96a9e7e11e82afc6d71b4559211d280d103b83418297577440087ff107380b099587a0d9bea50b263aef4f01f45a4080642414245b501015d0200000db82210000000000cec7a58986273aab57355e1080ae345a466aa8cc3e063fa440013a80b40d9509008ff8fa42d0d6efcb36ab5563e7fade622fb54813aae0e6e30c41dea3ac10cbb2a88dbed873c7e90992a34b3a21a7a7f58522f553025c9547511038c1f3b0a0542414245010102c2ff6791292d6691b7806a3129726e211f3d02cf03bf006abd78b06a794c120064d825de23996af3256e24ed2dd15b6ebbd668db4267ab0ad2c62a38b27181bcf205ac435fdb84043217067a43cec4d9355b9adeaccd73e18bd69998993d6726d4e801e14e892b2ba63ccfe1eeb6e2a1f01bc59b7cdf755e9441ee212824d9356fbcb80f1ffe74481159d32fb30734a873d4f2b96e0a82f980697f5715c3393ded046e080642414245b50103c20200000eb822100000000086d6b90d3f8ff3192f0da83f3362e17cd1a7b7148a37891de2e56ec854f261432cf429d980bfccbd52b443ca892a573731c339dbf4e748f2b6c47741e27eac0cf2b519d5c04b78aff718f17bda8bbe534412f1533e57f2b11604a6a272dd6f04054241424501017018851d3f10d1ecf7a582ea78e0bd5338539f243989a745013ac8b5026e800df1dc2f8da49e737f35c4fa829024ec7bf3f926a9340ebd7a2719b069500d178eb9001ecc1ccbd1204a0562fe50524fb3d493cce6854a1b17df6c495cfcbcd2332ad4e801f8c9748aa4115b69f1414f216e532d1097ea38d8f9af5222f567d080bc397565fa8bedce5e7d7c47a84946121f921a2a407f2db3da3f1ff466c391376608bb97080642414245b50103740200000fb8221000000000986b564d7ecf19aa307a4474d042b598a5bf4426abd4f2925c46793969f30d04fe9f53addbcb82c90ae719e68fb8f291e3035078a4a7a27a10a3876788e6110fd48b6c467dc18546a844865c2490765f44d79b9e4db0a33b756a26d3fa6d55070542414245010190fdbfebc9f92795718c5e6b7e399d8320a470cc07362279cfab129cbac03b19ff00f7258c5e26b446626725bacd4a07a69ae4df3d330cc5ecc23cf00ad8de887daf0c5c6ef1db0724aae76d7d78dad572fdf8c1e64aa0e959cbc7f58cf4a5312ed4e801c1b6caae4ca2203d51684e25b2a662880b131c0803af889ab4f1d2f5a06cc582a022038193a0bafd747e1da9aa68ad9df13acceceb56ac9b7c8d38e7c85b25f2080642414245b501037a01000010b82210000000004ef63059f4c2775e1fb26a5510f471bd8a95ded7d051cd0df5dbcd5093e20e293b1015ff34c619fe28daad74df976c44c43e52fa7ab011da1b56b236f784de01a2aa7557c91dfae6446f16acd0d28168df9b20e747018cd41af173e986307a03054241424501010282e373f2bfc06d7800ea5100d71a4618274ad14179038059edb5fe8d889b2fafa86cfb3935c68ebbabf8c5a488bfa9731f706d0f9aa6a642fca5801bb76e8573373fdc6469ebc49de70270c86fe3412ecee784083d12fb2db73484363a4a2932d4e80129067217d91302d0cdf2a0d74b1a2af48d1c8b61c99500483578efd37a3b191be825732c7ab7011ab6ed24acdb3e8c1a6ffe505dd61f9430c2ca43927fb6a457080642414245b50103af01000011b82210000000005c32781872b2d0b225b8010fbe74923ccc56f74cb6f1fbd2e9aaabfe11560a46315d26c2486797619405edaedb309098994ed5e44d18bba2fa10d833f2a0f000303a90bbf4eefdb64aa6f1116bce98745449dfaa8126cfc407a22b673bb5a40505424142450101d2463d5e25400e1f4a8dc2ca24509a8baf396b11a242aaaea433c24115dd6a1053b4f601031619e9612f24f3d2c3cc20dfbc30566a3d83ef532af3606b482f8ad64e3481283f7fea99aa20b2558589db6e79c41451fa0305b7f00e5a545ac31f36d4e8019dde82a96c03ee673ef9260762e0255463324df85879a3373cacc43d502a613850e40edbc38d95df78b566d3cafd1e28f13fe274fc79199c82ab852b994036d7080642414245b501030203000012b822100000000070a14c21acb0324c55559b82d8c4b80f2a55fc17a817af8ea484bce57a2a260bc24c9064803d4c70ebf6e27a4463eaee3ccc32f0e4cd8622993b22e62905210cee881afdbddc4035c18783420b75bc128e7214aa324cf8c053ba997ddbc4eb03054241424501017ac67e6a4c1000c9402a61f8a4f35fa078f39004d2d6e220030f535f73ce8873880f121737db8292ce71e13b4beb139a01992659cc9d5ba130c27a5ee10ca888d99a59db2ad6e868f125dc776a189f229218b02f75ea27a21398761cf80cbdcc3ad4e8011ef320165e9e12d20d9b93541fdb3b90fdb57356df990a4c50064a34dfdc18ae054ed604a58a87a4e81d1d0d3175c9aa414d3a8a1c0bfd70b0992270265c0855080642414245b501018601000013b8221000000000a8f1629b160f085f9b397fd92c3fc698e1c461617ef7d09243c52a61de6cec396e247b589e96c6b9e38bb1d015bbe351490efd687b8a8edafc3766541e223b007a155b86aec010a570ce99a4f58ae3887894d6c0427a9f40c95aa229b54b9b0f0542414245010160f8dd656d86a3f238b02b455b39ed64d34de5fade9d6f994b14e0c14643456de0c9f0f2a6600a1dbfd6675f2d706bc6ce37b798d9cedcc0c64b692c27321c8b6a5d5ba9dbfb9c46c4fb414f5f77aa28c7671f62b41de3162e25eaba088072873ed4e801aa5421cf9427b82041c13a560e3a584c65d99192b3e491cdec6588f718f02297f9da25fef615f88f2265dbede348c8e7fb2735fb79cb004214533828adcc84e5080642414245b501030a00000014b82210000000005066cd3dd8ee311341defb82ab1d2cfbecfefc358307e74196183970060b6c760279f96132457914d4a94f57c940073a98de154658b3504da6e7810ea64b8a0bcc36215e3b439e8d269a237c0b96fd3a9b136f3459342235c812904386697a0305424142450101eab45501f780d848d3fb65d90e58ee5f948e1676e91903cbc2584c0541581d5428f410e16eac79d69e878188ad205274e3416f40354f1ef05b72e3fa62a5d68a8f6e0d3249dc3c7862cb58a64cfff4cc862b96393076d92821c77dfbf711ad3b42d4e80114cf96f32d1133b1195d71c96a074ce5812a0d13b9ae7353518a7c57fb7a459830e76bfba79e1edfbe25a00061ce8a97b54833c64b0fe6f08753af9ff0613ed4080642414245b501034102000015b82210000000008ab64b6378582fd8f3fb2029fcdae182ad52e727317c375970ea2db27129a4600ea60c358e8dbb77d71252b1154748b434413f5e2c643c9b8d4c5a2cf007f2091301ea5750a7b5aa4e5950487d3d86807a00ace14526a2a6ab26d7e9b0e3770305424142450101e85fedd62dbb266b1afe3d25cc022c3a9e596efc7925adc124dcf8dc0caed54de071d9c0d257ea7368e8233d615124d2786e8e3d9c68c7eb6f389f1d188ccb8dd1e68fe23092ce08f6e23838977f880ca236d92144fedbe6ab30bb15726209ff46d4e8015efbad0492048a5f42b6a65b67a4a75e6a3daf1d2d5a7d3bedea54d7c6bbff5bbe55b34d1dae1e527f945634573e07d5034deb1dad03a3a6d75bbd6132876a70080642414245b501012f03000016b8221000000000d48b5a9987fd5dc0d5578abe23509f8d078b7a2fc0d218c5fa47aa6726d6d138a1b427b02990b77cb1e44b2de7168669f069bd30fee029faaa6d49e4ccb9c809e0ddaeee1062467ab3fdbb68ce592317cecb49e07b24a8161cf5d2e9e969280e0542414245010136061cd2a197394874f42d02a8ace66edf2b877151e6b1a3b85eb423b70b106fc8cf15d5765f93b01804e6c5bd5ea1f9f33e6ca823625cc9fe1f5e119e47988152777d612a54913b1a2025149cb1c4d8bca270c86c5400adf7147711a1e56fa24ad4e801870dc4cda18b5b0f40a1498bbc94cb09ed197dbd4f6c2d17ad34bf7e7a10923cf3bfeb95063ac257ac1918b97ea5fc76df8991ad95ad938379eefd2b4de824d0080642414245b501015601000017b8221000000000947a3ca10b499ebe6b31d1b40d90dafadc24b30f30a93bc53e4963a36069396e5835e01597234c9de39a530618ace41ce1f8517940885812871b733f59c24c06e14a791a4333f5e08081c15d3db2cca1b7c507df391e66f193a5c2491484c80505424142450101aa2e30568e233495a35c7f16303e880ca78af2e81b3218c21f25afe0c310e93eb280cef659931217003e80e244bc694f9cb948403412b360d8c87783aef63f8a1ec933587b03e695987343bd46c8988941fe39129382031e01abdc809e4d8d364ed4e801fc78a84fabed64b0cc96899f2eed184f55cbc63039d143e6163ff5ae2c0cb89d65b5f552f6fa208fb1ca85b1b1b808e6281e6f2e387afe3aa98044893f759196080642414245b501010000000018b8221000000000628b4cfa5cbbce1a121886d41f138349c67b7a03f9e4c865ea95e8a56498aa5b7325e8aeca66d03d03d0408457acb2629f087c68e6bbb688f3d9043cc8254403a435b88ec37d2cc2d5e4c5960d74bac6addfea7e5e7ee740c38954490ea1150e0542414245010152dcc1b65412e410dc2ac54b56d343eff8e69ddd8259a77bb74a94c26d36e219bdd192f432fe8fcb37f57795c620101d04b939afc8c120313c3c7368c146758fca0c264435bd7f9a17cc1346f0f88951185c74ce1fbec1647bec3d331143ef7852d4e8017e42400b292fc20df557a2be6e59dff28ce701c4e2ca32694cc2f21c8640ada14a7f05b96b0499553300837f5e9411fb974325f582c93bfdf514110ab3bcef54080642414245b501013e00000019b82210000000006454ecd03e7f57a11dc0c422ed5f5f3b2df6a905d2963497db0d4a42e3721e7ae0cb87af2f5032e1694d165b38ee163e16110d3f88f68c3cfee68116269a3d035c379b968a3c1b449b30e140fd08906bb54f765380349de1a9d6b8146162ff0505424142450101c6b7e7be12f45bd9b172df93fb933d0ac98993c39d871bdf988ed90efd54603dd0d182a13cad689e49fb6d0c97132bf839e79d421f45d5af9000f82d476b3780d47d578cb9e6721110c15578f08e63f529745526141336b9d50cf619db7ceb6356d4e801d782be8051c80306249540e83ff10b17ac5bae8f24140028db3797d5983c6e67e2022457887b512434720c31c54a2248dc5131847ccd3113255bf55119e031a5080642414245b50101d80100001ab822100000000060821d61dfbddb03aca8727f9937bf2406790538d9849cbe99a71662b9043f22958150027c68ce03d7ed7d9f09f3437fc81d7e723411337bcb52937bee36c20dfac99bef8cecde169395b5026cfdb1b4dfbe5756058fd5e207870c7bfe0e790f054241424501017a808088fdec45fb0eb03110244e5c39916df935f7380db3eaa3ea34eaff9b46e08231a56bb16dff09daf547c836cee571b8f4b48482e4aae45ce6e62680f58ec0fe4d249c55c18c84ec47d73fc1e31f2ccd5b92eb1a6b41374f303229576f195ad4e801d78e8cedfc92400cd1927ebbf03defd17665179313a290bc6e79b7ab65ace8fed2776afdc1a0608cae43e4e2f380e5c97ca2410dbe0dc996380903c02650d0f3080642414245b50103600200001bb82210000000001ad48b9ea167a5a970f95dbca17b9d401fc6665cb12eb41cf4648e0da6a5ba3083bd5430802fde8f45832a3a22d204a0fff6491acf470cc85489eb8fc2571c0665ac5a2f0af3d08e7697950f3ee514ee89b66982df945f2ab27bc3a6fa264d09054241424501018a76f7c3b324d55e1c7818201e4c50ddab41d1a804625a20e03d3bf67dce0140c7d43c3d0ad5daed9c2475a34ecf7d1bfb9d7999bd9091861aed691e6f8e4a80fbe0e8637ef99a80d8ca236434e9e43d239e6f9c0852a484f931c5118a0bf36b5ed4e801d394664992f89db66a9103aef368800f32845cd5ddd68a9f5709f1a4dfa191b564e38126a7d648ffdd37630614f83eeaf2b8baed28d696cb0c3901c01426b8bb080642414245b50103790000001cb8221000000000528b294788d801029cb236ad5777e0fcc4ebb1ec3a46bfc5aef9944a94bd2d44df345e2c0dbbf2566a114cad05ad911ff7755f9ad46b1a501325e9c6fabbb7074c032bca807eabea6db74e36be8f7aae7a28d5fe9ecb54995268e647e789be0905424142450101b09fda1e688b3e7d4d24af300b2a1ecaa109eb33ce9107b14010ed8650554d640a8c138ac9a1556649a5ac83718f4b6f6683ab949ae8a68b0915870eaa6753864639d9c923d864056f752082ff6104eb923f436a82b3a75918bc20bc4c30d3be62d4e801a01a578de0ebd904bdb0ec33712d3be961262ea4e45c24fd9dceca6b8acfa6d4db1674c10e9b40e16752c39e69349fb3010dc01e66864c971ca3e0597b09c6bb080642414245b50103980100001db822100000000082e5a84ea4719f047c60a05b88da48a8f6ce72f368dab19fcb56149f913de160ea333d9a1e596bc366b52c4fa30c45f304335e51cdcd9c6b817ae0de8b6ed20fcda6c515b3afc05c668d8211c3b73ecd877cefa10e46f50f704853ef3803460005424142450101340c46ebf03a5417dab99fe8adb9a9a1a38bd9b2c6ab6b87d996bf421062492603ae4db64c5181c7935e4428d7be4bc6420133eb026fd160f0a21846198ff78a23f2d1e9aeb0973e0ecb4014986389972bdc301981d964fbe8bd140e059a7e6766d4e801a3560a8598a91365df35bb3c203b7ede083283ac438137ec6fd9098846abc31f199ae5d670ffc8293e1ecc4267c0341155ed4136dc1e95ca730b07c8335aaf56080642414245b50101a50000001eb82210000000009298def80105acc74c98b68e2ff06b73b550d6b338477518fba4ccef7d786b7c5ce99329105e5c47412eda41e5f3c004d0bd44a30af7c03b7306706ce6f3dd09386cc996c7ec29c81cd3a6cdf7b761026d0a5838b238fa460405fa8575e4bc00054241424501014ebb51a098461d0a67b29cdb92dfbdc4429ef1aad6b13b8295025306b94fd26ab313cfad9111b33bcb4fbb04995b2af84467c57e930b949703214ad619bced8817852234dab4321fc33a666a12a9a39a113f035dccd85a171f1abc4d092a3cf56ad4e801f2fb6f6230daa167991c13cb429561c25cce1e3ea55fd08d7f50246f8683bf9770834730bcb9101c8e1e27828df92b95dee6bac781bad861b0fe3c9d4592154d080642414245b501031f0000001fb8221000000000e23425816ffbbcc5949a75005dace4424017d7164e1e02f747c6a50284c0fb7ab332c7e860cb33c8bda8114462d789f2e15392aee0baab1f963a92df109272025a22b9c7c01bf18ca22dfb4cc149235d541008ef853dee8f46f40b025ba54600054241424501015e63d9b40487b8dc469fa25aac0b6ee1a50214d7b14ed139933f930d0a657358aa1b5df9a6d3796f1d03685a677a51f53aa27abcb74a6ab3ad5b414ffad1d989a85abb1a9efa45e546eb5b60ea3a3ae3a2ce1a70a10b689bfc3d8bb5efade9926ed4e80174b449787e5e45c890e0e9145653ceb61525cf170faad7ad1465a69ac619ea77eab9c7295ff1f23dda6fed8ddd998b9601d2134281d50605d8d9557bb00ae7a6080642414245b50103fe00000020b8221000000000d647d8155b1567166448a2e371720512e012132935b8c9b4728eb2d440e6006769497bda1d8d1f3c572c030fccdf92192a56f9cf0d33b9323dfb7bfa6aef420b99e546ec5504565ca2cf2b68222daf1532639e49d930d6ca64e96c4d6525da0c05424142450101b4d4422825a3fb2ceac021f52f8198b65467db427fc7391ceffb155b5d4c463e59b2a3751e982fcf698f926e3f9edf277a240f4aaccf6a9034206f8a83315381513056886732314c2c7a1e7d5b63755839c89710ead5a9572c5074ba02e7941e72d4e801b877af170012fe34d47a3ce36420f33a82e78343617f319fba4f59f5b8053935de7dc6adc4530f98b0375464c232e8c7c9cbdc4b3ebb58f510178629603977f6080642414245b501016601000021b8221000000000e69fa7b6ee12a50464c5ad5e76a0bdb564a62a21e7283f698e8a9c67e3dbf203a9959db78b4179618695c6f6f5d6699ab476ac646ca8687dbac2b9db6b54cf0c0d18fd61c812943e13f6c9b30120f92bca20f8aaf6fb0685456ae84bb87d980805424142450101b0e4c5b6c328f4584e452fd86be1664ec4f2b0bf77951bac0b45e87e702c582215b6a3af09d108984817f16b9646f4669de0292bf40d4ac782d8351f6e9dd18c00602c38601d6f7d77646ae9263b2c59c2e8f3962c5b2bc8718dc8ee452d7d5b76d4e801affa3f2ea00056a4b113c51c17a0ef6df6341669e817637a7f955cdd85276bc5710f62fc6cfe9b47799557570d751a71243025a8361875b6d47055c2a56d96cb080642414245b501030802000022b822100000000034e2c3ec0da50f3cf608c8b33908da57f9015c8b2db930eb5ebe6d8b00a098220fdb24876a879490a9593eaf35d035c5aa201faa4141cb61b7f2ca2ca71f2b025564323d3e3b6d59d0143b280b9aae8da353c781e9288c499d9f17feb4b3cb0305424142450101c8fef233e2f3f3f05109033f3836a543241c07a7f2dba5f88bad69aabb525167ccf01d983951c29f430ce70a45bca243ef657f0ea00293eb62ebc79ea678378b63b223b7286929d4745a0f0afbd61150130a993b242e87b65d6f9d508667873e7ad4e8016964e24c026cb8f89b122fcfb240f80155c7caced3236d547717cb58dcf88f46b75cf3c446a48d53acec8e940c4cc0d0078f1d91763bf16cbc633a38528bdd33080642414245b50103bb02000023b82210000000008a64bc6e04702bb9fcb9cbc9f0cf1148431e62980cffd9dbfb5d1bae6c54bf64703d1181132eed8ab3b6eaaad857fb27119ddcea729cec26689229dade580205cc2457e47e76226bd16f463d0edaf264467e430569fd8c85d0f2d311c389fe0605424142450101f80f5c732b02dc174773ad668f9b719a6141eb7c021d4212f27ebcbbc44e0e7a036088c26a8eb159cf4aa453327b8672be90de23c70cc8202278d5f351698286fd9a9cff4772eaa58bef7bf331913e432ea4d5bb21d97a7e7dbac6ae44047f937ed4e801547248f86f8b245d3e661e2fa35599e9275fe627a474098c01433bb2c2452b2a2e6e4f432bf3a5a05188183de8c4b5b07e95392b4eb8ec3b8ff16740455de1d5080642414245b501034100000024b822100000000096185302539120aa1ccd9e5ec9ba998dc7c809abf1fac800d4b67316d3fd230a368a91a22fcb4cb1453c1d145f4332a08b799caddd1ae3cb7c176d31434a9b0fc3d93f945681ba45f2b99d69a50a0463aeb1d5ee659ff286b4419f0cfca9c10e054241424501011e8c990fdb44b7fa33910cb31b44cbae958089ce96ddf037bdff6419cf697b64b3f2be7981241abe8d617c975afd591bcc5b6d531f7741f9ccd4a26a09520f80e720fe87cdcd2c09474ee527ac611fdef30b7c4c17c622e5bb499f410925f53082d4e801c5d8612ba10a18affc638c7711f0289e7c818193b7e71787eec798d0899c58085c7bf352e2e0b312959a26a7ba885cce23fdf1dd91ad62a19f21d4bd29539a88080642414245b50103ce00000025b822100000000000ac62242dc3e064b46e80b2fd7a42324b93f2a47320cdf0100fb6105e8b5e75410a17ced3196da5336a4242c8ea160d47390f280e29e80dd430e677d34d240873c8c5fa17cceb3fb89bebde93baeb97b2d4e8f283b5b326eda177705712e2000542414245010190317c893c94a2f9118307ab9742c9ab7b2810628a0aa2c30028743852713b2e63b16aa565675743b630c2bde3f1c132299423a88b6de747349b115cff00a38c00d1769567183bde350ae22d0cb5ce9ac5951b74e803df37e2fd7c0dfc9ba5ff86d4e801106c47e345f8ea01403779e8aea8e14ed8c20d8dedb26df85a48235f9fd8450afb0bdc76d0980c9e3b4c009d7e5c62156358823666e493eb685ebaaedbe4b115080642414245b50103be00000026b82210000000007c94bf42873bc9879430e46b3e35bb02b7cca88dab5358acfb0be2a016cf7675fad5568d82fc60cafa4794f048ca1e7c191cd8f061346898ff725347f3e34a0a776db90a07f9e11065db50461fe301b3a150de546088c9e85d0f86bdf465d70b05424142450101cafee8f4039a52c490297207785bde0d540170b3ee2474e0afc5980ef10d35032ad19714eb1ab8c8baa45633efe1b4d5d88c85ee79153c3984e535f33097c48579e9f5c989d9d6ef8f07a0de86e7bc7878eb75b16a0653c50b0d3ae9f4c2004f8ad4e801aaea77484f012987b1581315ed6c220bc4ec7e66c47960e976463256de6ce10f78eb4b65ec136a3fe2550a4c4f4b2f7b8cc6a9efab22389139a6682f8de11802080642414245b501030801000027b8221000000000246ca5c1b79f13cee3c3232e2789f5d3e7c967c0e2617c06823b2dc372cc5358aea3462fe4e2af99851bb1b8b25fdf9bd00fad4ff4629118b17f9c074b103c0da421ae28ef6e5dda9870c000ff56e6457f1358b1fad1d69b5f7095c778cd8d02054241424501015e13948aae766b9180d21dd0d38a24c78332b30d471ef26ebdc201f82e561b5ce848b5f7a69aae0ac5408b92efa4f57e2283fb99416fada5f0e24b10dd3b708dfc149ab97a71fb0254fc577862f489dff5437371202e8d2513a8971a429956768ed4e801498ee4de453dbd97f5046a197d213e4100df59c7af44061eaba487a452282cef708c5d1ab230ec012b30256e233518f8ecb9e48da86229284b1179f69128234b080642414245b501038b00000028b822100000000006215b84a12cecd9962a79ee37bd35a4fe29312719c5a71d20af174074e9b31c77293c65b6e1df8834565578ee6d8d37a782141ebacd9fe5f074622c11ee4a0f85a3a18f82a1911b2b89e8ecb3cd11d7d1ef26116db6ac54512213ce38dae1060542414245010146146571f965d27f431e7f05a73c7dc545dc92e2389a8e6891a8cc2fb244b803b49efe9bf37de320b5c9115eed249adf67b4a56976b985999105d2ce24a1518ec699127ffbd197b2abb4ed5c54bce9c624565b68627cd6c548be18928a11a28292d4e801cc9a90ea372d6add0a7951e98f174389ca74700330640498ccd7b778a9d90e6b29c82c4a8a99e0298815455d1d887b72adbf845863ed077ca102d882ea10d2fc080642414245b50103db02000029b8221000000000a4cbdee774a2c9e30266c376aa740b3d797a8555cf9fce62deab4bd44eedd141c3f4c81266a25ae494d055786a276893139674a47df6f6ff537941603b6fe20ef8b4c695182fb50c56e9b45718c1c848008bd0db3c283876f1333d60038cec0505424142450101067cd50fd786efaecf917e4460cacd53f8b05e10a7358624b3a13bb0b4fd907ff895c56728f45b3e87e0b4ad6b8bf6553b3f2bb2a5ea2b58f546d3dcf694f2867d584d047845df7ef86f9e5177874db2c11e39a847da2de992695532171f0d5f96d4e801bd8f1f119e3215c752e1319a7089b67e2bf46254b5928abf0f8a648490381c2b99082f204400f9fb6037e28b82d9f30006221961a30dcc0cf4960660a9c65812080642414245b50101fe0000002ab82210000000009ee0b501da20dd28623036a0de51400b1d5243331fc43405b25b486a05a37078c80ea84a1d5d84d3eddc56f41583d95f45ffb91cbf3d7f8189bdbae9dc1a610bb2d0aab154c3ddc5b944c539705f2a2556fd105468d1e7f344a071e7d1a2ca0b0542414245010162ed69bd01f9569f41c476d5b3b3d9dde988dd16d59a4dc8cb22e2580654094f982164132bbb3821fcc9035d8d0b231545d6568d7919dc7f7bebc6790ddbbd88527c0aa12f7839a718162782abf27ebb540428f9fc8f62ddbe767ceeff20e76f9ad4e80176f8ccee2a26313f8b4680a343d5d5e61e4f88783a2b55befc295c128a1a8f40e0361ee363af89fb145f0fa22d234933241b0e308b94abe41edf54779da8799e080642414245b50103510300002bb8221000000000b66cf4d7734b57cd7d53eb3b056869375d0058dde669a38f28b5d54d335e5a6cc00fd3d2382e6f072a0bf15c72b05e154a978998a5605a0213fbf064686a970c444e11fbde14224294034bd5333f9d9898d5a0e0356b18f081174a6f478346010542414245010190ef4991016fe86eecfcc119175dd87c8214401609cdcde98b91c57aff55b9045ebd3f6a426bc02cdc93fcbee28521717cd699ef64d5336b4917cf4836c1f58c0bc8bf4f45c5660e392799c16106dc31bd26125bb2f1db4d8a82cd66ff528d879ed4e801f30324050d91e0cd3288a9b1811accfdc1ac8177013bbf56428b7c69854dcf34dcd9a3a9082c8dd9e4b678c1e9616186837a825c3fffc1a90939e176cbf573bf080642414245b50103de0100002cb8221000000000dc6e644dcaadc576915cfff3550c51c7012ba1e9f24b384ced847c10b62f8b7ab2287e62bcc9aa70350e428b23b7c56593484bd1bf6cb3cf1c8e8ab068e82508796afc7ba28383a7852fc43f822e31473652204fb0efec96de02eed436e2c80d0542414245010102269a06ff6c90d5e8555147c257a2ddf813d8f8465dbeeab9e83dcfef48a238e46d8e50aefcbbc202609f4a8fc2c0bbb93a82c3e230157ab7b725db40474186d7782c86737c5118403e39c64782f286c144c48492b99cb44ea104017bf45cdca2d4e80168701b480cc51baf39c0064c2227ea7bea3b674c7cf10d8021c6f9b4494ddd6eb76cd0f1e41aad105cddafeacf7efead69514b661a7dc4cb546d1cf5f6491216080642414245b501038d0200002db8221000000000e8efbf29a1de5470f5b6e370d43ae1f976c785257d172da125402137a3111e0cd568ad902a6ce6371e10dfa3acf609e153a27b78894fe60a0cc757d984937507a7cbdcf1ae3720bf7de60be24adb485f1a8cf757840b1c654bcb97b92c49040e054241424501010a0e1ae1b802a5f1c4638d8b82c42c4dcea3ccc3689a70727cf3c746d8573e29a4c4cfd2ca9b30ede355ecf0f7365be09d7d7a9a0b022d77dabfa4fcae3e0a861026a9d3e8efe8cdf5c3d66144edd848c87d68a8b7c7480a92908ba782e10b79a6d4e8019f3a0c75500473dbc5cce19685fb932680747a4af71c8a4376c89fdd58752fe0441b91d3629f9d7fdd12129ed46f8ac9210bd8e4a15df186ae84e2a47311ab37080642414245b50103f80200002eb82210000000009c945d46a68b6e0494778c49e4e1eea7af69299567068486451044e50ab07865e97337b4b4febed9bbea4ed2199dd95b8458aaff37e00ef54748c76093fb270f4038ee1bc0938a5b56a4a09e7afacdf3ca0163f62620ce8ab6d333073b8390030542414245010118b9051e2b569f5d589e82bca825171fc7bd761803ffd7a55d063cc15ed8dc5850c75bd2b0b33fcf5e02447e4904d5ac9c1f0b1dbfe52f69c082a09ebd5c9e8ae7c7194682fe2b4ad8549dceb5f4fd9e0d851ab6fdd0e691a86a66b09f5f3814aad4e801d171870ba4a95c1b8eb2d56f40ab6822daf284bd8a843e28e7ac6f754712bb5596dafa5ad416eb3686af0766967a301ecdb0e7fcdaedcb544116884c4300d683080642414245b50103180000002fb8221000000000e601e29746f9e90cebdc36d1e213c90386a23d1bfbe6f8f13e33201c19f98f6fd4aa05d29644d0c7d3eaf6b4781a53bee4d2868c58cbb58663b2435744b45f03af0c021acfc1ea146c7e10e3ff2f6d0e2bc74d2062e2060afb654d2d8458930c054241424501013844ec0b252fd4ece30af15741981246028baf71cc3820cc1bfe67ee244fec70e9348a9688ba8446afe16d4bfd477258bfe85018700ebed2dfbc80384236b68e949f33aa4b70c8d73152b360221137e2579f321a51b38adc53fafd904eb3d7caaed4e801ea47d72f1d822f7ef4ce3eb5267a5eea0fcaed954da4955e0d55e978f2255edc7f204767bdf2559224280833104df2284326288ca2e62acf26870d32d30d0d70080642414245b50103ae00000030b8221000000000e08278d6ffd051789e2b8933d854d8d04b1befec65a82e4805affe0ebe249d402b7b9c6472be05154fb22163836788edda6f57ea7610edcf073b6742be761d016d7ad22c2c533c14895a3923e5daf0be38dbc186b9ac74bc7342527fda73170805424142450101a496f97467689c57ded71420b05a3b714fb110844f57fdef3517e662969a8f39128dcce87c7b861fb21fa6aa52e09cd3d0b998488ef7ec015ed4845c1d27fe879a9a0b4f74bc6c749cff2c057744c21b917b21d8308144ae417f349218f699bcb2d4e801d1428050618997a69148cabeac443b95b18821b23d9b9b5417f53fab61f383f547fb1faf213245b0506976e72eac8ae018d0b90b28bcfe77075365d48d1da9bd080642414245b501037003000031b8221000000000a666f1c728fae2558c3143e620f276514d7f1191225b0b3de03a1b778e477c05091046aa336deba0f50375ab0ff271cf1aefd3f34ec21222b334210b1619150b4732fe41c5873aa521ba31be2f70a9ff0f63aa409bf47321406a959b413c5f0e05424142450101d2e0fbf9855c3c9d87ea5cf5fbb646f92b559a45003273af159d46ac35fd5b185d40ffae3cc13a6e0793622b9e17bdeabfec9ec9847e5c0bdfe13fada1b6db838bcb24ad3620da087094cd2b5de2ed5b482cf4c5fe7e65eeb0d943a220963b69b6d4e80172470ad9626b2111165f5a5b51f7be67dad47bb09bb24a5309452b918107517d38192d5a0d13fa21887823c059f87ddbd5d0dfcc813be944d939929943f03410080642414245b501038e00000032b82210000000000aa59b1ab1109dd7ddc0b3717c7dd534bf5bb5e54298974b4359ad472fcc78585cf3b3f798fc9ba1f3044e685dd054d946fc1856a0e743da6d9d28c5cef62107ebf550ed11f11f7f2af8259589227695cb323fe6b4d370e3d9f4be314676150905424142450101d4ba6f1786cc7b2602918174a60932e3ec25ade1fc7a3b4683cf774afe97856f1543c390ee00ed7f3ed22f3a03d29969945ad636dd57e378f597a1fdfc28c08032857d1b9e8df0587a89d137684de36bb30997f3dbf35c1896c32d65151e13e8bad4e801463bed476da4bb7c7f6630591519f7f1fbdc0ca018a02081703e79b4f475dc1ad1252ce35c42ca4dad275ddd425e49c88b64f2d446b3307c18e140117311e596080642414245b501016202000033b822100000000066c48c53630fef8138081da8648fc5c5d3824bcbcab302842afe35abbd41bd41f3f9eb7b17a324c0972bb43a56202497efd60a69d9a9bae74f902fc8b0436102833bbac276b37aa485db46b1bdc9b1d329a786d7c558a750f3f23fb20b484c0c054241424501015a5e568ec8bb6349dfe0eb6625967efd00f401235712b16b1f7c28075728c855d4a028e002ecf7849e0cf19b62792e9e19fe4b932f850f95bddadff555f6668454af8eabcff547af7910c18c33943f31bd6e6cc10d15f66be3ae7a387683baabbed4e801ab9c2377bd783e8ab19b542f841805fd73ecbebb849b872cd5636d6797c01a58abf63d3c146bbfd7fec435bab9e23798ca63092fbd7a5fe2f682f53562e8c514080642414245b501037702000034b822100000000006201e299d490decb94ce49b3419ec240a002da96af227b879997771e1fd2268f75cba432b033a24cde1e782e3554b3f5bfa9ca0fdad7e82ba87e86bf2111a0aed82933936ce6909d3390b4acc880c083ca339c72778546fa75692f72edf69070542414245010152d77a72ea4e2e40bfef9d66b0aba37e02e549215a3b9624842f6a178426681c9820f6449afd6767eaf4e6ef8a9aca6b4091d7db47dac6ad51c21d59ec528b8e2fc8605935175743ef965bd474fd80b8e0f3588b4dce80dee6abe17ccb45d407c2d4e801d9069ef18b0a26d96ec847f5387625c760ab7aaf51cb524cc51393cc84e0e5bf7bddade518d17e508ecccc6f6cbf0500b394bd9a4aab5b301a39dd14046fa67a080642414245b50103b502000035b82210000000000a703fdd475e74e446eb09cc60d63ba50468d46da101559e1f552f22bef76b30cd162f9f1a981ea88202c48743114aa74fba8fc34a490ed91818efd0b25dcd0c392ebf117d8e4b2ba4b9fab984af58614b2d878a682cb663d97726aeb8b09a08054241424501016ccef1bb7be057e97f772f3a36f4ef4451dddb5e558ce52ea1e0f20f78b60855bd9d69b073cfb782c1bbf13e600c567f3617b953b4507a9d01cf6e1d887d768aae41a1e044c40898400e466be890e53ba965951e65c2372a0d22d517a3a8463ac6d4e8019524231f00d3c24b7180341ee90a612452b2124b323f8e4ec97d325b5da1e2ce25ae6e3c966db999c0c8bf71ee68a6393937dcfd86e1b8b9f44c6ce0ca3a7a6e080642414245b50103f400000036b82210000000008c5dc602da1e4a4c022871a5396a662f04ae90ddf3d181468172f2800b56c63372ea3db54e89435c4a5560f793f9c41e1a30aa741c103ffc66fc0a0a0988cc0c7f374be998bd1d9eed1995102834af7c2835d6e59ab4b579a6cb2ad4f5b059070542414245010172da20e3039f61431f9ceeabb76a5ec123e9eae01e7dd2a8a7b10a39cb5d7764fd60563bc770cbafecd9af55893fda906d6d402911a055148a929a1f301b27810ec7e018f3d7d72208779e5a472c8e524f27cd04a76a39f5406653b38cf50c1dcad4e80155fd88eacfdb9bb9b4c7aab55be63b2aa16ec4019e499a9b35f0939482e24c84446f5a3b1cd81736a611efa9e634c0c508cd9ec072c494a0bed2b2d459b042d8080642414245b50101e902000037b8221000000000b4a600752c02ad5de04b7152e7fd90385baa2a7a7b38a1bf2a2d0b203360684bd7f88765a61f1d9bbb7dccb06eb6b57dce7338a9cfb67e40bc30f904a0ffc501d4382c11b17d21c9709a71dcf6dcd8a2d6003c94ec605ea0547e0cb88e87730405424142450101e6b8285dc7c1b350a82b5a2baa23341c111fc2684bb2ac563436c77a6461d056230dd3134d7e668b46c994bed23c4368e072b7cdd6fccce7a0ecc77670b21487d968282fbfd4e12ea030a12baef00eca864fd742adc558147cc2f97bcc9dad57ced4e801b5d7d38deedfe436759f1b659390263ccc8abb33b9ee2f6914082ae855677a657d48ec9147f265346d8f212cd788af5a2cfa3f903e0f36baf7567a8917cd8273080642414245b50101e002000038b8221000000000ae7b3436b42eaea51a88703949da62847cbd4011847f6ad198d6e68c26ac3403ec636ee35c0ca9e3b4acfc225cdeb3a0bb1c1a51c204d77d942d44879130c4031a81d3cbea81c6f1a33c817f7fb8c9c410e4f18ccf566cbefdd936ef9d6e140005424142450101a26853c61e1eab6b38bc18fa9e25a55745db54f69a0796bb77d267f9820ad111ee989e7c486d44b828449264259e8ac2efe00957fe230dabe9d78fe96c2bed8f5e725358d917f2b0cd8ec1e30c4cced9b34de925fecb594ad8547be0bd092d74d2d4e801f117a3f5179f601a9a17c95c98f586022a16139d285665de2efcab36c27e23619bddf51edf8a3a360566e39f08d31c3db87e676a1eb0f90beb710b7951550210080642414245b501032a00000039b822100000000092ff58e6752cbba0d717cae31a92e5008e4f466afd0ddc6db0b175a718a5c35f923656c999fb3d8775eda3e6d205f4d47a2f19b35e1fda83b4663bb90c5f3305a1cf7b1f834ecddcff5d7a56dd950b4ad4508b402a56c84f7f44c0c2c98dcb0805424142450101be5a3f218248fd0fdf6e839b9a2c8ea742bdf080b6864518e88f28895dcb6c6f51fc10f09f925c733fa1da310a008e1173e5ffbeaa314c0f2f51c2f3116e40845adab668cee18a651afa51d0ee2fdacff972c777d4ee7c3b2f89ae8bc3216c4ad6d4e80166ff065e1dbbfe8227610f1570d42bf7e91ad2e79b79e3460d2501588de5b089ffbbf8489547a8837ec84db6d6be77c889a2ce2ee4dbe1d54669a1f4a7130d16080642414245b501030b0100003ab8221000000000900362c576671e6bb00a9e81286d751d261b117ffa1c985240985e5043c01e38d408be8d46732b470dfa2ffbacc21c41aa08dd8f23df5229a045c9a2314f230220858a7fb2eb4d9fe5e9f86bd6de86530fadb903e6e0dacf2f2a99b5537fbc0c054241424501019200b23fd7f9020e790aa2e22a247c238bffb8c948064cad9fb0736f8c52ff7f7c7a046837facf9fb65aacf0152a65e3fc4cb95d385e39207f4bb8df67bbba8a57fb0b68a086b5d9e64ed46fe1c6b8c533fc2d18a348151369ecf7599c21669ddad4e8012a57faa332dd9846e393dc2fd94b90f4ae81c0776b5c234a00e89666a633599d5e483e69127a8d15e68014b2f623cb198cd12eb6bbc32e16504199a882ce4367080642414245b501036e0300003bb82210000000006ab9f7bd4618bbddd19d801ad06c173dab44ff63e9fec9ec924b4114750dc7668b4b69a3432aa44bef4a3e5441cd4630098411492a943454c89c0ff0c3130c019845baf130016507d085636ecb02f299f98ceeda5575e5e295b70cdd2c3bfc0e0542414245010124d04388fc9944afc6293287718b858c0cee92705c8100568c4b34b980c5573fc5de315f3e1f542dfb4fa5ef39f64a125f618b819627ed932a3bd8dcb4bfc8823b5ea874a125fc2032a7c31a81aca49ed923f8a378f3c5445df62ba6493b9946ded4e80133ec96402eccecb0a5c7def6df6e1f2018be83936a3f90e3c1752f28a9de1b786157846fdec2932fde13f92ce0c7587e445bc5188a4aa45409495dbf8fdb1b0d080642414245b50103eb0100003cb8221000000000aa00ff23ded389406d5ec7b33b1875fe03f08b5fa19ab5db54805c370c5e222a39b9e11a37f3c8df4e907214f526425032929787a0668f597c5c22e45429b2066f61718944a22013d2c245387a687b0443e4344f15a8002150d26d238a05b10e05424142450101e2dff7b985e558b98a1928cff05567fdfbaaf628feb2d621313e95645eeb04435606d84bc2fadecd759e091190fe6507d9fbdce741a0b8a96953269b5c66b18078e81f8be043a0089bd9fbc396d17a20ea316153e0b8acd13ae14400ee4067f7e2d4e8019a6464412cc59c5dcffa82a5a4289448cb3af2768a62a30cefe235035695220227063e14687a3f5d637ab836976f05021192b4a6bd79f3b82d714630dfc0e750080642414245b50103c20000003db8221000000000cc438f736ef9f0776ed46394f47e164d0b0ee738992388b8a99eb32f2466d704a561a4c45f4d2b9d819c45ddb70995786696f4cc07a00ebc800d3728ae725205ddd65e5f706d3de5f0baa94a31900177207db9d315c8bda7651beae145270e0905424142450101c237b56b0357d42496f38cae28dd9fbf28c08d2c8d5d31b456ff04a96fc78d2f726f3d6b9926a8040981695184b9f883ae95c5150c69245c4e06ee7565fe268debb169d099c8bf2110bba8c5a10124cfc1768a5161472d1c827d6cccc4e733cee6d4e801033452c16cec4d4087cb9b773da9954fca43c9fdae060134883f2f939a16a2e4c10d0ddc5f10cc6c34663e63351d5acfda7bc7213f0a53a2079d77b2ce5724d6080642414245b501017b0200003eb822100000000070426ba2a913747487317dc5c47884a17f41003af40d388caefe71da6c64f938aaf57d8974ba00767f05d44db9b1c64220fd66873d3e2794a980171e32a6be0d1e1311b29f4f62029d1815ce5cc144ba5af478621ccc86f4de852cfaaf1d60020542414245010118df002e931660e3d5e962fe0a3c8bcf8ac4c7035ccdc39abec8874b102a4b029f0b22d363db95bbb43d5cbe39b3a3a4a4f7f9340f60a63b20c39c695484a78d772e03211a11f569c2be9f9b5a196017657396883b667b080bc63254a5249364ead4e8019dccd22af0b3112496444cc9aa6819325c0e6b26c67a46d34384c30ead5fe29bf27b1e184480e453f2ef7055071f5b9a82b1f84a80338d7d82c9fa95c3f38b01080642414245b50101900000003fb822100000000094729becf088d15c83d996c91a7b9a32a57e5401049404857bc7cca07367da224672c4193d6c0d95b8b1966355ab6808162400b23ef0af9314b41c44e41c0009c87a91f9fd2532f46a811d944a2cf6f7891e0a4146cf3471cd6d5ed90641920705424142450101360c87abb93b745c672490ac20b53056b72d3f40b450c7f763d3359c56af6e52b5de87f16d93f94dedfd572d18f5b88074658c8244f0d9bfa9558398a42f30880c4a231c622131d5d2d919d4a278b48a52c6f57f5409ba44e9cb554ee8ab6945eed4e801c565780858e91eba6e25daf0a561a2f20204cbd4590a2904f8431e051ff3969a69e16f1d93cbdd153b1b823e924c88c1db9e5231423252086733ade9bbdb85eb080642414245b501032e03000040b8221000000000b045261876e900093c41ba9210d5030c36fb07a0f6593fd2f3bac45b2fc4da78a9a22a3ba9c0208ed0a06a3f5eb0a0f8b73d1c7b8e044931093e0fd6e59c440d1d2967eaa51baee8af9cb50c8ceb09503f0136b3a1f6e0c55fb25dc9d180940e0542414245010112ac24e015bd497001c83b41c10680be7f1a15f33b8ea9c4b50bd6781fb3f810882462ba2990f3a4053074ce01e51b008df268f16515b9f89b66f8812fe5b789f0bb895339a738b362a150abae62b574f0ebb7674149ee4bf856684c9f997fb5f2d4e801e39939e4a5fc68ea7f20e0979b923c300711069b9b6026756dd55d97a99cde55c985c4d4e94e6c4d10d939b851227a9d97c0531164431569bd36be470e9273e4080642414245b501016e00000041b82210000000007853cbf07ba994cc500abce680ec5a80236c97f14d6ecc88fd324afb39a19545454aa6c510d72cd79f90f4066d266a012d9d1c87af79b9d6faaca5d4ad916f0902f54f402e8537462635786c87d722696dc5ee45da5abb1c982d8518b91a650605424142450101e8df41e45d2e105a30e7daa683f836a610096ce333539780bc2b5dac64f1d601771d2a73523d92754f8d0d3174033a226dbb50da81113ee07ea1593bdeb9708f1e19c2b31ca4b147670405baa77cf6fb7e4ac35b28f801c37b020ccd4dd6b60ef6d4e8014866d6056e994a1f021367ba37fc04d6848415743e3b26f13053a21d62369f0d4c3f408dad747f36ca532e303094076678c28113d980ed75edd174b0ce0589bf080642414245b501035c03000042b82210000000001c5bc5cdbb87dcd570ecf123dfc576fe4af740e93eebc7bed4c57e7a4942025f36293a0d1835c8fd16702ce33c3b08b27c03d2b05ef5127692d5c1e19b84730b2dce5170b1e6fbb9a91f75a472b11b1bd1bc9ad4f6b7a458b0fd9920daedfb0a05424142450101e67e6727e610fd8cf9db58bc35dfdc9def862206cc2e795513e578ab6fa89a7bf99dbfb1fda6526d5725e123863591d96c23bfeb7d80a1c6c2aeefac37cfd28732704d73887094e54e915ec8df88b0345ac9e6fe6cbb554d292cd107e5da1b79fad4e8016a31c1f61c276e2525fb90c1e03d699fbfdf7722a7e6e24d473e93e627e82c4d1189e52d0a5dfb3a2e3180234d2ff228356506031ad6a24cd53045ad37e592f5080642414245b501033e01000043b8221000000000b2c3f903e058c920ba03787b820722cc68a810f2b33447de3cbe2187cab89276d4a196cbc82418ab7e1089f3b68927e3a2976c0d3396cb566043ae2c2ea0ff0dc48cdccbc082118af630cfe1f5c74dc155a0c2e33cd3dcb8b716cb68a8bafa0d05424142450101a6c818c03a4dad8790aa6f05497440f84b013fc0ecdda04315add214f39bc140729d0e18549159dc0b95ce5a3f0b6e816ddc4ed94d336ce1c20f684c66e1868a279fba1dd6c4df8c2165398059520619ac84c3f893b371b7a7565349c712198bfed4e801c2855e1eb302cc00fe1db987e7c2154d802fc973d3eed86d00a9b478e6af17de59f74b896052d7b3e50ea607b639ec6f502dc03583cb561055021284ac1f23fc080642414245b501015b01000044b822100000000002243501403bd798bce3a412352c989decc3812f3f04fdc2a13d94b1334eac6456b9e1f216ade8d7e6c290cb224243cd91a891d6aa61dfa38247f69e7a327605603c4e7d15149b790868757f37a9331c27f321f366d3dfacb04ca09c415f020705424142450101d483dd2f2ad6921c605128262e6098c014ee5d6af4f7b5a064636bbadaa1281a150a5a7b9b5e3a9cd5b3a183109b4c1d48769b209709da57b3d7b6b11ff57e8c3e22cf9413b730279fff39af795147a018d033748c781c88a07dda99f555181802d5e801c4dcb8c2a3579f572399327041b85ae4c0895c9730700a64f463f412b5295ab2b768c01be9ee90f616db2ceec28c48a17bc78cf2a2f01c19ddab9a478d71e608080642414245b501017a03000045b8221000000000a4f12f619cf336087f7b9a35c4d39015c186c61e45721d1dbc0f0afff729453d20589a9079db2c54634afa737cb89c4fbb2697e4c915521f1b6d866e85de8c0501ab72eaabd0bdaa2e92eec289e6936dd07962620bb35b1200b2c4ad6f0a210f05424142450101d4f88a0b62bba8c3270c47cc366f74b92152dfa1cec14f78761ccf23ea37c02680e9f1d4ef6da27e7fce21ff1791cc8b124d49db3cf0d956c02468529c97d3822963a8c033f07985300322190792ff91dc073a0ec376ca2401c1e2ec6aa95fcf06d5e80197b4fd406170a15cf47f949e0bf4fe6bfc92719966384d1a81e874ab6b45bf67fb2b15ffd641aa907b4dd796b172890f3d82f621186a05dc991a34ec7695cc8c080642414245b50103f600000046b82210000000008027d0b32344bb645563a2fa8ed22ac22fa20f8353e6e11c1d19040dffb0987f2e310ea78f80d5f445f69d502784590a4891332fa4d589eb7a62cfe759284505c1bc51140bb1d2150c7df0791cfd3353202c17c3ade4aef15f11e00214a39607054241424501011a3c06a1f939b4a9da6bdb8f11f48d4c9f5b8d607922c4fcc1adf7da8b874551b9d203b9911493ab924c6922f83118af3fb86944efa310ad3fca278f93bcc28165e12eaeb2fc84090b6d54256c68139f1e76132664276ceff3491749cbbd11770ad5e8017dd19a779204ca39d7cdc1b19c4492f72e15cb3be5440a3c0f0174d872d2a9f62a826749fba1e85ec2bc124877c796126954d577f470230b3ffa75e667c60fb1080642414245b501012e03000047b8221000000000e2569ec0ffa695e737d4147cb9988fd8aa3f62135c6d8e45e2107d563da84f727cd5ae067fe387cbeaf87c82f5009a7619c54d8b27a0d38d86bdb9d494375c0943f7e0e7133b8c87330143a3e37bec4b0d740bd27a75a26e854b2312997ba501054241424501012cfe84ade2774bd026c9da799c7829564f04e5bfc21c3c63bc79ffb6ddff36473d5e57e44245881c7a19c7deb4f42304557eb9ed0c3d31123bf58d65bf5046868a85c8e18b7983d7d70565f7d9757541aa559ad8360fc924298411710cdbdecb0ed5e8011988b55452dcfe9ca3089d2b0793fbc73ef7f591560cb07e7834ceed23f5a85f96604185b9ef7a0810b49c61a7534d26843d8fb85ee64c96645fd04a179fc2f2080642414245b501033502000048b82210000000001ce80901f213af874346b222b7056b552b102c67219ac12481260184544e8a7a287fd6dfa29beabb5e73317a6d40d2f5875a543ef04aab433842c65e45c25b01253dcd2f036e823afdf5eaf50ce14d6f4a91143bf402c09a15151c30232d930c054241424501010cd368f5c73f502a0756bf80a5ef9626286c814332a5ca446b11b364d2b039122b4b47e88eb364c9064719ef56661ea3ab304a4acb0a5006a2449af8b3daf089a4c14b74ba0ae0b650fd3b09cc1e5b47c50c4d47d8544e31c2fc5204ca5e219812d5e801feb401fc20aad52fb3e9c24c600dc3548adda02f91e65af93a6513376f04f2f14c45e939e3dc58317a481bae89bda1bde917ae2f6f6946c8d2eecd650c13d8cb080642414245b501037600000049b82210000000001a0bbe75242823c3650c28472af1fa8add98ed6864e761611bfee9a529e29200ee98b9fad81f08214de190b2e8bd36900937942bd9e3a0946a2d1dadb43c450aa683759793ebfdaa578c642e0fc1d41c0bbbf7e28f35f844ff2694c7bd96f509054241424501011c7ddd493a2295ca4ca1fae2b306ea0a88db3b0569cfcd2be71273bd53937b4fa1264e80d16fdd61c8f38b4a19dcd66b3c54ca08d2d2ab1b0b2d61c16856f18a3a10fc7733c4dc36cda7623eaab1ffbdbd0b3cab4af1935cc3431a026df3e91e16d5e801fb681c2216eb04f1b6d473fa1bc65c8c690cda675264a5239ae92b5ad4876273a21cf2aa5bd5fd24ef4cdc45d27ca5c2e96459c5bbb0b11d8f7d07e0fc84c2c8080642414245b501032d0100004ab8221000000000dce6ca8336d9a24632e0031060966e560d4f5ffe4adde92a547e04f15960902f25cc4d218355d6327449999f9056a4c4de5122a76e9b5ae04b6c48c92d6a7809a3ccfa4188dcd8c894d69acb3833573343d254005b0e6f11f2c0cca5509629070542414245010146f0760af63873d22c600e348e64326a0834d484e0a8827ff74f5df8576a115f9592bfd05616f33867ce151732fc4ece55256dbfaa72fc07571f346161e3cf84d8b2b1f62ca3bd7e29e851c461c80892d6ff0d403a3ea9b32753f238499c39301ad5e801cf3fb8dbca7b3d1a44b243749d075d63a7c5448828e8873e4267011048d2c4e07462701e2e4bf2bfed76a965f221e3bc9c3a83c1f5856eb1aedd68c8c37e0a83080642414245b50103350100004bb82210000000007c9e05fb2e04d212ab61b258661fa236eb42a0e563a1f4d9631218460d2827077d73cf1c4f583c41cf83a026c2cc7065f002960e9f7a4a380910be51b33d1d096d69948aeb9522ba2e51172eb06eeac104392acf6a9b4f33a60423dab58ffe07054241424501019e2d4a9536832f239d98a3bf9fb6aef5f4409103279dcfcfdacbc20e7079b614f77524a2a796ae5c050cbee8556b3d0ca7e9a8eb301b726efa8917139b84168617326565512a8570e9795a392efdc52611749cb85ae0c8018e9d8f696449f7ce1ed5e801ca33e273b0c1edd7371e40f30451ec04dcf687120848cbe831032c180c397aabfdd8822eb9be7533a030e89144d9f4f64cb0aed954ba46c778107420aed28c2f080642414245b50101f80100004cb82210000000008e6e729c9e260bbaf22eee516702a0aed63ad908e0363d9490904605e303a02925b261a0aadc3784079dee2fdc43ca6d46af43164c1d297b5e661d29ba0550026d04dde73461c06e6da8b9b385e4daf69a4103fff56b54c20b98a2b5950d1b0e054241424501018a1774c5a10e784c17509b355870bf19cce9a527388dfe4182f7ab7725982d4f1c8e6833567aa5f2e6f1bb28b97a0cf304844feb7622ff255ef2eb9264446e8009994a63d0c6ecd172c246676eace334d3affa885ca915679510db4db6ab47eb22d5e80117e8559770f31e9c7f574feac912d1b38d883bc8bbd47eba2136a8250dc4dc15e9bcf1c49748fb24a5b2e2519bbdf06d5d5d9b7f188118680f69e6b1abf6fa52080642414245b501034e0200004db8221000000000d21ded71ac4ca9b02ebc3231006a37797105ba533bbb4b9b0a26c7ab78318a29a2ed72707db1ae9af095cdc86cfee396138de1490014dd1d34cc614e26f22208c6519f3b006c107af661c864c0b5296d65ef383b35ad643d6b941fd1c3629905054241424501016c1a713181aa941f834a94c4f5165c70d5ded017e57011e9f0d6a19fa6294d149f8b1aae21cb8ffaa6d33e30a7e10c209c7ac550c8de977e6727addba73cf982fbb99af53677ca4b22328e89824d05c1e45a69415258a3f07485797fb76d619126d5e801235268757d746771d265c503fafd77e7c5940eef2a177c537c6e9359e8949a71e96a650bdf49f14e66ffec894c40d2da971d17a81cb0281ffc9a18778d8c20f1080642414245b50103ee0000004eb8221000000000b4aeb1b2a8c626844424a563d97c6158b1f67b5bc364071cb328d27520f0f81b4c988c7bc3e0c286792eef9bb8dfcac267304ae3bba0b0884e08adcdbb39270c9db17b0a4d52286bb73fdbfbb8af745b87de2407fd3e3edecc4d2160ad31350205424142450101665be738410d7e0b7bc262658a0c1f6873c1b0d94912f7c76ecb4418f4311e19f640f4f4e4e0f518220ab88efda70b4b4141929bc30a1cc9182b29bf15bef782ca0234a5fc60fae1b26e2124704bfa57f82b9c7e2a21254b3eb40373a4fbff472ad5e801215314835661a4e87a3bce8ae99c9540789c3a7e29b73edd5900aeedf5ecbcb0f8b49a7e922692743acc00f7b565672fc4536ffa07e13b7b751abe3eec2542cd080642414245b50101400200004fb8221000000000e241504f7c37a292bea97f0ab32ce32a59d60d45b530f3339aab94bfeb981620f5ac73080bd6a319f60d59b6366210ce500f41f06deaf3024170cc6aecbf08008693694c95b876fa264fc91f694b68d07a84bd5233986e51279838cc8ed2ab0e0542414245010162664bfb52004b2c1dbcfe5218b9242fa6f80be465363d9878d5f464367ff135080dfcc98f21894c127796a570f06a07c6b0d0040e40d34a9d579b6569666186a16c288c71d7bcdcd324420e84a3823f926d67a1da2631dbef2c57b3d6397c142ed5e801edd36aa566d621170813fc5f660abd5080b86c013c1715897908f36aa20bc253fe8e078d8dd093261cbd8cd05a6eb0745128a3cb19ebab869def66d3a8d3fab7080642414245b501016c00000050b8221000000000782ed6e514911bdf3699956583c5c471b8f3b1eaccb46ff44aafef730eac522706478eab19b827c8396f6d7bf1bbddfb3fedd57fb41a78c7c895a8dd864be709455b96eac7addf502bec7285d1f13bb210c27bb95f289740ecb08e973d5ecb0905424142450101749ef7a5920dc694ffcecd91af3e84289189e03729a37c1d10ee685b43cb72080a3181e451787060242c7c0995eb56d558bfea1ef714d6e6cfe5d1a8c04fdc87df749e356f9544c88cd22dd21cdc67e6cbfa834d7d4030fa8e511e78fbd6c8a332d5e801bc605b5a20493c245acc837ba05a2e88d0e5a0594cbfe6b709af1db72af2b4317365db4ff02d2fdd71b7cc67700922e5a834e4bf8192a6e1deb45c588cb746ab080642414245b501031a01000051b822100000000066d32986119719e1ae1bcbedfeaf6b459ca8277a586eb00dafc2a0f94894fa3364a4c9844be10b8a24f880a1f00cdfeac41fa0d1fd6079e12248c9cb3b60500c7836ba515164bf849b4a05b98ff3206498c3def038c1d73ad2a77175be98b002054241424501011c9039ed2c60b8798aed3867d3689b0745b4aa38c72b25dbde3d9e5e15d62649d5998944bb5b127bfd435a61da64ba668e4d5fc59a7311517335a8210d0ec38e2081572f462aa1c890cdfaadfc19ae2741cffabd158bf7b4ecd0a669e2f5943136d5e801114d1c39e49e0a370fabc362fbb7a9d60f58ff59fc8c08a8f3686029b693998873220fb4d0812420bb0e5729c2870a5cb3dd3bde786259253fde96a39adb4056080642414245b501031501000052b8221000000000caed30a128617af306b52d686a6f671ae8c485eeb3d61b44fc18f7269495504f7e1781b91f308c40e1ba04b41f324fba44fd38b8c054a4201ef8fc0627fb6b055f173e0f9156ff54eb403d8e75c559ca1ccaea2ad6140c5d9d57299864f4670205424142450101b0fb61d9b975032b3a643b57b394c2f36718bf485fb4dbd84528accb8218135b7acd42ef78f736ef1d953758f947606b56806b02be0e4b08d084c45775714a8a546378f8d0e230f2b09a1d641292809e152b2874e5d13aaf3a540192d023071b3ad5e801b3c31402351be3a064e00dfa5c85fd816d15d83da792100b873b9b513fc4682dd599a35167fd632f5e740d4520761a782694c3ce73d25bda5f03de71bb8b15c9080642414245b501035401000053b8221000000000ca0f15d65cd8cb0e2c12095ead2bee4a3b94a72ecadb4bf4a1eed4a8bb40a07adc6b0306508c7a309de35de5407c9ec1c837c80a0ac5fe1dadded20cc304ec05934af35e73da48210b4b1e44bc0ed48d3b330be536641f7f15a7fb93e0bc5a0d054241424501011c73dbaf53c5b99a029b7a74873ed23115198b26e3ed1d3702ae120b11db730d8daea91e38995394eaa9b90137d70f86c025535477c501e5c41fff158869f98be3f5b1fea5145f22074a3d0e84a3046152c13f8238282778538e50e163221ad33ed5e80151b6287e45735844342b590f73aa0ca507a8cfe0551315918e375599dfa54d45a813f3eaf322813d8ebb9f7ca185ebf335b82cd86ed49cb57e1e0a7dc38f2b61080642414245b50103ce02000054b8221000000000dce26ebb3d68488824773a6b6e40f593e890dbd0ef5bde436ed3e0ba64b2451976c1711d7386654d5dfe4f8091cd882643401ed7d5f8e255031d1281af905c075b1bb2f46f8fbeee060ac358805ec25d1003320f592df63290dc02d87e4c5e0805424142450101a209f689b8af78c42055c8459e53201ab8a3741289f5f656d562538515bd58409efc261bf03da326f56ca85336770d5f6e646150b0e9fe5064a00b7e39ebdc800a51658a3e4e1671185a8109f19937c9002b875f121ad924265519fdb5771ebf42d5e80180d130bc03f1ab0bd3b49aa7406e1954c279c340ff89196ff5df51d1cfc39f2ac9a728e523649062d68d3ce92831fca5e8374d26efae085efd40e5cf7b28c98e080642414245b50103a401000055b8221000000000e8df9e7e47c0c2f9115588bf5fd2955bdf2c7349054c1de250d1ad28c86c594539e37a50e4ad9daf95c1fbe4a2b1ef687769b6beec5ee82318932b66e031ea019139ed16682d0e4ef3d78080bd76cadab180ab62897b9e9e7c2a0fc9ebba340a054241424501019c8edbea8cef0b03140ba9fad063ee404ae6bc40f53378a93ab378678248d02cd675a5bd417dbb81eb395cf2800cf02c98b24a73447a4eb72573547080add486310370d8943250301776163a3ce24582aaa9e1484638acf3a63e08a63fa45fa546d5e801897f801e6f8cbe6d223249287f1264ae9e0285fae8b1b3635a6907af52286ecdb31d953e693d19741fe544b5c308656c1dd0e198000d4132a3f5467eaf5035c7080642414245b501035003000056b82210000000008afb9fc1c1402680b8b556084fa63a9d2377272f2483c6bbca1a65c8aa898c6059d7fd9ee0fdee27ea3f03e62e35461cda076592c4364a8199d8b35fbd8b3c059c209ba547351ef4e5f99808e86add2093838efef89d4bbd6100ba80d55580040542414245010166b3d11e1015ee5e135efb2929bd2ad0bd6aae906aec78bfc24c0894d374d9052f5e63371064b0c16ee95a09d96a14613af43efb98dc927c6aabf6413a48b68bed7a4f906045e8848ffd64fd056dd04054f26a7a0ce5434930bd5df30278f0364ad5e8019e5ec90754a630a5141e8bba077ad6bf7f43d8eae16713507791fa897f4822d452da6e877ba1c2c131e04ac414d1932d0853f25dbf29f9d09a5926eaac4c4138080642414245b501034402000057b8221000000000783710e60ee86242f6f144ce766fe8f9b1c9bfd2f55873da3812e7c0306b2a3ca9004491d7babd72056be7f5482b64addf584817693ff8528c84a6348fff6c0e1574aecab63b3d97a2b6d812a4263f6396a82cbcd04ee6adf61b659bf65aa10b054241424501014aa5d10e1c34e1805b4907f39bb1fba25d6333031e6b5a16687d4b4e6568a74bc2ffdaeecacdc4334cfde6edd0d2e58aa2a3ee70ac17f6f51a40ea0a9c7ea7895f49c6504a2cb0c3ad29e5db672e7e53db7e65f180ff1c9099ffa5ec03d9c3b94ed5e801d0d14e24c3c88e027bf936797d1e86b767807c1e42c51e406d4fb40550b1bd21900de2c9a7cb6936afc17efc5501d0cc5e8b511ffab16d912dd5c442d303278b080642414245b50103df02000058b822100000000096fc5c12b33559adcf7bd5b55532523d46c55af21e32018de08c3b1eb70c4c5137d01c267a974670adc8201dd3cb33d3cb085849bda615e9f67751e60d8cb80b480cb3804a7e3341572c82c6f9fd20f2b07e093c6cd25312d736ff5d0b076b0f05424142450101dc1d860f589ec4bd6bbd0515bff7d018f21b6a6bc5908114465246f458891757a68a5d2778f94e5daf482b6c78cac04e94d28e9f392a88b37f97eac3571d8784ac171d2feea1f67bf67611f35c6ff7015b56a03cda57f0c0029cb4cae593ca4752d5e801b7cb1f88f6f4e523f1dd701c72704eb7dd178f583c069a74e06669cf0a82b7d01c9e0e3a6b3c1b7c914292ce0f4ddfdd9b7805eef29419e2c9637f62ef6cf4a8080642414245b501010b02000059b822100000000098db80b77328c91b63dedbc09c503c1d651d8a809ce47ebb7f4e7b51e40d9f36f1fad71d1ea2a3baf717246bf13ffd4403a450c48c435d7f354989bedc825c0a73abeb1e8d2cbf9ce2084c8314059220ef273c098a65cab6c1b6fb96fa9a990d05424142450101ca5ad85f05fb6161235dc8d84a23f516779d6858db16ab4646c62a29419b4836bb93684d27d4ca138964a4882523b9285ecb6944f170ac05a5811ced64af5c8abc288822016ac5e2857eb5679c8ee20835070762048e233d8e46cb734945426c56d5e801029218bad4b2bf92124da046fb53dd45d2938d33fc4deec73a22f450365dfbaef391bbd7c1ecc2c174498d76a35bd41991bcbb9284d18f90b848b4912ee62962080642414245b501039f0100005ab8221000000000eae4e2c8e7cb0562c1af2368bfe25c4207b26462f7d1c5405fb6859de1cf5c219231ceda679af8bec42b3eca71c8d6b511a15e976943e7065ebd9310933cd306bf069e584917514ab35b1fad1a83f36c37a7b410d47e7354aabad567ca70c908054241424501010a18d8ec52f04024d15cc5834075ec2f6bdf104ce4667a68da608de2638ca911996f922f364dd98501669872b8e4b6ca1ed6163b5d1a9be24c58dff2c42552871b8a80146ea021462b68777c5000416e36b7319f816c3d467ab760fe4f18af725ad5e8016c74b60e31307d4646ea2985a287bc8000509253441d80520c33ec68811e9240b0641d758ba97030b0123e692742b11aa3011915d0b4525d4c2477a0f7c503b3080642414245b50101bc0000005bb82210000000008c3543512764314353bd4943bc21abc4ea5cb27b59ad219a94b0d317e55eef72dad570d8da1b818fa23d5428635704babf6999232cda1d5497fbee3c77bdba0c8650953623a826a7c366f445a9baa0e0f0bd27a227eb4648e389801184beac05054241424501019ab10d01d6e8e05cc55a302b358dff1d66642638fb7d90e63d3d88fb656e33047d2b5c72000d93a27c58d8782e8ef65b56478cacd4d50133d6f27b217bba7e8f5843fcd779aaede4a2204c3c8cd1f40ea1e34c5dbcf556b200b1b933eb77727a5ed5e801f4d4469fb1c3fd14e008c051d5e7edf7030907b86c800bdda6b57c24e0feae69f4bd5b85780acfcde07324b124282a957374f67eb895f7b522bec7d70e427a8d080642414245b50103eb0200005cb822100000000022fb4b37746c3ae8250c81ac57a0b71400b3286c5c345beb2a03381623066b10e44d62388349152704681cbff7b4128093060761284e8ef528a6ec6b3e88c10268a4032f89326417a231bceb98bb599adaee8c268992d075c07907a20c23ea0805424142450101a68639716f5832d0ae20df67a0de90bf65e3f078a226c50f7fc9d19cef37980962a9a89e401bf3e22552791ec03f731f65ea71e934f2273ed35bebb31ec37b8ab1839c298a8d7bd3354d5339982d859a190cb6eb2d22b43e435830151017fa9162d5e801c3fe95331beb20641b25615a03aef971760d2428fceb7ea4290d16d55d3f3cb26c56bc689b7ffacc919cf4018c5ac7dc68a616ce86782af619647ff2339f77d4080642414245b50103950100005db82210000000006e0e1618b402d71eb770a5d07b3b0968d633071be0515ab88b0f0f78ed20350f8c972f01e6e1542fa09a41a146c18f9f54a25a6d03f6cba2b66e684a6d723e00048be76b0e083383fa46e026fb42c91840f22a113d6c98ee74a22bf16829ce0905424142450101f0347a7d688114ac12621e721b47110f3008fddd21aae0a39d5a2205d967c457763e947200b74460b2648f527c2fdbacb598c01b386cde63985cf79f63d3c58deef577bd2b336fd9a00caed5fd7bd3e3e87032dba4ab849b4027cd04bf37093266d5e801570901c4979454bff483044624f07b5fbe255bc6a95163a0b60388a521889363ee253a32f3e94c4afa1baeae0938678d7a3bcfcf1950da9de48680b2c2ef5e0c080642414245b501031c0000005eb8221000000000ae5c58beee25c28bf3366505edbce9cde35459a5c0f84233b1ba5f985991a0212fa9cff80468ad9b3fbae0bd1c686d414f62484f7bdc57f4179d8e50d6adf60b16dbbcb46516699ab05bae21dd0ca695d1f493c475c1a5742f09a4d524f5b70705424142450101bce1c5bbaac58ddd1bd58ffc1a8fc03421f3dacf534b9c622badf7f792906176a1429daee8dcad41af2fb73bb098154f5b890f7e15e05a911ed77d95c26e878a6d43b2605911449b99ce4d284c8f2d3d38f980f5cd82955c95dcd43797dc01ba6ad5e801ad4de8e95a319b5cd892b89cf9ec1a0374a9296d6f0443f17fcb5b84ceb3d590e1ac9c4c1ca14a4f208ce977306c6340d392d79b1efce108feb469903f1006f1080642414245b50103840100005fb8221000000000faa915b24b47deb1abc6b6bccf7880ebae38b9ee49d622e1701183772a1edf14fd5996ae37173504986f4320f1564f9cbbce531f32c956b722aa64241d355c0d3a1bb65988e9ba4556e50c600bc2b6d8c5a8852f5a7f535c411c71bd72ca28030542414245010136c58ce7e65d9852f4046566c6d11031c93b896b0ba1274ac0a2010e499ac70a8ab6e3c35f4d51a2e8acea3f7c224f2525eb9bfd01eea5414cf769a41316f78798f71532e31f9c3c9ff4460252b83a5439ea377499169d102023dbdd5e7372136ed5e801a5041942cbb150c1786cd0d007792c5e2a2f2b5a6f7bc2b61a70bc3e657a3b0cbf49dee5d1b3da9f949f30049e5763f25430417955548b6b0c387b29d42013fc080642414245b50101d100000060b8221000000000389963a8fdb01fbbb45445683bfa1b1765cdc8e9aca353721e0023e223f3e673d2da730a453a4612c80a99419d1f03fd356644193ec270b07c2c68640eb06f0fc7cfb95401e73807d7cc07bcce26778c74ef43ec44da1f541498c10f11fd7c0c05424142450101c87b1215659fd2f2eab42b3a03a69e5696045fc488d912434632fd9799dae94e2c48a192ebf313928c86527499ebc72b665e3b2a4683b6607c6219a17822d78dcb16cc84e11c7adf9401b88f217aad701406810ed33b2ad8a1268e189083a5d972d5e801996301a9c80d8db62b8bae28aca212ade7020ea444c0368a21b6e7c254cc74ccf9533c0b82aa5ed173f645f622aaa7474835054fd4b7b01ecb84609240f178cc080642414245b50103ba02000061b8221000000000d81482638b1843612e703a3f4b6dcbd1629ce51e8336de2e82b9d74b858492544cd9fbdb720c0e51d6ce2b2232bca0b2beb3f837b412015f7178b5e1ef789e09ca219621030c0104beeeddd019b27af4b5ba407c6297cd30e7c218f2334df60405424142450101e093f72078aef50dd31a41069e7fd7a51cf86d5436ee28cc9ea3703084953714d8a9f18fe809ac9381cb56fa3039e01515160f7820ec93c534956c895661828b4f2e2e963d19ec944634efb6151756707c1e8f6c6f50811d6b97ed1d904f591676d5e801629af0a4fddd8d88a25acf38e2263e0b21ab892df95b347194ff8e9127a6bab02c412ada2a88ac29d3c713dda58d708fbbed656933d28ead1cf3c6a1ba3e9049080642414245b50103f500000062b82210000000001e4e73adaa114ddbf549825579734cc311062e8dfa666df8473babc09672c6062b60af557f618cd316e455f037d81a45bbe6387d0364d53ef4f9e6240b6684019c4d12fab4ad189a5ff787fa2fa10461584f31e2bddeca7a448a697f080ce303054241424501012cdbba2590efe42609b3cf392ff81995bf1537facc7d71bfe31b3b382269770c5cbfa8ed09a381fd4de2fa7a18d2f36b6e1b3c3eb96b1d9abd98c9fcb8ed0f8544738a9a630c6347fc3a364c0a3b39217ce82450d800cf39cfbaae55da8fa6717ad5e8010ca536513501197df75e88fec04774a502ef7fa025d2b23dbe95e27656a01ac235d4e10208dd4000362bd9db57ea7cd007ae75e54c72ac4d057903b05d01cb46080642414245b501032500000063b82210000000006c37c8f0659c386a23cebcefdc18477671d72dbeffd39619145547f66e2d8d4fedc288b4c20b67b342ecd05e3a53009252f4c4af1af5b4f5271eb1c606353e096248f1a2cf717dcbe841fa99270e0534a24b49f117b6c536b4e9a44d198fca0f054241424501013637e74a13e5f0b51707ca185f3d78fb3cddcb6012d46ae7bb48641c02e60e172133203d714bb6ba3ee46302dba69a6cd1fb5538e6a214a83da4ce2f6b0416884e0a841813514b719e030c2085ee6809478bf534f6286d4e04bfa4a0493cf11a7ed5e801e8e5d026478a7177bc9a7eec6b7a74570de86d4a668bd9caa3260aea13fa95c5e633442bbb627d029009e7a5e1f42a75c48f12b45de9015db428efae0af83f02080642414245b50103c401000064b8221000000000f2bfa28edca26c6d3ff92415ae0a87cbab0ae67133ddaee2424e4dcde855470a816f9571b52efa3ec80dd775414d383c7299654b51cf4265202e966ba59789070160b8a24017d92ea26046b3cf8e072b50d02eb64aefc8975662ea5ce2dd450805424142450101daa652ebada1e71d667fd54ac263adb12f9161c749ad74a8b56eca34ab716528e1a96d593660d797e4305592ec025809757e8104a9d9d8ae459e89127662ca8791c490cc51663b89a81c7bb92ae8080c99889968950c0168f372a8061d14f9c182d5e80155b66bfc56dc2bbc3a50dc30ceb251590dce530899114a485c864051850ea979bcd1064b72bafe4ae577f5110f19d1db81702323935d55bb12437668c99a5d38080642414245b50101ea02000065b822100000000036b518350bba1c224c869e390c4f543dbd876ba5cfef7e8388224e027af93f4bb1d1b09773fa55c3a0b7dfdf4874db4c75827a30ef6d1639e27af0b22cc0b900b2310363eb4599ccef66a00426968e7c817f0124491d38779b348c661a0f4a0f05424142450101eaa5e6d33abac69d57bf2db50a5c9eee3bea994e50dac0b722f0147c8b406d5c5617c95ed628496d77f6384468d808aaa3e515d2ce82e28607a6d3dd8931f481521fec8ab72bbfa02a929b098babb9f787d45d2d134d1b9a4f61eb4b98fb2ebe86d5e801ca5d75c49476beb9c4a7e7a544cb38a859f2faff4a60fd4ad48d24d418d36c66b7490c75317733500b71fcf4c78fd41e48b9fb65d69d21aa14a137eb0e24baa4080642414245b501018101000066b82210000000007e30d26a57c3bae1d9894e292c1370683ddb2c5777e62d4316d4db498d8c65210b86908a19b6dad982874077953fe705208c9fe8911eb828dcaee9b1a26b4300b30bd060b2bfbd52b55bd35f51f5408726c9dbed9d5c6d20dbb0ec41086db801054241424501019e408a5ef019b52934b16b4396e5f9a9adb7797eaafdf1349eefb223ee281c56403605606d49ce8f6a53370dbcd6776393312a498c50472e31f63ab6b0382a8f9062e27c685aeb7c4f1f0f6a2cc1c5decf7fc86534cc1092e986cda6d0e4d7138ad5e801e022574abf2d8e06ea0765e57bc3022559d46868e73d0512dcdcafdf7742f73fe81015f7d626998f83b9d8553011517a51c4f84fb6ca5209e3b95edaf58725e5080642414245b501038300000067b8221000000000d47039129bbe8ffbffc5372affa7d0215ece4af4c4d7b839a565d6e58885e02bacf2863b7b2ac2de9df1854f8387ac798f9bbade9305bbf284f1088358651a0524afcba83319f652a841cf884515591f942fc91bce903425f56a7af2e2ba4f080542414245010102f50dccf1f577f10c4cfbbc7acc5850f76ef351bb5ff3f209417b5b26cd7a62743c9e3c3b8c778cbb9031a9f36b36be2c6bc7fb16368a26cba20e8f3d61bd84e670b66d038d1b722e60bbd9c2047888f16353637de49c187c911c10cdf445498ed5e8012b408f71753eec7dbff17845b3e91b529ec8052488548fb0ba1376ad3dadb2a1bab2ad69a6dd8f2839bc1014d51c8614ce78934c714875c28ac2ff54c8311b74080642414245b501035703000068b82210000000004464c9329d6715d8d6ebcdc7a2c5ca013b31f1f6bf355308910c0286d6d4d4790cdc992826ae953e977bda5aa352545a6739a9cca9909034f0b25a4c8bc77108e16643c58df0c6a7c5023a4fb1bbc1629b7ea3184ce817f766b45f8f5a59cc0c05424142450101807d1a3bed07ca14dec96ffd1e2216957e998423db0c6895708f240389db9c578b4a7a05c2cfbfcdbaee5f292fd96c51041bf262708e231c62442ba5700df68347c58d42fa493bfae9de55fda6231aec6ef5632b48adc6548e7c97ed1a3d55d092d5e801ab41a6dfc5b54c66db74b978b09a374ac9e7b35158eb355ba85d2e0db100f7bebbcf9e4f2f17c6ba409cf7846a705f95ad6325114d7cd839bb0173393b909ad7080642414245b501031802000069b8221000000000281c5ce8f7f930183fb8d21ac34cdf0531f101fb7153b3bae4bd358b1a72bd0c77aabd398b1aa2079fd04fd3ecdb81f3f8f60629cff3d4bbfc68109825cb770862b0b55b78063250eafd2fd10e3fc240bf977054c4df7ae833d77ad85fea090c05424142450101dcab2bf5f47fa244db737dd137a04cf0bb7a086f8a6101daeca2887622f391089e059422b04322f026b741001bb0bfbff38a89e9c4580290e52c30bddc91538e36afc240ddb62beedafcc3b35391acf2320d56a9b68cfe6dd74f91c84e5073b196d5e801a3b37bc044276df275e6102f3964bfc728fe1798d338904494b2b0af77f03c5e0f6ef5915f9125e5812f0065d978c95e134241dd9af7deab03a016383598e87b080642414245b501016f0000006ab8221000000000481922c632df59b9e828d499b6b2bcb89b958623f0797392c5bdfb2e9f33a3104c9cd2b1ef0f4f478d239d95d26305829cd376413aabd42b2f377d1f3b54c805a70207fa4ab508b18401b572e30fd34d7018de1679a5fb70d13e3d227597c9060542414245010182db88582acb192d0e06beb8b775a97e9330728d6d0b22eb7b79c5226cfdac65fe5c3ee578fd0173f7a87f29e42680f1e831e97bcad54f777ed0885e2974b1883791e46f01de6f1df867056b06ec657399abf0bda038b37c4d60f7dba08eadf09ad5e801e768a1127e19ee405f0fd8dd5ae04a634cc3c44fa05f1842d52e6cba8734b275bbd654c0d0a2b45e6ff2403ceeedae8288ec42aecec0646163bea3f48317f23b080642414245b50101c60100006bb82210000000005cae4cb204783733bf55ba6d7297f729edded9c7ba89d879a47c4f0912672d23840dcac1595b968aeed71717f5fcd74f6cd7fa63917331f84c4821475c865a0355b84b6f21c1fa1badaf2cd545e1e35e5546709c359e1767f95f5e77f98189080542414245010142d4b80fad6f85cbb85419e4bcb9e0b1a22d16c2c7ad47a569225b8329fb42377b5b40c5551a4e6b1c4e46bb91af8283d2b7b220a2c7fcb7b2a859af1ef5b38436d73fc6fc4d6cab209ab54a282e0be4e2be29111846810aa0b783d2380335329ed5e80112e1258324b9ed04f657f0cecce4012eba9820b8f525f4d5780f2e56d66618a40c3fd4c595b1c2814e42e2a3f2ad13f35187b7fefe7f5542cff25cf97d356785080642414245b50101d50100006cb822100000000066b0f2049457f393d9ceccec248ae29d3fe48783ee774a7fd79c72af32ea31228dd48aa6ab24d691b2834a0e1f898312e360a0359d3e7772ae08f5938a0d5d0a37a87b608c6ff9491189aa508b5610d77d3fbe07e507853c6c4248bece91c90305424142450101b2616b53d87739c2756da609f75010068b4c3d97b3c3c671705d5732a1defc7d630685325e675de90a8a51f049b4e0951db209db9891bd68f3408f7610426a8515a29684255ce69b89035d1eaf992b546dbde4c499e17ab76ec09660cf4477c3a2d5e801ce4876456934c6892de3e5de0625293dfd7cf7d5f2de5a0a5f6a5bd7aa29f4421be1ece7149647925ddaca5d36b2430a67abdbdbdc1e302bcd4749e258be211d080642414245b50103140200006db82210000000005e522972cb09ee688536483af1e6b3525baf172fdac34073e3749a2947c4065ab5db6645324b1539949122392fba10a314bbce9745629ba1aa24d1e77767cd086b276929b6ac913496dbba0391c197fea9113ba3ccfeee0c7de451f46bb14109054241424501018ae524923cc6af5784d63105de153f66f5d2eb339c038ddd3387fbf7e497670748fdf19605310ac3d07eec034b89b1fc181ff1d192a6bd6b0a708eef358cea81c4b69adcf5cd0bb4a437e42e348725fc1ae789b0c469b44633f4fef8f029ba21a6d5e8014e698abd91aa82583b6bc3d26ecd9f16a134ebb8d3b362307418dea355f120b25a4ab913206cf6d42aaf7f3963b3a67042ca19adc12533ca225efdb4289bbcc2080642414245b50103670100006eb82210000000006ca7916002dfc6193ece6f20d6d599c61e6c8abba87d8f86a5c6edee8f2a8d03252c3662b845a7651470b6a1c1d5f61e27c0700705679b3e6d55c2cf4481c10799ef9d0fe2cc707f79d8b9141d9eda62311464d73741090343283670f101b00a05424142450101fe9ded7189cdd38c67eee152bd9479f7dc3d4cd6df21c94b281667c951f98e539fb05713376ffdfadd685c81bf8d958f49022c6b9c5b56f1ea6e0bb69e464788b182e60cc14ae2d5e88fd51785afbd34776d463669c2bba6fcc0a273163d8df6aad5e801d186f7be5472b519bf4d2c6d41199f3ef5603cc97fc11c1a632a08db94c3cc5fe69d56fb3dd143ed5773154ca428d7ddc4814035681652b71a7730c7736c4220080642414245b50103800300006fb8221000000000b625620f6390f76b7b3b16b784f5de0079991de6f212dbc22cb57d6a6a7f21071eb4d2c61aeba84fa34991db74bf3c6a4037fd52bd406694d924e5264207850fc213c4960a49d238b3f46973821e99dc2f316eecd17320979e0073bda07bfe0b0542414245010108e1cdc22be6a0ce56af3e215d45fa4fc83d89ba288f7717b7fc6aa953c21a5cf66187192141e5933f4a243426f12aa6506730c07255940a30c46e4096d6e98116f7e735595e34ab9e84976709dee2396b085c389fbc8749a13f0a18b738463eaed5e801fa3ae67fa4e3fd21280d3961794089ca1baf64e319629983500a71650a2d24f33bf850626f12c3d02611bb5e90d859b26245d58a6cc4be884becb313182a85b5080642414245b501017000000070b82210000000000c0e2f685d1b3149d37e600e25d39cfe4d7a40ec704eef5a2c75cfad82134707c7d6321e008ec13a93d870f3c60e1c2d8d2ae20b55112e015c2d21e8cd7f130af537e3b3a98681bb2d946d599e9a1f6651c9281e5276fa73d2d1f961a2b6980305424142450101709e4ca49ee6e56dd7c3ccccb1b71522a8bd220ff05c3225a987d572696f1361996572604999e5d770de2ca71494b42f354e02f971115e9ad8f4babf5d19e5888fc14606984ce5d1bf202a73df003518804fa0906c5583a19e4b7b705084fd28b2d5e801262df84a037bc04aaf16aed3d828d2bf16a2bf25d4e7167a020a23faf12e2ac493326f687c31af3f0336d2ad57c3b9c52fda6aa47ede9e4a25459e60d7f448b1080642414245b501030d02000071b8221000000000709ce9e5be2bc1eb702099642203d3e34ea01079456d9a9723761f4b4482330ad5c38ea4592dacae7bc01dc0c53424b38c92a74539343da10f138988d2a61d0985897f1017490df5fd4a8d2fac1d121733f0df80e6d76d845d465f7b98729307054241424501019edd559bd0bebca4810035402b11ac943d996b897509f91c81ea9bb78268f71881d4b8cebd4a8d2a02e62cb08aa179bb740cb5b8b5423410e305a14302fa8b8b130874caa844f1a3c956fee3fc87c1fb205bd406d8cc3860fef7ad214ff01701b6d5e8017119bee1a04c9353dd936fd332e71d528c1edddaaa12b2f76572364781d575fb5efbbfd1de09722b7b2a7e10b75af41b82b05ca5c01bb2a6d7813ca3377908d3080642414245b501034402000072b822100000000036b6433a6e6439416748d0663a68b70b3322347651112ad51b68ee0026efdc2145c914aded9d1b89cf0d4161649eb4835cda254a8f86d6b54742695663cd900018867a16ea5cf2417ffd222098b61678b27967199c9e60667d2ed65a4fe01709054241424501018c1aebcecd045112c1372eb218786af3fced7efd7ab0e43cbc29a54a390edc1a2828781a6b6cf4424baef779e28f5763b2b2716b3dca8b03daec886909a9f68c94fd9c10b8f125a26ba956788501dacc16698b190518cd2e56856f569d4077f4bad5e8011148b625d03b55a82c3f031deaf9e95ac84ceae5f8d0951593895c67ae8fdd67b2e0540b32af302a8edc9d3727f0eb8665910d4ea7654520db3f8b97217e86b7080642414245b50103b002000073b822100000000080af040ca366159f091fb51252d7fbcd0ff5c6e55c1568cbc0ceb646a086ce0e314ad23fc3ff745857381031cdfc48dab309689dbe168443858a17bd319cc507c224b31807f091d79442e5047d0e53726cfac1e86de338db87bb33459c13e208054241424501011a632228bdf15ef4afaab23b102fbba28d700d2c6f73e5129eac76dfffe5ba71cb2b9808b0afdaae4dc65025dc32b0bb641757e8f340b7e4637d7829fab42983850c658bfd6a60a5d67b074320c9f0e178444d330212ed52375ef2a7b0f2c57bbed5e801fa383392aa4eca9388e9209de1187b4a397dada3e521c93c958c4ff6932c1c0f134982810922b3b692db8ef57027f5054ef3f46ac5104ed9a734b6f61833d1ca080642414245b50103e701000074b822100000000040a6201261fcf1a2dc11b105742d2ddeb3af57e8ca8aab57fda34dd251a847207e7321bc08d1a0c011c414bf1540e749965dd890ba692b01bd64fefe267fba0f8a86c2c4ae4c6fab45fcb4dddbae94bacc6eb488c439c8a71a892106be8cf50405424142450101b288a8d7fecc55ec72d39b802b04cf26b539f480fefc3e252ad0ba4c0d8a5c0d2ee2a66a7888320ef26e1a901377c812872b95aaa254b2466dc047df3919a382417e06a56f27514bdc420f4cb09b54898f79f7b5644b876a147510ee33b6d576c2d5e801ea0c960beecca5e9b4d2ecd35f0bd9028151821e25e770061b6e44adec751972e1d6f6f10178d02d99eef258b6076ea0a2e6f0554a1201f96fda9634e64ac5f2080642414245b501012303000075b8221000000000b448026148ccc8812d4878ab66e21ac1360c017b1b1851082e203ef02bb5cd6a88e05b279cda5463d8e6adb27dbfb90bd205335e9769a562af7a7058b1d35b0d6f6ed25b31842bf9bcaf3a78476d1d90b68eebc488f3af3dcdf9a4a0f9c11501054241424501016ea12a7fa99a6e278c9939cf7625c4eb11f474969bccade726fc11f83e781f3a4e7a79efc0e9ae4e0178cae4b7323cdae1fa9dc27854aa02aec227126063da807eb5add50c1a7a0c5117e5ea23d187d9ac2b766411e81101e69b2b232bd73263c6d5e80177adb0bb1d8bcdd84ace66304244e41c72b95d20ba349f92706078c43dc37546624e1c832bcb1891240281d3e0aa067dea7c38674b563676b40a0447d59234e0080642414245b50103db02000076b822100000000006d1c698e76fd9f2b8b82fcb924bb41fb62a4bbc8d87d418137e6e3932377923376c7f5169021d90170df4d3c6767959f524f228b7868b6b25473517003d650a244db7ce9961c11825424baabf2d1ff99b4a84c99bc4aa1c8d526ce1bd5fec0905424142450101b4ab87c0fa1453e0d971f3208ed303c5c78b7d965ec53ac87f7141e328e83278ae45fc87d87552705de8966db8a371a7970c8cd1a71ff5e9d7396010875aa0895ca5a494e858f24e2bb2106d8fadaa8957c95eea4a68057d66135b030d15a25bcad5e801094c1a53d0354db9234adccf9134d5451fd16deb16a36bc92753442b044fdb8137e1808052b397b80d249935e7620cd7e075182d33820b65daba87b84f7f173c080642414245b501034d02000077b82210000000006872e394e5d405b627c71016f752dc35352a9276d5d24adef049b55114e2062c23cc8ffbc7283b3cd6152fa1239365e05034d50e64c5e3556094e2b039f0f3072971857195656def37a53deab5a0d5ca9100ffa76dea7af81ffe2dd6e4c5ac0105424142450101606c85c13979f6d7aa5983a08c038c94ace41813dc5cbab823e1e42ec261cf3c8e4ae3e2bfa2d102f32dff0136bcd103c908c9f1cf0787d9b0f9e2181bcdc382904c2062d00e7213a58090c60a795b64222de5de596f4e550d6483c5ae71d9acced5e80188fe6d252d4ea4a45e172fa262c628addc71e63b6303fa4b6fc25e916106006cb2128f5f94e50d53007b0c2bec6f9f3f2b7ab05ecdd9bfcf4ad7c5815b99dbd6080642414245b50103f101000078b8221000000000822c5d1e03401b79101bb712ce6c57d27b6fa489e8495c2e21f1f8e961f02559cafc374a01a49cd9364cc58476b1d47f7f3887934efaf5801e6a452726623509ca6ddb133d25aec531020a128c5650b0fd1700527cae7dcfb7616fc149f3a30d05424142450101b62d2612c4375f085e78049e106ef7f9a351dd8933dd9f9a39f6c6a65b5ed356e7a3197c09d58c1a33baa88f41c1fa7be289d4a5f4184899f9b6c1ea23f459866c0ed73a3002cc8264345e42782acfe5f5cb9236789ee29da9cb65129c634e82d2d5e801bf4bdf2d0e492534d8bae0f6e7bb793158d8143a6e0af6269d2db41e93f85756a7f8f173e3f060e993c425f142fd81d1d688583e4face63bb5994127fa63292d080642414245b501012900000079b82210000000001c5c6a586dfe77ed880ce3246ead6d7c8db2e41d218853a5f2bd323948df0d28a210b87d8f75f1a631356fea5757e80e4cc09c109f5a4d0e61340fbfbf514e0a2bd7fe66af32a0ecc7a4e587dcc82df12ecfac5ec9e083c8969c4e8f35ed7f0905424142450101e4bebffbebcc6aa219b50cdaea78bdfbc010425d22e76e6f3faa0b5634b1ec4adec0f6ffdd3aaf0e7ea919e23ddf05719a793fce30674f247dad81e7efc7ff8a5672eda6d06fa2f50e902048da1594c2147452c7e7f5d59b8f115720888429c3d6d5e801d13d8162ac66146618114595ee3869d3b1f7c8b3d0b39272cac1d0ed9078aaba83895b3ee4783a7e0c1c35b17d2ede48114dd5d7bf59988598cfb599d5c3b948080642414245b501035d0000007ab8221000000000ce818a4f36f81a59c84cefde963c01ec65d672e3b81d552fdeaa266dae30e814757c2bda7aa25b10af52771943d2d3e5b897d6d6d8bf2a1f0c115b96a169ee0f3c92512daa265526e30d08eedfd589e5c01905b4cf1d06dd1bdad1043f99f0000542414245010174255803701ba0bac9e4fff06b9e6407edaeaab5f9c0fcd05777eb389fd7870199902743a693e2eac3038716e5248a2060ba61f6d796933aabd5b0c0fec47f8049db4dc79053fd671f3d97b1efe642565a54dd750ef71588bc9c8245f0db121cdad5e801a494bba41c9dbf0a47c44a647d4479d96a1ba7654aa99b42ae6f483dbb1eabeddbcb9756162424d6c1341e4fd228ae138b04bf3be6fb96bd2aab2b9084bcd717080642414245b50101770000007bb82210000000007c45ec918de94a56a0cadec49edcefd113238a9fe481d3e58a5717274e326f69183a529cd97d6477dd33ba0fedf8b824d6e7cd057f34249e7521794d808fe70f9af6251c25b14339127b90240bd06aa3f4c85bef6cfe8c481859865696b14c0d05424142450101f8d29601640ac7d10984d52292b553205ea5c76f0d67ff293786091f88740307b76e3de6e58156a6c99f281b2f4724b4f142f50cc8874dd9d64a382db6a7878c0091035f31f4705dfa6f519f94ce00bf464c155bdff779d80f9a0c9da6c47c16ded5e8013c74ec3d7cc5a6f94dfdd543b09d40c74773c275101a72c1ce5eae3b7c82027ecef631164535267884b4122ef4517be2eedafa3b6fee4c05ac90875b9a1cf213080642414245b501037b0200007cb82210000000000a56ab4075808b5a9fcd8fc4ab352e78282e66676b9b2810490f6fd3b487ad1c6615af2164a4d1ba0cec892cdbcb1b43339f9403a08f1e41d057fdb6d003900be29acdf1d3871748a59231c2b3a60c54f0162c6f05b7bcabfb0f9ba103ef080305424142450101b2f2d539d466812086b74c5314d70d2f7dd0ee23c7a47020199a2ea30a2fd632963612370e4e356a39e9d6e1272c0dffcaf5e5a380ab7bcadcd7804dc0544d87258948c11cea54e414e65fbd6289754677d4c892934405425f80ea314cfd1858e2d5e801d3149ee90e81c253201531190c954e8a0d8ba8993cb7c3c59406236618ff19d250c4524589e22e4425b98034d7d919ebc3367e9506f3e501189b74d911577034080642414245b50103780300007db822100000000022076ecccd0298d83f25380d2194aef095003e1f15109e906e16bf3bc6dec6673ed0949f592ba4c343d1d9acbddba09e4d3ccec824f89f4b98f23cf9df7176001846bd37a4cf7b3dca498f4777a83ab39f748dd148e6f928f23f0105d2f2680e05424142450101e67b195e32f0138bd8736fd43dd74b66b8d5b104a009b59deff1e93fab73dc669514243562402aaa2737027a6d6966c987739026db6955b8116d9018df21be8aed4b3b520d75d6081bf55bd2ab8ad75dde3e1b5ad0bee79f392f244fd662e2e8e6d5e801d1cbfd60a82be2f709964faf6d0b40de11ffb7c21b4bb64d3d4a85ca43c0a5ddcc535c045fd53af9ee5359924018dbd65f5e72b3d6d4745af50ea69707256037080642414245b50103eb0000007eb822100000000014c6dd844a0f8a3866ea9c69e35d8ead54e4d604e803c59c538f2870dac6dc1687915dac3ff654cf850801da74528151812ed149d256c6956d1ac723d202c108f9a450e9bd4633519bcf4fc18bcc43499a8c5604b1f32b7a9b0fe0ccf9c14e01054241424501012282d388c82f36421f8b6380322ed061db2892fb9589527c27a5cdc1ea2f87123eeab42c653749c367ead771bb980d68ffd09e763092e043a92a3e88f3fa2b874951cb58de47d545a36bad3d80b383f3eb90fb330e368b77a1815c13144d0596ead5e80166eb0063c11b439454ff41f0f4302b56a94e0e1a3840ec506c42c852c9c387e08a535c40ce27cc0977b4b3bbc800839bb62d9fbc95a83d8acaeec02c59b5693c080642414245b50101210100007fb8221000000000be2563731afa3dfe14024cff6a19fe4e4cdb22efbc675f677d316d397b53e54363e6cd1243c7101374071950e2daf33a1b9a01570f80fd5fd267259a8881300bedc50c264711e0c124b17359f05397ea5dd57c0c7db7aa46244f2f55b9fef609054241424501010a5e63277a47fed9cdd6aecd1e71c81053cac09fb5824ea4260ce7ce665a07750e449dee228ad2c500c3f4f16cf11bcdfd910784a3f5629176105ea068c0a9893107aa6f6ea21b92a8238fff89289141cf10327e51c016041bc053ca51971900eed5e801a67a6349e23cc9fb6ab98fb3e1a1d17b87accf53eab018c87adbae1b0a6e7feb0179b7279b7d9b0b09575ce9bef4f94c6ab1752b3ae6dc2cbca391a4e1b18a30080642414245b501010801000080b8221000000000b40cef06af61b5734a6e70f6cf1de694e6fd21bc38395a8de493456306c1eb66afe8a5d758f829f70f52002ed9c9fa464aaa0112701902192f9fc11c1a3900090bfb709fb8d1ace7600a35d3102abc4a5e640b5ba12b28132580566be718970205424142450101fe39800d10173f575d20b97260abdf6d8e53b1859d664a4ae04b1c2d6b24435f8774092cfe0413df1f0886102c8fcfd6ef591b3b1ff135bc367ff73c085f9d8d2769e537c25017ca81c644aca29f370dd2af727cabcc58d690b0fe8c0950ed3cf2d5e8010b42ebc2e21b27fb74c4c152833496a9febdee66375abe7cf3163617164dd7830b9f1df6f4ac8396a1755d4026e7501c00bf5876b825598c834c21df49e3c2a4080642414245b50103a700000081b82210000000005eac0772b254e0748fed8dfddaf2c67f4b21ec814e5705af571ccae717abd03d1a3f13a374d567c7723547b8732d2ac050cc43270fd84783b57f70bda66d270a565bc1a55309779ee013946ceb43ea5db7c45d68aeaada9dd877ae94f91ece0e05424142450101625754a7d2fc7016bb887d8a7b7d00598828bea01a421dafe36178fbb2bcfe503e57916cd843690f81c442154ece668039949da77c39d8044f57ea9bcb952e89a209f50183afa06ec44efb9a4045894d12f392ed713967e0001641117f91bec8f6d5e8017f55ed104cce4c999f190baa07700c19e14c2af171a86af8f1dd2d0621c618b1c3befa4005b256771ad1cca60b4f2c25b4474dcbb7f4cd483654ce56dcb5667e080642414245b501038e00000082b82210000000000e3d2ecd0706c571355c363b4e0589e509f60eb0eb2a60fbab921c4632d40d3b6e7c791656fa55adb032b9c800be29faef7d1e11e9eb87b7fcd94c0bde58fa04d908989c689916d0daf7ae83944ef8616115a40abb6587791e60bbe378b1de0305424142450101c89a26ad8b4888ba4fefed05ff113ba3f15106ec91ef0373bc5a0c6afd5f6439ffd330a92ada00304044bafa2a37be794d580489c85c39c472b72a69f1636c8017897fea64fee3297c42c0dd593e98bd0792ff1d3621d7a27d4cae5b7c06dda8fad5e80103756b62340754b456b3b93eddfa84695a375b8cb21aacade5442e9ba96470e3145f1422699c0ff1414ecdc3912937d747d4075125623f91594804dbf449daa5080642414245b50103eb02000083b82210000000009ef48ef6134b678a0b5d65bdea549f9060f5a4d685500e737c2cddea4dffc96883eecdb94c93e86a327ce363b055c12f6c5aa6b2ce2128ad81d5271479894d0ec9e8caa77b61e48ad12a783ea834da9c38f9ba958e7920b4c642ac16b5b7120105424142450101ceb17d29e7ff539cffa095d2b4546b35253a2edb035aa4f90af830e8c5a5ba10cb2d737136c1ad487bc42fc117b3eaa20967949d182d64b0403893cff0014983cd1780871ad16b9367d2728bd9d6d5a09b1d09dd8a5b2fc51ccc883d3d1e72d9fed5e80128e9b0da00d305eecc1f031d52a0f7d7f427864feeebc9174a1949d1bbc6a6f0c53f40f1100886ba0d295fe8a9c6caa770d281bdb8dd84b4187ad172b78633e7080642414245b501030a02000084b8221000000000fc8030b006f1626b87c0cb8c281ef946e2443d4e7d808a6a72a0538f1506c92844195b364bdedf4a51e8f6b6538da58da5bfb07948396c112f8564d653357a08bd55f83f3caaca85168e67b454ca1d9527934586c873b1321e2ea3cc33e7670505424142450101fabf53a3a654cbd84bfc67c4c0fc9f01806c7e1d4230bf9c8995386208a1f730be24d75dd86ccaef29598d9c5ea141c0b6aeb7e1e68f6e47e95cf042b137e88c139c5bd7cf70666d452571b163482587c53c46be93b611562fba7e44b2c2ba8e02d6e801e7c0ae3bb83d8a9f8965315f27a8450d704b8c517ed8953aa2c13f16dae108d275f7cc6a8c7dac0efe8b830add4a6d5189dd238157740e3dd020736650802444080642414245b501014403000085b8221000000000bce49eeacb2d64caeeb14a9aa43dfd95dd7fbe51989f6cde5bd76a4f2058073d57f0ba5cc42be0ad466d6c1cca59cce6f4bc57a8e1a513aa0e9d391a1d79700438083d3810979c3b8337d36750b85ab96709569ddd580b4db7421bbd16ce280005424142450101ecc09c7071af92975f8803e29498819333e772d4699e959361925802dea8a41c4d40a3cc17acb8e3402723dc4f14ba05d0b351690bacd02cd507f232c44436864efbf09a577e0e2edf7ba4713cb2bca1fec3cd6f7ec837b347904eb3dc5c56f606d6e80153ac25761c5da761046ef66a620cb31b767244f1c51861e3945cc296827bd32b24688db5224fe77c73054d577f79b4bc3ae6e21fb8c41e3d2548b8cc059b4484080642414245b501030800000086b82210000000003057bbffd7638e1abc26d1adc25d14076e2133e7a12eef34f71b743c90a17d54dd81f4de9231ba4a23996bdb07f8f84f2a52437508abf75a947478e9c70afe04d15a3f214b14b7d5e63977158b636c684baf95db0e545395fef2819ed6539c080542414245010132ccf43ecb12461d41b19a090c47fed21334561257ac3489188f05961c449323c3d35dcf6369dfb7cbfc044c164ff544bde814ea5d99a80bf650d2ea433fe38c1ff2f4fb54a6c7996b97963f7ed852c48c7c03066aee00dbbaa74395b6b3bab20ad6e801e0fd2b6486ae9940e5e580c3b3fa8a4ae596f68940d289ffff68dbe24ad305f00a30ba08d7bc4ab4a63ee96fb1e6a2775b9b0c07ae001817c986263dd2d165f4080642414245b501014f02000087b822100000000004d2bef4781529c850accb5791b2dbe59fca4aaf888b0fdeca4257077abf5b41fc97ea19922d1fb8cb05ad5e606ed148d8a0a1f71cbd69a08ee9e336a70d5a07003a561eab07d6ee1891324ee89e01fa68a8ec63439684b34cf16daf20eba5010542414245010140bbb6e9fa9c5c50a17455c5cc5db1642407ae129f3e24aaf8458c35386c6d22994b811f84fb009cc92daad01d6b90da5c3fb07340bc4545f8e17185736d538348af7b8017bfb4d3f19079ccb5208a2906b9ad460e57026c3b45159b5410893c0ed6e801252aac0dbd7fd045c37f2b450660b2665958196259ee7ab2dcb0ecd44fbfc4532710b322aad4b202254a764c03ab1dbb70a6bd7f427897b7e30c14c9c1063c3e080642414245b501033a01000088b822100000000032138212a3c36029e7f6ac25390a555637319daa1e2a4c13ad7b804079be65288598dbfab1644ab4cf48d8ba53d864876c6d54d7b86be85ac56f4ef1cddf400818f79ad3fe33e2919b39ae2b575faf93989e0945040cc363bf35fd8b9e07cb0b054241424501019e58bd07072c3510df3b2d312d628fa8b2cdae62d3f449504ed70de004834831a69355b88e108992f074bb9d534f629812d93e405d254c85513dc6f35c361d8d19a975552126835b1f2b291145fff42b14c78b397915578d06fcd3344390df2a12d6e801befdfcd44e1e9db6537e3f49f0c3b496a0e2277703a61635d23d5ba454f6c64435b5f3ab2d5b82112500f29bf218804893f0e5914bd10022c161e490e764b899080642414245b501031e01000089b8221000000000b2ec8c5bbf727c7679e2ecf1ce6cd2aa94b1cce3f792eb1b3d5894ee0ec9920b670465f4d3b18c8076accf5855aef65c1663a4250df6b7bfb9fcd643a7558d0170cdac3a30cebb074574e249df4ccdbbb52c83ef1f36ee8e501daa58034c7c0e05424142450101e69c7548814448f05413db374bccb04862c795508d735bbc161d13bc1c941751376262e002d1b8ce5c45377ced00a3635d3118003884418cc6f9e5d42cb26f860ce3ac7fbe6b43fd6a18a266e1ca21855e7aeb25bf4f0b4e7c0d1e2cad37bff516d6e80124066dbb61d4cf181df98f9dd917d0f6069d9ddac78e86fbae41a64201aec99d89a975b8f5cf18b345cd07c5ebf6a3386790e7f2545f0b24933f0dc458905b08080642414245b50103060000008ab8221000000000a8922897e4dc3f67df73d5bd6b7e643384611965639da66ab0b7dc492cb20652738144c9d04b3f21c0287be0aeb19ad53762f0e2600371ed0f4bbe382b8b5f0569f1e2f49c40c9b657fe30fd0ef66c76e9e116fbb5a10988d81e298575bde4040542414245010156e6c3b779ec564949b90df860da759c85d729c0d8d3de9090aded0aa5e6775aa656370cde4c70dc23743462ca516c93b94ff358bd4b91e968bb0dbd90064e8a49c372872b3fd8d5b75f2a3a9fc4bfcc0dda926b9822711eeefd1a04577608081ad6e80146b1718b1553b5b6bd5a99e694a0b537a55f334b12c55cf57c8abd088383bc913879fdbd4a9da9c07a37a0f6d71a4218ab9035f05195e6fc98daaa6e8d55aba2080642414245b50103c50000008bb822100000000076c6fbe55ed30df60aa4d33461f959f923b8a3585d54754182b3426dfcf8495e7686c5ac8795436802493ff5cbccbd14d22c042cd2653fdb01d3cfe267ebad0a32d4ddfe85d8c500f412feded3a288a3ffbc8cb8cfbda35cd99c62f892f85c0b05424142450101e8cfd9278810317b0cf9f75537e2feda19a9144106f73fee9d67cf6ff6f6c52c18c33897cdf38997621bb9b58a155740f36c01f7b55581cb3aed58b10107a48eb77f5bc551d7a1f1903a3f916b798aa94e7b237d003854050c64c361c9b37e0f1ed6e80167131597d7ac0afe422860cde2c82c400430c275bf51ef75249fdf9c83c3e55053cb474570a889806d3e58d530ec10083d8989d3f4442de55f8e4c435b156b53080642414245b50101380000008cb8221000000000f25514d9daa912e0f31385ebe46c640ba4a395b19c53c787a4cd252daca98e1790fdec977f55ad8c0727ff94635f710fe1a6dcac11d23517169cfdfabde79c020e12a7f2ecfb6a5e2f051cd91ae04069dd68edfc7a971e79b598b61610ce550f054241424501012cacc2aa013dcddd86eddc68df6efcc021c79a3d5718cd394c384b66cd61592a4d6782361485f49074b3010e2b620132f09d5fa86cc98b481b3e7ff153fbf18fe1421a43b9059c305b3d2cad8341ab26be7ebd7956fdc77ca3386ce0a8b6667f22d6e801ef0c795b056445c02921443732b8e65cbf432cc9533d66b70e2b6dc9c6b73e66913cff489ae12e23d1bb39981941e1d3205a29dd00ec3e820bd9d9e964034c12080642414245b50103910000008db82210000000007653bcf9c31e0d6864bc70c299648b53cd87e41f0a3b16b40593696c44a77a38d29f74b95d404a69baf6cec48afd13d00c95432e803ebae633b36772aa71be02c682330a6ee1a4c26c729bef82f1908e0a70912616e6de57c006b757816428050542414245010174d6e47da9b957306962e0810e142a09ec9b95b89110a7eed7482d06d0a23b404d7ca08b9ccbf6a40a0814d3621a0da76e5345dfb51d8d50976c12996e98018231ee0ab37aeed54c0b2fae3bc07b5843579d88845844219afafd8ef3f326542426d6e801d92c91ce490e03d6bf5ba405081a4c1551be40ac3779228749700440ca618477dfef3a626772e59e9d6f17051eb08a29ddb88e20ceaceb1d42778063295dd5f6080642414245b50103a90100008eb822100000000060d4a8b65eaaa78613c9c14a0cccf54c1a7fcba682a15b2da8492edd8ca36378a3efa6303de028ed2815fb5358c6c6512490a912aa9c5eec40c5eee7c06d1301144dd9967874cf4f623a9c8337658a306bf98aa8d61e4a040426c1fbe762010705424142450101a483ca649b6680ecd849a75558fe51c2fafd8c45c7d1b85d885fe32623ba181d5e50569934a0a30d7db65c25e31fb2fdfa75a3b3b63871bbf8a073794728658fa8f28a553cf9268e411e11ba20a107d7280d20f742a163218bd948d40e3114232ad6e801ca06f31b564efcdffb3dc9f45c5f230e415286e6504a73a838dd3edcb9eaabc76955deea302525efa6354c89b47318f40bc64a1823c2210a44d0efb831aaf2f9080642414245b501032d0200008fb82210000000008e788bbe4989667ed9b3bdab827d2528dcb839186c687dfab522b0d6b28591426a862c043615bf67cf57f38fe394ffad61b0c18d878da9a1ae2186b3a1a2cd0b8339d871603c45ed43398c67d30e11aa5850ae3e0a9f30080b646ab4ce01e408054241424501018ec23639b3987ef28b78f1ec797cf6e173b9e6c83d5ad5a4991096a9d34e346bc5c2f237fa0bf4e98940133edbf82bc766de0c4ec6f04df4e7756a9376b17b800d8acc0d1e10e24e2a0fcafecd0d41801523ec3bf17bb9b0c5c11bb7bc79019f2ed6e8013cac4c778f6ed4a81c1ba4741a51850848f1df8a01830cc0305f719c9c543332f1056de412faad0726a9c8ffb7df8ddc149c7b0f0ceb2466e15b60267a7b40a4080642414245b501012202000090b8221000000000a2887f71f9da3ec7112391ed0c2a9caf17b4eb568f25b071223ac2ab4d24352e3aaba32d44be4f954184dbe4ae699153ca366c02af5cc575a5c6a5fad5ca8402e6ad8c1c85083f0bba2795e81f6796372e043258d3f87a47ff4f2c21ea4d8e0705424142450101386638cec121db1394bc37fdbd2d465216628d8187e97ee0247fe30f53f4ae09b34922e999392d29b437619b4ec67a426b32d75fcdbb2802587947660e71d18d100bb858a243350b5fb9307c7f36d45ff3fd9ba8a69e5940995357e8ec5ecc0b32d6e8012c880b5137db4c9ed90bfd5d5534c87c00d1e7db5750a970db5092a4b02cd5d178e634f5a963dcee06d67206f9a3ac866721df0d3b9655246f8d27e76e152eb4080642414245b50101f001000091b8221000000000aa7c93414d9d45a8908f72dd9f53a9cea0c315c6707283bdb3771c3d9ad2e3655f145e697c40fa5f632aa6fb53f3a2b92e60cea4d561739341c9301584ac1501fe453f160faac6675062a2e25ddf169c056f08c974c85672c2641d832e7e640f05424142450101764b7bcbcfc06f76d385294cd1c7e3185dc6f86d31368e56beed14b109750c04c5cbad90e485ad2fc9f108def48f21de42373bed9e32d46fda6d79c3dd35e5852c67e495c26021e7f10a905665f034a7be892ecdcd23b1ccb8132344e6bf0d9336d6e801c352820772a72797077c96f9223602d32f54c47b51eefd3d16349b4a4f12e54569f93a6ec86091d356c48292f6554fdaa5114de52234682849ca4600fd87e969080642414245b501036f03000092b82210000000002618d44ab9a54e3074c01d31fd05bf14cef4846d5311ede6b117f7a5707c765881359ac22643d17ad3f0fb7cf71a5d7cf84e5fc9c3206f1284f0444265284805a72d9f64ba059cc24c2b35591bb6f716d33a57f6b3942138f7090f051c658a0a05424142450101fe4a94840f3bf3ba83e438a3c96acb69f6ed84edb20dd8bb426661602343c216250c23632b12a8b36a1785a7a7ef739629fbd3b7db265abe56470befb7b5148f7278b38dcb4608d5f5fd2a6f10635765cc943db91e8235b9d0da811ccdb3b4183ad6e801e1bf8b06179c1a61c48f882c59709f0aaa75b46fa350d4d13f237115cfe9dfc8ca666dc8dd74f2356e06692848a724a8f6e1d55c0b636c850c9171e5d38ba20c080642414245b50103b800000093b822100000000074b1bf9c4e27c139d13ccf4b2f82048d46a5ec71e9899c11d787cb1477b32751dffd1353bbd783bd31444b1187bb676194d984f93200c5bc1342201e3b340503af0ae40032c7b671b6a672182efd724922b250cfb0f72e15cbc549c29f1948000542414245010150e0c3889ce466e59e95e349a1f862904f9dcc4f2ee0ef372511f976607f1b7f403b5b864f6cff32e30b8ecd47eb379d7cd785b81e4c3184e5a1fdd11374018afaef3c301c597567fe4f0f2d668f0bf65730605e5e14b7df7cbcf52f5c31100b3ed6e8016c6527a2732d92a4d20b7f1a7d961364593a3dbd6c22990b500373de635b7fb047d3be3b3b7e30b8c3a76e81750e6bd813b3f5089232437f4ee1e9eb268fa5c8080642414245b50103b702000094b8221000000000ac9a6d300bee116d7918fadceb2eafd68483334e48b37b24df529c937b284132e6cb9a774777ec391f68450c4367befa2eb78fc43506127a4f827bda51cd2505136f0c1d002b4bb093aea3d3f16f030fff38fa0ad24444bb1d73eef5f3ef960e05424142450101cab2fe68f4fad4bd6170c4eb9fe5e774954508fb231df3042e8dd3ff2fcdff31506b6a039030c99c32566e051347a613e269e41d69da7a0d5d064810894f9685762f996190099a6e6ce09204dee5f2f89d069d4e06b579e20a0095ba878026a842d6e80155906bcabee6c8f67b887360fde23a619582d1ffee5a5e0e55fb8a6e7ed77036339a59541b600b0a679221a5d7b3a035f4be1274241f879dc09e2133f6bea9ca080642414245b501031800000095b8221000000000bceec7f87bef8802b78dc20b387bc7bdc1f3a300402ae2263ee047bb0f86d95fbc3f845f0109e50e21adce42050747327417d8ec17b4671f44fcef5b7752200488352c95b4ddbc9d3ebac820857c2a60d6343898d65f885ad7000c3c8e15190d05424142450101724ce580cf1a4fee946663f03ad6c9e93a9d9ac0aec89c4c3b79582a804f0129b6e331a8607d706068ac8851611fdd03fc6647c1dba5ae20246efc63938aeb855271a81a9f9085d71b58dc15f5cd30cfbd25d288b48b59fdaf2d46720d0c3e9c46d6e8014367760f34c9adf6ec034e7a27e76dd214da3fe1857d96e4af7d3f37484567a785d964162cf76075f19d3b87eaf71969003d8851f77e99f29188210bd401d5b3080642414245b50101ae00000096b8221000000000268389ef038cf9f91d08c0b2e480efba0d24cfa5d2d410c5ecd9e4b41594c152a7e3a71ab9e7470066a5b5a14f836804a519bad522650f7bea3a21127d575f079e33f6060d715f2d765ed18e5edfafda5376d2f792b87e1c89f246a4e9efd909054241424501019a60b7421f9dc577fdd63e5c51552093686dc2eb3a975aa062a88f092860aa1dfa70e1e9d8f17aa004bd242894eff1224a7ffeea92638fabf7dc4a051856038f06803d8699bd66b0ce8d77906d6abb337c055bff440a8e80f73a48fc69ac15894ad6e801abe041f70daee5e7fc787ad9fc1f34fe76fc72a06852c671d793555fa66a49abb136f34e311ba35d3f6e6f94c247be0b11162873483bc9ea3f0602e23a479b41080642414245b50101c602000097b82210000000005a0bc32dbd4d1e5e807441681089857f1b7c937136de8fa2b844d2211a528a567cf6e613d5306f77b8fc5306bd3b5bbf585ad2f90213d7b04dddc269cce8e20409dc6d6f77caba824b484728de3bc0db51e277783f577a4105883fe054140e050542414245010106a64690b8c66d24f3c6c75eb3cb77dff882639165371f8387218ecbe233643908a0a6598436e6eab9c4fb18dbfe2392b920538467b7e920c53a752bda30a38e69531114af3b1c20e55a76600f00cd5c126350954bd9f45696ada2ebaaa551964ed6e8018dc44241923822f2a86ea87b091c68a3a35b220af34f8b54473336e3b91c6c38255e698e7513d0893020b5895612b7be764890278f6910620e846cdf549fd977080642414245b50103d902000098b8221000000000807de2bfa7af5c0a032c9831969fa4132fd3621de029214e868f9242758d9326c859d0d8cf74838846af855fe985750cc6642b243a2ed2631818e4304ae3a40fdf55d4cf0eaee2612d76aff3b7fde4069d5b17d7478f947e1c7d2faf853fc80b054241424501014caa6c44bc5f4194b5349b84950d147b38093dfaa621aea5ecd924b5e9e058790fd6fd5dfde2ccecf002d68d3e829bf420fca8dde4bb6fb63b16c5e015517e86bebb3b434aafafeab48be7f04ff6801efc346cb63bc800d6caec830afe021f1652d6e801b77dc6f4d55bc696d17eb19410d26be9e5602642511f918af3b7387469c43fd4d44475021a2f7b9c6c9a8afaf88a7954739663b0641c926ee0f6b7636a3ae316080642414245b50103d201000099b8221000000000fc36a63b5d1e0d21d63519c2391e2999aabda64e8219b8692417e4896a7bb91634f641f923439d268c44383786719567ca2b526ffd1c9a36afd1c49682f85805bb026158c30c2a0fa296145ba4964489529c49e362e20d595b3d51b6e23c2c04054241424501015aa39d2a474599716a0151d9b2bb462a26f1f8d6b52f52ed7b977374dc16103fa2f8450bbb84917b58cb05d02cc9dca1a02c81a030e8042fee7adbc869f39e8be58f2f94c7ce731a79be41e3889b84e5096b11ff44a36653cf57e98407bd994656d6e801de914600c574c3a7e2f27532418b2f6015fbd8e75d4db059168e8e74ddc47b52b0dbe672222d42c2e873a8b315e5f1e33bc4118f4c45204743e5dc7d47efc5c7080642414245b50103620300009ab8221000000000709d383342434ec61673ae88fab978d83c990a55f12f4344a23777b834e8f212b620b739bd7ea39882e42cc34eacfe437bedf38b9ba62bd288d6a0431841a40331040046064acba1479a3fbf0a9848fd5de76f718a5fc1e1602d5229625ef40405424142450101049beae5a2248356ef3c5a2155d7b366e90b9fb1893e33024a60cdbf04c5ed33a52698b67e01d7f0d96f5ff092f9173d3b73997913ceb4eb92ebdea14633c184080e40f6d2c9a473b51f40fafd6fcf5e49ebebf8ec491a238db6adfaf3281e915ad6e8016f074e229331be9a33da7801da0f61bc6c29edbbc8fb0c12d0c4c62d1cea9a08e7fa5fa06b29d9110bbaaf88e0a80202ec7bab5196f8315efa453cef0c4cf2ca080642414245b50103cd0100009bb8221000000000040db29259845c2729811c670d94c200d20ab2ba78f013ba303f38a8086326233ef83f54976eda720349edfac5eb2fadaeaa35e09ca981db5df5d0c532cf86011ce5ad93e41853d1dfca72bbc2605dba453574e9792ae98ef0526bcf88ee430e054241424501014eafedbaf285822a61daf7e4b48c4bb509ebd77c8c6e243d1cbaa2a434e63042b12b11af4885fd5228998375f7f36e45d3c59c930d4e61294ead35ae00206a850356ef97b8aaef9d4e1971f80e96b2d43b8dd06fe1a6760a9b4a335363310a355ed6e801e5840bb8da39bb6cb7e4fda1b254f7bb12b453c3687b0d120ec1f82be380650472c47f7adbad6198495cab14138fe1ed87dd430ea4485c3cfb228d4f781f52d1080642414245b501033d0000009cb82210000000004a216cce47e5d654375a1557e45ea2c2c98cf263fe00dafff70628ddacaf4066dcb9b241c5070bd6213465640d6e399763641cd79b4183d636828ecb35c40c0f06c51e59a6610bbf3bb8206e48edbccf1af1ac41a1e8498f34ce6c4659dca90105424142450101829bb89cbe8ea60fd61ac44e44fedfade9a21ac484b91a45133508d5d4bac908a2f272cb5244a4ca237f9bb2dd204266de947b432e7d3f2c43fd67c382cf3d81e7ec0b9640ce2e80e9b4641e8f2f471e7f9173b7039d489bad4222eff4ad3db562d6e80111daf367ca2891afe2cf7a4e1ba40bce228b960330050dcb4e746e26fa44231df514113596f1804647493047caf07e25ec9ad8e1f13977382cc555a8d0928fee080642414245b50101bd0200009db8221000000000bafd3f064f5a3d75ab6082fbda22c66fa9e5b21f127fadf913d275bf14683e0a3831389519be5190a2fb13a10dbb55b20ae39d7e925d9eb58a14de08edb97b0fe140447fc763facada770f9c3a6d7446ec22815afbacec3e44955585064af40c05424142450101be8e1d3d2cc09e1a9459bb6112675d4272dec43b2fdaacb1fb9d41276fd24c64077edccb79350cdf19ddca6799e1358e9a3782c6a747dfe2098ea6ba0545408a637a23a3c789d90fc9784a8a36c30347df056f73fac07b12332af35bd6c3c23166d6e8010f5482228f123a531194faccfa550718a403172a8365ad8ca0041a2a99cd6cb55985ffe5454acfbf3a258fc9c4fd6a77a48211df973de955aa025f14610fad30080642414245b50103490200009eb8221000000000bec85cfadb8c9b7761e10ecabb9565b6226cd0184ba590659a49a1fe62b72c62f22f85e6982ceb124a5c1e556ae3c04b5ee3af3df527c4a2f1bc5036d2e7410ff65e8380b56ddc92358e18836180eaa81a5ba29fe782b238d1afd53211c86902054241424501013417fbdd966a7bdcba8c9ed32aa1338bbaeacba359b9e386c2078f3920d0437b3390048277f009e2079fad9b6838388de9ec74806ee640325d1c79d83b8c2687f0ba4e0669640ed2874b1b9add2196461c47ee690c441018ed5c2c0e09a7ead36ad6e8018c700756d070810c5446aae9e02d1b9502869035c3249cb94af17fb1d08c1663248788ea0092eb5b446f55d57fd2b3feb1898ed779d398c5270ffefc60582496080642414245b50103370000009fb8221000000000b8557a3b4e76662d5cf387bea43fa5066aea5dbd2e6d621381abd3f34b7a094a574bd302470463a5fd655a295cf9a978f3028d75b20d087405d3d899286a4e058258a8c4715b7dcc40964e935220deeff3497871caee9ced24a8bf259b4e4c090542414245010120b1df79f8080b0c24aaf5584cfe70b6190f13aa3e260c46912e35660d9cf7120ad096bb33159f81e70fcd158f1217b03406eb32a0a29a268f508a3f339fb581e460c87568a9c0615a682aa562fcc397b44d22506f2524537b54b00f234bf83c6ed6e801558b018d1f4ada426928c345df6de6157626172503e978dbeab65a3c25e951668e80506c69e02089c6227c2a78d3dbadd80e44298a1141767687677e5ec48ec7080642414245b5010324010000a0b822100000000060a3fe807764a7b49afccc024c4d099df446d0bea023a57c5ed400b7d3ce900c3843684b05bfc2041e34ec7c3d7883ba1074b588bec7ffac54fa184b467b8703e86fc20a2d6a1074a32c107020909e9f82ab1aa99436b674ec00fbb07491660e05424142450101b4398421a767ae8d338da93ccdd535d518f314ac27bc4a7a8832a013af5f9c71768efeb10797945c2a25ba54691e555e5640d85b674f126df3e1169cfd5d108f2969db5ac3a1592da2a6a3cfb0e2f1ecd19cb737019d04ece29a34603b685edf72d6e801eb88abf21071984f7018dea7e536dbc44afc099c5cf2a71d6d01357539c07d4b289ddfab5b25c34129eb644c75e93c77a8f85f77d46bc3f15e130460d728dfd1080642414245b50101cc020000a1b8221000000000086ed5251f58eb86ccf579932ab4a5bd3b5ae5da38d9a63942a5073d93d16d5c1708e077623c78e85759ae2ce78e3e47be21d96ce45d6c0fba823246e4b1c8080f95ac1b0e46590bf97b4a1df7189d894ce3d4ab944515865def9caf07b98f06054241424501011ee109f2c8f2196effa493be2962ffebfdfa2aaba6240f66106083b2bd062f5b334eb8fe8d61e214160f832c28069332224c6dd83ba0c6e520a4b8e384453681620da65f5de66d5675cf917a401674f94f9fece7cb3a6e601931bee0d4fc744476d6e801d13a0aba18944596982fbff02b4d9ee1f130403e0f183a5944db6e4e8f0b43a5d05c265e8da2a399d89b0ab4e1cea085840c8c19b60b7fd45b302cf22d2ff20a080642414245b50101f4020000a2b82210000000007235bb7207a470bc2e1deba2ced6a7165f8489bb04cbee52a87c6166eb61d653fb6bd953b3aca531ccbf893d7ba7dc5ccda7bf5520e80f01fa7b89c59076ce01a1d27df573e36f921dc303aa06c4d07eb7f9605a38a1c730b5c2782bdf61330805424142450101060f37770edbab99103e6d554906f0b28b940cac6388cd221e689133cbd167608c3ed0db2c4784b3d127b6644242bb62311f1893e41c210c324ecf46d0cf3f8a21969cb5cd1a8c65030ffc15963161c6280498960d04dcf0d147c0b3ca491f567ad6e8016eb492026b6fd05e8dce0cba05cc5ce7a91001014653c4233611652a5af32d57451ec74f3cddee352dbde681b34b0b34b33a6175fa1eedd7a8b7113f4aad7be8080642414245b501032e020000a3b8221000000000ac2b56146cb5d14f7f13f4faa75a921dca56292024a2562bca5614e0d6ae163234af695743b19ca89c78fea01f4f4302762cdf21b173c018cd804587d080850ce03a3236ea438f264e03ae267e3927f9e35d954bc63a4bf309e6d6a5b5d9710d054241424501015081806b1d41654c76dba72c71cb0d7a56b6475add166fd5e654daaf356c2b26d66cc938c88aa2ab2e838ecbadb1490af46270a2b1b98bcbbb9cb36f1982ed84677166ccbfca376a4b04c3e238e11a66536fe264869dc2749f530b3255a012407ed6e8017f4cc66acd431b22c49e759985332202be176c33808f68518e77daecb72871bcfe8d9794fcd6b5d550c1053cdb7bcf2d8f849e9c272de5c3c1790a2386f4113b080642414245b501017e000000a4b82210000000005e3d6d6c1fa603ba715420815b1f4ceff3a73664f6bb5415cf3cebbe603fcd58ed9d8e064976df5eb543e114975aaab60b4117440190d9ed3ffdd033ad40fa037a2ec8ab8c8abd5a9ae973e4dda5fb0b91a3feb063aee30fc6b8f2a61e08b40705424142450101120408c68d58f1394e8f5ee98b896a8254119dcc8bfd365e847388d78e03b86b4760d0c1d2e3ecada8f8685007511d02eb61eead8c34c0e65172a5a7294a9d82b9a30d67bea13135239bd513c1d4a45f9d662d4f82f0c58520d24e92ae0b2a6b82d6e801342f84599c9feb550f6cc17269ecb03880d52081198a2a59455cb05f0c5979d0890612e099041368e0ef3719fa2e7ac2de6cd4057b71d7983f6eea6c8086fb07080642414245b50103cd000000a5b82210000000002e0b459c50e261e15c90b463b5d63ffd2b435d610598af6c723b5c8efaf90d26eb5bb597cb797f4c381cad22b6f2c3cf1d3d2805748e0f8655c97b6623b23d008ad9eadc3daf0211e499e86533750c03af519b3a26b5510b085ffac675d51700054241424501013c020b9334f2d547cafb4e36ab465e488bda662bad529398c80a6b9ef8a3d2676ee2d1577854b5acdb7ff01e6e7dc83af089fafbb0ec87631097a0a4de443a8ccc1f431618d7d285d9fa4d449e68b07d05d0a1366a47e69d52d22e7bb82dd86e86d6e801a1ee81db8633ad03e4c6790c4e297fa6d949ee30179ba22167b5a5a18a579d0a20577304ca53898640747d5dc964bcaa497c9b6382f9319c541c092703e0956f080642414245b5010315030000a6b8221000000000766dd858a52891d22cadd32e0b5974ac07b33488f85e253da4ef1b8c5c0cff7aed94ff856754371b4d46ab24569b366313a8c9e7717a281d6afba2f769071908001d388f276952042662f701b5839b04b46c036fbd11bd9af73c894c044d8d0805424142450101de6b6d77116b68b6368b2c2855ca128541b8a591ca779c6121533aa20c2b6436a970e2f71b954c0a63fd2aa6bb8a7fa27d04bc226a88ba1526ee4f069edf88882a4e5ac51c4500cdace71cb04047f0c31839c654b8b3cb8359321202a01af3e68ad6e80151663a6c7283c5bd1a61898d1f0801c902185a01396d229dd0ef5e9136c343765f2ae7d14ba42b373e2dcf694c8a228de122e3575856c022f01decd2254b6221080642414245b501019e010000a7b8221000000000765718285642a6669a59c3f347fe5c50a53645c02decc80dc714ea43ac0daf4592a4eb628ff5b58964ba0e9d5ce8ebc99df8f31167a69b60576647e69914b20523b1b1ccaabb1de8af0f330782ae05c78e73a1f3f74b0dc03cc3525ae09e350b05424142450101325735c040e3a8adc1105a82dba90a80602f897e78785fc3fab3826fa1da9c36a0096cb59c08419cf6c163465470aa6dc0093c535b98c88f9e0d2a987e9b2585276a2bb86922c996037ecbee8f82a2a4a787e777084665d297a318106b8295018ed6e801c639a07636f5413c4ba401cd4c64c5ae57e2c5cef6e50013c61e8d4921f8e67e9c486a1fd90719b1b30591117dcff925fb0a8163da8237b7fc7730e7503a07db080642414245b5010169000000a8b8221000000000e6a39f56a919eab5f7bcffaae803dff81965fca5ea215783dfa1588fd7d79e66806014de0462aae6570acfc6374e9328e84e832a620b4ef75a64381138f14102c41339fa2328552c91c58d7e49828a0be1759b13a5bf39ae840f71b1a2c6be0405424142450101aa14118c57ffd64e9c6ba8b379d5224b6cb3c0c25c1c0d34b31d76bcfc2e7c045779d3a0b55d24a049cb6096dc8477224f6dceb36c6288e79e0d67231c13778f05839aaf53cf05a8ad99a18775c3bb42ffae1bf3ec45347529a34c35f4d2c15392d6e8016001b11acfd55b620a158b7d8e12b0841051b0f43a1c64c1c8aed695070f1b8dd77c3d2313e5d38c0deb1afc1ac1d0d236b25484b0c4d885ba734480ec3a08d3080642414245b501017a010000a9b822100000000070ff4ba02399e60ed5954ade92615789467ce5fa3a05bf06c9fe94765650e8195da3559bb4ee0a811187d8f053d83df9ca179c4a53e8895bb9aa61a07f98940bc660f080685b9783af47c09c0a87698f242370435260fe263318c29a78a93f0605424142450101c2a4878e52668218c01b4b9da3ff380eea87306aac7ee4c3908b3dd5bcd2b327a4197918b24107f3593798ae6a17522237e5bab245ec7e188e1be1dbb8fba18612259134bfa7c8c657ca537598ac46eac1ce1404d925bba9bc785c90e462a09a96d6e8010eb30df8ceecad0044b561d6c7512fa401470608ba96a7366e3e7a284c0a8abf8236abd96968a9be6b5050a3ef3eede33c3f95d984135634294e279bf862c45f080642414245b50101f7010000aab8221000000000aedbfa8d649f2cf2699d726dce65fb7f04e8e8209d05f277974adedaf1b5e7363f3cc14c0a99365fafec9202f96c28b1282862b603dc9fe14b557e17b5df1004a030739cb36a6f22cf47795c5404e3e9035e8343dca2b6222c8bd8e1d7d53703054241424501016a386d89a24267178d68359e9c54b7a6d3408e4e39b253024e9a77d75d4309535467ba41fb33ced85ed80c588c931c71492aa51fd644c3daa1884bb3ccc392892adcde1396e401b824abf70c4900268ffd74bb9af8703e314f7cc85cf94d89959ad6e8013f2bf299510adec79871261592ac1f67bd62910b908cea737945d5a519029fe49481644fb9b94edf5e1eab956bb974f99ef2cfc8697f00652d684ace9ee9de26080642414245b50103c5000000abb8221000000000aaf978fe2e73f37ba2182cb16f15a9c657527f6201b1dda973406201eb34a05753914b52a4a45334d9b78bc2d666244157efd850af4365fed553e1e271dc9e03a48493c3e45ecdeb1f0f130e4a69545c579e67ff91d1562d25822dda32fe490405424142450101be526e5d28e74b5b308d83bedeb8a27dbc0ed29d51aa7ec7c115803562e48778e031330e7bad9125431cbf979c4ad30ee6faa0d8627c36c82c3e3e423c10d58f2c6e83f69718a1a00493a274ec151cfd724de4e12de70f78ce19b11e947d3d7e9ed6e8016819d9a0bf73b83c1eceeaaedecbfecd07b4f6de9c18109a8ab32d6123b0ff04e0df4a6d43025e5a9e3b7d5c7ca01ec7157a0c66da94dde1fcb26219251683af080642414245b5010335000000acb822100000000082ddf0e0d6ccd69c061ed8322620a5e342c4ce515185b0a91eda68b271860535cfd0648e2f8599b6370a2ad7339a5d590db2733b5b6c3e9d101cfde0384b6c0e21438069e8fa625d026bb0e1644966be98520eeea2e75aa3862d7c8959cb7a050542414245010184b9b8115d171f193084593dbe4008475b2dd993a377d6e5ba1e9c2374da787a692f189425abadce01cda5e68b93c0845a4b11e239649c526c5ae2cb57ad1d88fd1497b7f9c09396997d09eca51c55f3026d33ebce40f40e303575678b6e30bea2d6e8019c1617b0f9c658c3d3228dfa5171e2bc696544f6628cc618e628d5a214f9a65e17b6bcea9b31e35ea150c777390d3b726fce6cbf2f275959d41eca76fe547db8080642414245b5010355000000adb82210000000002263f7ef606785211aab7abb47be81cd3b4dbf549d1e4d50ec9225ec3daf792987c8b2664742fa49e57900aa489079a1afaf164ec55fa5e4ee0663ddecdf090504ed54c93270eb9b173c95297ce10bd82f2c84afb3ec80f98e71916138de8c0c05424142450101286d7af7bc2f6f4d519e4310d24ca4dd2ac6230a8c503822407f37de845f1c6598c2ae12ba6535cb87a0ff34f955e7749fb33b2552de6f3d7647abd96ee6a085568bfabaa3fdca71e3a8eaec8fb819dd5a413fc9521c779fc31b01b07e45c3f3a6d6e80184c0f9742e97764c4c3131c99a1f9989cad9c3da52e99613d0060789dd639fa98516946f7de5feea93b2e7aa6510339c8f186e0c571e87cea3c7b753339e2619080642414245b5010379000000aeb822100000000074652f8151a5c174033bf295204684a94e7b79c20cae74c7eba902fbbdd4e20e55f3f5d3faae41c755987dc486424455eb9b7de5abc618c1bd5d895f6f813f089fd2ff6da7357eae8822788b620961e46aac3ad65c8c5605f3ecbfd297ef7801054241424501017c0f3671e11e1305764c64e55c9aef6e31b313a027f5efd1f10f6b7a5f571b09cf66ca5dd9f2cba4b938e2011ba0534ef15280624e08121ff8e4a5448a09b088234967d8a14a2f46aa211e217a7de43da26acf88f6866175107d187ef4709cb1aad6e8018e110d7de0b851af904bfe819a62287e5dbcf95f0b9a06a8f4b21d673532c558d6912733ac73ab3085c83f7479df6f2fbb536261b79339e6cd8e42e1b8946e79080642414245b50103e7020000afb8221000000000da58affa69129a227610772d2d55ae7e8d8ef7dfe8db562ed2abfc9259245364c3786d7bef6709a62b1d773fd39e3764ce46ce160d91c5e5ce2eb8c0bb722d02eec541886bc0294f55f2ebaef97ead9d523d30210efa10958a0b4beff15bec0205424142450101ea62b2622d9a2b4cf7324d8d68a2f32b6ca984f50bf78b89aa8ead727876166249c2c4caa51b30d749cee72f773a29a1b99116e238c5cceaca642ae77b9cb68e393280e1fe8c9415afd8d34de3c24e694e5f1a4884f6b2c4d6a88a00da3c5c75aed6e80128b4866318074e86831f515ba00864af1297401257f6baf40c9869c1d2c2bfdeca3c2df58f20322d1120818cb4c29dbe1920dc023330a123aece276e94620e2d080642414245b5010382010000b0b8221000000000861cbdf974bfd9c3d3af9d8bd1b9997bd378cffd69add05b34ae7dfff0f26f05ade7573d23ce7b12b5246661e4a33778ed79d077189c6dfd000fee9612f4450a7d1faa5ee482c101677a7f0db34fa2442a00f5bef8ae1047070b803fa9c54f0e05424142450101e4b5cef9a16bde4ab7fa8f8aecdb71fd3bb86576698b2dbcc9382a8e0dd1fe51f47c314ef619e5aa49e7b979623642f1356e1fcd0040db63170712acdaf3288622c158dbdfe12d4625ac63d2b77566c6b9b2e4aee5bc631db1ef16a12016ed4fb2d6e80185fdf7de084ca20afd3b53d4e948dfdfbc291c5478da2592c213a6f8b6569822df7353f04866274e5ce937c7a8e3d51407656f4d301e62b9aea083bd27fb4bb8080642414245b50101e2000000b1b822100000000088e28f186ad4a8225456d752869c75756cc33f8d9ddb6aecf6028e07284a6c74ea2cb4165c184cb29813cf4f9a7f3ce6a6d03463b3e1e4e127b909e782a3030a0fe5dedbe9a5ade897539a25a5609381ec5881cdb44148a2cbc06b416ae8480b05424142450101dc5fdb98775df87a577b22518cc8e959ed8fd5563b3bcbdcc92a038014e68405eff2be3d6bb9ffdaa5d3960c4415bbdddecd32038bf3ce3187bef01d7eddc18301b365a48c782a8686eff80b7d177c8908754b40622cdc20fa3df3972d37c8dbb6d6e8012fbcece61a911e3cff338846834ea121f42fb15339149449306f984be58f04d58ecaa1cd955f485cb8569ef7a3f307e79df9b2354ca1de7b3bd95cc086af5e35080642414245b501030a010000b2b8221000000000844240a96623e9d06b97405499cc3d2cdb1c60e0c309bf78edf5505ddb232f11e70f9e172593be7836304dba02447f09fe61f8cc1dc646bef404b4b776740006f3147c4085e90174506616fac2e0df73c5452749d26efcd7589b3680f9f2e40805424142450101eef088d716bc848b6168e81f3a49257e14dce076bf347e703d3f373c183d81681d3b6c0e3d5d149f46506bb9f1635dfca93d602d150da66e2a719da10942be8076150b6cb594aeb831a5f43ab0868a793a0d5157b5f370959545f3f370eed136bad6e801ed172baed37826b28dba7dd8effb9cdce670c722387b6539c12f2fc20180eba45af2c637078f96df5a2bd8493dabed9ac583f90eb9824e8b2612231e51b485f7080642414245b501039c000000b3b8221000000000a0df9425e38a8a071709bcb5c3d78c993a76700feb304112aa2d8b3b6ea25b6038b4aa698fd34d5098688d4e36e061b7768ab794d74403439af11d6c2c3cfc0adb5ed8581150db41708e331d4e11f8083c7a43346b9a910d6ce83acbb5c4340605424142450101105aefd37f7451e11105590e8ec8115fc8924b904f00e624e5ef6852bdaf1c609bcee41f3f1a172c5cf5a45f0dc787760a604502e30caae69547c86ee8ed908b8a6ef14f2020599d0cb7d1560781f9a736329e3df343a6fb941587896f03fe12bed6e801237e5cef5380255c0835d1179e0baf076259e9903521a80ec53908cff2bb896a9ff8e2a9eabcfdf5ea9a5a52a50dd96d445e6c9154301afc212d3a9083f38b60080642414245b50103db000000b4b82210000000001e2bce7df7de49356fb0c0d35b52597f9351797002f39e374430da917996b04f3a0e0a01484209310a35f8660ccac52f6b6e74577a7bd75009729a11e3df300d3e2a3d67ffd2fcb148dacae1a92789c7965b350ba5cb9deea1e231565d7d880e054241424501016055d32854fcc23ab7fff6696d2f8f9b2fb8316d4abc9035b9360c33f8b9855f92d192f3e260d0ddaf350e00b84ac5f605b068e7b4b7c29c06fb4a4a3e9de28cd777025b915b80ea1461d21b491e828a947423bc53ae66d975e9aa9936db9b77c2d6e801147d1c0aba901250f3ad44f1a956d3296599467504439cc43afb275b5c7f054eb8a095996e9a2785705da792a2eff7e923bf026df1ee61ee31f94a174f6188c8080642414245b50101a5010000b5b8221000000000440991015ccc0cd87475cb9e036d2d48e208b3910bb68d52b010754f66465860de13f4efe6b4e04a07c3939238dad729640011b2c2f619fdb04c3c0e7250360ede06b0c4fc3c2907c25c3da2ee2c05af082996ac33bd330988a9504b8aa037030542414245010138aa3fa0c0befcd5fbf4b31c70e54f70afab54992c758fd82c325c51e5d0d946acd837604747325cc07b7342d044372c84e27ee3cf8a0d40ae55d9873a36038737a51dcce2fb8418b1d0aec36d1ef8517e951f2664bae0f842944aa659947accc6d6e8011084d4db07abc70ee5516063dad64a8cbcfab340051405fa67a26803299c2385f199a6ca80756c6777084e661fb2a1b0ed174954ecc54033d666a6839c0a190d080642414245b501010c010000b6b822100000000080633d88af03efea3dd68d5f4fab4f18e83abf9b76f106211529097cbd4af61681a8445126474156abf14aad4fb3f80df31c558d685314984211f0363985a70088eb6c67c0a5cbe2747c5161401cf53f8d1b4601e36ed5bb06e9fab9a616ff0505424142450101e60ce51ac7d1b3d08aee8611ac39945c0de4e3da7afe34df267c95d148a76e7880d4065684749d0f2390b0b5d88ea8843a4d9a24efbf89dc464f7a420e9b918dbee9d0a7fbda9d7de62a8d73b9c072897640cd3ad1894784b840c95ef7db1c86cad6e8011c9e681bda412d83bc2a9581fd611f11207c40bea761d8702752937a2dc757be4dfc0eeea951d38628f9de879ac14194403d069fea06fe00fbac27c6fffcf3f1080642414245b5010314000000b7b822100000000078ffcb3bc2cc2afe81df2f7cd4ab2e1e2f25124425c2ff6180d223bfffcc9c3bb70cd5045c9892a05dd945ba23f5a4e1b11da6dce36c8f0d021ec3f2196f3c017c5394a2b61c76fe52d5a4ad641d56f9ebcdb43cf0a3447e770e08014b3db90205424142450101201e821dd14ba1629d2db3328d47598095fa303344bcb3283113fc52a172425723b8018f48eea0492b27c6539ef9f75e89f663da39ff1c2487ce6bdf0fddfa8dd23b7e33bd91848a0ca2cd67c9f40c6e8cc9e5305840589046cba8244201c35cced6e8012b908254090989c88068ec0159c408607b524cf0b2a17b2d98a67ce86ab6782638beb6ee58c0b796b434956bd2c8c66ca2e6086ee39641d6eb3a3fa641e3e27b080642414245b5010323030000b8b822100000000082c28627c0e75c37b5e6df47b5b99deb57f8f61054361498b67d2559cf93aa11497c65da0b9a48e6afa343704de25c58fd6d6e75c471c60ee6dda8214db4d203cc3aafc6833bf3b715b23a72eca7aeebdef33ee1fc5ffcd5dbc6b623efe98002054241424501017cfefec2c973a867398ba0c95d6e32d7b089fda708fa433a7b3e50ce24a98a0bbe1bae20e3e32d2c76a6bda72a619f72a942a51a6d6529f2534174f39bb5ed8c871cd922dee036800387bd98a72b25d4fa716fd8c79e4308439f121f6bcca07dd2d6e801bd9affcd17616a048fc5b9b911e36e501b35286296f81743c238a0931577aa0da6925c1324491e53828701052f41b49c3482b4cc192af68c3077bd87173bafdb080642414245b5010387000000b9b8221000000000ba7e34508828f5a0f3209d1417721cbd9fcb063a7e640e05ea02e9531173cf2d3374fcd7255c58ecbf40b729d3e8bdebbae169b03edfc2d2a0da6c7dde094705d227dc8641c426abed9434faf2817ab8a6168b31752302f7ca9c2dffb1d2780805424142450101dc0abb61653f84361e95774ff3fb96c83a95ae145a3e7a1aa49227cc66efbe157fb57d873f7d123c38f27bd13b49ae51751197c05724b47b9f6b419045e6f28616328b6a11a04e886b357fd9e08d9bb81d633bd2fb3597b907e92c620c3808add6d6e801b88b271375e24df15d209e0626dde2ca0fc5d739716a4ba6f706408e30ace6caba3a8b4b098053d145461124b0559cca43da7901e2daeb7ca646ede18db538b8080642414245b5010184000000bab822100000000042ac43633ddd3ae5b769b5751a8cfe039aff411ccec3b0166b5a27f1fb20f433221b11ca93a77101cb83564aaeaa4312cc6ac2acd80c0f98f89775d5a7c10d0d165126133f3f09b74f17de31962d90500a3dae79424d185668a5953e50736e0b0542414245010104bb8f2431aa8b1fe38d30a3d6ebd0d54a90e1fb8d9edfa34e71bf54d7a12765dbf71a488f677da31b1ef84eb8dd86f9f2a8fdda676f9b990acfc3560c8364865fd034f419badc7b36771e17a21cb2ba624bc399fd5f08954238138dbc6bb9c1dad6e801eca7720e049a5977c3d10875ff9d06d6218ad0399777246247dcb6054582c91a83bb503d2d27146a15c882ca8061f1613b32c0a5dafc2acb62bb414013efebac080642414245b5010357020000bbb82210000000006029b8209a3acdb51269fd611061c24d70599086b01bcdfe591cfc981fcc3c6aaa92d78c8a252bb767cc6943411cd3bf147649cfea1214366d5cae8defa1ea037725d0db95507f1b0dc2ceda8fe809018887e776b83d1e466aaeef6c9a3c320f054241424501010a41e71ad5d9a34efa680e52bcb9dc6cca9ada5bfbcdf949f7d1305023ae2f30b455fbb612a4b616ff748a2eda99a90b2893651e176047c7785e9345af68c28dcdc734d95d64e33c7604f1d8b5736c9218d2595fe43e206e19ae23bdb887f6f0ded6e801ba8ef767fe481ef688326cf001ece2b57b82bbf78c1cdcf4cc78d869134031934e2303642134f93ccc4850a21ed0616c1842eeb7f8be34f986f3244d59b660fc080642414245b5010135000000bcb8221000000000a4329f508d55dec39c6f91d34749b3b212018c357331d2b29d5b45f15d5637475ddfd72a12e6801e9dbbe15b98b441b060d8113de876ea6a0a013a54cec6c40c0d64ef3030d703a5ec0cb143129fd12c7f48cabcecdf56d18d4376134b88140e0542414245010196ce87bcdbd0cbf2962ecac203fdecc5500171c0a9fe29031b8ba9a1ab065f36737e940e96fea97864f66b81d261bd414306cb1324a7767a16666a40c985278a405df7e967d62223798f5a530d1a077836bd5e66b501554165bc6a562ec9c31ae2d6e801b34eccb91ea620640326976f28c092ce76c78e9e7e331e48eddffadaf13ddd327b1334f649e2bf24f06ec64a730c28496b2a7b4527759273a5b1146a3858623a080642414245b50103f2020000bdb8221000000000481547893f6e73f9287351cc806d636794c2a01f66b32aa3bc701712494b283416cecdfc3681f90d2696009263700616192e7237193163efd7efaa7c225ed609c5c71738777130de32bd95bf64e0246faab40117ed09b6d0ba0c86886f8c580b054241424501013653fff6a523ad2be7d9a18633f0eb278d02657ff14a3d526b3c4e2b9ac61d662504d84ef9977e47f3b41926456ff3829414cd138627d29d87b87dfc82fa6e81b730480f6e5fb3ef61b07a3f9a166228d39f365fc7bae3dde2f45c05ebe3d2c5e6d6e8017b7e18ffe6870f8086866b5a55122d0ae396ebbf9baacc2e91b593da78755b1ad7620eeefaf7e79a70877d4cf775057faf0ab165178241bda759f59729ca82fa080642414245b50101de000000beb822100000000052799bbb2e57399c68b6e487f84f8e95bf8b32c40ef1094b3d878e3f1fb4903b92050835742de57b647ff855c61db6c96248719750f8fa37f9d8aae92700080b38505f4fd26b7a69efbe5392cc068ddd8b9d6bcfbb0d254cd45c0d4af4e12f0c054241424501016aa3bd90dd609b0590a51abb975d2bba7af616eb43576bfa809c29a3cfbe5b5744a8425c0700272074447458ca8d49d4cfefe819b5e55ec878b9db32f3be078fccebaf471ecd41e8d5a013bda16867ede48b6e5b18dd7201a6f56b124b38f6b5ead6e801f8ccc6f83c2c0a3f4d6b933d57509ff38c4bd17d52076803fbde82a746f01f444d05b3b512b0ec44efea201905219a64d8161b383a409a3a4995dbbd7d78a0a8080642414245b501036f020000bfb82210000000006c8b8356c10f213033fbad3b4e403e0ba93a89d2a4d8c0258a6d79f0eac8616623faedb5c170d5836fdfbd6bbdc7d7c504f3d39724311d5dedef8a2064f44800a3a31d03efdc77de21f945f9ce89e8a4f6f8d7f7bb33ee4abbac9b7885edfd0b05424142450101445718681c527535a3cd54823dee63d09cebdf72c1c7318074d56079f6bfd30364030b93c919bc1a9bcec0843dce8c910e570e089cf90800302615dc39ab4b8bdbb144a9b103170aeb3acd25c0b2b7fbf282b7c97fa9da4a78e327a46d28e020eed6e801af6ba7ddbee95f08aea29e1e402969853f069b95aec81c16f17ba42af88a73748aca879dad16795dfb2073352139d124801bbf67c5a05ca6ccc6b8e2f4461b01080642414245b5010327030000c0b822100000000028d1d1087fa1ad9fe3eb7214a283d3c939cd76d2e39c8012a34f2ab756e33c2f16d76f80440ff33415a53de843d4ca91840d1d3892c55b1dbce1a15872618b00e6479a16aa538de0a0ca9783792db72473ab1597dc6a8eb0787e8a5d2142870e05424142450101a06bcb372cc2d9266bbebc2255cc70dbc311d004783ccbb612a8f296f7d6ec5bce8edb2577bb3392c7d7754d7a35358b3ef4520b1a7ef3017cd594d55bd4d58ca89d39d21fb46cda9fd351ab13bd1963491747d3899acc11ce79e9255a5d5d12f2d6e801e370ca98d6d263c187e646391142cc19f24f0cce1cdbedbd81cf3ec009860e4b9ddefba2b048537dfebd7235b9134251ca4ebea1eefc0f811f4f859272416438080642414245b50103d9000000c1b82210000000000e82293c0280ae8a1188d5123ca9caeb9236639d7b913fe7e37bb641f08b6f05a1216cd78e381acb01fe11d2056d55f33b53df2a2b50744c95f0790f886431016cafa249e54c7140416371c2db87f240df7e7054cab6dcad1da73ab2ad8fbd0f05424142450101ee51d6968366c0418a14c759581321274038be38b2601bdf45281eb793c06e50770eb54c14ff0d17eab9fa2556bac9c6dc67a56e425e90d30405add262c5e480f8b83fddd31625e7d2eb92df679bc7503214b8680d357e2b8eb3f6e835e0e0eef6d6e8016c93cf22353150af5f1ad4aa0b7d048dc6da89b81e732c5776a70b0cfd7ae1dd6b1cbd3b63bbd812edc0b8688c044035f7613ebad4e34fad821dcde31d115ec2080642414245b501037c030000c2b822100000000026ec286e7951e4dc699ccd441944f92673a5a89b7e2e9a3cfa7f4c3da62ae2499752015cdff274cf871a93edce098f75afbf37f49d9346580d2c8f1d3aba7f0d402fd07e48aa8fdf38b61b14de7175b07b84a931ca951155a2a87aebc0d35f0405424142450101a4d42c242d8d5fb149a3c2a7a349d09bbf2343199ce766b58a0a6ab33a7745437a6b7b3c9e64e8601ef4cd40311675cf7fbc0cf6437554c1506b5886e7042688596dd97d9e98f58d57e1a84bf043de7a5b55ace7dc4f1f393830069cd36c52a6fad6e801f642e852ceee6601455c86f172f1104ed548c30f1a9459cd906035cc26b27313561f01462c7305088478af756ffdc1d8bc26b025e3e99213f92cd71d32f5c4b7080642414245b5010330010000c3b8221000000000f244d086abae143587f1aa04507e6f9121aae2111864b097e0d2d5fc4714196a7b065eeac06b0a13cd8de81cc7d16ad9541f1e41353862d36f56c373d347ae02950619dfcf6e15afe8d1039e87785342107b37d2681a23860d7ea8ca752a460b054241424501014a3ffabede0e98b3c4cb3b7ff8f2cf8b6d572e9c8439b77d16d68be77be1502d6ebd9cf551cb792c5dfd7ccde19cb5a595266c932d107c561a985f9555db1b8306c29e7fce9fef42ed338d01539ccb45d8b90e709444ab1bb2f49ca4cd075c35fed6e801c8cfaf405c712c671213293f878461e2f2f97e5513b431878e6ccf614072c3023248721a58e40e6f860b3a021ac3b3bbf13d7f2755ec48bb1869b2c1a3a0f4ce080642414245b5010392020000c4b8221000000000c6d1b552899abd22566e79a2e6607b7ce7e08865ec75040b327cf216e73a5b750b77661182c98aa5fc1c523f80c232fac39820a7643a0803523a626b4409820748607ca6927d9290f05b128e3b686515db4fe76b6e66810f10aeb6475bdc130905424142450101ea4f4a9c6cf1d0359024fb09dc89d76dd138e14d47c7f932f72530d07363f11ea6492e42aa6046896ab75de2a34f6af53d008ff3c593f5ce6ef8db65ce27d486595886dee742bd267019d3f702a154ff8b909fc76614902e0fedfa05eb1324eb02d7e801f2d50c45824811ac3ea84eb01df3fd86eb1b66577cc42920440b6b963128a032bd1a4572c0bc0b3f391caf30615394888c17315e870a53d2f36df94da0bca390080642414245b5010372000000c5b82210000000008ae75de3e287a81d56b0b0180ea7c2095573453e38d9d0b6cea3a43201a5c81ac3f15f9a0c93d8e6a7d0998ee63f6d94d70e3adba14860d6cf6a7ade11c5f50933a1dc59d58b9cd237f2a5236f8893db2a40c304a2b8fb8da9e2032156292a08054241424501018211c1d388b0cd1b1364bbe6c46953b440a864932b88b49aadb5e6513dc9bc7c5fc2a6d3b975a7014cf508608a432b7e300739276b0d0457b4f1e0be873d5d8fa68b59230e34ac0ddf1c8905c17a8a68b6e32c51c4a77c54964b56c86788805506d7e801f776abd62eae2ad82b840d81347db826471f19caaee516622670327c944e363bf909d1338225c19f1c4afb93da3221da97070c13ab4cbcbfa25c48dc901c370b080642414245b5010325030000c6b8221000000000982e3ded16a041a193fd02aed3a50b2be98825a040fe1940973ae77a124c1c11878c9d18601dc89a943431ceef30ee8903dcaed8004898605bc5867a564740049532a7ca7d4a9aa16276d6a5b25e8ef0258d6e50e893c648344440f2401cf7020542414245010130f665826c1f80e99c404720caf151d145c381e17a8081397e32995ff714b37d07c4d9873c7352a1c65f3a16e8f04a21fe5fc588a7c7ae91c11cfd4ba065978ed55e915ffc627d0871e2d93666ce7128815bcfc088f2301ef2979d31c28a9b630ad7e80178cf42ff70866f0ba9c3851a8af367d9f4468e8da62a16e67e1b76ab1f050d9db54b8c2614cc13a6be17bf30c71fe5b16c002c80e81076d0b7189ad6b00f8375080642414245b50103ac000000c7b8221000000000325ba54c22f5522c46b25c4cf122298fa043c5889fc32d54d7141d87776b422fcd5f6959a421519dd290fb474216fd5f21b3b3ce91cad6df9f98edf4a3bd060b3bfc20b4f88c8e4ef979b6314f4e4d62a1e1446ede314518c5f3c3443d1ad00b054241424501014a03147cbb758ad81bf844cb958b4538c910056aa6b333e84b3df60a0d65f751dad72bdcc4dc74c445afbbd083f1fdc27a92bfceae8311833f176a1e7e0fc2882ec86b9e3b25a798eb8d6deb5732ed76aa6c25109c5cfaf668ddae62026c4dd80ed7e801a18591fc2092724666a092a59abb32c234feb55406449f10b41dd1d3f74e1d5fbd84f8277fdc7948db675be1ed661528402340ca06636b05a36182496fcf2837080642414245b501015c020000c8b82210000000006ab5fce3b777735084d2e7bcf9ca45ab7a7b530f879d059b6cae4df3721d604fc3e3c27bf5603727c665d8bc088c667031bc5b762cb36488d1c344eeb0784e01c455f18a9cab4c9c8230a2b1a07adec19ce18c35d1b049194398ffc33d35c40f05424142450101ce6b4e7b01d3631d77b8777bb0157f5d45d05d611ee8069e041fcff2ea1dcf62d92c84648e090bbd1e30e424b98f6cdb942b56d41ed3b5c77bba8c68aa521185544dc6ef241d2186fe3f8f739a71b8da614f0e7a07482d5e1a11457c52f6581e12d7e801c9130b6cbf2e7caf805bbbe3fcf92baa5597d4a3e525d241c55e2a0baff180761b2c95be755ad56a5729a362f24998b84ef58dcb93bee1894d4148f3ffd6ae94080642414245b50103cf020000c9b822100000000050b956d878aadc542e1861a0c6f0c8c62427b2e9c96b78bca04f634250900a4142872530479d32779dcea14fcde87eef0e34426f600004f295849d768831f403dce3be4a92c7cd4481af7f05f4ed2d20d11d5ff86720846764516f114d84e20205424142450101684f7d24b8e0f7a395cd7a7303247fccec11916496dab55d1be8efdf50704c442985a90588c4ccd5cdfedaccb3c72fef230282ab1a508fd8ffb6718bda579f8433a9f92c7d0d2c8506c8dfb13ef9c97267690b770648deeb8ca1b0f9da3588b316d7e8014bd87957bfa7d856ed1270aa9111233a13811d5dde4038072f29be87ed886c48d9eb5f5d501fa6730650fe90b5238025385ecbd7bab3b4f836b9d2fe91d2c17f080642414245b501015a000000cab82210000000008e4dcdd7a3e10ceec49abe86d9d92fdc77edca49d2bdbf2329a52a3ecf21f70d8b62b0bf415253dd190a2cd74f31fb5a1d5590bcd46d235cff1115cdaa32220cb6e7142510cb3ba4c67c117f7900101d0ca6924f7fc9a40369357c131a38f60c05424142450101d87d7e5fac7dcd67c311af0a8828a3c724cd57432a776a3bb795d8ac6952a17944b836e3f2caea6503290cac9aa2d70a7fe8a7ded907cc1f2e9f24c4d381e78a452f1289c7dba7871bd24c890a91e4fba3179bbc151f2413d3f8a3faabccff101ad7e801932be40032de1a96e4f7010e0e2f1a4f94e3b24e668190dc64b1514a79f494c6347c2b45cc570bcb4009c66b0a1da866f2ecc2f0030fdf46ced4e9135c8bf19b080642414245b50103d9000000cbb822100000000032335fa0bcbf167a44d2bbd4569b5a0fa2a92629e6d593c49194c949748e5b2e7516af8adbe079117f8b47f93fc313b984b8c83368e942c1af379f3b3c61940085c4d3b9e89c3a6b03164eba2a0160f84d7da96ca07435503bade4dead2ce9090542414245010176766c688e542a93a715df70842470745ce4322a2949218c6687a0b93ce7717bc3108d8fd913012128ccb4123bf684230c9a61f1275e4719a44e6357305d398f0bdd628fbb9018361d72441752808afa6b2503a58d3cbeeade97641e6f02ad771ed7e801fd8b3a9264a191448fc32c6b52fc0d7715a3aeadd1a849c348e2965868d73ce33bd14de9853015b62f5aa75e41c9b008dcba8479aa84b1c12ffbab2008b4c5b3080642414245b5010375030000ccb8221000000000bc2bf37f7e530bcb679d6bc45510bc1ecf1f8cc1a4d4770850b5fe506ca33752d07a6eeefd2b736a65f622f70ef1b6899cdfc07bf78d79699947b86483057a035319cb702c65adf7112b18b1869643d0efe8152a7d44ba444e182b5e467a2d080542414245010148d557d0430d816d12009c69107ed2a335fc5071389ed0048f1a0d41a5f9da5481450fc893637720d3c73903ce53db8d03ba28dd09d3b3396f86a44e2760488e6ff393e1c68eb64642420624633dec214b2fb61ef23eb50b8106cc8f9af39cb722d7e80140ee7da17409c92a406fd9fe121356bbfbf80d972c2410e16a35ceee81bd431ac9bfd4bdb749dad65e84f2f3df976381b8e31bef4c4d2f31be9c54722e57cfd2080642414245b50103d4010000cdb8221000000000f4489216c8853f2bcab92b9463188055e8b4b34324ca6951c9af5d14af835a760f48d5302bbd2e218631b1105f6779a619dd02e158e57ac48284c07cfd25700169f53940ebdad3c06cbd7792a8c39954a804bf9663f2b20b4e063c30f023f60e05424142450101308d0a813235d94af8ed96a6a8b1e27d28ad6b855ddb8a6aa965216e03b7803059376c83eb38fe9af3bbdf2ebefaa47ae8c27ff596cbc2c8a3c3f59f4307d9803d4b70db385108aadcdf99a8dfc11f12d9f39cf0ce27765e258c6dc83cf58d9d26d7e80128b7ef5683dfd210f9d1ce5f783a1d4ca311296eaf625082a351e5a7f3d547d2bb288ff22860b3bc21dbd60c5fb1b2e40e7c36c6adcf55f1dc3a097a2d007a16080642414245b50103a6020000ceb8221000000000c04caaa1ecd8f204a13364d339b84360b477f85e2326f4cca7ebb6809c11ef2799b01a0dc9b032bab9b86f294708fbd484afaf25d70b873e9ee21261ffeed50fa8e031e2469f0f9b0bc586f2029160afbc66a9f03e3abe08ad39c2ecc6adeb0605424142450101489f680089891d80727b8096e6d587d3425c1b0a52c18121d499edb83d22ad6f1655af566cd0e43e427e0c09002da615a7c9f85813ba810680392b4fe3dc46858be4134c301ae5f88c6a03c2585264677b9aaa5d860e489abee7971875efc3c82ad7e801798555529f4bd28f611035defa422053b6733a2e20a4cbd31eb0fcbbb80184788775c88cba4956578dfe9eb682cf2296a3194e0cb2c32b00b90c65dfd40da9bd080642414245b501032e020000cfb8221000000000c003ddcb5a98737007985ca79fa5da84a0f3e70ce0190efc074fc829b51cb15a9ce56b1d16dbee8457ed1167d0700e6060ec325b72accfd65692e51e4682520c54b8ce7c60ec38ec5cafe98f7b984fbd1e7c610d89ee753048396b1fa962260d05424142450101288581de38b5782c39518567aa5ad572518998627464dc5e58a4bb12b65b2e099717c3f8aede81dee2f194ba6cc11fa532f16903d94f593c1152b58e9fc7fa830dd296a7be2f2725c4c6d0a9246133af9f8e0ca8bbef80872c42643f40fa00052ed7e801bceb07f9526f04bbc4a3b291f4360d2a03938c95b46db296e0b055f8927d2c63a0618ea17705b804a257aafb5c5ffd0da18aab8f7b2a34317a4be9dcf998a4af080642414245b501036f000000d0b82210000000002cae999f2d4c12ef51fe3d067fa07bd538c7231f1f492fab3cd31fd26369880221c20761a8f73665ad3ee1e29f64e3ee2f1aecf6529dea2c1171f5c33957b40f95a4d300fda0810c874f5534fd4764e8d42e5627c714ab0a7d9598a158ea7c0d05424142450101fa2b3dc88b0c6722ca5d155f125b3e180faa8b89a9499703200ba9c3b805b6291469c402a20a038bf2464188135d80cd1d2c8f90437a3c8a9d34d858c108c081b3146d74ef56315114ee24ddb8014895ae0027b2112ab91c66e9b7c91451555a32d7e801de8352b8223f7a7185f95d156fbcd9ebc8ea57e2b5867375b5e8eea8b8008ae69c4967badaf8ed6fda85c21ab0b87c3328ab571fdfda7e662c4c0480b3f4f6e1080642414245b501038c000000d1b8221000000000aa83c0e1ff75f445dd72fa3c728555126ba38ce387e911803ed27b67f202107d6b5d794a58b5a5066a2a9e65398fd4bddea8c0801bbebb2205f47f04010dce0c8b782174b073eb821d4628dfef40eb1ce0a1c9a011df8c308e751a3570ab2e0405424142450101f430ac563cbce31385b38719be4ae3691770f66ca7b7acbc6d905cb16152af4400c9066660def63fc08fad2d9f105efda3d7079577be94ea82d3b35df3524981640cdf4702ea534381514be85e6d594d0e352353ccce1899208d13fb315523c436d7e801eafa51d709ca99de7069ff501e67c6933a726df5e70627f124a93d32ebf0c888eeb9370c88d534dab898e9106f6a8ed2dd83381e72fdced8a83210c442220039080642414245b5010149020000d2b8221000000000601d9bd927cdeb86be2483bbc109f616c0ef9a6bb765830477137fe75de311160d57c0e14d30bbc8c9200413916da1335521e79de322608c9e6c58848d019f05dddd1d41d167787516e645e99581ca0a4c0001be0d30f24378b64b774bc25f0e05424142450101826cbf22ab0ccf4787a73a6d9cdd43245a60b59e39e5fc998f1b846389235375cbf37eb69266af3857d85efae81333404b4d9472050c7b0efe1c142d13edd185ea58edfa168597c1936c771d1372021b560e16de2347330aa7319fd927f5ae2b3ad7e8015c138fcc5f332b36f57cfe05608024c4b65e320ce67a180918b3637a0547c43f12790e4030432dd96e1b31008dbc4bc3c400572b1a04dac3da4dfaf2266c8387080642414245b50101e3000000d3b82210000000001e0e63df686b3313aa0a063b3a2bdaba40bee9a82e2f99a1915a9e84f9648a773d640da3af5486b76feb02fd2df2afcbbc2d4c3e5a0aeff427ea8ed42f98a203355c5532c153c47aeaa4ad6eb51b8b1d95791fbe99484ad7eaac6e20c6fbe4060542414245010140fff99a6aab12e150a55d73d64e27cc039ee2ba467499e25508566c022d047a027dceba475d8d4493f2362bf1767f8fa10447410a3caba0f5a3786b135ac78ca8ba1cb50bc0169bfa6a8d4dec9752558a71b92058fc9a9df4e656ab71c64f403ed7e801da25c78e264259b3a6b50bc66815a20d39997803b4cec745c834df889ea705724788d410ca0e031104a2d397c31ff072a2307289858bb0c5b3364993574b159c080642414245b501035d010000d4b82210000000004052fa2c6303a5f8e04cbe3e229daefa6a6cfcc288240af11695bc3585b6e737c3feb366a2cf20d870b2edac6a795476a4d240280dbb4b371d183067e289b40f3ea59ec58e92f968d8d816b3227092509265d8e8ccd6d4c318d9ac63d4ffc40705424142450101acb6cc2c25dfef8bd80f3fe82b50b396bcca23a9bd038c2bc4d7dc76c6571430f8621b32ea59b2c1d11a06fb76177bfee2469fc763500c5ecb171354a4eca7881e1e573ae949a209b65abe9b9769562ae4fdb25067783ef8759c274f4af0795f42d7e801fcceeadb2ac8ebc0bf539ed16c590bdba671e6fe27667607e2526d9637af1b1313370ac71a17b87deb1b5ee91bd92535c46a5753b97519e537058236a2d0da30080642414245b5010362020000d5b8221000000000eae48d15c95325f9a67791a90a1d4c5165d472cc51613b87fb363f0552475f645da070580ac2960a5627e06476542235055c8927433a56907c3f28932511fb0975cd1c016a1976daa567e7833d031c7011ea0a08ca9c90bf40086f179383630e05424142450101729ac4d23fb781fbf7c5fb2edbd35552e1f9dac4c8784259d021edc6d3b30f3392714740f7aa1c85935150b13af0b0104473501cd942d1ae12029f7104b1de8f66cfbe8ad556d880f4c494a515ac26f6212bf74499c23e55c392823ffc96cf9b46d7e801546e4d985bc6195c7baa25340a30a66f398d4b5e1d78423543b207f6672dce8b156129965f3087deb1665794753732611beeb55140494d610d837f0084840d86080642414245b5010383030000d6b82210000000001679eddc43da158cde800515b415b18ec231919881b3cad75407016e47ceb74129f29ae6926757875966c1607cfc35f4f8e24ed51297d9cff39a7ffb99541a098f75d95607d660f958414dbc01c06c5f81a8c1033ce96b0daaf5ca8d95221d0c05424142450101227f2e002147dde3e96688c7ab81e5d034b5d2b9288ddc8b19665c90caee9f70a77fd7628cd9bac0143c718c5567d5497ffe791002d1f13d0cd1ecb62f71668eaed9fa0838008e379a8861aa1798069bc149abf2fb8e9c8b7d4f9b1250f5f7a74ad7e801c1cb9bfa8100d28c11bf5be3ebee28237326634cec6edc0a729e8d62dc6fbd61cdf6485700546cbe55943b38840fbf4dd10788dbc05ff3d8d81cb893ede1d9aa080642414245b5010301010000d7b8221000000000f48de5e5d8a9d20382bb35a6463f9ebc9a221e80fb68f5dcd4f45394ce74d67946b8ff5d95b9e090dfed822da19e2a2b29f1890dbe8c926de27d4b9bd1fe650c74d42772b338dea8f92fdb638bea46921cec685c765162ee1bb0e02540ace20c05424142450101b647245d6a8196459595dfd44f1b0dd5c1419cd8b5fec9254d208f6ef6a5c6721263e9bae59b78f8265d6a838cdb5d050cdbcae5fdb35fe39b83c9441fc0428bc34170304182242ba7815d269e27fde6d15bf4b24b8eefe80716cb48506aeb894ed7e8011f97ac5a9ec3aba290b919724a89e121fe9ce1558d6b718e4e58d4dd9f26ad6a43b3965caa6cf860e92c90f6e380b04790de121d6bdbaffdbc8baf0107a29041080642414245b50101e5010000d8b82210000000001a2581f6cb777ccb5d52eb9a0c09d8e4d326ea0341fb7c90b1bdec219ee9341ae37f981bd9abc6601e6f3fb0976dda5abba4f8fb5664207d2dd80be7b3c82f0c36f5369518f191ed76becabc12f279cd20352e17be3970ad50621fcccf86040c054241424501015c9f48834f9f223b37e8f2a1b31c79b9c29056b42f9a70209c44a31028419b09726021b94c7ce868d5f2ee538b7172d0bd6fa4f8ece66bf3f334de4b70531486633de9f310ba1ed7648ac2b90e3ff4c7d629dc3db66acd1ec2f129db857f207752d7e801c34b221e1406fd0bc9644aad6382a1209cb99d974b5b5caf7896b17ca4e0985a778f1644fec69ecb94f64c9da4b6d5f568e0be78805a6f5aef16e5773584181e080642414245b5010358010000d9b82210000000006cfbd719a461f273b31aad9828b235c63d249558ff20fc13dd4b9464c89acd1d5e1d85402e0ae5710956554fddc26cad1f1f9ae52bb63e38f680f829c6b1210d47fbd241300dfc0a229f6b516dfbe15ba03c3629530a44fef4253bbad5096b0d05424142450101a2ea9c454cce9822105c6e615934029e562a14eb3d7dd2fb2869e701913a346a9e18f3816c39c490bfdb3e7f75bc8b640eb9e154a3fe967121d0e875a96208887a97f6c79409b2ddf0fdc66f3bad5659dfeb0b3c396a10661f968e26ab7fdae956d7e801a5e27a63097e3a75212711724bb32aec1ab272b822bb3fdd7a96b9810c60d218c02208bd8d70c260551c38d80ac10c7bfceee2e5d24b9e0ba0af98ccb24448b6080642414245b5010376010000dab822100000000020fd8b72c2574a96ea5bdb02baffbb30b5fbcd1f990414b9a1de84a9b23b65161e11ac7939bc49d0d7c5498c6b01f284231f143dce5a011ee309c9a06206d909380fd7e398c3e19fd1f99220dbab9b9c9f57a3fbf0e70d9b2a95de4b2e619a0205424142450101564b44207806101854bf916a48317518e411f2e50c534c8be5168c4788fe1764f808b012cdc059c73a47756a0accba9bbc63b3b39287978ea778051cac96758a451c713f643ee02bbeab7cb74e0da81100b8c1f4fa8976e178810eea71384b3f5ad7e8015dde08781c02df07625b6437accb20c258d0ba4a674e7003f0631ca223343ce86e8840b33df6c316e9da961fbab39182b99e558a5c5826e21bc5970b8ca3b49e080642414245b50101ca020000dbb8221000000000a6cc83b60b2e7ef7d0c97986e4c322634106db26011ac74b0d7720171f71d779f74054c4f53932badcc1a210658d60cee548b643f8aa378a0996d8007695540b38db77419ab9306a7e9351ad84a070d6dec412481809ceef241991e2fd44af0e05424142450101fad5bd1e4424eb28ef0fbbcdd8157fb109219d17cb0763ab9ff2a6088fd54e30b8c806c3c63f6a98f8d4aa022e650b34d520f096a85d0219a59877e315911687b631354839478ec735e7cd86ff50492778644273ea9c8013057453d6f186a2fd5ed7e80165dd7ef3979216a50805d5fa4c7c7e2cbacde2421ded062a63cc67123d9cf59f07fd618223a5aba273e78043ee6ee716733a228c093e0bf5e77c0b61d82089bd080642414245b5010364020000dcb82210000000003095ec1cf8d2856d6cdb7d3a745b7bd39da29ea17977af7833437c7220ee237a1c92b150d0ccabf72aec7e5895ee272048f656612219e970004e8c4e63d0c70fbd6b4ab7ed087135b4513267549984154f1e17e64de0ebb5753f2fe598e02e040542414245010168f6c16016ba6b5a1b61b7e1961b7db88f9d0b80d934650aa3a7f3b287ca176c38e42d077667ffb3af9c2d410f16663f81688e310ab2218f73c0cd53eb81c98eabc7f4a9351cde91ece7fc9710e24e5832801a1c3a226b345e77605b0f1c949f62d7e801f125130534faf365e0a8fb71b5e25ad6115a6721cd1c62d3934bb1e07165511bdfb998abea185cfea60981b3700f305e78b1efb37cedeabe6b081cd2f7fe5581080642414245b501015c030000ddb822100000000028d617ff3054b77c70fcfb9ffb5ce29a349b9a970ef207db4dde7e0d8a250b4f0fd377b73e77bdaf2f51c3518565032ff1e94ca926cefa00107ad112baf6c70cf0e7731a53e151fd2971308cd75e36f837ed6e92aef7d75a64523a9a7710fb040542414245010124f67f399b52b6480dbf8320c9f1bc03a34ffedb5c2028d77b9b229fbaa3b950819c8065ddeccd55530a7c9c11d7e86cb04b7c777da24aade82185c509ffd488cfc57c9d18c28ac0c1b6139306effcf1c9e37a7821996b3e0028b7cf3567d4c966d7e801f77ccc839e4f97de9eccae88bd0e63b0cb814746da90d3f64b490fc98500a40565d7e2ce58b5ad71659ae6763f199c325a5cbd17ddd5ff89ecbc64e0bc3abcf4080642414245b50103c5020000deb82210000000006ca25c3441d175825e4d55c586d56bf05783971035316b57c48f2d3b59586f1e7487a675e46d697624a409b795740cbf40d4b807529f6ad44e6327811a9b2d02bb87cdcaafeea5f13e84b8c6ff004af848f9b5e13930efb5bab0b8e23321210c05424142450101e41586442e35f551482f2cdcf27e9564abf134a0c6ae7da541c225021814c531fedb22e3b66cac5e7477484eb694b2101fb48079b974955a8f8daf4d4436768b18dc1b2d346629aa8e568e4cb89acc92f8fdfc8420b7ea19d25a532a65ea8a6b6ad7e80117d733716aaca53eb80666dec8c6f22f698031205f5376bb7be92ed06299229fbc3b64787448561bf7e725d16d6b678b296d976636663c30a1b2e824ffa3dd63080642414245b501031d000000dfb8221000000000f611df3ff5882018ce273ca8ff6a044e81508107627ac79928e8190986e3de59016e521ae9ef089253944f11dedd3c1d604e98c44c56fde444dd40c6bb96b70b77fbd056fd223e8041115fbab12719f54ba3c6d2c96d7256f576b5dc2f35010c054241424501013487ffee90acaa0d571a2f581232348c2d2dd799481c62678d0563d7542d00368f6aee9ac28313f391862aef9d8a8e74e5ba5164c53f6095202a684840db1a8e94ccdb7b93b8e8626e5e92f64dfa2148bd2ab3ad51d0bb7ac3f059b4ac8e50406ed7e80144b871e89bc145d3005c2c2f0e2c3e8354a4fe5278b30c2242e961b9cdcba510891cd290f9fbe553b7735b45b83cbc3f403e0996d75e0baa8d6b9eb58d35cbfe080642414245b50103ec010000e0b82210000000001e6e7d7917efcb9455088465d2accdb1de7abf994dea5ca951be73650b469408375626df554e70c080cafe0c3ec5fed3ee6250d6f652516bbcb9cef9018747004d8c94a31162521c1374fed5eef79c006423620be09b55e6effd20e911e45105054241424501019a035147c93699408ca4b9366f6b0f30f47e8f4f3e5014781fe8bc1108e2ee5f57b91324789dc7776abce6f7c7478cc91539ecf0d14db3c2f05b0c2e50aa788d248cda033386b0f05b3f53ebf6ccc499fa45d8ef6f0752daf37e16cfbd90da5572d7e8019537a5f480ae37778e2cbabcaab5b5fe5c4bd5ece519a20e876f7003f28d449fad8a24698714a30d168cc9b56ca44f29cda3b6bfa8331e5ac7d5684d50f5d056080642414245b501018f000000e1b822100000000034d96337114d9c6055bae140f177ccaf6d677ccc91ec888aea089192ba1b0d194032ef1b3579b673e9973d7a0e400099c6f9c2ecc917aed98ef5328c0be7730003dbff86235ac910156cf65444f1201f7c73e6eb8cf7f7f21117d3c8b3d2a00405424142450101f8fbb6490ceeb6e32271ee15b046db9c8f8298d4270ef031893aed3ffe9c4e52651df67c1fba8ace77f2d6f69b6e6a24451266b53f504365fa287cbb84d13c864016af961a19ad8e490c9cb16102a9fd804be27adb155fc3cf5367e24004685876d7e80155b99d720f36eb3dc7318c4a76e6080bc8a34fb092e26bb224217f5b73dec10b52f937a5414645fe76dee4f5aff8fd001ad62a3d95b740e6dba3389e8cc44587080642414245b5010344030000e2b8221000000000f68e26ec570fb8498293513c80d7beae08a1e59ea8be002798629a706050e708fecc13cb0ea92aa1aff0581fd3b5e846ef8861389c48af49fa5587fc526e14092fffcd41c6ea5133e29d5b94d16a1842982e6a8edc89bab60db1fd153b40a2080542414245010100fe448f38cbfbf620502d513d1f2e0d460ef34fcebdbd04780b2ec7efa28946840ca878d8e74ae9d9434ac78000caac762fe6c394704fc5b5688a33a8e73089d2a2e2102de3e138b9fc9150ffdbbfbd8a8c2a0a965215b4b49caf4dc79dafc27ad7e801d728e2482e77885df7adace36d1f83657af9f821bea4b079d38ee832b2bb7c1952ff81caf45d6fcd740179601ff23d70687c4751af398334a1b0bff453a7ada1080642414245b50103fc020000e3b8221000000000ba2793349df31fd3910db571ffa35b20ad747de32b93b17bbf4bb348e5c3c3577a594a709b787abd9f061da05c01b067696c96aeee1382c714c3196fa7bf9203f76a2a79d7791f8f47ac3c176542029df0c2955cfe2be7fa467deed50fcee600054241424501013e9c615264f407b04d3ea36d6d0b9d9758e76142ae93352570905eadb7ee6d711cabec619b305e74f7322f093c7eae956e3f65171e3d09c0c632f0cfc5f7fa881548dcc18af5c63c95b2bbd04a95ed09cdd41dafad4409641fa7885eb364a5077ed7e8011e5e78fbcfec51a02a344ab89484bcf73014cba54cccef0f4275a69abf55484528d8ca4b8bf7551558a7291dbd6de07fd2110cd7257f2061acfaedd2e3a93eab080642414245b50103ca020000e4b8221000000000c8d1b696f7e387730aba2fc2a0dd2fde6a42606467613a30af8e1a7ecb70ec0f11f2883b5e0892c9a9e953c2b1047edc6f7d0fa95ca409749c9a18b44053ee0fface4d0ec272391d8ad50aa014ef370996b8675e112aab12753761c8dd21fd0005424142450101c844c083a6f28bb73ab4c8059473853f0f27250b210c7b9d3d32233977fcb06a11465b6bbd2f552046a5cafe3beebc78b428c8d550cce1c4e356d9383f031386e25df2f77e9486b284af5019db4a7250fb1e61ede329d848cc52a18f111a2f0882d7e801cd61796900da5346d2460738a6abc80521ef1b264d1543bf47e002c1a7511c5d67a8284f475a7e6570c326a4cd62c16a6d89f12e8da38d67b1d3d03f5674cc8e080642414245b501031f020000e5b8221000000000320aaa5a0470fd7c10fc60af30b4fd55329d18ea1e84bd279804f9d32bfab47d43afb437642586d492c6ded7c00f4d2a48e53c538518fe85ae4ffada555e3c06166aa95eef482918d99c206b2332a4eece045c361656198ebddc6ed6e2eb060a05424142450101a49ba5c4621422dde4e20018ea3433e9c86416bc773127fd5f5b1ad438c2806ec87255e6250a93802b1fb3b8ceadc00195d18e60692704feeab10a5f21977780bfdc75ab128ecb134d9456a53a5220c57da4d96007094e689de037508e085b4c86d7e801dd6cbf7f4322d7e5b5898efbf41effab815533ab1e1846018d616023cbf4d54b4481338bad003d3ad5cdd1801d21cc458dcdf4d936323ce626595abd16ce0642080642414245b50103f0000000e6b822100000000000ff5e39e4ea5ef1a459051c65c91df80d90de65a3ca26ed489d3a3a8e12a552952d8ba41a11584e934355a1ea0ccacfc802d90147d5d3c2ca6b5f96a28d3b099f49219a022bc46bfb29653ce73b8ab3241695ab4b7a238ab8b0a11537f7a60b05424142450101bcb3b8b7108d53fa30cca8f4a83911b953b285e9f13ba6b6072cece285928c550c2cfae4afc4a585139ec37b82db652c52eeffbd7d6ac34d7f95c9693f1a69806d218c36086975d35cb50f2b07856810bb45a99bbc80774cfd916bfff248e3f48ad7e801c93926fe4494d7051d0179fd7a3ed6a89e8f452cbd44fd91a7b8efa1c693d489e89be07f2b2fce1b167c2c0dff86741178e6dbe20df01eeb5cab68228a98f6c9080642414245b501034c000000e7b8221000000000c8d9252f9bace414bd06c3bc48a5516e34f63a8f4305a8ecfec2a858f43f8818e88410e6028b77f6a8bd571c9dcc7a0c099f5fa92a5beca911919566df993601eccc756b232196d8351a5e23ebd8033c096c14763fbf4cf199c2854c45df3007054241424501016ea8a4b09bcb6491f56f543c2edea9275710fa76860028adaf86e87952e3d31903bdd850546b351f95b779f214339c93859aa386af32570378a3feab7dbdef81d77452c3aca57db72126c71655f370e5f70bacb4b62a18d730bd2f6a47766a738ed7e801584fe2d21dbc3831eea39a2a18ec8eda3771794e75012ffbe9a64f10623147d53987a4fe9cca1e836cd9db4f6bdc5af797975ad558d451c7e148992e9aa0ab04080642414245b5010150010000e8b8221000000000bc1d9d0f14da59b2c35572702fea27f420bde8c3aad99231f069171c00db690f4482a988c87bb2e97f7cb49fca89bf3ed9ac384de225b46d5044d39a34d7d30e529367deab8985cf1a87d5147f9f518987704a3d6ddb991414caa0eba2408f0b05424142450101c25de6731a2d51b0a5b944f0e9cab3685f73329dda7482f491b817bf2a79917d180f8a5df9dbc4045b71222275e3ce7becaf59afe0f51c9fd2fa7827ed652180141881eb26731b810eeaf4f5a87c8364da4f93c5ef3523f41f8890b4f8d63db592d7e80148196985a7f3d6990cf7c09d77432d950ef3893ad4489366c015704ab5801302368f9f5c840692ee89a715d26f0c9b02f797533126c235bf34f55a9369a5d680080642414245b5010325030000e9b8221000000000f63b1e2cc1584bda391604f141ae6834dd8e705d36518cebbdc3179a7c90c02fe7e1a88c57674cb3f0f9b341ee6fea65c1930f36d2486f093e9af25fc4047209e5e83b46ef9fa80aedb1f30914c9de90d3a9109b05f32689e70e22adf64acb04054241424501017c507d15d90d4545ff12375ded6cf70aca4ecebaa6f5a659a2de42e11194315f0ff9e429bdf667e7229a6a1136a6e64ed073bcc4575a6321e91b707c2c92ea8d0a0f35a555692d1b02cc8f1ee279ca356c6dd39d736c2147796d965a0e39f1a796d7e8018982c4e7994b4f77c6b75f92b4d20fdd48300404be94503ac384e5ebb279ff200e56f58bc2749c44f404ec00e81396e3fe4db7b3b2a43f8340260afea884bc63080642414245b5010336030000eab82210000000005600af96c8044822056f3eff406de363869f7ef817675f030ee0609d78b2af3cc425406f6da575ac3b914901c629fb7804dc168af61b32404e4c1841026b8707c13f5fc5e601a0a5c9d39755472846c7189cbe09e215c2774be020ca34d8660805424142450101f638a0e1f983da54605a9f540ed4073392ef86c949fd22dc76aa5bd4afff64022df00c079212d48d53769722938848e3c8d39f8a8dac479db03b438f81fc318bff1390376685d72b35039d5ad1ed0d0313fd4cff1ca6c5b701b1b68ee40824f29ad7e801faea703a3c4faeecb9b1191cfe5393097fa4e4af4a50416307a90a432185029c05398a6a0cd03dc9752906bca82d8a9753da50e3e9ed2a8517a81568d22955b8080642414245b50101a6020000ebb8221000000000669603ab2a97d8ca5b2d330102bcc05bf798bac2e4ff471ee1ac37c32ff5cd7015a75bba0f502daa73ddccab28c7505406293e26eef264f0014857dc25a1960ec93d79b1aa2239f2bdf68775871d0e1e89941ba2ece205fdf9e6400fefcb6f0005424142450101886b575bfb0af9a85695953a41737bb070e04d984b8879fd9ec16eb7bd340175c8c35e259e50d9b97ba2f28dbcb42c60cc6f06980a25648789b6d43e496aed8b086da3862a3049a759e343bfa18934e487cce132d57905176513d89844fa52b09ed7e80110c15c9ddec541ed5bebf034eb5a4fa0231686bdee977d02689553749b3bce1f2c1f87d2dc770c598a79bd0e19fa6ef31bc3ad5a9ffda57fd786daa0214c188d080642414245b501033d000000ecb8221000000000e87952b351ef766063e4803dd0c77b63dc28fb885729b1e12bdc9fe9a8285b2bd1dc6e09387eda5e607e5668dfea03fcfd6872f57c85a902cd5cfc0d53f10a08f81e466c6f822e4cbd5767442266334b6ea020048b716c5e1f1e9385e65fc90d05424142450101aadeb1fcc1877aaf45dc6391dfd2b670945e4eb0a91c6cb4d3a6e9938359e3536342f23888bbd8ed4b6150ec9d140dbabaaf88b27a2adae55ef31e57e689aa81b1383928edeae132e5eb9856c2d54be60cbfcb6986c0ea2aacd281af8119d7d4a2d7e801ddfba2d681829baac3bcd3ffe05ca3480415b861d73eb773ef8a70ea64e522504816fbcd08026ca259e153e79d7a2ffa1e7de063e8a788dc66eaceabf54534db080642414245b501016f030000edb8221000000000ae59f5fe3658b431de0d7097eabaf8d2ac2b0fc686d39875fb8ab24f00c4ee74fcc6b046b0d4489f28480928b889580efb9b0b910eb65f13fc6f22bf61313408abdd1a2b082f25eeeb7b95bd473720022abe1db3466e6a2bb2eb483938c5740c0542414245010152f83ccfd115c7d26d13340f90564df4095eeecdcc9f14f4083c7578db073a4f36772274eacfa3a5e2f24ba13208263f9f494f817b5936af216f4b8d2ceb20857076c54d1999052ce2eec6a3510add66eeab242304409df96010a849cd70260ca6d7e8013184085063352b171f9e5967e53a964493d2aa272a72ac836fc49172dde1b417ab1dc7a5c07f3458682e80e38fb171c34aa066e3e62f443697beeebc307d023e080642414245b5010142020000eeb8221000000000348741019ff173e2fa0f1217477b342531847d42f566f2e90135fdcc58cfa9731d740c15312b9653e54a2e918de27b3520e894b0a0f6a8f8c25d255d74a064009fd0952425e0514b2cebc47ec6b5fd71edb0c646b2df65162f63e33ae6ab570a05424142450101588822bd6371e7bfabfa59da8c581d35c42a4138785b2c70d8522c4b99bdff227aad6526878ff95489dc638204ce708c491ac55dbc9e7268a5f82791206a3f814e1b37c08478c68a11e08f0f3f51f068935699d1c1807bb12049e5d86b21b928aad7e8017fa79310bfe290303cce5e7c83f127ada9c466677254873c2e88184e99d5b4577794d7d767c9577f8c21b221604c7c14b6bf39fcadee0332271d31b8c7714faa080642414245b5010358020000efb822100000000086919fbec90f66eca4dffa60eb03c72c62c8c4e53eb519b2d98a2d46401e47179afc7f3d587489f93535dc3260f615c1071294910c8f968f7c50a7e7e6552105f4bd9f1327ac9587da2d7e56c4d278d0f7e79b43074ec9eae810879239f9d30d0542414245010158da3a1ecdfbf48fba601a35fbc90e1b620e43fdb5d03db3a91f8166eda213414a1b86e86e0b2af3a06348d88ad088e8f8f2fded5baefded6067d3127982878966e771d0d2511331a1dfdb4255a24936e611ce91e022787176b147e81fc01f0daed7e80117b9ecc2641c8f7e978fa446065f1795482aeb236531120f21fa11648d34aa88779225edbced48a5ba4cf1eef65c2705b857763cce2655942d03297b034ee82a080642414245b50103f3010000f0b8221000000000fe39bfca647d387d768adf82c1421b731bb73bd8931ba548319cb49a4d506e0db6048bb0f77b41befab91d3b02ba81f802a1baa6f73f0ddcfe3d6bbf7483b40a6a2f0d1639eca43e9b19e3f524a79724f40ea5d188efaa685cb3652f5e7e3c0005424142450101b42e623b92a57c3405b64ccb7f1897000233e627663bc54783bf2e328d21de6fb3a23901fcf8c98b0d18cd72cd45f3b72d4aa6731747053f36b49d4985ec05880fee01fb6a0276e8f56ddcdb9f38ae40dee6c64c419727c1c95821c1fed01890b2d7e801b124f386223940486c4246ce5c2f918e744989df64d2c8963cc79054df702291c0ebab31ddb25d371f45815328023ec9d4bd46b8dd42221090b87f90ee13823f080642414245b5010149020000f1b822100000000018b5c7713cbdeed38b57aad4e63a54095aa8dee093881242762878fecaa8f840b90690687939b7c1c0c894e8dada8aa82864e89016fc05c99d391103f75b1203f5aa880e51761abc3f4ff72ae41fcfbde5da036279366e01f422cdb4b9d2df04054241424501019e8fbd6ae30a0bf7826363cea37928e68cd8505b3d55c80876ebe6cfebdc767760424ae63aa47678f616f33f600c18eb1f71c30382eed993cfa4c9e0bfe2e98231d9178fcfe3533c995be57ba314769277e75bec70e25f17a980bdc065578970b6d7e8016c3fa11c194a33131bce02f973ee8b84c58c3bedad9543acb0a2a131c9d29a056e3cc78e87875d02def9695edb0d472f34b16ee4c4612c5866861528b4454491080642414245b5010305020000f2b8221000000000868989d8d799b03c2c3df394db79320cc7d3e3e309914e4177b6a57eae74bf2ae9e1c45dc02e6c6df9e51539441d94c48c2ead536836640da5344f045348a705d16bf16719c3214d2a6fee4f7c7bce4a0b84201d61979ac0d5bad00bffea97050542414245010120f7753a478565a71166ac773dbf0daf68d2130132be3a0c16dabea1fa7972714323467dac134446c800bd27896b0866dec898ce20d67a9a67b0cc93d6592a8a68b4f6c2de4e28730e6c8de98f961122e1c8a0f457238c4cbfd22032ef2eb2e4bad7e801edf29cb2207530ff32d1239ebdffde1f52eef8e2a57be04a32a22e260648154f8b6139d9f11737594216212f3ebe6f30a7003ca4c55381f5da2db4774ea942a6080642414245b501037c010000f3b82210000000008aced3b419c1db788ae7d4978c2ff12d86baf3c6d88a1eb54ec05b24bcca8e412f651d2b72103a1a519adf594a4f653bc5bcdf1796ecefc10f9506e7d716ac04fa4c11782ef6d87b19508b48fd4f3ab2826120c7191cc5567723ff8566d1eb0005424142450101baae86d6c8705d7baa022c275e50c8673c84bf5314b9a983c6fc23324064973383c2211319300cd67e161b80098c10bb6afbad8377ea0351d998d849c59abb8ff4ef8fbfdb669a53b4d8cc0ddddaf7c0dba044a452eddcef91d9c699afcb222fbed7e8011c947666eb349e822433dfbca37c7c0465c1647215fca8b50b3218a2848157b1d6efec883c14ad4017e286bc97c92b4a3ffc744a0d79de4223fcf7f7f100c8a7080642414245b50103ab000000f4b822100000000004449040efc573a1b47569ea3b8efb48397506ca1b6e98b4eb18f87c2a774e592fa0faa038ab9c724896f17b215a80656a904c34f3228615cb7c9cbd1dd93802f4b8a1e1dfcd19e6c3c71df60aac5fdf8eae217aa3e7726026f9394b8d037906054241424501015c19b1291d19fd4653c791c84ed94d965462d0a484d69235fd54de72908f761cb749c3c48079b67c62f6336380358c5e7938e88fbcad933c8c4d48b2228a828a567075282403ffe463aa8b8dfd9c332e86663ccf6ae18fb5eae4f1d5ecd4523fc2d7e801bd6488df4234f1e1f32c239fc7422da9a15d00bcf1b6d18a9548dc137274fe354838ffb5c1b8b3ab7154e50eaebe21d8a075694816f9a89c702ff45be579bd17080642414245b5010174000000f5b8221000000000e828acbb9d3a158e0f991fb2ae97d3e52b076b07ff367d16b61a51cc305a011aaf30701047e6925b12920bc8bb034748b352d224510ffa8ad1f53f934123850cb00907a7274aab923e5aa856b25a5437f4797e40f2ab1406ab9d622b47d1b109054241424501019e2f203fc9af39a95ece0b900b3ddd7b3e3cc5457710e28121e6ff9b3cf7002d9f19855cbd8f1bc1e7114c4985d424e739b2614deb5d9733f15cc0ddae68338a1599de95939b17967a0d03abd57f09ae9e66d3a23cdc687ac913b932a327ac96c6d7e8011ed581ed7f96eeef717879ac4c594125b5a5045e74cc325d139a1e82d314c80c4b3c369aeac37f8191b408ab811e09cb0bdcd736280f87fa3c5cbbd33460f742080642414245b501036c020000f6b8221000000000a81e25dd5c8469e8d6c2b1cc07ce8d74cb42f4d5c15db82a9e0948bbec4db60e6cf4faf606b624fb86c4008a4b50f6dadc70e73b492937a775c27f471c97590354a48ca63480b8ff93cd535840523bba157ec91addb20023062243816c619609054241424501018273efd85b98fb059e9b49d93450d76e62c01d67da5225b8ed2652b34ae3b63fc0221e52520843c03d4d2607c96768eca158a270d4cdb41dc1fd5a95d24a048ffde1385ecc9286a5e2599494a61ced5a52d361cc0fea034ba724673932f60fd5cad7e8014603157ebce9f8167b6985738334d90bbe0a887cae8f4442925219260c0b61eec3c6baa57fd5d74ab6460f481ebbe878c1420b3f3c118795ef3f05f02ba1b01d080642414245b50103c2000000f7b8221000000000b41e5ec254007ba6e8fe45f0b68bb0ce4ffbe8cf81a493496acef5e30d94d0023c9eb6319f4af263f8c7ffe671047710c7dd171623d113f2af07eb0cde0d2d02ae3ddf28eb740e05ec15e6948dcaf32b6bf0cd1fb5c7c39754fea82bf65a7e010542414245010108406fd800c26800e4de1aae7c013a10f272598822b6f04be6c15ba2c1e27b4606bbd0f644b7626c1ffb4d980cad017e6401a2319c3fe4c9ffce005f9ed34988ecec6fd9a37a4318379c925abc8e390c1a3f620f3124144a54a8d51104eca8afced7e801404c07c781491947af64d8353ff2113259711ae29f623d8e8261068c655b85bb3954fa248f416f690a967eda1db9535ed0f03a593ed953df96d95da7d80bd55f080642414245b5010384000000f8b8221000000000e29c886dfc0b6c11050f905d2a58d48865ac56ee8205cdf79f69cc4ae2e86c6341086739fbd8f91f434af8c5d6f857c74958391dd5cacecab4455b240a2ed90f2a6b52d298626fb4895b51681fb58dd32e4eb6f16dc4d38502e6577eef35ea0e05424142450101a6a3579980d2852a914b8af15e37e0d8ead29479db9ab9d273a9f89c04b2664b55c5b33b8709f4371db94e6e8efaca9265a70ab467c7cba7bbe47c933016ce8755c51101f96a0bdea0f35044f86ebe1885d1b37777cfe31b075a7f23e609ed0fd2d7e80154de25b5806c0fb7902804c777ea689c13cd86cd8ec602b0687b7558c8a6e68ac7e34624d653d69c04c399db90efe59e44e4ea710d1dfd9da3e5a3ac2052251f080642414245b5010369010000f9b82210000000002498760db4d6b2898c5e824f506b2a3145738c80b56898c5157ea8be66b505166f9586b6f62f117a28371bb047b71a2d4ca78591f78bfa2b8bb78b2bf27b7005a239c98719767f3d7eec1314b0512ca107a4570cb2e485cf462e732a6a28ff0d054241424501015e9494f2ad85827ffbda8201ea368351583538b7298db847cff71a17cb474a718d2c0a1f1d7a4a86c98a8a32b6bbcf1532eae07693971403c3431aede7e5d08f0cadb8d4d693642c5f296e3103f9eb980eedec2eddd09e9db2239c56deda8458d6d7e80115b1cbe5116b4a7751625d0889ae8f3469d3313c938bcc0033369dd0e386f4925d78e7c3098caf117d15a24dec2ae2c5657787ed59e4f97c7c434ee9c9e81e19080642414245b5010354020000fab82210000000004c34cb0399788764ba2a54f57b5c661b8edfdf25a2fb3e04131ecb951190b022b5b0aa49769190a3b331763504eb651a04771006b8822f673c780024c45aa40607e0dd728293c89fd06ff14b57b8193c9a0819a38e4b1315061ec16735a7a90e054241424501014ea939123dbc3a25e1216304aaeee7c0279f1810342a277d1298149cb21ac472394e07ef6d1b8912d210fd7103a62c51aa782f177875da3d4fd57d8ccfb76d8a848c4d8b7138abff5d9f6989957042b2bc9dc7948a5ae6c5fd2ebd763c22c360dad7e80169f19c9237d52431c7638ead8f086fd13b293c6629bc3e6c2058afe70ed22fa0863adf8bfd8d3a5dcac20b3e1c3f76acfb2d39c2263fb2060c379938c03b1319080642414245b50101cc020000fbb8221000000000bc97e4c94f8b1e8ad937ef8ea1fe1e936f2d5a896e33ebe6564e1137f205bc244e4b9911b99de863251c6ebe9b289dc015fe9efea4d52d3f0370814a9fd71d0a48ba7576fb84843f517d7f2967901901c1af551bcfb81330d9bfac53f65c1d0c05424142450101c605c167d8030c9c8cc007ff6c9f5079d5356b021028689f7b99dbfbb5a0790f3dbe800b50e4d6a023cd0d5699574752cf8b6ce8edd25b7b61f3eb69e7ccba8f0399886c360400b8aae1edee07e4d4257975ed6f5bf2fd70f7f287887a83471bded7e80114f97217a9f4e2605742991783afa7d460c72974ba6ab039ecd43575ae68955bd1b5ad09bf207f093274aad1e09892b6ff90d1bda9e19e97b969d0055e21e3fb080642414245b5010300000000fcb822100000000022257b31e63b4821efdb88e72d06b04cbdaff673223ea2eb79cd14caf386906013aeda288f2e62c894d2bf0cca2c74737361506dd381fd55f87274a94029da048ee6618db6eff9b59f43d791878eefac429d7b464c60fbcb55826e7a2ae0e70405424142450101009f0e124fe5c6e23212d713f146e772b0012f4391e55b2ceeec10c4762a3f230598e089ffcca15a62290bd9fe3acce3d2b953dfe333393121b90e0b91c9b88a609978e5cd840e434c8fdfe70f86ed128af2427f84ab7cbeeaffba0a79f03eafe2d7e801e14693bac831ed4f108813e1028e7a2f921a2f4b2dbaf668b8d6ebe7b0db9fc32d57d0168cc6e727354193574565a1744ca4e900cf3ae94d334d11664b9ac980080642414245b501036b020000fdb822100000000098663b239b458fa3b22bacda55cfc7bb917650d0d50935c186fae04fd8342232421d22a44c58d17356ff3ac641f7c7f59f29a959e9e8ab9ebf481d2d4f6c6e01f28b77883174beb784fedf15153ec75b16243fc1242b27f6a59e06c7782f4a0a054241424501013a54846548ee5b2b9da444fc1782b5a889713add193f06ec4499e183ba8fbf58d47f78d80e1e33d8699f7c4a01b327f89a93666d8d3b7d23cc6faadad783698066503dd8aaebe166a20a9ec091c2590334f1f2e2c3774d62e6499882b095a6cde6d7e801eaa81d361cb63dde41951f4bfa857d8c9013b75061aff8aa4921f189e8df92c22ce755e3c52797f103f7ec284090f4c6bc3ef635b0c13c46bdc075053c4ef62c080642414245b5010346030000feb822100000000048dc82582ace1f571985292c776d8f8a0d2f47d07a2d91954afd052ea03f16434dab4b737e6749a8f031960b629fb5d2b4271c8d729176114611377fcc8837064205fd222419887b04a180728c5d076934611f4c6a277197d1b60ef030a27d0d054241424501012c933b2c066261bbdcb26bf6bc53e59460db947bca7925d2ba22c3ec9846507f855c4e040fcbd279d5bd7bf0190db41de8bfc90082ad112912afe8ef00ca878939f607ac5ce6486cff217889225d0e2fb1c293989501902101c4331aa8029b6dead7e8010290624d09a21d557d64d9e0940b87c6112f4bedd214bc56c0dffc5cfd88333f4ab59f95e437f55363653d237e54545c18cea48b7ea087a5997ee5e32e13a0c4080642414245b50103da010000ffb82210000000003494f8750a9d0aefc36358938612b9c3f3d3ef9881f74959bb7454255207516206bc87bff5c89bdff4d048eb05cceb6622b8b20b97f1fb49c8922170fbe7750ddf8e2e3c4cc31c50eafbe9c5e9a2077ed3cc7fd85fb9aab3959347aebb56580705424142450101e6d18070fd3ea25bd938cd03cfdd1ee1f05582921474c88605c7d039653ee36daf0af3d38fec6dfa77cc34afaad0fb11378e5bfdd8fcc151c853fbbc58a2df8ad92e14ec2790a50654cf00ac308c0f48b0cb6d30f5bc8598855cb4af30624627eed7e8013cc7e410619f6d68d260a114779d8ed561232a924bebe8489b29e7814da09cd68f52c22e1352f764be0cdfa412531b1773d6ce99f37834a7c0bf898b9fcdb0eb080642414245b50101fd02000000b922100000000094bb02f4fe1c1d525648dda0180007038d0851323820af21aba0d6ebfb028021b9d9ef7f64e9da7137ddeeed69d54fd06177344cbcd7bc16e085d336e0d3cd0cfade4f40d4722139b66a1873050e2e048c0c8b33a776374d3c8b435a8becdc0405424142450101bcb99cb0f569b0e6469b942083b23e354b10f3fa322e5af9fec96cc17650ad645cf52dce126e73578514f0036f31d8a81c7efc18eac0c05d524755294ce3e988f219a327d85223cecdf230e696e63f9ed00844d16cf0c94c581e13f9ebaad338f2d7e801084f422b78b511166f66d19db0e3e276ec15de2654a82996176b3bb543fccca72f10e824adcb9d80cc0bb756507dcafe2403440822c8ba27c2c1949e6c5a5ef8080642414245b501034603000001b9221000000000d640b2f400c1fcb09a19b22f4ede40c0f7e63d4ea548cc604e50dcf02815c76600cb553a6401cb0e4ff2ab343a42648cc62d4425e2c0f124f031a72896f70a0bd9c0948275ea18f9fc2f0e619129980d5d4e898902d77683f27ac1643a84fe0c0542414245010136caec453b684d7c78dc220a8bccb36dd0c6bd2be75701c96aa80724a4f2bf792b0b6e18317a48eeb2605cfae414b32c211c815b4dce13388761d3ba3f373283b5947210a841f75f1180992546e46b0e40b6a2d65c212f25d130f05399dbcea0f6d7e801c7c75af1c1c1b72f31ffd45fcdb0e9c115fa2b88c276b1dd355b68782f37ac808923baf45cd206854e2d993336f6cef5391c26862e9321b3cb6c99555cb53e72080642414245b501034a03000002b9221000000000504881a63f52cd5da2258bac892bee6a281b9a7bd7eb22ce64a4a1f092ee917028ba8eb93341495282ae9687dce4cc4a5d6120adab5748a1c4b2103fe6626f0037fa2ded254e226417fdd58e10265ddaf7cca48b087c5f35551476bfa67ce406054241424501012cd98bf315402d9c1ecbdb2d782332073c5556c00da362d369342ac25e18317b978ce66faca817c0443c3f6516cea295da3b14b7c34d692f6c747a3bfa25b28b46a049a4ae437185b7938da6fa5de3a4c67baddd311bc3dfab925b8fa75eeb19fad7e8019cd45e82ef0de6312858861f7c801522ecc66ac276a1814ce2e39e35f8fe8892204b662e18c7ecebde0a9c5944184e0703a8274956c9513eb861ed131c56e52f080642414245b50103b901000003b9221000000000285d0b9efc124e01246ee1c02014dabce89cf6b0c8c106932091923f8a9f5e3d93f89d4577a7b16439f01cc1316620e723c58c03d8b895a7f4912e349742800b81d63fe974b5e93215c59dd778396cc1a2ac076492febd2dc690797636326d0e05424142450101ccac94a2d3e1484210b716b358c5bb707b45de2104261fba77eabbf7acb0911811f3f0478397217338819264f778618c959495d69854daf40ff52ec480a9778e9eb641e3cef66ef7b3c449f0bf0476b0beb34d06ac972fb17a05a176fbeabc1cfed7e801febf6021b00c73b3468588e345df42ce704a0a62b3c99a1ccd51bde821001bd14c899165f85464b08698e289206108cbb82ae13a70c58a4d6dcc2d652a445930080642414245b501015102000004b92210000000009a55557b7fd268154cd1ba2de7aa702166aad44db10b614f461432fe05ee1667e2e872259373caa78848a73b4b94e9b4ea2842a34ab369a66606f7a427b8e9020c951605b9ab8326db1aa738a854d2fabf357718379157c86d1e94f73c85aa07054241424501010e80dd55bf09ed1591b0b2c449738bd77983895adf89e6215c8a21dbcc9cea08229200761fe2db36947fcba48510da054499c3a9398873edaee0a059fd37f98217e0df5e9d6ec8fc4f3b5e211c0e61db50edcc7803236bb1b1a587b9f69a18ee02d8e8019ec7b421ac689290bfda53e62c7cde3a43b811766cd7677778bc1fca5ae92668b49cbc649f522330bea0e273b3faf3f3722ef12302d0706578e8e029dcb7b44f080642414245b501035200000005b9221000000000aa9fac0896e8884af235cdec2fba415d9daed27db76ce223d3ad67ba1cb9b74d1ce5da21fb5edfdab8a991e70dd4f6531cffebe48d2ffb9f66dc09350c90850180cdeb983d68d4484c5f47503228a326f0e85f449d14a20c283781ac2f77590005424142450101fe113ce82651bd93b6a1a326b1bcf8947d78a38cfcea0d0dd8dde6b5ada8ef5077d64f158362800126717aa9b4273dc3d67f5d19e297fa46faa138afd14563855cf2fcc417c56fd56ca7fe35b9a473f860cc4a0c5f39d2accf4a475ac8c71b4c06d8e8018c4b4bbd3fa74c4005dd4392ae0f2f714fc249874c935e6ca55bdaed5123f99fffadf6abe24cf6520a3512f39a781c440c195710ebd8cd5e6c9fb1895f17738c080642414245b501039c02000006b922100000000028bfafb51c41e3c8c80f1064d93a3f8f0015ac25419702897faf477a7f718a05d24df9833af3091a2b8c0aca9808cf0cd5128b95b6a58c33cbb649a3011a4802bdc0577fa05fdfb84fc06394e784feeaebd99c262eb105e7dceb6947c56cdf04054241424501014ad22ae483273a398da544637ab7ee44d1ed4795a1db362e0d59a8716821ad085c4354d484edabe6f0147d52f9060744f19d390e34e9a98dcb06d99e3b875d845d0e08a061b7a9f49ee347c1ae0f3ef3b824c243ab43226207dc17d72fd03cea0ad8e801900b8f92a78d9fb5eb00fae45207ff8009453ae0e8bd04314cb84419fa23a9371cf523cfc28c085dce04231ab8656073b9f634efb5b472cf3be68a4dde9c87dd080642414245b501017301000007b9221000000000ac204b46d10a70b1bdaac99689725b76b862d0959065d06be7b638ecdc9fd0408ea759ba162c8552f9672ee266516b7546fc8514348bd7d59aab683fad316c0f6f25d0dacef968d9c9ec277a2784762aa4f364aa31e87163f2683cec1e50a8050542414245010162f2c7d11ab5eb8ac032d48eaa083fa19d03fa2f57e43a95a05d3526d43a633ea3b6523561e3bb8928956638ccba336fd1ffa8faf11d045e020246d3d266728ea3b1b9d506c25a69ef80d912e65887310b71a0dbeb1bb2328aff375e0e2b2d5a0ed8e801f1144fc9747d9673e2b4b3fe2dab41e95ceaf5c959809a491ae6eec1d21bdc184f2887ffd2ad0860d63bf37c425839371789da05427a9734edd756ed9c527f35080642414245b501035e00000008b9221000000000aafc048f9380e0214ca17293993f88b6b0c9025077696e2b4204a9e2f2f7f96aed1cc9f76d9ee9649187d22501c4491bb85771add9dec8861b6a1b0cfc176b07f04ad1b882a54465c68f335bcd8918fc094624727e7250fae232f8c3058b290505424142450101c4c8a81b357d638ab67c47ac858f4bbfe9d36684bfdefa5d08567a92b18f7852d488321923f724b4a933f50824372f55309e2c292f5ef9353424ea85b1cd968e39ba502700ac840080ba92a5afc051d40414f7e1f0a794c1760c04aecb9e0cc712d8e80109bf7f484e4f4b4f8af9ca87e13b916c0d41e4ec248ce347f0b5dd1cc036140b1c48ad5161c9d711e2723a59c084e604b0e73c5497db4260f7a4829cb9b42105080642414245b50103c302000009b92210000000000c61ef93921b4bf4e95a5d21662eac96d0d4b999a0cd0bc828c7ae438b721f42049c3e4476e92fdce7ce776d659bfc0b053bcb15f658d08e84cf7bb5cabfb80b43a5aa088b6829069aee6806d68f875f94aaf55ed44e95022d0b7c5795a4520305424142450101e2f636aab889b250714a3769695e4bd34ea1fbd2b23969100c9c0ac86cca45285cb733d8e12a253b8a29494c5cafc414a4b44defa67c056ba3498e96f03a398823c88bf988c1396861bdaa87a83d2157a7d64c2ab25219d4c54795d1a3eb926016d8e8012a445f11de9850891f32fdd8beaabcd067ea0d5a04b11630494c76365e34f0f2f0110c339da637320a614504fe30414fa6d09065f7b92c49963461eb64785615080642414245b501034d0100000ab9221000000000765b64a0ff33bafc96f66bf04016522beeedbad20be60fd94b4dcfe011e92064143cc4c4f5ad6ec6649b6dda320f77ed0d8cdc688d43b85490a9990947abe201b1ed9c5e9046967278fd84c3c7eecb2dba5d724388769ed879fbbd9dbb08ec0805424142450101da61ba1946f30f41acfcd67506fd7c6a21e33b698389813c2e097e642995b2412d1eaead2443725966933dbf62592ca366bbce582137957886a5e4bbd586fe810c47649d65f74a5dbe675c9e8eb1a1878a4e15095d4ed4c32d0f9dc4904b45af1ad8e801d3db8f3604cc6fc653b910de87dd2f7d83df89463c293641fe513b6f7d5e9b8aa7347a7bd520b8d73ae8c5ab6c582df6ac04245bb181f641161d2963ebc9d7e2080642414245b50103530200000bb9221000000000aa2367c3798bbd57756a41924ae6544c131acb83794e64d5dcfaded3044e4931161c881ae0be556214dfda851d13ed6cfb6f3e214413e116b0778266b7121008c36d64f4c3e6051e80bc90ba659a8f148ac6e99fd7961dfb7a09c3a7091b4a0805424142450101368737ecf9da6aa398790274b639613cc04117f2e1d2a1fdcfb1e3e716a9944dd4b4d7223b39b01c0ab35be7e0c984d160d3ca521f773abd4dc2467e5471ff81d3f76a1c4a3379b3f69e3272393691e666ec5c70d66f4e35c6e4509b506eaacb1ed8e801a9e098b7cd7f7335f4bf58ad2addea057d1cbfb497af947bf0513e71b1e719020978a91a49e9e81fbdd7308dca51e31134258245347aa8ca2390ef237484b70f080642414245b50101cb0100000cb9221000000000fe97f4bf2e4128fae41de360e420405fbab7a14eff53e990e620e488c8145f782224da49a97da3b8c28d6a22d4ab2cb9dc883dff829a5b4e0d9ea44bac778c0cbf425374be9b4fbe7c86c6c6df08af736ed729e733de0f013b3f286a718118090542414245010128a36083df8ab51f943b5c288f5b9ad18febd625b9cdd3ffece8f3311070280503a552bd19157def3f8118e9e58560334d32540688e85da43d33d58dbce3ee823bd9bad17a42b00d740354f382934ef9ff3a3fa0646aa040048eae55ebd93ad322d8e8015d701389b30a64a04c174b1cec2e3865c2c86db73aedd4c010f918af31dea8f43984cd17d69a921c5069ac2bbaac84db664b45bb031c7f73b5118cc7758e7db3080642414245b50101b70200000db9221000000000a07fdb95384ad14a6c076683ae5bba266df1640108be7f57bf0b7152a6f89c776e00a71c9a71e830cce20115fe43b0af8fb682882409f9d69da57107ba4a3400a4aefdb8292f40c3defa7fab6881f947929988f8f1d736a1de9adb553d08480c054241424501019e9f1a0350609b046bdf2a1f32a0dee9602390408b6fb1f9bcca8cf42c5e6b07d6d196acc0e82c575d0b67112dbc9647d6bb22839e3b0c4619cf75c3eac03188576f677a1fe1e1a1314ef9c854a143d853ace7d8d00363db55152e49f33ecd3a26d8e801a2274e8dbbfe1a1f5c661a93f9dbdeff0a507805fa73902dd85c63c46bbee8c4e1963b0f93875626c8b1b050446db937331abe1c4392e1b0037910fe5dc43979080642414245b50103140200000eb9221000000000e66c66c9e92d1a99f03fdb839882d24d0d11b80c1078083fc9e5ccb57518b60e456b44d28dd91078a35766bdf3709da1032b8d5870346c5dffd8dbb93bd7560dd30a3849db7aa1bbff28806adf2913302bda3028f680852ba53ea215d1a4690b054241424501013084a54738210f2ac44b8986e245908e2ae4c00e0e924cdc997464abc87e6949c86a62e7b09dd2d90f9fb4827af3a768b210a3dd95faca81aba81dc5fbe7c7836284b539bb98077b387329678b70ca866d0bbbc03054c17ff1cd963f8a4dc12c2ad8e8011985b303192ea93bb411fcfeee37e6bdabc7c76967ff9c8d9b9e28305040d801909d9d0813dd060570ba9c65e67d9ed8b4d90719bccc87cea6f5025cd8b05994080642414245b50103960200000fb922100000000078f600d5c6baf250deeb21f6f746754b196997e77b0a196df3931986f422ca57d479ea36156fd917246ab226e315b6bc6db699c24e7789aa9563e58bdbd1830cce11f05ea9a71e136e400597237082a04e5c2bffdb047ca996a0f2afdb4dd30105424142450101c4e3da797290657b62238804267e001f79a21731f926328cf7efca4f885e61657d7702488b58f6c741c7c3ebb57e3923c061608ca3840f3a8eb5a4b7026c438d75a5d153a9852b733398e692e1637a6f910d30e2220f7b5e99b4c7f376820ed32ed8e801b63756f943029413035029245f93d12d2bf6eb4c7f404e417b18976d46a9799dfae5607c5c36d8babf7996dcb191c5356e3466ab37b66617447af5630fa688ce080642414245b501016a00000010b92210000000002692cc1cc75f0760bbe34a30f7e0372d5930eeca7fe3e635eeab116494675f5619f13805d74cb1bd363c32df4f823c4604a3c5c6eb1fc34edf86fccbd79b990c1709a9f99e97da45feb77d58c26ca218e6af74232af2996c39aeb2646a3e450205424142450101ca488dc2d0fb8032510867969ab8a145df64e3d64a4f676baca5502d78b296354008a7132e2ab3e298827557ac918912fe1910ab11786eb024ffe0087870f7810f43f90935046dc8ae86529a0c7d59e11dff2aab93c74214a2dec6aa2491618a32d8e80129c194beb760948a75970a0523fb41c7725fc256c63572b741b83067824f511d80d3677cff933509655ea4347f0f8183be0db00a12a6424d85208fba1635da9a080642414245b501038303000011b92210000000007631c85a6cab8d3bc5ef911256d3d83dc3279f01639660b36445aecaf2120e6b17c7eb6629fed43515797416873929e6a7414c68581b79e1ae8e318eee2a50014a1793520d78b6ddb3124206a87075796c87ffd91d7a49b792b4f019daae620405424142450101b65f0d62872d79f73654f27ab1c6ec8678bae7a4ee01de10c8c1cfab40d29713efa22496f02cdb12b87768e7b3260d7654bd23df0fdf86058c23485177eb528d907a26e397fe4a9e66108d292e5031519cb4adb88cad851ba8e55ad9e36cd51736d8e8011c0b807a102ae82610d5012fc9cf36117631573d20295c77db53f27059a6380abe70ccd81bac46d8ad21875e4fdcb6139a0f1076e42746f60d9e21988c121336080642414245b501011901000012b9221000000000060742ce5b0617f2a746fa9f64072146030d5692be1511c6ef8a836e51365d1d3ee2d2ea068a999e5a946ea653b39792edb4243b4fe962162dc6d99ffe6d880466becf6d52218fd3dab50fe39627d346f1837153e3f2c0a8e9e28315f906f90605424142450101da980cc92aceec7dda611b04d045c96107dd8ccba4d67fb2136c518548cbca3e89f4f3d751524f1eb4aae2565863d7126cc5cd82d52f27614e07317b7d4c1e8940473e216a28064e08cd0d21e99082445a69811829ee9a67679ca0ca9270cbcd3ad8e801e5c93684260f64536f102eefc1b4495babc227bc282c41c2c79f652ad60511e4fce909e151e0211f569474519d466b6366db7fff6737089b992e9e63c738aee0080642414245b501031f03000013b92210000000003ae66193475ebd29181d4b0450772c12564d1c0683e0b5c220dbc61ecde0961cb94b8f59b948a2d3a8cdc0c22d49791cbbc30d0184b4fcef979fe7c924dc280ee8753e3023c0976579a3ef210929ce43dbf83546a9ce45090e3b2f5f199a5f07054241424501019ed56039e4a8ebbd357379c3913f69d85e5725a8da477f9f885b9d5bc4280f2b3d5909a09f82d8ebeec17996eae38af24bce34a7c3ff49b43366092d80bdc78a3daaee182dbf62956e7bf124c0c354574264ef5fa9ed929ca7479254c0b9cde73ed8e8017d02011cd614b662ea551468d7b735e837a9a3936a6d566c19ce3bc8d907e30f2e55220a96fa9cf79b93e2d50d1250324fffd5aa4168c4a8dc4f49d3aa9ec94a080642414245b501035800000014b9221000000000e80f6836286ea86c80fd01738d696d48879bbce8361340d55be170273c1d9e3a511882afef7009b7739a29b3887a5671db2519844d618ffd50e33cb4e5e10c07e3e09944bc353f03af392fe7fce80c3f13667371063eb47e6e67710f31c2f90d05424142450101527e278c97ba80819bbe0f03d0ef2c498f8ad649eff9d0fc6918f0f0b603dc24b2a98259db666f9731100033042e7ee5068f5b5f0f3c855694ead1818659338332c4bcaed3aad6bb316f83d66d86e118940406916c9a5203ee84bba9a1c318c142d8e80176ac1d866186d59b4c008e5f4a2966ab2b011464ed586bf3dcfccb856d450bb2fad3569a68008dd674de757515e6391b0caef1901a92f4e5cccb7c0eb19988de080642414245b501034a03000015b9221000000000e67b4ff3dda2a2818c99720de6dfa1135de47de39ed951ee6d42bf7d90fbd909c5a5140d5f1d7da61febc3df2d6082a734a2e4cf2cadde91fbf82e44050429073f9d5cf0fa996ccf8abc2cbfa396d6bf6d79d3041d35fed1ec3cec269c9fe900054241424501011cbb8ac9a890e58475641b94402fecf7ec4f92e2a12b6e3d50a022814485710c672eefe3469c6609903b66d2f1448ceadeaac8b5ca8b4a02ccf7c71a96a5ae8a450f5694bf4fc8c7dfc6c3684a49e8b33b56521f425757e418d828cb342bbf2246d8e801ebc29daa19745e7c3a1d0600a7d773ea0286ee5bcfbeca44ec44f6ae17e49f7a4e4f5feb050374e536eb4184f1dd76cdf88af3a5cfa3eed01d77a7bc5d5517c2080642414245b501039e02000016b92210000000000a9a757ab0fd4321335eaadad4647134c049121806dc7b1c5e314414859623654c69f3e379c911decbb5f25848ae7a2356ea324f10d9b594f1d468bede648101aebef6743ebeb6139f87e2f34d9bd5269a11a65d8dd4b7c7c1f3f51a0763d70d05424142450101a4a74a7e36be97b5d1c1390b019f47f606041de026621691877fee4d5f58a645c9e6e0359dc08a1a3449cd93f59e93310c63bbb08b25225b4236ae0853a35e82af99ecd6d5d1dc0c905374cc4e03ea2dfa6d90f0de6cc2a28c76b957e6c5a19f4ad8e80189502b59fd73598df194019d07809c4da8a281b68139273639dff122b98a17e7bdff55f68ce3691f214ea86e9fe014669518d3ff3be1cb83ddf6fdeac0ee7338080642414245b50103ab02000017b922100000000060cdd47cc833f35d0a561bfec377dcbb021fa585afd5aafc0252733d23f0b84aac28b1cae496111eecb6586f9cdb4fffbf557d15c4816d4bfb3f90b5c8e75201cb0ace7c31a164b863373ef74480419dc63ea1899d1193f4fce34c5e3cc29c0205424142450101069a38787b3c4d553a6c378f0dc660c1dae42006cc5a65fef798a48e2b985a32d7a6a59903eda5ed6a6ede8291f8a13bf9065bb6b4e927d7cd351e1b3bfa3c8a569f4d88155979547b43e54972fb4227be1a25c4ac46548fcc82bdd2f14d33a34ed8e8011dbc2fb249ff0fc7f296c47d2fa29edf0ac53729766a71e222767c8bbfd62e3ab4290de36a851abefad9bb14e742e40eb9b86152b20065181a461fac7be27d31080642414245b501016203000018b92210000000002e289fcc0d4bbf8095aa834a4b271d8bf2ef9e9549b092cd6cfbc2c621dac147f5b8529142196935e0eb2bb2ea9333812effc5a99f651728e0d244867dc46a02f4fe151a6c269836735f5af83fb10027556e2653a8633323dba86d4d5f67a00c054241424501018694cb102e312f79ee3aa5d8035bd6d26041fc7c68b5d98bc10cb3602ec9f75ed0007fc2829d4b8b0a8e4333d00ad0975746bce341eb5400d0ebb039d2afa58c7a487800e42fa1f97534372c2d9482571396d17d00e028693ab5b8e9e302b90a52d8e801798b1ec61a6ba2a98246802a2eb903a65a267bfdad2aaf9bdd194e8e81315207ce3db5d7c8ee38cf4f6631a93142e1cbb6c7f9b2764667fad5b581b18051fb6e080642414245b501032302000019b9221000000000d6865008837d70d02085bccf48dc54b27a8b2bcca77660d5604cf4ebe81e5438e42adacbaa0268adf5b0979ad84977c5f9bc10979e4f8eb6e1ea0bf730db4b085c0b319c039b50f1163027b31878e5c80fee52a3ffa3a4de0100b19a7de7b50e05424142450101e83b9fb56c9ef7bed4a52fe848ac5d890af4d82c6f1ad3345224fae2e7fc631a8300fc45c08ad546c8e4881d6bbcf92e71df53b8e4c2a162fbdc811731eef685aa650b7f07b31e5683022c4d33b94110e88f85e5a901d7c3662f802a9fee991c56d8e80156cbcfeef4d7a8ad2aa4eba1c6069ee4f171f09e3cc96998846c961885e4b3964d68058636ed05c8a1cd560efbd4083fedebe5a3bc7b332182ca313a29769fbb080642414245b501013b0100001ab9221000000000d05adba9177e85031dbbd5732acbddd237a17c2722a80a61cfff6dfc0ec9e65d305695c24281bd6be374990928ae1b5c17d7dc8ed82aad2d3a3d313205cf4c015f00e197ebf92ccb8f2667b4d9807c1aac8bec32738b6eb5e8c07aa792c6ad0b054241424501018c365e46d4f83964f0750ddfcba20463017c6e3003ef4c4c1f4c4abb014558710e627d89a1ffb7031de57338bbb2f93cd2a6eb5acc6831c77c5b7ff9895692804764d5cdc000aa1cb729dd42229748c9648b11aa86b38ec1f193fe164c1e1bed5ad8e8015fb12d74d2834b993a928238709796b841a2c7c0743c49ed5fb495dd19c3a55d73cc0b984c838aad02636ce84c02e8dbf251093ea35d59cdcefbaf6b8539b2da080642414245b50103640200001bb922100000000064582cad56d8b9421c8ef5f690e59d2ff267a6076d740f0b975fe58441024f4a07a0089adb7d407155764859f05a69090b05d6d158231d4e8b85ea6b07472806af43154da20b8629412d3ce071612c67daf26d063deb702512b1e81b5c037507054241424501013eb2274b3fa5f5d2b32dfccdc4b97d2cc043aab56eac410393dc63203df03b6c5944e5e88246052ad2727d7304842d42b7fed215f8e96af5fd72091e9a263083a84ffdee1beccf11cd30b888f43d32209c0cf5570e4aa1dcdcb0c3f0be85b1875ed8e80140274768e2d3127d2d83f1f0778fcc10f4cacb4162366b6b122296953a2220e4605cd12e1cbd5bd074fd45e3b5b3c8bb9b490aea3779cc6e09691211641efd94080642414245b50103ca0000001cb9221000000000946975c34d3fd5daeab9f92018b237e47e4af643cf8444db2438caa82d92fd67dbb656355631620e28a2b35076475a5400680a0840de3ef8acb00648dc3ad3045587ebc0dcb849bed08264b673080ec60879865a2b5b5da905ada0cc88e2e006054241424501012420d6154a8ec0693327bb61ee9025bc1283473945534bc8126019f965b2123cd833a5c9eb596f2ca2e6437e024e85be4aa653cd5209ea6567ec3b39ffe5678fffac8dc4ef4081ec6299dc121ff317adb9e9c8ce9827e174fb98770442c326b662d8e80115701c45149e2eff9e40e6b83fc3f15335c2b442958b8e2fd2545273dc74585f4d6b303b770f0f170336ef3ffe9a2d91832c130b452a3df1d35830ce58f2687c080642414245b50103680000001db9221000000000b0415a81bf4d8766ebb62715f93bfbff4e4dfc9adf9e8bc97f9a902ed7929c6f36ca4aac2aaa15810cf4f8296151b13e25f0aff859dc2484dfe12fbbbb5ea907f306b2f323e08781adc1ff18ca93aa0dbeb3494ab6bb622c80f35000bf74ba0105424142450101586a10452792912f93d361d034f2b7b37d1111433b9b7dd73b92e07084b35e718778e09202413bc908a985e68d8c0c6aecd5dd375d2b4463021886bb0dada78b83e977c5d09c054dc4297b7d26a4b4c134084cfa18f7dde7f90138aed21e30fb66d8e801051a7c2fdf097e2542877d2d6b1872ffe268c9d2609daaaeed4918f32882c7dc75d92c5c0505dcf3dfe86c547953c5e707f5a3352b6ea56dd77af13e28dd0d71080642414245b50101090300001eb922100000000056f25a76b9bf4a3f1204f3265f0bdd90d830991c8928a9c6b4ef0654d4fa2f0aaa7e8b91a393a18f04a488c7e1e61f59c15eb87ba18fd231153c20f38dc9970e7d4c8d85c734665897f2a53f2f99648313f475615aaca6862dba686c4366880f0542414245010116b6b7ba32de492d6851eb5dde372503f2681db295aa936186aa8b020774915d1d4254d8726a6c4d8755cbc6add34493ef8794243a64a03bc91bd75293936d8642178fe05e9eb72631880fee7bd43fae1b3776d8c7b9757b0349e6446a9c5c826ad8e801ccc38e7d454e014fd7541eb449087e1d31f4c074de8525f7b4cd5cc59f35941b1f377faacd2a3bba0d2a3ce24e30727ef5f8d959f27040336f4434ac30aa362a080642414245b50103350200001fb9221000000000e639d858cf11cc5c0be80cc3db8c78ac8c36a6c3cee52e880d9c44029e9cd954a707a8e1fa15d004b2567d8ca330f7a29c2c47d42a9385776a42de9c9f1dbc0b0e855d332bac1195b975d5953c851cc6c2c345251618d141aecda91db8385a0c05424142450101d4ccd2dae8c6e712b1581a8ff12e71c83db9cd7f59ce0f454f0962c34771421d5489c7db7e26fd4d5a5238a4acdacf4171848ab5fa654cd92127ba3cdb222583e2f2e90d22a02d018be97a4cb8e05fe110ebe91c1b5f8e1db9465429fa89fb176ed8e801d86104d7ec45ee76fdfac4a36bef56ef259e9dc21529fb2be712f82f5ec8341c67bb1e6c08f99cf72af49ea4431234d3756bce93e3c5d726f1f333b641e70ddd080642414245b501038202000020b9221000000000ee7bc5a8da16476b407e95d87366f354244e82b9fe4e5a6192973ade4b8d747efc7b9a3297aeebd46ec10133a76694e4d23151df2dcf27e36b5ef6acc1ae470927140b0175d2b1d2de39023a3e9c79290f94e7a3ddab1f03e49a739343bd8e0f054241424501016a165cac34975fe47b6bd846f55cfe90383f4d2ff133e951e5c779b27fd9cd3127f253929e7061530f3ee397872486499417697347c24a6e677cea54fe6f588aaa39b9271084071b93b31a77ed8f719fa7460be32076c155984bf2bb0562a24b72d8e8013cbebe40f8954d90209d92b26c31f5624429f7c22aeff0a8c265aa23db4381cb41d3fd7a1769e17c2d5b2692f642b766a5f5f517f5061052ee65f491b72edd92080642414245b50103a902000021b92210000000003c7e448db95553eae7081c4de38a83a3df1aec52d0fdb9ab4946d82968e7e3035fab6b77b9471bb7fbf95de0df12f449f4c1109a7b39ee69cfa769329b6db0054c0d63aa96cd81284e6fd1dc6d86db6630f5a557fce4df4aa6af956971a29d09054241424501013086d907b61113ecc383230cfb07d760f936e024bf3e53b8ea0bb9a84400145952ad62a2af6fd4308ec52744eaa556027c5d2ae237a555cb4d5bd2f857c5ae8bf26bd56de0a2ee7e535a5c8b8424aed4311e6cbe436689520ec0dbb4492e7d8e76d8e80128bde20480e851e662298ec0dc09b87dbed86f430ebabd4d69de1e3e6f728c66dfbae9e1a8b101d76cf67192e3023d6563c29769a69abfe89aa47f815604a02f080642414245b50101ef01000022b9221000000000d2b9b48bd1e48955bea4d1e1ca0988cc2795dfd4428fc215c96f3b171415120507afe0e01df61fddefd49ffbe5f788da9903f153bad63849cd4f2bd73d1f550bb60ccb8c761ba0b6cde5e2624644f69915ad0bb49c2478a9adadaf6242f3360b054241424501012252c68342c3255189d1eaccbc86744dfa7a3eefe13515c8c02ae6082c96da3e6cacd33aeb0b900c9ecb102ade3e1a4fdaa260f791bc3f01bfd6b4c7119cbb85ec34fb477da57b46207648044047c1b7cf7448a83a69bb86908694a8603362dc7ad8e801625c578f6fb16ec7bdcd9213823c6c04f12cb31231891bf3ae633bdd6ca9fee5b3197ef883582631515800364a5705e6687df7ce876422e95c449bff948880dc080642414245b501030000000023b92210000000004a4b4dc3a3e3695bc5a4843174bd3e70da228bd381ab0b25dfd3c7ee4ea99d4fa9e89c177895f954ff50afd510daa06edca9931860f4ffd1842ff293d4ead30feaeb648eb3fa55e3bd44a56764281db8434a7d12f264fc87a4d1469406c5740b054241424501017aceee169ac98695297de6773f4cb45c7acd73323f0373069c03153ee32ee842069c2af0eff854db26153ef412c38c47d383bc22f013c01cb8e8621a6b4f978ad2483a383aa5122ce1011c558ded2748d8e1effc850eb6f4b9e7b974eb8605fd7ed8e8017d5a40da1b51feb4d46db88f2c20a9ab480a57303e20584419a60291f618fedac8746255daaf8201504cd48a09cf87fc1aa86e595ad6c6047203c51fbb041acf080642414245b501038100000024b92210000000004a5d0fae13fc7030d6904c7c80ea7517f1784b2a32fa3a1985ed1252952b6d19dcc429821e632b97cf5aaf11939bb4776dde629925fafd304706f652abc55b05966f3f3ddfe3992233df754e623903ffd3fbf29c03d34ae195a31a96e8f2b00e05424142450101f4db1dfdddd7ddc3a3c434f7589630bc12da6c948ac359443e39d6a3a1a8fa39a1257a342a4126c0803d80329a32d9e6792a71b3157816594e226feb10d7cd86acf33ac85b51e8d603f5606746eb728b90445ca704cc4cc8cb23450ac7f0066382d8e801940151c9905f23a00312d20867d5e2e453b63d6ad313b7ffe303f3afe91d40865c0c0a8ee6f66214fe3b027b944fe5eefbe08a1a8b118d24a02cec66d16fc037080642414245b50101e901000025b92210000000007aa9a0b093f3298962650a1111e7bcbd0d2c3841473909f8265038a0538a4c18a22b5c7ae9c94a67d221fcc9bde776a19c3e31d8f6b63507eca7949fd971df0038b3543cd7da4be0ec13c214e787278facd9d9cccc2985942943682ffb35910905424142450101b2bafd4e2eaccfe6a21dfdfa665f17f41e682331a3329847de12d579e777ff10b6f910e3291e730486a5400c02f5dcbe1a0dbab59359a9f2882535a8273aa985c66b7c2f45964dd5b86a1bbf04ff42f0aca20cfac9e26c0e1df8e054f7c05dce86d8e8016cfc618443613d24996bd618a9976b9f90a69b9e8129600911613844092709a53b9bbe9fffed8d9cfbee30f79ce5094e52f32c2b1fbce96d4abc95d5e0f4ed78080642414245b501030201000026b92210000000005a844513736ba5d9fd215f90a385c965612d12b936f54c70db4bce13fbda8b6b78fb88e9f0961d8e5c2e59f30713fe10bb66e6fbc08484e9e651283e2ac74f076f84d2d2b7f3443814132afcaa88c367f4c2221bb732aa8cc2fb838353f5f401054241424501019697c1b3738147cd7cae22cc4fac913388bd287c5ae80dd8033b10588ebbb56d808bf2d5c8b2013801ddfc14ba9c932f1301c7d56e87f700f40e3f25d2c49381203715e9d625d3a4c9034edbef18faebe1238a01d73a56efddade9e50988000f8ad8e801d61a58822e9d38e39384444337f3dd236b52d098fd86b990823b6d8fdf5a56b22761a8e7772b0c2db5a447b2676f399d6521927175e6b172bf3eb8e12c3a3f5e080642414245b501033a00000027b92210000000006ca24ac519f6bd4bd17c4b23145aac34804c397642674200bd1a1de8bce9cb3d77528be56985560f3da74155fba3dfeefb9eb91679d3a493546c7c7f5d0d1d04da6eb4baacbff5108f044ed4c51be589e7528e15705395c7ee106cfcdf04a90705424142450101186cdd317164ac9a2b3154c66dffa2b8853ad406205dd9ad210e8fdf5ff7351716223af0bca4be401eda42009b61403f5a85d515330d5368eb7f8baab7f74b89b09e58b5ef0bae1fbee8015c35b8f8e811b3e76792eb2801b7c63f990c4de2a68ed8e801cbfe205c36b1872d4b5033a716b4e316cd3e970332c2c965126eb9afa78470f8d0e42662e04094733ec3ca065504a96fc2298790aa39f95225a18582c7a67c5b080642414245b50103dd01000028b92210000000005eb6a61e632e5eb63dc718b8ddd517fc8cd5b79fabf62f673b746b47e183584cd5067f492db405480996bbb13366cb5288c0af6ed5633f2ed4a7a597e4ba020302619e4fcabe0a28a464ce88c65d39cb52685bd5f0a6eb8e4d05221574d8590b05424142450101a6b0c0d4f87a177ccb58d1e20d688a66a7cb0067672bfd88f1daabc479c3182ca02f66651d12c1a5ba20a24adb3307ceaa7a4a25f5d9e34e74542b46b0ca2c815c1a633ae0c7719dc51d9894115b02a76f6a7de5c3ecfa514db1793901da4ce492d8e801e43f6855daf78f8fb623d3a0902a6f67d94db486e1afaf0ac3d6610d7640de79f7588e9f957f6274c3d5758990c50e3f7e8618b0974ef7197501643543d7931b080642414245b501013003000029b922100000000052070144d440e41f56be32c7754d480e642dfda8ea0acd9752b33b6c1c80966fe0e89a9c816d112fc63fe34c3d25989cb5e37b7abaf894f17edf91aa95d7400218f55d1e47e4c7e3d21587ad06d5ec12493cd1bc288793d64172d69cd79c690e05424142450101ce9a11558a3c43081fbcb282d6ebeb69f46d6773d9f534cda00550eadfd2456fb3ad050762c4e41db192db52a7b9dfa8f223b5aa8813098ed0d2f924340c0c8c00bdf8757be23e121a0dc8c999fb576740a2c5fa07e23c537078afcb564469be96d8e8013038b8fe74031f3a1a2c4e4231820a83e435183b979dc9574e5a4fb99d1ad91114fcf884ec1cc411a9d27709fbca06d18781bfd9d335c8ba8615e7afea0b47e0080642414245b50103ac0000002ab92210000000001c0508e7559be394c1525eb792535d31704eb499a558f444ed994a97a912253d6493aa3140985dcd0ab22fab7f4b1e6e8629cad512548b9e371e1caf29422401c2306b25e17bab11f3c7778ebe4769c553addd26e97b00b2c0e112785b14e704054241424501013eae7e8717a195c2b95b24dd5af91a3631d5b84cb4335210dff76547cd1be66df9226dc804ce43d32de84c39bdea322ca82b697876d9ec62d5b573c28ac897811c9a6029abfac1a5091624e942ac554ad1830cfe61302900f19d21af527b7af39ad8e80191fc3cf928f0a14d477d727685c0cd9261367fd2da481ccacbeef17a8c0ebade1fea7396c132b710a023f3dfc67389521321fa4552a63ad278e0ab91dd7ffe0a080642414245b501035a0300002bb92210000000007a0ed95f0a9cf9b13ead774db67ddd9da9db69d03532fb192cf2a8a6b1dd8d0e0fda5f552226e81c813f5823bb2d0813187320ac32c2deb2195aacb2d257210db0890b5ed1498ac35fc708b959534cd586a2a9e84ec7023efb5e8293c88cd70d05424142450101ecbe39c539b0a2cd93731075a9eea8fd7169d33e6b4dab9ffa035b1d16d53e4e238329dbcd0ee56efb7fe244a646251988127995d9b2e610d5283aa9cc323f896dc80200bf04588ec0e3e3745cd75e15c2fe039b6632222a39b56cb6231b257a9ed8e80186ed180e204216e00a2db18d29ba4627c5a91e541172f8e791eec8ace7b5f6b80b836251e3559785ee140e07002dcc25003171c607e6e91694a75e1fe820b2b3080642414245b501033c0000002cb9221000000000d819931db963111199d3c15f806745b38cb5fd1c67b0672bf38e9d90d6568f429ed61168397fa19a204a97378daca1b51302a7be313eaba15cf64412323ebd0e30f073ef2d0ecc12e8935b62fef1905ca2877c8c69f0dcdd38157ce5b4422d0305424142450101dad6523aff51cc325ceb4c81f9f5e35af8dcc40b290995d51d57c25046d3ef710fa1a442f6d9434c8fd761399ce9e316588132e07e814052938d378f30c3eb8618df20927192466d2e5e12ab0846c5695d4fed438cb4810b33188739196931baa2d8e801d8a2ec6848138bc9a77bb562e4a80df5d70c38f963832a9be7a082c49efb2ff583f11a41733a66eab0ca7c22163bedda50c0368ffcd03b371edaf6fd8c475e25080642414245b50103b30200002db9221000000000ee4db0e31032f965e204f333b31680f369cea6a2bd792e5ec12ec40071193b4bbe1e193eab3a0cd4ff6010a1de1e9d238b4ff7efc5769a667ff6f569ef916f075a101de75477153c66eed173cf87de3b26dfa739bab3599d87df12b29666840e054241424501011239e7bed37b1325630f7ecc6574d1fddd821c5b0e5f42c490fbd5192e5ffc3645081b72c46fa7fc9c5fe800dcb9baa10a2551f33958b04f7467e43b9aed0b8de79d5e3178aa18e9ecaa7e6c08a43978b6991979a8688a9c65140b1aa6b7d8c7a6d8e801f345139023202d80cf0baec08cde92f3bce48ae616b43272223ad509ed89ae64862f402d18d82448cc9550cfa4c8be3a46ae8c96ba09244756780eb0d318df24080642414245b50101300000002eb9221000000000e634aeb200224cbc4aaf5ef6f2f4902e5aeab493120be1cf21da146b61cd1f6a1c8ad1169013e3f3c740c80fec4af7a82accd32964dbf855432004431316290d78a707b73dbc3531d22be2ea7fbc746e2d325894c5ea1a9db9d37ecf93306509054241424501015cb771dee0e9de2d448ca43813f57221007cd5e60432ad91120db3252270596ef00bfef90bf6581b4ad722fdf603974228fdc81da8be8f2481cada92b0f9f18956eec273b755b35007fc43411f780fcbcf37b2268e5e943948c7f1ff78ff16b9aad8e80188ef664fd9a694d468314e7a1a522093dd0d2eb75d56c1d2840b3b202019ca8e0a8012ef38997a74bf163646bfae234fb9b81c49b87052e652631a31ee8df75a080642414245b50103230100002fb92210000000007e03735b5ec203f7643c5e0f801d99538d781d10c82a324d4d2da6ce901b0925b45b7956d4281777d85dd03614bc9178f07b8e8f46ab625b91eba299f7fb3b0a8347fa7b5843a6547d85992d355e89dc423c12f453c6e87aa7f65b014faef90d054241424501017284eb72c29c63a2cf8c34409f6acc933154c9ca1cd73ec652989628c7888814fde802795cba9be1d50b26aeddfaee587e9401ad10b7bde5cb5556ce86007b890571fd49464e43729a99ee448dd34004f5fac47bdbe6b1916b7192b4682154d5aed8e8017aa839e87f7156336c6ed323f2009fad1c10223c797834ee4e31aa44871e7b53dbd12805ab93f8f48f73159e5aeaba94c15204e67c5b686c249a4e1c24b6fa27080642414245b50103c802000030b9221000000000c0cc512f8c67a6c08a8ff09d0923da64e91a2f2f8d57c54e8daeb3f714d5ac1282b002e0f0f09a33fd37daeae7575494e779e210b00a49d15ad1a1a701cd350bf8061adbf1408ed708b4b886f421702400cb5f5afa6f9c114da3d575ea1b3b0a054241424501017e52c874bc0b85de02a29643958a24e74e6096852e1e1d0b6412f29549fe066278e61ae9c4a9aba9930eb894073aa8a71de914b992e31ee7229b9d9f86f9988b7ab05a3daac98e54963147c54ccfcb606093a6cd0044f55ea82fa665baf8082eb2d8e801c3d4d2948b1f0c6202c00014f1183f8f5c55b1da2e1e93e01f668406a67230f81df48f6402d22cce5876ced9c122e4abc2a0eac45388884935312a024101504c080642414245b501033a02000031b92210000000005cedf22f909dd7f36783703d31dbe5408f2745fab63772754bd607710207dc12185068923fd0100d03cfbcf17917a42be163e31bcab9d439214dea12b28663036fd9c17b5071be7ef80aec611749939e455b06860e316ef7e1a5e3e6d4e31d02054241424501013c22372916d16fe80ee13b7085b9f597341bd600ed75540f6c756d7b596cbd7e0c39c02376509dd806d1aee980ccbcd661318a3de49f21cf45ff9b1d8662388276bbe0804116db4e501d8da7140dbe1f806da94aadcaa329f2ed399e0a1f4008b6d8e801b3013b28f34809ac9a19109d6178144083d226a454cd369f881e5061cfffd5bf5b4aaa519c35a779c56a4ad45ecf55a8057dea408782479f0be0247416dcf7be080642414245b501019e02000032b922100000000080d197092865fce86fdc4bcd2c781acc20fbb2641935dffe28cf39cf595c1d345ee38d6095c4d2a6d720ab38d2e0f3cd1cca3543670f83b671a6da781814a20e2c3cd1d5cae0bd31f67cad112721a7ce7163ec73b3cbf8d3cf91203797f7090c054241424501017815181a195d8468a099ed24b3ae8d980171903f2b1bdd994a3c7e6dc4bbfb3fd5130d2cccd66be388b7d24a502908c9b0f77918b981264261445c7e572b5e842e194f058569a71e757d29a0abf8bb714bb00c57741abb511aed0609121232f7bad8e8010c179d07ee97ad17ace68de0cd0eaaf4dfe2df318878c41b4a69a655a1bb4dc6a0a5cefccfc3285112b3a5110bc3c741742a24823283a97c4b7397fd25107064080642414245b50101f700000033b922100000000016e2efdd3f9d6743da8d17a52bda8f6ec62a6e91505d93333dd1be69a879c2635a92db867c3188ba80a7504c59a4fd680deadb3521587ab062ab94499b07d50fe6870b6845f7706d8a7c998fb5c0ac4ba6637d858729ce5ba7dd1ff080d7190305424142450101f02b1e9f64852bf03bc6674c16e9732935c0568e81e59890f5076ef1f53ce11d24443454593cb2ac5f9cfd1128dd647d6316ef0fe4bfb82c83d2e961dd2d4e8e77636b92287c877ef8a687d35273c59cc10804a5a8946fcf14756aa91ae00738bed8e801b43b2888240ef219d449ab5ad0c4ba4ea7c94ebd0bb2f3e9e82f2832ec8fb639e7a0ad0cb4ded0248d9b5ba55a54a4ed34864bc3613af4866cde7291de80f1e4080642414245b50103fb01000034b92210000000005ce9c66f5fd4bc99c777adc1bb519ddf5efe1f8cbc89a5f43fc85175e3eded0f575074ed21fe5538951b223b977642df2f0cefa0e417c1affe270a217c06760476cf155480f6dd6fdb8e03163191908ed5a208d9b3f24cadf38ae99c6fb2b10a054241424501019c6a5ccf83aa640a35c75c3438f8e05e22c12fc0d04704be17bfb223635c4a14994cc2d26a21d79a20b8680848e5d265fdfea31065fa1b4d31edd7cdc34f6e839318f76edffc3f23261a9ff91db0e31741aa78687311ab89394a05049b82540ec2d8e801423c8ff877cf60d01ec2d02456667e958f1fe247e74c52c89f254e5024b3664bfcf153cd6475fdf87f7a6ed9bb949231026c6c88da52442cd71155e7d9372ef8080642414245b501037e03000035b9221000000000608b3cc32695356f6d93ca1a5456258642f245860a33932363dda6a3d0b8784f5af15a4f747d1f397b312a054688995a7a2edd36a9a23baf23a6add252cedf01c8dd51537a4a92d554fa0d28aa65f1035cc112e948d07a1815c9448074e8810805424142450101e4fceae970efd13ee669224270506e25f42bb380e748292435bfa2959942c750dc26c7339efb4540d88efebac413d66f2b27637a5c33e8ea3920219784d6888c0689e77cae8e0cecd5380f05e0b18350e0b1ee4fb041e5e0214cdc704d434bb8c6d8e8016debf0f5bb5c47f7bf0ab54d378afec5dd1811e7d1b8ccec7cffb5a01e7ef1722d409a4b0266e0ec8852ade38c89ed5c9a5ee07a15f7b6df0526b03214f3b6ff080642414245b50103be01000036b92210000000005641c98947fdbb7c139988e4c3753c7f671f792db4ebea7711f880df1f939a2493fed22b444bfd56adebbe585ad039d02eeab5e9eea7bed3695dfc09ef25f2007305791019b9330f2b06a35f7c68ca61aa5f55050f0e2130add3ddb24f893d050542414245010172e7a600bbaf050c68fbd854dc4940e26234382c8ca3092e89016593383c9344e4af34efb86bf7bf632fc5f896e69f784322c5565de3a36b0a0bfe962b76e98a3ab86aa939c70015aa965ac7cdec5ca6c73b088ab764fa73a7393a6804b8714fcad8e80145d7686b5f32e3c78bbfdfe9e8fe63b9b0d1e6bbc6816de6079dacda296ed9b2effb237c661ebd9ab8781294a150dababffef0d5b963d327b21c6d6a6b201406080642414245b50101ac00000037b92210000000008ad2715ad9115689f17d342bef868a6936b61f356b5ed392df84e34592d31f6b5df049153d1a3745235af1e293d22ec749f5cd221b813edbc3d351e86c7c7b0c26a0f23f3b25cab61f45c2b2d5831e73ac248e121516150ab251ac944dc3750d054241424501018c9f8937b8470d0306388d1e8d0d5fa2b616e3557d1403ea1d03c4dbdc56e300ad660e92ba24310ecfee3f00a5bf928c7b135c9ec241adde5399a9517937cf8bfcde87b1e44b43e8bd0cc4f8a02f059f7072557ee660595e4ea42881564f370fced8e801069896df7accbd55dd059439a41cc38648e2ac1518dc84050db5a59342d900da701cbfbb46b1d777d45fa8ab18a25514d684daf87f1b1da9ede98b9c2bed8ab5080642414245b501036201000038b9221000000000fe51c6b804e4979ca8e520f99c0095d841dddfa32618327ae6b7e277f2892b6bd6e1113a35fd2e43726823b1b871d179fbdc4758e5226c30fe8ec6152f47ff049f1945299ea948b029f8f1b1ed522549529c2ecc77b54ad66dacf7705edb7b070542414245010178587726749bb29c2bc0490909dbd140be7262b99273758dc1340e2b54d2880643ad5c8b0a016cb06e68d2448c79893d40cc4efd757aebdcf5b7eb94e7245c837fda4ecbdf57239649c238378d9af12856f5a49e6350353de159815af5633251d2d8e8015da5c45e3f1406e615259614dd83ae265145864933160e6f3fc92aea3ec5441e8b21f4007742ede1311eb7cec42064ae2288089fe928e03bb4970dc11a62e426080642414245b50103ef02000039b9221000000000720a826fadebd83a230d808ae5fc6b80c09f93f2c0f3bac6b7d4e90baec67616e5b2d5aa387b1813f5bad0780474c84d2013b5a59af1e0c31dac36c9235cc606edbef174d2e787aa71d1936d5613874bfa519f091f12b58f865ffe03ba1fde02054241424501010247540e2bdc301bea480de0174ce1f5800a9864507b570af6842f1c54eac47dfc5ba2e1ffeafff69e6b41d496dee73ce8437a1169cc245b92543bf8ac68588a062756789d7b3a5d27b58a2db737d75a87e7c89f7904cdbe657873a47491ba1fd6d8e801e9a1f696ee952f05bd00717ae9166141ddba5a2ed77bb2cbf8628d62859891f1502dc94629a2cd7137bbbe612931532fce73035cfabea85ec8f5bce6a00ee916080642414245b50103a50100003ab92210000000007824a2632990a7042456f1f960b5963bb5f6a47fb7c2888fca4360d85b02bc31718f4a9f51f6e38506861fc820ec8a01d0ed919bb120fd15c884d7375917e50677495e6b7ea908836ba81a2865335dbbae35ed79fa02c6be862e2d84425de50005424142450101f4c10b2d7eaf6fff3c3d247e422d4cb829e0dae32ff99ff20a843c6565996d540bf6a1a6a12b48e172a810ef9ab615953f4eb4352e8aff2900d4f582139dce89238693684b50905e7dca23ff45311edf97b6670630aa9df16d1f20cd8b0a53d6dad8e8018b561c36a78274a32bc4bed287ffd477a0f0ac8d660dc93414536972af65228dcc66c931c1c8acbab84e7587d0633e4f7fa99576b9b60e0edd1b6ff7b57b1099080642414245b50103ac0200003bb9221000000000920e8afafc2e6db824901c64b6b25d58c8d5898be015f0641439663a63de1b698fdebe68e8a9985f7b44643350dbe7ce2157aeccfd0f6701101ae27f861f750945f73c0012658ec7c05bb71ce8edf155d7acbddce216456c9f921713b40ed40f0542414245010132656790a4ad0c7a82ad62fd9eb12e2a74d92211c346394ea7b93e93f8a45f12a127f5373a85d11a2267e8d96fc23d5bff73089c994858e4ded2afebcac85782046e7bdff04cc420fa4bc83f6d96a8de65d845bc903d74831f988d32683fd534ded8e801be18ab14f0effda6750a573e5bab50cf32d4634c7b34c0a81759249a3d3f8b3afc56a8975488278ab6a5983f9f97db929d51d2e56e912dd0a4f27f78de604068080642414245b50103970100003cb9221000000000c83a50edaa63d8ba623323ec611bc80d40b86fdfccafcbfa3981a9358983f857a79f5feb2850eb3c0cc4efcb63ed51adfe85bb8d61b3a39cccb1c103e2cfc80f7263892b4d5d17d40e1af40d597c2a699e8ec32f3da89de1042ab9faea03e90c05424142450101b811a6813a5d75e83046551827c9155189e39adbd467fb59db00e4125a2f491506f9b223c728d0bc48c9634f8e60e1375ff21e93660b99663254033afd13e388653fbcff83ded94d6ef19ddb6e3f546386864db98d95e207b0f728876b527d3de2d8e80168a42a7706608bb57de6b34acee581ccc7c441f45c6072478e9810b27b4a7315a46f0bdee37f0356dfb623b5659415780516f7465198314b9a0f4989a9d9975f080642414245b50101820100003db922100000000032d26c3e4e1f2e90b7a9d980390ebb1ee879e81dd8d4412c88ce8347de17d44d8157633fe8ef8d97e330720c6612455e6c43cd77bd77f08ea878245bae61540ab0c01ff79409ffa40bb282e48dee09735ddedae787718920cb459a887ea6a70705424142450101e01f72b15784cd06bdd9b4f4e9a7082bf26ceae2c5278d383e83611bcb3f466cca5265d4e88a30e1eff73e851754c9b8ce793b1d3d289677a63afd4848e3768a6eb10fbc3c9914486203e76928bcde734f1b84c339c82f49b397e709c1edd779e6d8e80111c7f19b11938f1e1aabcf131bdacae99b10cb5336aea9b7cbbf784e5215c1e917e8c40d132421737de2a52ba80deefe845457b88eb1fafcbda399a97a853fcd080642414245b50103320100003eb922100000000080f8cc8567ecf2d7ef0654d4b7bb8561cbf8639c9671abc164eb511851e9ba2528bf8a873a81396cc9cdb59b7b7984110f17f8979c30f9daf1c7c6868d184f0e11ad1c35b2f5910425bde466627c1ede54d3f7d04a1018f4cc927d3fca7e4c020542414245010166385e3b351995676068d9ccd91937f06cb3f9db91940f9bf4247281dfe4d04e2f7bf90710f6b3b334162d9b135acb0dbbb070570957c810773ba922e9799a85afcdb9b1a3809eeb78476074efe3daee3b767e577d8abcadca5f1fb8e64b1d02ead8e801da7fb2b6cfebd9bcca52308c27a80da6a49f3a987a1b95f0e297b3a31d4b94f4ec25aaf991b6bf3b6a5af0f13c5a2059373328423300f4032149f88f6b3d7395080642414245b50103d60100003fb92210000000005c1e49a73d051baa9dc4c531bec0a73978784ce322bf906478c5e2f09a66664796e9033ecdfcf97d75bf6b6aee76ddc7aec811081c76eceffa84188588dfad0ec4e49764fe47ced13d932a8761f91ef401686457714f3819db105409b129af0f0542414245010160282d30e5a6c8bb3aca3c23e8e81dcd935e11592591056e1f70e1e115192528c18a1b50b59d3fe921d6c0a19ec058d41cf43fa5cc6055269a668040f46f3686a608c04fe1e5bf3960fe16abb57119280e5062e9a6ef062556ff3654bd2c475aeed8e801f9dc3a1b98559a42bb5d6c021e77bbf405f03147fd3c06fadfdcaa1d756727af16e3fc3817a29b7925cceecba9e6cb2e8f8945de21306986a5040a2c37ddd77d080642414245b50103d001000040b9221000000000e2e71b7d526438c451a1f8f04f0eb6dcd3274691b5135eb7c2be1eda26dd9e416f587d35add20b9c28f5d735d08be345d81c04f42b09f7216e6ac0f4e83a2b0e1b3b4b069aa3049418113a3fc4c58d0058babc45439b9fc935b7a1031cc2250505424142450101bc7ce325cc72040b49ab9eea17af2c3ceea019562651f7c2ce2140f75391db4b00c85ca668535df02cdbad717c5b82cab02e7785f8d9378a32d0ce1ae8279682079423e2cbe5c4885f5e6343f11190e19b60e70a651135e97b4fac5b618f7f58f2d8e801e3d3705490a3bb090b30c990379706b1e422e8280de1c96c19adca110c5f7fa852cb78de87c757576ce42f913d45a5cd0ad0b583af1d56963d9c9b9b1792728d080642414245b501032f03000041b9221000000000d4fb3a74b8f6dae63c797024289e62fbfa80563a634d59a09a0cbf618ceca177909a04757da72a096e2efa9669768237a49ac56236137711190f714000e223043c24c5ba275a5f183552b9fa3dcbd67f4fbe8af399518eb0528a0eaa12367705054241424501016e9cd5d3796859da0a16b94e19e27e39225b43bb7358572f78c586dbf5044233de498ba4185c9a31e5769da3ab044539e9b895521f5e617bedfe3bbd69077d8d62560d1c584619650e27b89ecfef40672a5a4b3ea2b9c79170b4297eb6bf9b00f6d8e801c0809ae2ce0a6dd7350659c601e9b9b5c8e46cdbcfdc900e56125d96f325409c10d8cb95d94faaf76245be531cecd963259453b4cafb14ef863c6ee1fbaff4b3080642414245b501033803000042b92210000000000c72106c13a83ba56b0aa294020744c8409a90d8a26c43c060ae6198c157ec16ca9065fadfe521ac81bc5b6e8521d7ea080d9163baad94887fd13c1edaa32501dbfb72caac9f62ff9caf77de4ffac6658a03d928ece8fa57ca9198f7d548470305424142450101d836f12d700e591f9446d5258b097627b79281033ed23bc30fb073a17c4ce3281dd70d22240dc54e429118746eeba6a5191e6dbf940abcfd59cd9e1fe78e1786eb3478393564e9bc76a46759d7abf16e6bf0faf176dc35e0eb64f4232a594584fad8e8017332d397654a0afa9f4c47a7381159a7d9921290c5dc0594c92fc17b82752dac2bffa1a3373c970ddc9aab8597d89338f09ffcf331157bb3317fbda1554edc22080642414245b50103b700000043b92210000000000611e12fe0936dafd9acadf4c7cac524991ecedb2c87713399ee0c45721ff01b526cfaf8e2c9aa1e2df967ae6efbaf6e1988d52c7ed8633e14feace1dac4dc0b5e0b7cbc7d91f44cf267217f2fb8a735a237cda605627b62fbdce82aaff2e803054241424501018871e1498049ac3ea2d0a1e28b1766e4f8ac397fd5efd3e2b58f5be18ce0e0377eb09b736a2b76c47ada33fc81ebe028763ef3f2359b47415419e130b28aa48e4e93b150a5af8364ca52feeca049e8b71feeea9fc926c2e790c777e2d28325dbfed8e8013590a2f4217679c65b98d8e57df29fa9e5282db10e2ae587c42270da6e8f43938aa76b68e617e3182437ee4b3f3667780dfee5c9fe926913682fc64af0659e49080642414245b501016b02000044b9221000000000865fde1785571661621e7accdac86c6a477ec00436f23b5cf47ef89ad9af5a53e5c7a0f8c12296403d65fb3757fa0e572879c57f23576d724d6640bb97941a0ed4f99e9fb5a03b9705006e5c4dc6ac124134eef35f5351bb5fa396bc19b4880205424142450101da7f27c68bdef2106bec3cf62badaf0fd4f084dfa2e2c6c328153621e2683717c7f13edfe6d71f259ad6ec39cd1505bd628dab42d555181cbce239678c1c3a8992bcf0015d649924b3991e20036156a21b7acd0384b81edae01aa6aee5b6646202d9e801e5b14b81f716563137979c9927dadc618fb0ac21e189c051812b7705101ea58658900431ad731ffa171b593e26e60fc66b059eb05e3d6fe9ef903f4726c781c0080642414245b501032701000045b922100000000090f6b6ea17d732f988575c0dec3ac57a410d95495eb3787469ed34450daae6225d20028acd5baad587cdbe7d526243c27563fdf374410e4d28c05fe29567ff07c44fca4e4720446d4c9bc1cd944d7a21fcf1eb87986a9796e0003247f238ef0205424142450101284fd7e979e64ca19f2c5e0b6bd5e581973f902aabe3b856f2529ff50297f60c916200b1634f594799fb2a8da80134a503176496c5b1e70ad1526c746565598324f81a93aba93cbcbdb379e9394d8728cad749f3ff4810d2c089182f2466906306d9e8019bd1968ced69654fba0e7e1abec6bd4e698c63ce8196652673c8e22ff816e54d9eebf8a2162433e9203b51879fa7993b4ec3d759d70606e7a73ddeee0a8d6295080642414245b501030003000046b9221000000000223d59d8036b408b9363e06ea8f77f91749e4e4ae11bf776a60069f7837800468052999608ddaf05149d6dfa2a086c47bcf0e72a43a53e476bee3bd8e828a00378e9cf036c2a6f9d35d8382cb46050d9790504d7fd0a395819443908205bbc0f05424142450101966d654ffd368bac5e97d90f2d3e51d8a01999913f7bbd254cc5670435ddc54f79f2e8caaffb5e236d6112a8e22d64f9f71a2313ec8a66d305aea236fd49b385e58fb76b5f7e9cac8143a83c65c4af4516106530554995950547595999a3b9300ad9e801a56e9d5355db6440c82243bebd710219976c4a7be21b2f43a0bf77e1c55268bab2e2c2925434a058acee1a2405c6db0a00c4d433fdb9834ccb1c5179910f0fe5080642414245b501036c00000047b92210000000008eb09da5735e3297e174f0ea5745ab15c104c33634f80c0d75d11b153e43f94191b7239a34d3aba238f6970b75eff7e540c2fca96eacb5014b3a3d1ce2c14e0ce70f0e51d1c253683bd7153c4207d0bac5f398a3e997daefb8e0a8256a05ef05054241424501015af33825b515494d5de68d1439f430795a9caf02da807bdb8f28810fd1c0f0060c89a5c0ca704691297d1218fa3e0687e06907fdf94ed6faf258e0be9b0d9d87f6489194a58f7e6157c336e61ec9de3644f495afa04da82aaeeaf6fcdfa1272f0ed9e801b4c1de6327f3882efba5606c6d91b3f02e2055cf26983e8f6f17614ea154a67cba17d87c84509b2083f9648cbcbd8ceb69e6f7aea7409092c6f9b7486d7bc28c080642414245b501010700000048b9221000000000e4608a5e54003fb68132961898d8fd30ba8777953213fe9c774aef1f7e9b270312ce3274eed11257248a94ee012af3b8c46f20a2bf0b93219295b4cc62e3c40285f6184ef48088b647b6d7ac1732eff1d9845253ed8035fb0dbeb4f61dd96c02054241424501010c4a9e7efb727957cad364ebb8970a3a4528d95eaa7acf6f2af3951c2e83900350c46afab6b33f764c5e140ee816f749f4e80873984cafd4f03316c833319b8764f18d21b1fba7f15ddfa247f9f1d073df60f5b513464d69ec49f1981eef272312d9e801805f7c601f33c7373d48aa007a2dfb3867a8752b20681c52a16ef907d6bb9719eb057d0febd60ad064b3586036d9451182408bb81cf5faca7dc5b9bc5336ea88080642414245b501032700000049b92210000000001a2903c9ff05b1e700cbfa9dc76b09c9f2d5d28ab9e03920793f4cfb3212894e0ccddc810a443d13a58c8db908cddbb9114c7a9e22b938059c7d7b41c8a6ce0792e2b5f2b2cce8bea6e15432177bbb20281a4294719bfde85edd20429e57c706054241424501018226fe9e7872d40f19548276715286b0ea645113f4696aa7312629f1eb67125bf477b3d34a7ce4939f1ce217f5afa6b463fff8a69f06be81348cbca61cee0c85d67282f7b0f3dfa556e9bb9087037230d8f28bcad61fcecea11a6f34d6f8b40c16d9e8014194734166c971675855a9fefe651d58c34c7e38091a01cb0a7cf16ef1bdf63846d745119b3014494cb2c7df1d0e1a6b8eb257ba10bd5e1010478efc54a00da4080642414245b50101fa0000004ab922100000000000e63f435cdc7bc44ec5583e9a38c7adfa9aa45dbd16cf44d3bcf7139e114754c98d35437637a9fe49bd4870bd5e4e3282069c06c6aa6ae75c0cf6e974383d0c0c013333eeeea707bdc94a20846c177d0c201aca2c1473f3bf25a0400e625e030542414245010158a0c87058bd77818632b652e8c5e2c39c0eb1fa9a2d90d6d8e633e13caf4441e0d1544ff734a2f8a0b3c87876268e81b4fa31415fbeba87e5d9338509dacf8c20786a9cd3bb643229fc8c1a82148f45159a7b54c96c4971da5c91cc9c14280b1ad9e8016d7e0caefed8c828489d1fb00bd8530023c4707e54764e297ebbd71e467dc586e40bc67673daffe8913a3567dc8643176439953e1a7d1ad07a3d02e1f07cb940080642414245b50103340200004bb92210000000000c40b80a09b49ac6b23e7382d0fb59de6cfd7945370ac076c28b08014ca9c714eb780eafae2f25c4942bc98d78d87a22126c369c55a750943aa47bcf42d03607cb7cd0d1c562356e1ea170ea95950cda4c6f6da0231c3a58631c6a158cf39b0a054241424501016245f8ecd49393d0d183581fafd34e74ebf7b9ba5bc85b40d0e5e953cec7061592208417dcf6a046135abf6bbc7918aa5f161a59179e3e55e47350102d7b0488592d464cb90d67f3f6b2e603723d6c85c409bd736414835131447ce4e01631c51ed9e801ffcfe9e8836185866f81927160bf131b310c9d4a699a8dec5a046d4acf17d027df437cf09aeda8aed8b7e9cf1bf50eb0725b1bab5d77a984ed462147f5f5f049080642414245b50103ec0200004cb92210000000008a352b77251cbed124c1745ee9370084038f3174cba302ecfc4d8ed425b25b55f969fad8d41d130eaf6112102590294e22b5747f86f899250b8237898259b607553c9605d0f9edec9a51273b4f4f83aa6c60e8f25b924ed7ff0a77d46ffaf70405424142450101468ef461275dd7ef8673e469078014204265f1efb2fca73bb91ff5a8cd34de74ac8916747adffae1464f531ae697ffcc9e80ba2693ff040c066034244335bc811ff19d8bac30dfe8d2e6314a48a1e2cf39d66079177d69b6bbd1b357ec973afc22d9e801886489847a59bc6c6956134ca8a47c9e9d4f12dc328d849b7ce4c4fa478996a99023406f43c7367c17a076a0d99ee8e71847bd2841aedd64c1d21a734e8a830f080642414245b50101fb0100004db9221000000000f001008d249b48e624b99ceebf11e05661db83be81741f6e901a13c98df87f5f5f0c3a700be39706b972ffdc762d697f5b9fcb8691d647921f64f5d9422f5802485c617c7d04cb4b20eb1011d54ff98bb28e2479676911ddce0cd5d2584e3f0a05424142450101b83e7c57d54ecb8cf7b8cc04acfcc7bf2e2cb49410c75d4e75ecae9d45fb8961cb95ba6c5ee2c07e0d43d42a212ba73bd6c6715b7517a4f94f3b23202d76928b4ffdc5db20ab7869e7208d36a4fcbf77d4079378f4bb8b271bced33d9e391bac26d9e801f1a6f2dcbf4aa7a24802addcd52d4f76d1b81832c96372ace6b1ea4efad33d791f4dc46fe8c38826d1a01c678f6d4c4547630a2fca55734e2ca5160593477ae7080642414245b50103700300004eb92210000000006895fcb54e82330f8d6de8c70c90c6141e42b6e3d1125c260646c3b1b4040c3e1f8695920d811127ce039ca9b399cf9c2925ad760750f2d8ebf207c344331a0806709f5d9936dab5f764085cb41d51553d9da11ca2ad828790d2f2b82dcf300e05424142450101627a5ebb3f857b0cca84e8c7d1442556669bfa514ea81c8769e914e8a64ca54426e9a98b3e4ae6da80b85c578f28bdbe3fd8b7a18d3f7cd9315a6c831565ff8eb6902d180e1def8b06b55ef81b30e9bf0df4a46f0521f6bee09d9856b7b0ab1b2ad9e80180b9e69a9703ffa56427278db851e3606ed09b15aaca4e0f8eeac09bf85aaf8abbf2900eb1a30e64be6b11194c423167ad12e69bf11f28737a1f0a4aab858232080642414245b501031f0300004fb9221000000000389f46c6f56d26cb4a156054fec1b8cad6d3908ee262165adee3e75352689d34945c8ce7a37e3cbc514ac975b185c606e9202d620b259dd365fde82f538e290cc79a3401a2ab0835b1e9d3950e19dc746aca789a837b98a50a752f2a261a9a0d05424142450101d83139a1b4b1d200f51eb471623627ab437951e8b53c7321c00d544ff269806ac472758dde6eebb7f429b7786ec2671b1fdef4fbdd16d9930d57c3a624e1e98032f7a8ea1a7323fce803758314715802ea971a873a6ff9f32427c54f56298f9c2ed9e8017d02ade13b4832b195c9d6312e742f03ebf8a90c85bbc71e020f25ce70f53ddbeb604e0c2edcfc9c3dac22d57a332308bdb5e8b7b696aa41c0f7a963cb8772a0080642414245b501034f03000050b92210000000004e8dcb659341535f6bb4c1b314e0617277a9f74a8355d55c2241a19aa8ff623f681184d1aa74edc8f76b40bf542a75a3d51c202fac601be5c8f43db8e616bf0fcfa61f20b9d6088296a49b482465cc28f84a69c0e3e79c3343f5a8b50410dd0a05424142450101c07eb0e17705beb19096ade49d010c24f3aa6c6ed43a17a4a83b4a91cb78ab24c67fc138bf95c32366d3f5d3b450f83a091980b9078872487acf44713d996b819ef560c652b1fb35ac7736b8730587a0ca9d1264cebd92da8e20307eb7a1805a32d9e801be5e14b3b1d98780faa90e589c7fce62ef3954d81476fdfc676def1e09d9ae893f1816c737eba024f58f7b43cde3deda5a3e327487f9d42ea760049927ebd56c080642414245b501016a02000051b9221000000000d84d29fab16ce231cc5ba8c8146fb357b65564816bae9bae57a701237e326c4b2cbbda847919058d8435de0bbaff94627ab352d22fcc4d92a40565ab6ca34f0a471bf80d06b85d6179116b14d3131c896626f2726b56bf879782723d403a050f0542414245010112a8755df5e9dfd5def037b8ed8218d9d345f2f98adeb8f048e0a788e2f2201aa27c9963a71dc50fcff9132a3be1f45df9c8aa238ff5e0794364f266c1071c8f77848d6c0a1f35469357cf7bcb2f5550a24646f064eac4c1f57dd0b1506f22b536d9e8014fb80f44b73abfb6b5fe430f103846777b125db2d50c81837cf95bde0d74828636148be42f854fde7221762b0465b6a7776383ca204ec921f20b264e73451da7080642414245b501032303000052b92210000000006c5fb8dd7e4269d7628e006d12b4e90ea70e2884647ef15ca992c6b546f4053c2e6015b131aee3d534b71642addcaa6f81ede09e68b85fcd5a1d2112e535b90d5776d3cc260926c8a5f7bdb02cbb32deac7dd696868bfbe1a1c12007b868270a0542414245010160582a559bceef4ffe9c5c5ee2bae5f995a993503aa13a1ae06db65f515e415c55939a34db16862323ba878b1061dd86f42c64835d0d78273491ac38832d9185a68286d2b2872e8238c6658c07fa755591ef7df68dee9e2be40212b9a04670143ad9e801c10eb08205f0d0b9055d48f013901d042e584f51938843060c0546aa4f989e8c20418072a3c146354babfef445264496e5d15bdcaf608941ed7f704e65641347080642414245b501036500000053b92210000000007ea45b037dadebe5bc7bb3797045017814f5abcbc3b69ec9580169347bf4126caa4ecc9d6c382d7dae5a678fa6b01d492893d5b9bd234ccce4801e9abdb9430c50e0129facb7aa4b0bb5a52c6683e241a1084d4ec43bbfd3968199965c7720080542414245010104b37d09c32a5873b4329e61dcaa1cfda7dfa9201c34a10910af0878569ac2236f625dfb6da1c3fe21c25e023bcf2800620df6e13cac311729a76e7361dd5f8cc832a112959a23f5c38139e9bff66ab35ab3198f26a51ba56603ef0c5c7967183ed9e8018831d890bb204d3e8bc2c1c72d437b22cd3e07d1c0821d4d155f30960381f1694f2e4b2714e865d6c1fffd4512ee10a7291c07cc13054d782c2aca7120c6f150080642414245b501037901000054b9221000000000e6b66bfbf4c36092a37ccc117b6e33dd14887d88fb3061366acf25e53892f87d76bf836eab0f6e401e3b08b4386b019b10b2b01d4bbc261de33a3de07a51250f1ec67e0d2b3f5d7ddabdcc9ce3f877524ca75bf8195ee0bd4e8c5030d9e2540505424142450101e0e553a3b90a915ab04f5a430065f3a305919965df12909aa5908094abac25404e4ef437bcb767b4205c0fadfca715c7c220eb7f83b33944df5c980d74d7278e2738bb621bbdb8f8e894d61788e2bbb90087e4f7192758879f9bc46631765e3242d9e8014e33e0bdc738f58ade7408232b41ff5b6cf3f0182512c9ab945af59312f5da1cce01290723c841a8d23aa9f249e9b8eaf3c3cfc48fcd84c95fc0856702d66154080642414245b501039900000055b92210000000000653d500f5520ed1c9aafd31b82c0f1d12393a711ef2f5ee4c73d476b403cf5143baa3e176e00d2d9c9025482cffb64dfcdef451851004a1bf10ac3d90f1790a0fab0602f9c1fb282f85ed4bbfb77046bf04b648dbe253ee19dbbaaeb98708000542414245010184704f2cfb1aad0591f542ad95931a1b554aaf6101511c67c94881bd57107f156fcd0a90451c7327e728cdae2f39f6af13cd457a5e6b7113c9b9fe97698d388f94c1c27a3a02e531cde7db850c3861043df89df6aad6c97761028fcc19ee707d46d9e801d9c444d40a09b78d3972be59a7d6a84712b5f0f6eed60f584c7ac723367c632e8dc4594db20f2035ad9e06512d5ce2fdee4b52e9c1c0d7695e9f500f74bde72a080642414245b501037400000056b9221000000000b2d07ca1cded5f43315c676c641dca43ac6951e85eb2245e2eb65d473f9b5a6175717378562bf81dc3b8df50ffa4348190606e48d14739bfc5599006793a4906481183a133c3eccd72c1bd0648d464a60684439840df05d3ea9efa087cec220205424142450101ae0743cfab88e50c4ee2255e06f5fbc52432dec142685ff4910bcaaa1181424acef48ed37cd469f006b917012d3d286732e5527764d9530a4affedb14958c081f553819d84255865f6cdcab1a0339dcbb78487eb76d9ebc3e595d93eeaad35f14ad9e8013602213d40e323117eedc804577f5b09fceb5d0bd8e6be96fc7a6dd239f7c3e0bb81bfa7f64f407017414d39bd218c1905f26ddff1f4fbfbc7da8868a79dc358080642414245b50103fa01000057b9221000000000ca67b07861a21c45664113fef8ae90c0638a852d751121a1c8c6746e359e056cee77c51efdcc7830e8c61121ffd4e3800e8adc463e1f84f81442ee59740c330ce4f3ecc7afb404ef237bde2e1af29fe7a8d0731ebac7195f5d362971f78eb5020542414245010100065f34a2837580ae123dd64ba61957db4d029f1c75a21729b4b3e6a7a9e54634c3f4d19dff6f0d7a4a1ee468c175974a32ec8950077516c5b53ffcc9f0cf8e68870a7c0ba275cabd89bc9c2cd35bb9b42b1ebacde846e4524709da9085331f4ed9e80198fad107eeb06ca2902d5ce666ed02c36afcba3f03034c85eb23d76384749a62d98804bf378b4571032336364c38b9fdb676a46475dc41e8d28708d07a95f1aa080642414245b501031403000058b92210000000001011961e79ebfecbd5e15d15b8b20de0bd6143415e55f58a93f9d79015339b77b9701da83ce7e1e65af60bf2abfa20827639d633c2bb3c3e8eac80b21a750c0ad5020a2bc17248dae52de05582970ff7c665976c50c1fc571456d6f55fe43a0c054241424501012a14387ce73babd583c72123e0c184ddfbc01c5fa9fa0491046043401b24390d0adef4b91a87cb0cf07f6967419997cc998fd67bd54efb5762da55b5fbc3f8826d01b8f75afaed7ff8a602159e70906cd6706443701a04e1d07429c90587e2d652d9e80194052c5748041182940f0eff883227af47c2cd47fd794c9faad113b3e25ec8308772ede7688bbd6b4cd05180d84efa2ce2c3093e3dcc098d46001e8b05a22ed0080642414245b501014901000059b9221000000000a4c21a8ec3c5e2f0492b9663d454340312585518df2d50a1667855992299d86069c280c1b8a6f14971a9b1d3335046360efafe43deb5857cddf6dc4d2799b105591fe34d9abb239ccab6200cacdc5ef952f6dce7f0213859308484f1ece3510c054241424501016013c1f01ccff4300904e35df3b0529a157b7b9f1ffc3aab74eabd3b92c9a7476313c7125edc9b832ad15337a9d7f12be70fbbfaa90442e4a8cd84abb16a158ceaf380755e2f63c98c164f615a0d792f352805ce23b0bc962c2f474fda12eac756d9e80156d436eed51283b2dc3ca561deb4d6db7cb3c33179f27c112e30a3b76738c64952a5ff86b1da651cf9fdc6baa40793e0c46ac4737be7cc9f36249a9bb2217732080642414245b50101870000005ab92210000000003eb21eff3e95c0dbf8871f9c95d061830256a04180dd567f63a142973a67e17ba8eda5f5f5a332d4b6c58b0548b9a0e6ac2448765d071b04877a91709224c4037b777ac7045cb38e26558d0439bffa69451f1d218a3b4a95c91242ebc3e7cf0a0542414245010172cf156928e79b5e0262192e655a28f5bfe0d2e7dfa3768d99c2c428f3cb914ae1d9cd966934eb9eb184113276a7baf534a9b4d1f26655fc269c1d755083098ba8a9ddb1ec437bb75c5813cf869144695ad1b9787d776edbea27e28ef5e3bf0d5ad9e8011da36acbd71d24c2977cf2d871a90802c65417c151fb6a387d7c0274e84958231fd1e743bb810c14ad000d5912b6a58bec4ff60b8b08d2818ea9858b9e4883cc080642414245b50103160300005bb9221000000000c2ec1c81637650226163db13b20134c737741d8ca6901fbd73c8d5d4bbf28834a06591ab6b2a936454b1b382bb0a856ed4951df95fef71be8c4cc3eba1e58907e46fad309428e669b0ef9c8185b126570dd7a51fe81e7e4defb18b96dac3550e054241424501019a2fe6206ce5d7eeaccc8dc7e4bc9c8c0875fe9696edb1c022026545ce2375715a8991529e6c0e5f9ca21996fcee56d6a5a7fa8356065f12e34f67de4d435a8767370a1ca62482cee09d992e5baaa317b0d4d6e764fc3aa59792dea500b10f2d5ed9e80120782f55b46db608ec3169436d7c62fdae70ce5bad628fa9f73bb86485b22d24343b4e20be13950e03d938456efeaea2d82ca1cb288a27131737385320a9c8f7080642414245b501036f0000005cb9221000000000d610dab5a85302687587eb779ce106f6c15ee9d149f46bda1e2e6725d533054dfe3f4307bf78b73f8636430a911485cf885402feb18fe76b4fa89c8173dc030540bdeb69435743ed7fa26e9e5c2e3786164c45030aa33d801d0534b6a19d170c0542414245010184ba11bbac6428ecf0f9bd8617ee5e9342349d875f62fcd06d37b8ca4e4ee0737698b8523b1c96284c12b9460bd24da02b764784b9d79324921d3fe506954c8d948ddb5cd11dcb9844155b63a5dc1bae16d142946d89e44e0fd4fbdfb72ed0d262d9e8012353c0594cb0b1c2125a04623354dac836693b95571cd374fc092d61acec0df5e8dd8a2b270bf400bb91e78f684b4cf406858f75b7f5c8aeb7382eecdd9b785b080642414245b50103050000005db92210000000003853c4412594576572b5b92528586c87bd52020f8365eac17424162b4590986d3a8521859af49c936863588c75894777f97427942bd2cd84233faca9a25e890e5833e9197f04eeaa395b6b2c42efe635e4dae314d74a124184e4d55a150a4f06054241424501017a0788bd4e298d74d43ebcda9cd283fe836169d999aad3221f7487c15bf93509ad94c8c3f4ee303f54c69fd2408318b6650334fe979f9b65a73de5addc7e958f4556af3b926e7178d5f537cbfa0cf658a7e9a6cee4ecc5e142a2e439a6757ec466d9e80101fb1ba0653b78679377ae0805d9febdd1451a111d0fe478b72ad5a04e189be3e814dfc11246060c68111406316f7611a0ab010998f83a9bb9f0d17c18e099f8080642414245b50103f20200005eb9221000000000845a890dca651fb367d94737023b5ab90118a770c92356f139ff78d6b1781340a6a1d2a12e36df5723a89c3cc10977d09fb3369ff226a9707fb7304c56e15802887d7fda123c37157de801cac60679bd31d2e519c6648abb8eb45d0cfe281e0b05424142450101c4604510a8eafb0fe3cec5fcc43d83e1062d77c396d2ae356a575afe31d52a0a370002c8129782f8301f740d8dc7ca6df9dc296577d77a8d7ac7e629d6b8f58bcf7fe430205de7ce07e7304dfa70ac09a51fee8454ea5d56c8375e36f73fabeb6ad9e8013f7dd1c464bd71941c4891759b12555f9fab0e8fae7dc74e2c267690b8736934ddda80bffe5802c48a9d598c84f1fb682d2c12e14f5916d571b536d349ba9a07080642414245b501037c0000005fb922100000000086e0c2ee90166c73d08fe941f17f03d6aa9d592ee7f28b4ea7fcf111f968d317f8d5207d05c6c4f94ed7d0a73bc35b25b3e3b21204d6ef1a9a830506751f2d003a0f0206d3267166222dd77bfb409b5d68d0044b03e14e7ee9fd0b290bdfa00105424142450101eab464de9878bb20206482ead81efaa853be5993724c519835c9995760004268d4c04ac5f96519615c2a3a87e415058bd94a62be14c62c844e2c4296d885ef8d680e430f88f5404471401cc5e385c84f0906dbfa68994ee494707693730932d46ed9e801177750ec52275be23fbeffae78aa4bde5b73d94cc841fa3e2cfd8263977dead76474ca03e6074ad533a6aa79ecc8aa1a1d03e4be1d974b217c799bc0e614296e080642414245b501035b03000060b9221000000000badc7f56d1dd0fa02e683557dbea039d2983a50d8c34e135555f5e38bbd7443a3f216f12c711725f13390e65b45a10139681860d174810f405b1fbc982a87c0d77327dc95d7348fed1fdc3748b4ee0b40118b981a5a331939d28db36d7e21b0c0542414245010172fbf63f083d35e1ebed1af368a4b4ef28cbcd77c84aad668ed47661264a3e58f04902b888933361e8481479cd0ba2dc69f729fea5af70f16ce14352c257c18dd4e6d86fb85f25bf1cf01e14d367b60cbe497b5083e38025ea4c624b2320a1b472d9e80167dd1362f4448a9c9a8194cb10ebee811e8c9df47bcd4ca9f1cfedefe02ace9f713c2a4361c174983c3424d541ab94a87cd629c3e54c087dacd422af679e22be080642414245b50103ca01000061b92210000000005662e420c00e86be8957fd3ef8d17c506f1d339a070ea15c905c58138b25e574521c63f85a4311abd7ad373d559ef44d64a452a3fd0eaad58686afb2c9fb51052923cd75bbd31ca21310003da4c7e2dfcb2db08e30232d8078dc174d1d44aa0c05424142450101409b68d6cf0a67557d088c88c331c3d2a47cf3bd4a66b891dc1291d86c93514fed0c30ddf8db1222b72276e4be904edff00c0bf5c2124022d8442aaf19f1c58a39a1466b702f63d6eb87784b5672bab50d8635e9b0c22c0235a5238c59930c4c76d9e80171610a900f3a0e93c7341ee14c2388c33e3b3343e3ae9f2cbc025e801dc59d4e7ef4a33a0ad1e7f79cebbaaea2b0f44c524e382acec7d2d40bddec6bbe2420bd080642414245b501034d00000062b92210000000002a2e90f079319f8575e572709baa56cb688c3c3ae1dd2494f5f1fcd1e5a219468282147a39d0b697c759f5dddb400e4e19a19ddda53b218da8f8e5c790ae0f0c08bfe4e42b198d05426221ea3e4410b61e51162840398498079856bfc3c7ba040542414245010106d8399605dce25e1186e9af96b3a4227c6357ac9419ee4367ef38dd3bb97e45b66f28adbdcefff0e8bb7c0778dbf48b3733cb143af81d31d7b640cdcd2573834fbbae5216636c3f6fbf9e3db58eade1c86b61ce6f2403b890b7e6c3d721b9487ad9e80135fe9428b2c7a22239da256c70d6a67978cbc4707431b98d8a87b1baffb3b60232c0733970b55c0a45389e3082f5466ec65e885b9574981bb13167db28a415b9080642414245b50103c500000063b922100000000094b8929a6d1fcc7c02905e369cf261ac13bb10f47e164507ec8ba050f0a2115e2b674a959ee2a0ca321711f5975689e6dcec79f3537228e4289dd2bfd756e80a906e1273f5dcc8de154acb9d5d7940c18f363c92cf869bbded9f0cb61bb6610e054241424501011a1d77e8cfec270f95c69fb7b3517f12d434d38ad31a2b343827333d105edc33e514dd92cf4554d4c4083515faf18c0930b678124ae9d7531a496900566d018dd8d6751ed3937c9cc31c218586b23dc55196345a9afa832538d2a91d367257e87ed9e801368579ac823833d9183a13fb5d1190f2c86b2c7e982e4d78c012bd784418980b4145399048afaf9b8a1e27713f1892eeb2b7e4ae4279014a2c57929ec192aab7080642414245b501010d02000064b9221000000000d059304851fc2ddacb96f4add336186c1b84092804bd934564a9be09fb868e63e6948de30fb6459d15543f4a94059ed0a961e4fba337f44f1632cdf7a7db910d2e89adf2b85120603e62ffcde77aa91bfa2b71e1faed0849d0c67f3e1ad06c01054241424501017ca0e5c84a7118418e751ab7a4dba27cffac483e7689eac4a3cd754f490625054dfb1f05826ff866ebb6c08900289530d4e031d683735178ecf1c6a1db4cf387cb2f51861a36286d0c5324aa199442af1a7b2b02308d185f4d922d189dc22f5a82d9e8013e32e3d8449201d5507b7313b9ac40ff715f87784905c063d71c6be77153d27bcdc36fcf7782a53c103a458d6da96919413e1396f268769da5291bc7dd3588de080642414245b50103e100000065b9221000000000cc02ad86aefdc0471cd9ca1db95ae45c06f373c77df60f092b919604b8cb9a4d6790027202849da29c161a4d6e261f89b46afc626829c9bd5f581b60f7584d0403ab3bb5cb84df03916b276e2d9a38810edd926550dad3d6e7d5b8780577a903054241424501012c0951f12c4a32bb5f58684fbbb52acd375a5cd4f4d60cf4f2c44b31c4bebf069284b3b209d6f0b1b7fa7ff2526ad8f70f856f2a7207f942d1cd4b7d0c0cd7869f36e9137bdbaa260c48e4350f4b7064a40d4a04f067e8cce1bdec1bd9ad532786d9e801697339b261c0a5667d93b687ee2412ff96895d92343780fc9c7446d8a2618618e283d2193d91a9bde81feb765ae8f4f1d191936489e5a819a79bfb8386d35af3080642414245b501013a01000066b9221000000000face84e32303cb1c0125be71c567d44eed4897933a2ad9580a7c13074e9ff9164b963ada8510a436ee2e126ac4dd905cbf3356abd1ff9ee7fb2c8c9c78bf2801b8f8fcd5008e032e96bdf24fc3623881fbda778bee070adde4578043e0e7c80b054241424501016ef95c3acc8ebfc4db78323e183768a2beb3baf5f5cb8175acc6a5bac067ab2d33edc6912cc6f7f82678c184588062c0cc08bb83d404cf12f93c30060831dc875d8bc1788b0f32b4f0d3d42aed3136494db410b52119a988e8dd06da126850188ad9e80111a151c87241ecf9a4f36251746a8ec0238bd77fed8ca780f6ac2f76827ab48dfad68336207f4620bea53ed8bf5e63927bad3f1cd352e51ec9f08eb1ab97394f080642414245b501017100000067b9221000000000dcd61a0054bb2a77f790f7fb3251ef7a5cf6f4041dce1750747b5d3f85267e5330de967ab64088b2e06734d86558c7c53032aedac53a086c1101a0b5ed6d01092f91ad958475e23a72071495505f6e4a554325d0f00a91b25e6cd57e9187990005424142450101aaa6853eefa83444fa10f1f338b27dd2ce6eda625e326bc42507824b113c5005feb29f0b8f1d0019127ad6b83f0f4037c21e43ef36fec39cccefdee0590a4283e454085c69dce7ca82aec927457b8e3bed59775edf10a86705214d6bcbf218c68ed9e8016c2df9499d8b69d7a0077a7a28e66efdf8e7822bc63419bb8036cf216cc11cd5a3cd70aa2c0c9b3d364db7c8b739b4d86807ea8d445455b0218fd48cafd39211080642414245b501011c02000068b9221000000000be1454b42e7a0a52ccfa7676a1c7c075821b8dbb689fb108c4e68abe632582360e7a59cb6baf1055e4eb6d62c234a93247bba56102930fdf4a9f0b97697ddb0dd3ebdf0478376b4c6a1af2e4c8532b977370e099ea253e6aca87d1b60973410b054241424501015c386786ecabe7e9bfa8ffeb549198d8f1696c64caec47bdee07d3b44bef1b5fccb0ffd7db5f1124654e241a8b3815cdb9b142c7e8742c39fdc0b3add9043888829291adfe4505d5eccf9028a59380282ce0cd4f48204a07296bcd35a6310a9092d9e8015b3ac76e3bbef7e4cd0e40e06d5922ccc3fb9c6f603de495d9b000569b7cefe2052ee3081277a0639b948fe9ec3a17904a1b4dd07b00accfda489949bcd4093f080642414245b501030300000069b9221000000000ca4d5dfcb33b01daed96a91bec1ce5406f78ddb318c22fc0e06df0969694744007f7a49087aa4850711715a6f0b5e84733c178a380eba9c3a3944e5d1341210e509b881f3d58d17bd51662c614d00a85cfddc7ff39556435ccf716b536baa90005424142450101d23b16893c586ed3ae19e2660224bc57fc89347edc9656dab3e91814e0900c3b34d90fd1ce19bfa14e1c8557432d363f6719c4bb91999cbe18d281cc03635e828067ed213c6e6af0917a44016deb025bbfe8950b0c774c81541ff34c89bbe55a96d9e8016e21b87df045fb75592f2e3822faa72df5a0bbb4cefa913d1a289c64c3265836bf269901ac7bde9c8261fc99754be6aae296e40c02e4fe9e8477b353bcb394f1080642414245b50101bf0100006ab9221000000000e44017b501101de6a4f3fafc6faad550e96696495d0bff6ff645b6630bef0834921f8d4880ef1327a67af50da0db90144a1b59300d338981dd666e25e770860876334b71a1c83941bcc279fab5159aca5184c42c53af5e1ad937606ffff9cc04054241424501019a40a34acb54b215cbf729f834d2459e9172753545d1f24e45fb642ed270a04c9bc964f77b49eab9583b50a7836ed0aab3a8677ae10054c2d46b4078374047805de87af4040980b659d772fb38b6b8fabe170452404f336efff21a1023e5ae6c9ad9e801dcac10f99977f42b28c954c40595f11ba74dc0a9fd6562840c02842e26438d189f301faedf68fe58eb8c2ba15c6db634d813b2af4a044b6a21ad7ce9317ba9c2080642414245b50101400100006bb9221000000000dcfe8e249f93b9bc3ed0bfd1ff622c7dc012708ef25391beb6118fd32235450756608cb5610c2f7fa378dc37fd37589c422857a60b42c965211fceea13025a0cc4c1eee2fecea1d0a42532fbfdaa0ea3a5f497445b20d734ff1b74d11d396a0305424142450101c45510c930370e3484085f4d1141b47938ba0145cd1bcaeb9b9bbd7391899b4d07f8dc9b6f0304fd9744f97c9661668c6b2d8f2a262f99fa3ebc60d201ddee82ac7722def586dba7ec626e640f21dd67934f55e3ea7857c50d7d96eff4dd5f479ed9e80113a570b4c5b94c27711e6d7efbd135c199c55a8da3cccebe31c15a9a9041961844ad29bd61a42c7b8fd4905ee53f1bbd1c0348b62014c1053413d3c5c9454dae080642414245b501037e0000006cb922100000000082fcd92e4a5ee94fc0760a668ce57f2e53e9162a75b86395f9c158c9dd298017d38bd5d8bba4e766d3bdff9504694143fc779cfd132abe4ef8938d86559cce098bbf5c601edab027c16af42c97b00230f95cef0b60ced301c2c02f0cd45a3b090542414245010184262340b57d25bc985a6bd484e79cd08fb86f75c403e1eb1be5e71b6d558a215cf35f43aa30839df57eba08fa55ad34ad6d240b005f5aa4a582ecbb96fcfc8cdb6868f4732b15043bc7e5033d5fe0640b103e1237b3b831638ecf699e573c25a2d9e8017a45a3c114a267db7b9e7c7621b7ab1342303d51605469cb9e1755f33a749341552827d342f2a04292fb13d076ae4345ac35f19fe8e3d3e9f9a19432afdc706f080642414245b50103b10100006db9221000000000e01f0e0a5952f846283947fc1e59fd235c302d91f8729acd656b8ba411fcb73c9eca79f7f8ec055350bd4fbf06a3bfc43c48e16cf9c653883fa01543b5ce560c8ff6838c7c75a410d77a3f4100325376ec24076820d5221b588a9333da34a4040542414245010110b2dd71fe1978dc8ade94a42bf0d011b5f49bc71c87f4d22ceffb8bf2308729a0d2eb196b2c73a2af165b7e31d87040bf04ba453e4500ac9128cecf074a6e80a29edccb145a85fba5ec5cc22fe5ed43141a65ac9a1099986d4eb074053f06eaa6d9e801bc08fdd0b3a82cc27fcc4a90ccd1a4343c419e859ec5a550fb8426e82d764ee66d2f7452091b3e15c99c76d4096fce92abf0c0416c898641420b095677b63a84080642414245b50103620000006eb92210000000007e042baf027050c7b1b73e14818a8a8d82484a9413b42b3a5b539c3ef3a5b2381c3c399479b05566a2ef9b467b5c6ad6e758c7372bcd338a1454bd5059a0050264674b26fb8388cbc30b24cecf33f7246d9f5af28c35f22811ac55e71be1f80c05424142450101fa84dd1b2d1239043521396f1357e8988a723a6267b3f74504bdee03d7f48a4e4d1c113a2ea1b2afa8da6a426bdf513423c2a10197e1fcc0fe74d16e07c117875c0fb1c5d37b0fd96c7bd1a87097880dd57ced8f1b6a498490f7189ecaeb1378aad9e801c249b6954baa2eefc572612a7fd56a347d3122343d9709fd6de6de6f473a7e1151f60f1a8aac3689b19fb8328f7f464b9a0159827e0ef7e85b01f2e5dfb866fb080642414245b50103660000006fb9221000000000ea3bfa33093cbe96ea1f75bb74faf3b1c5590652f38bd1d8609d86cfc4af853cd4ac26698687ee33518e8a29530578ce468d13a797f615f6ed92afaf8969510aa5b5dbea5eb0330cc198d48bf4096a369392d1fedc349fafdec46d02f13cf20805424142450101e8c2f038231ccb889515136ed18aca3fb5b9dae3d2d2f3158614df1e26663c2d0e205bfb4823acc594568d6584456622d48a096bbd4ddddda8f7235298f39a834ea859fb70a0ffedf9d301f43409da6e23354724178989dba09f0b4c538c5027aed9e80164b9cf6f9ff4e1c4245897b2bb2da9463ede61b7a12a7a72585f6fec535d9cb67f1a36ff1bb1b1ad4ce62a6a57ae1b9e51026332c399b984951e68f30549f81d080642414245b50103ef00000070b92210000000001afd11efefeb11d4dab7ab030ac78f5fc1d07f7eb4b005fb5ce9a3f93106ee379a3ca37d6167f652cbe5a372873f8d8ec8a37b7f007755de2387e0ae077d8608bfd029b41e402b69c6862ef84712556d15cf2b97238af53abe1abfa0fecb0a0b05424142450101425bd3c059b43c0adf0cd4716755ce1383d8e86d47df7dead147d4e040963678c188be9956297c309820431cc3b0b5b11834248d0e93368050d63f68a2f0098f56b68d49d9a53407d33a60dc5f6dc6d7ddde25de5113c1dff26b9f196c57042bb2d9e801d6248ab2e40f9513cdbc1b1663ec705f378a9a3f49fbf971c93803e93f7f88bbb36d72a100c5404ae04422c61867bf80c400128f3294d2963f5042d87c7d586a080642414245b501030901000071b9221000000000fe27fa98e8cecf7749e3eca7a4584da2b385a3f909e2d669e135e300b497c926328d0c99e04ec4cea3b529fdbee8e8915f40541ce0f74a6efbf794535642e9097c43510ed8906fd7b351084c6509bbc525bdace5a167832116f3fc4f4676890d0542414245010112ae8c4a070aa49a01857bd18468c920d82c41965386d1f234143c438ca1002eaf71ae681ce7e63d24b6bb59522b7391f45ae079e849b3451f6db6f15a472e825598ffdd9a80188c548979798cc2fc05f6f5db9ce064358ceb1b733bd4430ea9b6d9e801247110631e016b3085491dfd2a83a5a9b1fca86df90ab063bd358276ea207cee3073bd6653f018f94cf8a2341f0860dc26bcedf7590f245d4253b4d89b6d1955080642414245b50103c401000072b922100000000000568b1151c5af95095fa40195493a9ba8ee2d8f6feeb0e4bfe3e5b7c15f84447a163d4e9fb7e6879f98a4fd201ce8622d5c41e22ac84779fd50ba4ec7803d0c5ec3530361415fb92a63cca9526fe03a870a63124e7d8e061919d5ddcfb73c0205424142450101620eb78736e8d51074d7ba739c07d09762a38322b1055e4e80c82b2ae085aa7c4c4468875fdc12a01d608dc8a4cb071c512bc090cbc43e2d924f3c001162038c1421fb351ef8f2cf0e84e5efda71ed7f41e670f6300575ee7b0bf7b06e67832ebad9e80125c302472a5e9472381fd93519204ffe84940c2215714c72619325ff5a823c434063ce4656c3af73954c90555a425b3dda6b5dcd45b39ff7b4479475fec9b24a080642414245b501032801000073b92210000000004267c9f4bbfedd008f00c300d1e09304ccc5866fc68878b686996d3a10926e72cd821c1106387c1eb3e337b3c05268063e2b173d03517d123e5cf0823657ce031839e829ba4810af27795cfef6e46de9f386594134f5df79d938a2498729ee0305424142450101c4979a85c60d1eaf9791db530a06f11e75a711c011d93364eb2d69b1a048735ad1af0e14db5b5208f064e8bf75dcf101d779f0b8763fec73d5a677508c23a984b8b8cc91edcd9dbb97dbb7ef26284663cf20edd34d80066c98169adfbf84b026bed9e80129744a46e66965ccf2b0e217e01c013cf76e235f6d28263a1a2692f8290c6e80edc25ef638b660bc8a319d60b19c6e6de78c706f1dcc32b44f722400a8ea8ff6080642414245b50101ee02000074b9221000000000baab0665b0c9f70f90184f4c5ab547dfc2ab841638fa9f757336eec84290cd4f8277f5dec2f9802eb79d4dd17d956b5696c8495bac5403527ac366add1f8f40b3045dcfb9b5e8a44939e09aff9738c2d781f6a710f2335bb086920edc9d3b50005424142450101b4dfdf448a333ac0a21933f4dfbb164eb09d7b1b729d70d9d9f332f0457dce3c92a5ee1e986790bbae75bd197224fc46016725289f61bc253e7aab77dc9c3187aca0946e9aece0080d0d991f6d5bee4403d482407ee7e046566199fadab6c639c2d9e801c5fd42e9922e768e53bcc1fd63f4e21e6e764f099ad41dc9499a7965eb1dab6d6f0559712ab5355e28966864f9cd5791dc673772024133723a816b891150bd3f080642414245b501034103000075b9221000000000c4742bcb5b7e9d4a2a3a769d4a795421ef073cf5dbfe89f234938609e88eeb79857575afe268868dd1b2fecabbff14ee31a8a4904707bd90aa06a8dae826920617ad0acad31cbff688c39fab835008010429c35f03a9cfef5c3e82f3a0a70c0705424142450101fa0b68fab9ad6fb76d72dda8a3a616bb3988593ab3c1af16701524cb5dd2d91f62425523f0a64979aaf8366e7ed106c308711594c8a5dedab04791e2dc488f8d165f37eec345768da0d0973f2ae79c1b9b86444b8095f99fd84dcde2edb668a5c6d9e8018016e213be46fc78acb661de1e1ff9f18cf1f41e07e74b6d4c99863aee8163ff1043765dc52b8b296023a6d1769c92d5bdd2d59a57275ff067aa0582446030d6080642414245b501015001000076b9221000000000a4e842d5c25a4634c13e051fbf8eae0d1c4a3618b9fa29a48d4458094d86630a69649535bfed557bd9c2307744fb2cff2249e32b1c2f1748ff7b3f6ad57ad901288617eb539d8c86746653bfa0f2d58881ae682b153e9d58381e71715a1c1b0b05424142450101888573849c075d7529d2a01b83e6c1ea1ac58326ca2f4050193ef7a5747a4a3c9de03380c7481b88faa2af5f033066a2269aac3f429a5808ce837019cacc3e853f8fd602d7a987c13e0a1194337045f6b713a52441a64762f2d473c2350dfeafcad9e801aa1784581a57b8e140c50bb4b03f1463185ebc80037849b883bd9c658a9f36315bcf9bfe218879e5d808af27a90755886c17a0ecd130c626fc828291e885faa1080642414245b501035503000077b9221000000000980c127d3a37c25fa4a2c93c155495f771462005bf16d118cd39b7f9254d88099c0c8bcde19bf35a2a36a4d806c8411edbf14702928db05a5f8fbf257cc5a40d415d42332838ed58d27833292582b8b0761bd870ddeecc52bbe14423657a0b0905424142450101dc6111ac3f09e83e55cd60faf849373c0f83e3d66dc4f7c51eaa5a1bd4c8414d2b5068aef5829d7698d5879f01feec78fb3443895d47ed632148a0f5d288898ca1ff01eefd3d26cd367abd4bee7dcadcda96a3e510df768c09e85a0f93422317ced9e8014da40bd51bebbf11fe89b4c30a5f9801f1ba81c5ab93e1b343268b21696394e706fbd89fc4c2fbf9e92dde2cfbe4291b9761e2f8d119d1810e0ea9753df18a52080642414245b501030d00000078b9221000000000e8acd84eea3489db8b1ad5d4717088d215d9cfa149335aeafb43b5ad9455d6019a82ea59d32284832b63d23b8af3f64cd071692336eb970be719fccbec41780b0b40517b79d552407e7e3d3822c9768717ae206143490bb11396f43dfd7afe09054241424501013293d2b1123301b5841d27a29683ce57f89c600cb6f03f4d0d411b5ae8f266178c0321f9c13f9c0d72d92d96a45180aa89f3b7e4199a9a1bb815147f729213844be39727507a03a923f4c27dc9dd4e49a982181f5ed874bcec47848179244dc0d2d9e801ee4cb2da341670eaa6280c43a95a88fc38f97470130f4610a39f25c9ce7382ed048b1739e9ba62cf6da5cc444ed150a9e8be15743e4a665fae5d4a9d25276d25080642414245b50101d300000079b92210000000001e73869eae22ad9ea486b908abcac24a4a1490e05500156e64fb1b14795d220c50e5349a506a5b5cd00164467164347d57787662eafe85e598c0c9d63620f9017a861925acd7997fde84a483bcea16a0831f68e8920f63f3b97b6a32e0f23a0605424142450101504b5b46e7c48a2964c0bf016d275b4853bc6ec8538f876e02dbe18471316e3fe96f85478f66427d9190bd2d3b62f530b7be73f7b1ca6eabfacf058dbc6b148fa2fe2ce01793421bec9e360a08204b2430450b810fb3955990aebd606cd92526d6d9e8011e64ae6fd6942e46711f0a928dfaf26e3899bc465af355f443d7dff6e55727c3e1be4d399fd88d53e95ad32ac7991198a5d29cffa9b856937fc071d228c27e90080642414245b50101d40200007ab9221000000000b8cc7ff63b5385525a6928f8d2739e0f135fbfb163a6be5a88789bc2e051bb6c620d022e713ad0cf536310fbb99786a3baee169f18766f3c287311538f4d8f0710aa9758a4860ff74ab407b933cefef9af20d73b9cb2b7d0887b1bd2d416c90305424142450101a26c79a1f0e6a5cd873a7bc9a2defbdb24e726b2b5d712de42016a350afc86148e8f4001cfcedeeff65837c3bd0269d46fa754d21398d33579eeafc7ce60a481e4fa63d4a67eee7c4c469eac56778535acfcc6e85554f16f450f3cac83944f94dad9e8014831e28ce903ad594b434fb5a097e2b8e9feb7fdea5f2d03fb53739a489be83aeaadc5be649cd52c1126c378c4bed7a8b8ad26e40594dc11db18827584cfd63f080642414245b501033a0100007bb922100000000022b1302fa5da41be67515b4d331a555793b90bbdbab836a3ec25f554dbd7580ee90852d7607c69ebda1e5dccc296979ce3341eaa66e89949353c1c40740c830c7e5729bb57d5275fad1e1e233b4572c68cc2d8de3cb9ff180ed4daf49bbd6e0905424142450101cae5bf747ac607fbed0a029e137ba172b183557606c3e947d2e2a12914b1630ecd698df7a82a71794b9bdf64049621eced199d7bf8926a67f26d4af3bf35598f05c9fe5fbaf9345293cb2f8ff16da9f8f199b317bf2b042f78f570b904db3148ded9e801ba82957f2cb70c16367e04693b396339e0a28563ef2da0f5f5138f15384893dec03555bef3a5d7dd36ccc52dab27c6c8be37f3223742854fbda4650850f26f29080642414245b501031d0200007cb92210000000002e164dcafdff78ef24603803996091a5730589ee01cab74323a115965b040a0f23519e222b1c7c186dacf5d565251a22080c4f81e27c772f256317c15d42bd05bf86aefe1566206e9a6beffc90faa3ef34c80c164297f28cab220d1cab999b0b05424142450101341205755fe84546e529a2bb72712c7c55f08d7c81afe6dd9142766b83c64800f334007a7cf2278aeff1b700ac8b34be87466e2cd2811f6cff28ce6124013d893cefeb1a02cf7605525cf90ff479142a60a216b47b92f54b3150fed97390e55be2d9e801f49166715a1a1d81d40de95a7294ed24749303536aefa9e7e7c6542e1409cb911b07b1d1be11b566a1a099289c338d95d68a502556371124ce28bc4ce1ba497a080642414245b501036f0200007db92210000000002a9d7f051cc6b314f1b587e0a9bf9b2c89366887ee3a89dc4b50b09ec05564262864ea37ff7b477647e551e3b82d256e1c2eed9d9a3ba5db48ed09cad6555b0a5faac81cfa007d89b6a8c5ece26a0d39b6178d39a42925b51e8980b3fbff760405424142450101662d868abb4cf0ea97f3f39e45bd48f718fdc03c08c863ec710756dc8b68ef1c7b5c0906273f61c4371280424f1be3d4d5d517a7909db1eadc348253e0957a8c7ce372bb8f9416ec3d9ee82ba43b758314998edec9dd228132295e7714fa76d9e6d9e801f4f8792dac8e87d9a6ca15aab69c87ff32dbe0785f1b8f254df33e8db28fa90356efc78ca93e56054e8cc84fb2efead7070c443bfbf03a9977cbaa5a6374f901080642414245b50101d10100007eb9221000000000aa3205c86c0c5e34d097c7409f678d9fdb402b39ea4107f030a5cc21abfdd133404a42c83bad61fce2db38bc7eb484c0b925149b133309e6c332584e51d87a0b1ec656f0939db16e34a0b549ab895c0c9bb9120f25c5963c50b816fd7f4bb30f054241424501012cf564338d0ae3d0adba22d76032bc15fb51eec82bb4f3843d93b9accacfe418a62f4205721d6f4ac668fe1a46b5ca2ae4f8ca5a36378f97c3bdfd1798161f868639bad8f6a48ee8ed28d69af7e2cf9658a33ea913b85be748bf5546e123e7afead9e801471c9e731a5523802b23c88c0a61bd288207e5cf8ed4cc07a819c3181a58b84d0a50663273ea74e0c4bcb561764bdd1d2ed9242e622daa14d59fa0c9741eb85a080642414245b50103250100007fb922100000000008299e6e6f725ecc700601a1111b2bd5d2555d0a8bdac29277b3166af074455c0780ded4de2a2abe1dd17fdf991497c03798839a16a470837a04e270f2b8b002187ab401e03153073dd62e843df97e1ee3572658e0cd650e4b71ecaac117560905424142450101a8cc442de564bd06641a0b4ad226c830210dec9641cdcc0f03d05cf20b6fa61410176bfe8c23fd32c522cd8aeedde15253436e148eec8a85b63947bc1593af89128a0fcdec00376b1083be21158fc2b7e62093c57a00261e0850ee2e4ca9ade0eed9e801efc812ac348243a64fb26267e6b022399d73a102720760db5636a87b91ba0247278a1898e63a44c8753f3f58730ffad2848dc5985bc017a23491ce10ae7e21a2080642414245b50103e900000080b92210000000000e1374827d7a09ff4ac64f26ab21a9abf63e50aeaf6d88011ca6131035d25e7bf58315eeb0b8e1544a2aad1fc9a3650f6bf3ba3e1fa0528388be3344fb53a7066036e2a1d23af076597491055e8b1372947cc9d78151b2590bb57db01bf7050b05424142450101aacdf83979f1039446944b32220e82dc8cfbd65958b6cd03684d4810d86921601d7d85b33bfb6bf7eda63ca62c61ab0719757ee3f31a8fbdfd7ebe790fdfbd83fc3b48720b94aa3d3b5a140812889d53658593c7b23caf8b4be414e8c2ecbda7f2d9e801c7465c64c5db83a63931bd0bdb30b9b66e6bea1d025b215ec902d62122e6f2292c0c8072efe6b5c461273a30aab5b6ee1be2604ce8caa75129e31ecf400c5861080642414245b50103d700000081b9221000000000869134d1950b1770e9b3fd3d4d1a507167e064cb583142ecfc69eff21cd8c21f5be032c4b9df510e70992517d6c4f5d350bc24e073f60b45bbb7fc31740ead0908caf8058068daa7c612a609ea63488bb822f6a5853a69a1b9585f58006d5d0705424142450101ec4cfbe4d01f057e9f3142855e683acf380a8b1411b51361206de49578bb1039792dbffd20c9e344ba043090f8106469a5b1ee68e65f14919f901486abb9ef8d84a8266d96a713fc2585f60b7be03ce4ea3f6f088a954bd515457ebddf605c3af6d9e801b3ec8741f2671755bbd7b0bddfca011abde04fdd0549c530a6a87c8af07778e56af88782cfd428710832ee30974bf668bba3d6b1f273e7c4946e2e3e82d1e383080642414245b50103d201000082b92210000000003480d38f27a112993537d8449a556909b4f8d64b48f60a4e5d8e127982e8a60360e3275654eb5fe4664ec23f74f1b9d554d73f216e919aa17e1335a66cd1de0e1663d4bc21764d32b65d3061a4cae618abb47b5c338e064c36a249089884030905424142450101283d9c3503ecb8c24526841d00317142ead85f1dc9cf91f7466d0eedc27c8622a560f7a18e3bd7582b48235740d01f96226cc76ef0adf6a37a8a0bbea8119f888d0114769b0d319b2767c5310f2a80c7abeb663d585e0d5e3f190e0b27a4abc9fad9e801fc573cd620ffc96369fa2af7290cb6d4bf961d3db0ec551a035a69eca16592a9fa5fe4c48e7ae9e81756e7191a7d3be2cb47d0bfb751bacd8279a32869f71400080642414245b501038203000083b9221000000000084e124afe07267aa59511cd0fea0efc92d1ea5f1d5e748799df4f5f58d1b42c1727e75ecd419e8f6a1a1cb9b7311e3dc7170cbe77f1f53e481850d77e4d60022905186a4205e026addefb7208d62c541029f3b71b49c21991226963b8c8380a0542414245010104ae1617872b7631ff15fb816db8573af4231ba537dc3817ad682fdf12329343e94ef9e0187aa8509bdfedeab53840cfa7a9cc898d89612dc491151d65e7978cd4631b2eb5c0293da44eb1ccb584f1fd4b9bc30f4f9a3a104b3099895c80b4d2fed9e80183c5e572ad50037f20ea1d13e943fabe1e7bb7e1a3ea4d50e590bd4ee23d7d229f570b7325ead8614ed6221734c7ce1a98987c0634417a9ce240becf2b0b6a0a080642414245b501030a02000084b922100000000032af6bb2208eee6a6cb1188c709a95bc714c5e97e639d4fed74fd1d9dc14ac536695aa8034ad419de3eb46ad774f736944626efc9add49c2c48967aa90dad30b30a4ed490f21689488bfdc8596f8e71ab1705eda2c2ad01254e5fe19bd236c0a054241424501015477b32cf9ec427295cf5c66bf353e8ca55da38db39a251d973da21323e4f66c0c46319581ea0c78173c44da0076ce2e056147d74fc1d8b1120530fc0baa9984f77253edbd0e63e58c6d3ec097cfe85ad17439446b50a6969c5f2eca26bfbec002dae801ab8d1941ab81df496cf2df7ddae74d1a9af08007061701d98612903d4ef94946c86bf6531713500dfac6d83ab769f0fefd67987ca87a837648bdb5f5a03ca9e3080642414245b50103b302000085b92210000000006e1d59dc89ca001779cc51da7218a4e19cb1d5b965e08ebecaf050eecc0e673f475e57c8e6e4a397e06f456fe98d31feaea43cc78bbb0112b7ce1595d9868e05d26c83bb2bb146e7346d6aefc8a06920549a439b0a0462d7793ec7845ccbb80505424142450101c097b528c521e1b9dec0836afaa683697871c568242ab8481efceaa30a6ee51612dacdc49664f2f77766a3baf76ca440e7159070604f0db0c6a8a6ef93e26989efe7706f0195be3e78020915a8415d83d701fa2432f07511dbf7bd5bd0405f3406dae8012284d5e873876f688938033437526869a14d5c61e8e8fa82043e2978a0aeb0e8f4d9d6a93dec6ccc6edc0c3adb68c2ccb7383ef142bef7821383009fde6ee195080642414245b501036200000086b922100000000080f514f7c2a492abcfa83c3cbd26a397342de3380abe9c87a9b4b860ca1adc52de3a1343ab96b4bf2782503558e49fdce55b17f28c06aeafba935139498b5b096133458b8936946d99d345ded0731501b0bf0bdb98d2a21d0ff9283d958e6400054241424501013cf393e818d673fb8118af068a8f15948d54aba119051d2595ff5c48de69b70e175bfc9dcf9879d41d4ac83a7df27b2b9e918bc483836e2a38a9cb2b937cf8867bd50d5702f0becc573dd621404a5d54f676d2920b78439dfa5601a6fa0400ac0adae80100daf69f26e04cd3302b6d9078b3c2cdc776d67f5b4d66cd3f9a5abd201767381e61654d51f992770af4d01fa3451e76419db5e35c97ca5517f43e9aeb13ea48080642414245b501030f01000087b92210000000000ebfaf0f295721cb87ccfa18942456f86442ce54942ccc861158cf34dbafd104dd68ca89dfd16d44127e90bfae532b2b84868610f34370ab9176fc5a5dcd0b0ff57bf321c62b82b8471bc98560e2ff1d735488e843a48e827eff796c9475670f05424142450101a8f5db96d6d00b2ad159f87cadd74ad7686d175683c86c03d313a1900fcbd535d583faab9ad629ae31883462ebd18bce4c364066fa0ed89dec680f01cd5da48df2f519e4b233d786fc9052d7c1cbd08d8ba4dd2f33e2e9392a676b17154305c50edae8014d4de379f99859acc10e5a3f3129db01cd6a2167dcf94d4cf54d7acbfcab44df8acb86ebc693df4e3466fecf97174015f47cc4ec9d8aef23e7d60ad5cad5043e080642414245b501032003000088b92210000000009a800356ce21cf8f8876e4560ebebf5455d79d56a56586805b7b1ad75d9f313757af4f6c5845cd18858526e6cd899c7f727fa3b9b2ae4b8d4b91fd32031b930299a701a019f88458d062b38ce7d4dc55d692ff18f7c0f442f3d9cc6265630b0b0542414245010170abb08978944e445c00fc41b05b1355eda779b3b7734fe05c5166bd0476c748b29fa0f796ec438c0edb22b1c8a7c58f9414430bcfa097e6a87966b51061d28adcdf51cfbec8076dd4216674264f3251b8841c2b34340ccda9e0779da998458212dae8018668564a2dfb8604f4e2e7c2191c6f44985d979fc3423680664c433a57724c7e003bc0de8aa9fe645bdc04b35f8bdbc68bc5d7bd92c2786531aaeff563861fdd080642414245b50103ae00000089b92210000000004087b12721c78b25e2ae03eebe9554d6294ea9ee9bd452c1691b07581d0a2242575c36fbf49c52510f809c1f49306a99e068ea6c2ba75dcadb0616c560bc8d03b4008fedc9eb4e70402a6aac45f8fb1b93edb7ae5f8baf6cf6c9079abbab1501054241424501012255fda4027511127e46520345acc864134204957b299e5e480dd613b2075879b9b440a392ac5955bf7f09ac282c322696ac27ba2cbf43deeab7e6d2feef5385dcb0b9b02babfd599d3bf380f93c60678b140ee802b5a1146cda900c08d8471416dae8018879da9646417ef5028bb889ae603c7be9a5ac39919a3a0dae7ed04e9fab0e7cede384f4e0d6dcad96312260aeeb3a4d20b2bdfbca8de217df1062a4c423b508080642414245b501032a0100008ab92210000000009023f7b859e20a9b78180727fa5926e209875c704e885f6a9bc469aac1c6cb1a0fd743cabff325371544bdbf52fe5527c450cfce93c3070afdba55f202d4f10bab758b1de12e0eddfecaa31fd4be01eaf4fc05a60d6c4f010b1c4c3441b4ad0e05424142450101c6bb478e889bd4e4ecf65c3d8e8910dd2867628179c2341621e87e571d82f47ac071cc7a082348d2bd825972a5dce286dde4cce10d88f5aafbd50849112caa8fd816b4ed254932fcb5151592f6129ab4e61f51b07c0500be88e9e56a1d5d80751adae801c660e3b3807a3ee23ab22c30485c83682c1863d73b4a3aababb0e1185f448cfdd57b02c80b982cf0a6c3ce6b422f7bd97673650ca2e599c9b311a21c052ff546080642414245b50103c50100008bb9221000000000d2d5a38d023394e424d5ba23d6d0687077ff4187eb7097835ca3243b46bf3e38c67386b4ae0671a0a3e2304356191fc87fa27d865d91d8d2984d2bbb91555304f9c26b64e66b6c7cd60594cf9dd9d1c2b6420e1b81ed293d46a3c69bf274e40b054241424501011ae4f05f0c92e87d234dbe583fcd253bfec3bbf46db6c6a8ac0b903f93437e6b20170a3595ff775c1c554ac886c239824d98e4fc36233976c04425231f82608d42a76449045709055a7095eaca8423cedc8dad8de8de40afea76e9eaf2a8a7c51edae801b6e1b45eb61f71af561a2b747a6662602d958b743ca3a2949489f6acabf81a414eaee676c4a69f1731345c8e4a19f3a308f3e7f3ff5927fe1be093f9fadfa732080642414245b50103a70100008cb9221000000000e4f18e395d36b694d63792a60d844955488c39291d526b6bc289ef5ae521bb11f2875d0d15fffbbef10a994b15eda73914e3d221ef59a5767896d3703343790df9bc20c7e6d57e3037b0850b36e894ea2bf2cdc74b8d38aa161f7161c8c86402054241424501019e11d56221f77ed5b7f320237269336ffc1018413ade82e68b535de06a2fda11ea5abd8da6b1413c9044a019db9f84fcf642b6087ffcfbd9389db36a9b1273842069fa07c53697731e4c669948a0c1e3d805eab4108a99be6db2822eb6ea017b22dae801accf33b9e5b9436724b90867eaedbe5f198dd0079299beead3562f329fba980d0a970f02dc33dcb65f93b267a5f335c8b32b1227a5b07c06e55f2543843dd3e5080642414245b50101500300008db9221000000000e458507ae86540420b91d623c4da181757231c9bb7f8d92eafd31c9d218a8533feb9fe4b14818abb3312360bef886394650486ca7fede86429c71f47caa4a8047565ee561e95f412cb15dfb148a573827f208397b7cb8168db1b53bb077e7701054241424501017e880895a9ece98d88094b2efc551f25771d084def6467959dbd47110eb4734936e8a82021174eb22023f4cfe30c32c05219569a04b4d951ec1846ab606e15858e1b9ea2e7586e3ec1d88a2700e10eddff1f1f70f91c26b9e897230d077199cc26dae801c1539943fd5cacf4e24ab7d9cf115c1168882c9ab424bcb3dcecfb38cb1556e94fe9ffeca530d8fa41dabe73b64075e84b769e33f10d597aba8a5d442a488761080642414245b50103770300008eb9221000000000aeeb004fc9c4ac9f65d734ac984bb2272e9acb9598e1abf6bde546afa87c75121eb82701b23f02e46cd76939200972b14910ad604f7c3fc083341dc6e07b5504dc6a0a1dd92131606fd2f53b03cc6715721df1f34702af3cab1fc261a2a4990d05424142450101b8316b919de1574dbb4fe0a27baddbf78cfaf1742da7da8c143545ee34297115d182a2a02f26acf5b5dc85e59670af1624878226156939a66238e3c451009785b2915807aa2b77c93c901b4cb6853a27e15a26503a0024f9ba57d43b40630b192adae801cfac9d5d97714f6a846c25252a93dcaf0b43631186ca07c7819221170cbff3aadba451f65aa7ec28c611493381385d62971ed2aed3432586d2e5c9acd67d251f080642414245b501037e0200008fb922100000000006a8013de01de2808145d885f0870c3573b1778b9596e19ede089d685141491d54419d53809ec8fdfdeac98a507dc7ea6be49a82ac826b51ab551e2d8091bd05f918365dfdf7a57c8b4307ee6137090cbe734add16101f3ae567402552593e0d054241424501017ceb23a12fdc047df1152cda0769ee3e48323dcd8afa4cf68f1b4525dc98765d635703f008b34f370a4b6c9aba5a6abb0ca1184b70152df6be79fb10052d6087318cf06c8af816bf90d5b2498c88f8a1788ab6c2d7e4669e7dbd89d39a071bc72edae80170583af62cb8af25657c82c979f86e2b1874b53ec616e1d9af470214a7c259f7c7ad5b436513e34e020b5f268dbd4a240d839c62a5214342e604172581fb9bad080642414245b50103e700000090b9221000000000a41826e7b5869151d9f8065a290c5550f5b02285c899bfa13f745454bd5a630d45b0282d53f7e53833780685b26d793e984eea9b4f45bcb206c2364e7ad8b205cedcfd9aefd5c5c364945322ff950bc1ac1f813b4a609d018d947181003ff20e05424142450101845459099893aeb45d0cdacdf73006e1d3c5d0ce5509b687efbf6429c82d6a618533db3b80cf705a44a4ab4d676b40a276408b70c9da84c260336187269ab88198c6b8a02abe2b2aee58feab51eabeb5c0d836a466b8a551f050497744e7024e32dae801e33a86bf858b87325af2f5f04d6a79acfdfd0ce8cc8c7451075a223109aac2b5d37a99e3cbdceaa94ae7b3ea932422640b09d24c16153e77c955311c35ef0318080642414245b501034301000091b9221000000000766708afeeb3765ab13849e8fd227e890e300d96247b7b78be6adc5be62ee07de130eb49beeac0bbeea8315b08149787c56e1f7a2f211b87b097a486040a4d05b2ef3b158451f774068606024d95b436239322ba40c4aa9b66ed270f7c64810f05424142450101481c31dbf8509b21315f46f98454af4a4ac91be25021bc51364519f494828c184ddd3db0749b631f2fca026ad5986c92c9376594b91a6775ace86b0d63dd0d826fb8b34171457a5b21d4c970afeeb9de2ccd1474831b7703cd189faf6e78bf5436dae8011e193d3431df2b803a7dd696793f563110af5b3f363510712537f95de26ff0646e54d4ae31564d4d65a9304179eaeb9f31158c3aaa460cd002f12accddc51e62080642414245b501032d02000092b922100000000074729e3851441adca2711981fed229853be3ec43d6ccbe93106ebc970ce94b3eb63cf137e24f2048fe4ba86d18a0c7e3eb685e87fef552333313f8241a5396080e869dc2d2d1a1fa8702aded17b094b15abb2c09085450e895b5f62f0bb1ea0605424142450101109fd81232a3b9a31a8e63f8fe73e1f498597693933439febaafdbbbff20d47941c4a54c4227be00650c7ae092edcd6fb845f5c7bdd210a6291470e1e790b28d58d004af09c7e46a3edc4dff641c3bd13a15b2a2ed69ccb866160bc583d26b3a3adae801b49855cfb691fdb01d47303d35ab3da8dbfd23ce585eac16890ad966e85c1beca9b99686e1b9938dcb5266a312551f17b9d3f8492bece3213d9a1853d3511d26080642414245b501034d01000093b92210000000000c7f86d3b91f16f9601faa82006c9253da21e9cc0f2694e98bcecf6ad0ab3628f7f814f1baef737c43c238f68ed9547b4a4623b9d70b2f98f7cdbf0372f9d60ef7f6be269caabf01b9b61213aa53307757c3cf1df129158d18a7e389e303980205424142450101a2cba3cf45222162089ed332826ccb8ff607e44f714815bb198b8ad9239c3d55cc6e3ae85bb2aac2b96c8ca9e9751f203b7181eaa46ad7d7f0021600f1c1c98192998361bbd65e284562d3593247660057a314a8d326d8acaef84ccbc98acf7e3edae8012e0649ae1219de89c1544eed105e19155783096f0cc13fcdbeadfddcd114fc45d2c0bf6519acf7a0a83963a9f42aea8425e228d03919231e6094073a601db624080642414245b501032702000094b9221000000000d25b1688887e36e8bceda67ffba0eca71cd29759116525c724ed483c9544b929584a357d746cd0ed9e88c033c6bb6551caacb4b7c765ccf6878c41bc6127f1075a1d21032d1ead11892a37853095e61ae15ca8f3dd708d291238fd46e0fff8070542414245010186a79cc96f52a26a384830212febddb358b41f72927f169547c12780d55c595295d2d5108dde25481045de6d2793592fcbb2eeae7c37770bc95adb5a9021be8994d7c0dcf01620d1daa24221bc0941ec20c3d4ed70335cd82ca56889752a456042dae8015ab04c21e5fbe375a4201a99c2ea6ec53ff5f3620b18f37116e722de12a1c9a38371b8cf080f4365686db9a8397d4bb3f1c75c5325944375e90fd950e179af81080642414245b501011c00000095b9221000000000ccc235ffbd11162a1ce7ee1c6b15aa60f435c7173f761e10a6b88f1f21187f576ccbe399522083befe430f16c441805b205df2ae7f48cbb366b3a1b30e758a0cfe4231d9b5f6770a654f5f98db7316d85293387b09b2a395323234efed63400e05424142450101f6fd0758db5556f230ba7f7d1d4566f05d936069ab7a31620513fc7548475b12088e8f6f5da70c08d0221dd77274d6b6af6eba03e3869820b02bfe166eb2d284e21bf007ec0a5c656903f8e702d2be0cfbd279df584fefb921dc73349ca9029546dae80111b6d9bebd9370d4e9e44091251f03b1a4111a544fa04dd3b220132b63e63c34f6616ae717453d197333211d994624c8a454f49d666870cfdb6b932e17395e10080642414245b501038203000096b9221000000000fec9000b5978b8ecbdf15bf8f4bf7e3d2bf89d1127953f0a9899ac9bd197ba620dec6be989aa5150ae48039cbca0fb6b28067702b1ab56dd67eb85806885fb0e9f0fe3f1ea375bff4686dcfc339a28b58c77589d0328bf90f37fc476cb029d0e054241424501017c064c8edb9530822c937db066801bf8bda7ab94816bd29a0757d3ac06add3113ef3175f81f68811598fb61cb19396577cb489adae2d1e18f997002ff915fb873452a40df148adcda35763d5ebc1f740447bd8eeaa1b6df24d6229503f42d4a54adae8018d2d49eafc71c554e79176591aeb07db3b4698497c0bf38def7d4b96ea45f4f6cef883079701c79522f19c9d563763d50b68bceb1625fc59c89d37d4ccc65cd1080642414245b50103c600000097b92210000000001e96744bf0e4c9cf7616659b185309bd67accddf7d6a9c76610cb6ee3864cf5c0ad7103408a16e662b71c5565a8bfc4362a3fc2f1fca8628a0a7b786463fbf043cabff0216545a1661b5f375d4020a53e272ee5407579abaee99bc26252e1a010542414245010112c6bec218ceda8a8f235d7c3e2ea5b6c713b9a5080f905f2106b3b69a742932e575c14cc4899f8cae1782c0f907bca27433003f72ed75fbf7a00da8a395ae8b0de1fb6fc8e426cdfac13c2f8ce4c7b89b08c69c4dc20db5fe7a7e77244c16ff4edae80122fa1bec2704572ee341a892194f9c395986d07f2ece303fc07dd54052c09c93b12df5a8ec77e003a02f23f15cf2a949595fa45ebf655a2dc7f45dfb81bbd21f080642414245b501035a02000098b9221000000000187a5d5fc99482a9599290504886c1a661c637448e9bdbd1b6a16a3ab9097f3f407d9e25f3ab3050c843791dc2e76742ca36b9d29e8f822d1203d2530316d902c7a722e7e95fb286b40907a75e918b8f7609174f30c4992761d985b467204404054241424501012e2ceebdcf0b01522e878f11eabbb46da9ec4e8bbeb6b97e9e85c20fecb9c901808663ce41533428170fd33d11f3c32ffb1241e6b366d62630ea5a0fc287268b915587db36eb1a1dfa5def0fd6a5c172ded0f9d621aca8edc6b3774229687ce052dae80124f23da99303b0a647e9c99a7c8fa221c3a95540fbd3c2796a1741b60ad7daa5c8dbc6753170e84919776142beaa50199a2c00e646559913327d2be5214c6748080642414245b501036b01000099b9221000000000e86e27de58b727b6c1b18b11195d7db2faf4648465f57112fca1368d94aa787001b025c2e41b1eaa13f3fd7afcea438e8c9767d557a5640686d8f1f6c351a20aa93eaedb9eb30703ec0cb1092244fd42aa406ab66bc7a4eaf71bbf7fb0e0210e05424142450101a8f6c3d6a20ee0983eec1094d82cd565633be6db3351a62a3ff59cfe86aab96fb15115dd8fd6152fffacf3ee981dbce2c616a8e4c92d172c073a7c99440d0286cef17b5f5b187fc997536cb391c89b22a5b7c761717de3a1b36222b2ed56261c56dae801f0d48c78ce57a2470cf7a4508f69d9b5cbad40f75336379e3d8164161062d9470d7dcb733a4e78c17604d13cbf7d650f8a7af53aba69d2a3aa43467c950e2995080642414245b50103e20000009ab92210000000008e1af62ee864b470e735500d08d78264c0794e95948675c6e6cd7d7e10add6769128f49286e99446157a0e155a46a6fa9621945c168ec4f6a4540d17e1795101f95588f2a9b8bdd0c69f11e126a2c031a8b884f7e01c617c7618fe70435f3b0205424142450101062ddc0bfe0bd37adf8f126a0cf42bbb352165501e61b0d6987eebb3e97f4b14a5c811604431ef23b5be0ea07db071c940df2b833d36c9bfee7fa6817e5c618429499c59b0c8dd857c34fd4b19b5586549a15033579e07151d243d14c8dea9f05adae801642d3989bc413f4f776433a009228ac0031d0e32b1a7ee947274fff64397a5cf20b1da3c3e91fc6263ef6b46d717c3bf606d85fc435e63e7a8b9388e13bab763080642414245b50103b80000009bb922100000000010f01be1906118c0f983ba411e2e95d4f9987adec075e66dfb2b003a372ab72386dd5dac0fe498a1a1b6e1dcec5a3a2570952f59b93f0d55b1aaba96a64d3e025cfda7251d475b2aeb495d3518382648be9691e06342ecc38eadde77cda23f0f05424142450101baf13f97501ba5888f6f943dc0a39a232ed6e3d71892e4283c08e0dee1c3211532d28589b8aff1638acd577515da08bf5d2d8b36c54dfcfcdf74e7100e07ab851ba0d7c537849479307a14677286c2eb0a4a1f8a2e3470eb1a632588b69c185b5edae801880e289626613e1fb431217c0f66fb0f5ec9bb3f06e33054984ae89106da1d1ce6e5387192e12e0c569dde5ee872ffd9419a6d8d786c3737ccf8d029877d7e40080642414245b50103bb0000009cb922100000000018ee28945e96ba5be5fcfbcdfe997fd194765380b6f06b001af418362c7a522566ae948e6bafa15fe8624a749022bd54c187bca33b87c4464d9ba91452cf290be0346e5327a9a4cb8c5d2af7a384fb12e6a7a9c35986d2f68f35a0438e70f9000542414245010160c6834c1f0cd955ed3a166d2ec5058b076c318c4319b9de066da9f13cd3931d14612b3b62edb4e76a12e893e8c706e955048f442f326a827a4170cd1e66e088b0f1c66c7d0533d8440644eca6d681caf99e6100ad5d56e818d6eb043ba3e26a62dae8019eab2568bf7b6e85478f44a2bb173c9bcba158ca236a4d5ac4472befdc942d56eb87d3c4c5c350c8af355c4a5e48c6f4d2901cc898b37def451736f5bc01c978080642414245b50103260100009db9221000000000129b9ef710c24a9ab4e7c4c2a4abd0004b280c31a67625488e9973e7b0b68d4ff9a6d56ccb3af02e42bb2359cffe4a35a3b9c13f834ff40684acd7cb319ae70b62b240cada8223de32a3e154433274bb6e0cee0674c3e8cc1f333a7483b5e10d054241424501013861da534007ef5038dba21acd0c55e91750546065f440a7ae96db4c4af0a27dde769c57f52541043d331f9277be743a9c6711d2824056e252ee440eb8f42c880eb3fd95dc5765198d1b3ae0525ed20c4aec91a92955109639296f8b7d8aa4a766dae801aa7ec8ee72be526fc2d7a49910f648826f5cb934ca37b05fe83f1bee37a1a8128cbe82712544d875cf9da5f127aaffbad39961b41399f4e5bb9120f7bba7eb5c080642414245b50103710300009eb9221000000000605a7b1e61ddf8d589ae0372146ad6f73361cbe23edc94b0cce2b47cf14f060c807a2bd9ad7f3bad32ec410811016aeaf120c8bde236f1c2a0436a143ed7ec0aab1659666cae48eeb6dd49dc4efccec962fa8309fdc578409903dc4bd62d130e05424142450101803802e7c724c3c922eebb0a64951ef7e3acd4a8edee38f151c6a6eeede14268362899ca8cae7109df7ff27f86a5c3fe8dba2f35a8b47d24c32d4ab58548488108e97016a11bd3b4e1e8811d32ae0482d0b032e5da86c16605ac428a194ae5d46adae801b6188521cf821ff84d437d3ea4a8073b29b964eb2ded89e16f9847d45cee884a22ce70d8fef99336aeabd789deafc9c4d43f2181f73eaaf594511537a65ed534080642414245b50101120200009fb92210000000000e1f2ce0408889db9e3b1a3cd5838ba7afbf18f59cdaf06e3cc7a54830fabd119167b801897d9bb3b0fff377712546f2640beea6b1e644263f35b4bf9ef3ab0bb93d5bf693f568751f9e88c02ed4a771d917fb631b4c4f828e23300ad45504070542414245010146ade36f6f15294227c86c40cfc10ec9c2cfaf492b88c881874737b04e96d92469a412a2cc663c8ebdb3b685146ca6b7f3b628fd5a0688048dfa499ad99af78a633c547c201b8ef63c265238353965d1abeaa2d8d9902dd7049e73b063f2cfea6edae801f8e3a8328a6c5f92b574830c6d98bd9504df450f752dab87f4d067d62f19ece22632a7a1857a1dbb9df0905f935a6f7c1900ae530114198ff9c5d0d0080631fc080642414245b501034f010000a0b92210000000003c67427545c40ed32e08840f1a87b375f6d2c76e6d64edb2ee9188cefb51811c8213f29882f365728d135eff6c62622d1d0f09d1add90a44383d43f2925bb503cc2c3975be0f3073b594c3f37bf702ade52d0cab5668cf34c5f67399c366520b054241424501012e485e48b7b2beb325debe48e0f8dbdcd9a1eb875ef1b18485508a19f487116b06e7856caf1341f3c261a4d5ec8fd3b7d0424c02ca02289635f24123d7336986d5d5da31c5835ec81fbb1d64ede53d0370806fa2d1fcbc192ef2fb5b46a7967272dae801d5b213d180746deccf05a33d06d86fcccd5ae5929a7f877a8352fbf36284342ac123156cbe2ad2a54d934ec3382620d14cd9e44a657c1db054db0e9b13130747080642414245b5010156000000a1b9221000000000363c6b47555c7d3cbbe66db0d704db8291584addb1b94a0f000c3c8f1ea02f48271fe062ed256b54180a0c9b8d306ef281c71cc7aa091b396acb5f74d68c38020dbe5cc1ad76f169702a828e34ee54e3cfc100267e49161f0323ad284eaee80e05424142450101e46228c47afdab06d9ab6abebfd024cb85839b61f9392a0ad6052125a4018f50ad5b3b10bfb3867af50f87e1747686b0eacdd4ac1c3e020c25c06c69986c8d8d6b68e62b3f6f61901f570e5ba159292a6e26cf066db470ab0fc60b0c0cb3292c76dae801de760c5714014de611fb1ed0e4b04e4fe62195808e618655a943fff0186e86415fd9ee46b6e08645f96ee3ef82f838acb0377713bb767d5338a839309fc8df28080642414245b50101f1000000a2b9221000000000e822f7a44a8aa630cc02012a63986679cb4a67f7917250ed9a19ba663385ee1f0bde37f5c4eccb3035ce9249b695f800f654097dd86aeae271262b54b7fce503028f5b454bf54232efd226ec123b19ed54cd8978f6a0a1c086b554a6137a560e05424142450101d46b1e0ba6f8ae9b789eea5d8ca4a3721edcb000b5689504507e9ce23fde2e3bf92152aef27bcbb4ad5f802654fe0fa8599815d246ec0e248cdf7117fb86718cb87d11b991e16b425cf408d0f321453ac979027f052e3cfb29953944781f93367adae8018f00e44493156681d2da519660a38f7230ee17d9b89ffe426119f2147bef527b17229a5519508e835f4713feb2bed18b43d779adf760a0899906a6ab6e2a2a16080642414245b5010300010000a3b922100000000096b33429d3a45be57ab404e695c01754a8dd10b679852a62c2afd01cfc9ebb12732a6d1779e0f455c86d5fb6bf5417af129d089a66b46aeb1ba30cc97d846d0d0b37869e80f1c918085c5c6ee1ca9cdfaf56d07ad56b5adefb9d886017e2360405424142450101108a05259a7ebf5a55cd8eb6eeaee1fb2ac89461869a847711901f829292f3772517cf91f2726317b131712930f853ecf85b194915908d7145be5f071d1e5d807061a148739f65706cee32fe954c90a11a2009d52d95ca49e65dacc5b21644527edae801bf3668265b004190e9b60563206382a89b8fd7b42be68aa39573e08780df341830d2bb3e3836f77a9c3fa4fb776e0c6b0cc8ef1a48bf228361a65dd042da9654080642414245b50103dc020000a4b92210000000008e3620d82ecd80daa224e9f7104550806084edcc2b9a7eaf19faefc16eeb804a7ab1fe3fc4b077731261a1debb373955acaa0b83ae0039d14c6e0817a46f9905ad10d6b74618950fbe750b8aff3fcd7ea1ac1d0bee40d570f2fed79053765d0d05424142450101faf9afe843fe1d71be4302e6bd8d25cf23ae0461d736add3834c2fa1a6a1184f8ace3fd050dad658f31c3ec006659efd183f83080d9d435c334f51a277b7588e16211e69be9b3fa320d7382fef2ba702ab4112d87c18c91fbbd511a52b9a3da982dae801bf22180332568eccab806097e219d6e31c5639a64562c6703ba82d6f56b6ba00821e155c73ef9b9c82289b19199cae0882cce53b4887ff2d758e89c6a806f894080642414245b501036f010000a5b92210000000009e16e18fdd4c8f6f492b421be65064334e0717cc049b896388b3c7cdd131331b34d018262509da4e2b740a3a6a3f8a1864d06d39b565fe9f2a6ba5a4d6141c04ae72b4f37f67a6671a130dc28d3a132f92c29af194f5e1ee03d1c3f8c8919d0f0542414245010116c5e0cd1070cbbc5c53008698adcf037c28567a90e6f76136d7464b99700e2aabbc784ceb4f9fbb3472328ac71e975ad1be654610824ac1e47276e3b2c12b832dde7e0e7d9a67bae3090cd6420212c3221ac62f14b052197953506091ad23a986dae801141ca2ae95bd126e06c5485ca7e872996fd09a2d1cf8b9491376162400a722d1a5bc1a57652705891fa7ce685820aa732b18ee343c7f6b3e4656b8ee2ada0aa6080642414245b5010346000000a6b9221000000000803d432fee5009678009525a4537199b4c0695d84a5e5c45939c6a34aa55a93daaa944eb04197ac51a8190393ce620b2e85ae296d09e55ec87847575eb0730027adccb4aeb4bbc1f6ff484cb129c6d8da77ba8fcca282759648dcee1582d3509054241424501012819b5b14ba7f80b432fc8617d6d7164f9c40bb3088122037b8014af5c4d6d2ef7001cf36d444248495beaad6a809d99a309cb15af7cf2bc67c858536ad5398b8f503eec67c0c41d6095bbb7ccbf941dddc70b97c8baa4052aed331a793a3e1f8adae801337f685e77355b480014f78071161ecc1e8e1da888ee07269a04900e390944f120b17a5463906a70e74ed58beb2512268e66653a80c1c04eed010636a549be24080642414245b5010176000000a7b9221000000000367fd3900345ec6148dae7232c13644ebf371a783a1991a1180f54ee517dce602b39a90d363f8c84358cea0a376708983b52002bfd58adbf15ece0bdc93aec0b406be35dc707a95ffab8141e3572608b1eb33c55f5031b30017803493022c00705424142450101e06661a2454c98943a8faf720bf88e4eee42e66da2d38c0c25c2bdf942643b71b2f12e1fdd1de26889b9ef327b24a9400e03f63172342e1f81d0643eeeae5b8291be2dc1428a008c97b0682df4ca2ba2759b59356289f553c0477fd3b806041f8edae801b48d0a17349e3990119bd024f9432c5512d8de2f984c00a7cbcf0a71cf6b0301bdd1b4adb9cf35ba143383cc5559b3f28feb0f20db378df7bcfa4378a1e1d927080642414245b501014b010000a8b9221000000000da4b9a930132566ae84019529e0055ca9c58d33f78a51e39c2f9719026a645596b9dbc30a7e810d6799865a9f91d5337547997ac1368ba0366614e0509e06d08005d115b8d783cdf55e8de02949af680bf29e4aaf53865a661471d397635c00d05424142450101d487e86b89335e475b3e05e24bbbcdcd2b8a9f8b06c2ec6ce5f0597c51e91142e4658540d85d3690d2a939e5aa5ea99385add1447a59f48c08f678f41a460b815ad9601ed7bbc290ddd0484d97a9c91d4f39f476e376c468ab99e8e28806174292dae8014c164e84fe18a6b9036d834732bbf0292f6c2b8f23256f67bb2a590145c2727ac937bd969d71f7ee40bb7f3d3c3c7535fef64250a1960d3c3cc927ce55ef10cd080642414245b501035d000000a9b922100000000038fed848b04061e8f0d54df60666f05080aa1a2685a034e0b98321ad5f2d635dd3eb7d4e3b51fe51547fad595c97d5c6b4229b78b4d2b41af7d34394fb05040ccedd6c84f9061e3f98a4a7d2ca8d00189beef41a760b2f1ba3084c65d8f8690205424142450101ec67b4e57a3193f7f213a580b5d4a037a347cc642a10f644a865d67dc669bd0ad44991784e71c22d1db6db428a50bc4b257c9b378bfb1ee7edd3852e3f85858ea7b297a5099093cb1b7e22e85b4fd6efd9da6e1eed3027487d05504ccc6b603596dae801fb6aebb30298a0345ffc16c66680e1303058b1526156403889ffae3ab0e712c913d83166b97b4ef12470f397a43ad2951a9f65e95db0e63dc008d27344195771080642414245b5010369000000aab92210000000008c3745dcd7936e14261f660efa8018d8c45b86e80f4ff1ffcb146174cf86a1680425c3dabcb4fd2f43d9e8fb058d01d61903dd28c721dec363d20260c6ba850a6c7b4e78c20d44cf3dde18b7301175913f9674adfbd4894135e1c4acbbb3000d054241424501018ed5e9116f278c6ed5f381d1b7ae85e65c33bcba278cf507192a144b03f2b85991b857a2b6363a4f1f425c7840408e7c687d7d6ca3bc52089389b012921c898f141aca3c30cbf705e1ca5e4a82a5157ee31daea4b8665a129a3b96b2bbeff6029adae801b21fcbb27ce7af53cde6e72cd56390f5c6e5b16eac11671a0b70c81c6c9cdf9031d895425c9fd459a3804ef8e61226eabffab079bd98561d931b7c8107f33b8a080642414245b501036f010000abb9221000000000d451f376488a1f7a3c76ff79f1cea8ce22305174e8e05dde900f435d41b753382729727014c1a8cb0fbf4467ab1592ff5313e39ef7aefd66737d9ddcc177f4047bdb23727e54444f134f82fc16810967d14a1e2a44e0b9901bc449a63950850d05424142450101287cd4336b7db9c97196110fac5669c7cfea9ad6affdac61ef2829dcadd9f171a05a2e67de4d6868e292616032339bd0c53d88bef0fe312ba565d16df66d9080375acd3deb23fc785588946f941364fc9ae873d54f5df66f2b78c8db9f13e6479edae8015c316a9bc66f077d5c30ddd1903391420b3d3da4aa70e50f77f413d296e475d6cc9a33b87438fe82aabaafefb0771b0471f6dc399549811abe78ae1dc7ce2772080642414245b50103a4000000acb92210000000009e17fa7c34282264dfc7a266ae4653d1b6715135c6ae1046247f27524ab6ca02039d179c1268f168fa15fd9024a564344753be97828bb4d3c9cd284dcc597600dd696915339979540e535b36af86d8056d0f2a9178cfe31448701575fc61b30005424142450101b4629d14f2ac171be6bda4f478be9309eee314397f9f936a3dd3778fdf89cb7568835690e75637a34300a82876a910938d320359599aa746d10d2203dd8d548306cf4ebbec7895288bed791c6a97e46a3e6af990004b1cc08e2140bf76786f23a2dae801c7bf65933dfe573d448d60d76e4105df8b7166d51b878f5151f64324094a1209361c44fb04a9461e1711ffe8d459d2bf79695cdbe4f06d9cc7b0e43669a5d50d080642414245b50103ec020000adb9221000000000d453e34ed229015959eb691492c0fff74d6978b29e20916cf7ce4f22057168383f9c668d7c9df208f838be0c77d8967d503de68929957cb94c42add881bfaa0b7246d16224494a4cec7bb481eb007c3b8ca5870431ce85064d50ef6afcef430205424142450101aa51a000868a7318fc1e4fe661baf491acbe2528104d5e8dc638838f577127564077915bb4fa642260980040287786812780b2de4d2c0a1ef4c4b88d9af5ab8dbd5c19cf44db2796422e354905a0338290821bb6a9ef585233b402f6f38aa3bba6dae8018bf3103ccc206a9133c34d415c1da4d92b385b1f759d64aa2c98b17037d02924e6f0eb4942ca150258a87c872e9df2699563179cbccd90bdf92c4c8a7fc715c2080642414245b50103d6020000aeb92210000000002c004437ead6e7fa58772c8bf80fd7d0380f4fd99b16a9b217403c43378dba40d2973d7841410c635edf10082349635d3062c2ba2860a803fddc5598203b2c0ab409be47394b576354df275d99342eb08d8bcb155fc90c799ca958f20c7eec05054241424501013abcaf791b39b609903eb2f888b7d7a15296be3f7c6bfa4ee0a4bb63ea35035a95af609361bbac3e237ef78356f42053009ade7f720dfb4c0905443ec41281866c829c4f80d1d616e7a329d531b77eb3b1e481dc365f4a99d81c42c7d394d41eaadae801c0c27139fef5ff23960b60c2260757c8a06f759c34c1d4647b733d8fcbfa85c66552301022f21034590a452224cb83abd4a848fccbc53c15a5446f5b7d4ff649080642414245b50103f5010000afb9221000000000e0eb3eaad353b10448ffd7dc75dae5e527240e60b935e8628c82df75ec9b19611d7efde267201e92ec8a2bac6b87f9037a073f05583e23e429d195106d3d7c0067a3dafede1d3f87ab6faea3b1a2ece29cc0206730fe8667d8c9e88aa749460a05424142450101a6d39f02fa7228392147c22e214d7fbae61eae7b47177f818912937fc8f4246ccfcb2f73cf9fee570f1baed38ccefc6aa46aa72f3dfffe4115a2e19a3e76218833b88746b2ac004e01a6b4c9179764b3ff161c32cfbbf45b8b5c5126b7c421f9aedae801a1325d188eee52fec5cc998673a5ac1a24cc850e5b6a350b2803bed1a7cb57a02084ee82161cc3852766bca76117afcf47c5b1f6a7fd90c1f1cf55274a71087d080642414245b5010389020000b0b922100000000020bcd89356a5cb7918938e3fd5db658761bce4a387937617f94321db7933cd5a87406ef2960a8b2a45e9fa45a565265ba980370f5fc4ece3a5b88dd801c5f002300a0afce2e89b8df7083896872e9ee8e8518ac1d385056831e5cea6683ad30305424142450101082bad1e666a6e73735689caf017c15a752ebdb17a76ad6e6fdf5814c3dc8424b3c071d9cd85a8c45b288d1a45674171b46963d675b8bb8984551b19b17fe683e8d056c622145154cbdd9cf890380f000acac980c2175f0847e15d138bcec603b2dae8019a213b3c33bbda348135f2c5e14ee2e91ff53d05a8360395a4a61220da71b3b900a7d467e514084f3fb958f13ddafe73d423d7ce726e8da51a2981053ad961cb080642414245b5010317020000b1b9221000000000f8de99875fb8f527dccff6619a1bc487710575e4a85b6b55627d840f0fe4f009769831fa25c0a6db543d8bab4ad3f8c6b937d4e1449efa7fb348efcd88b2d702816501ff18a9a1ce16c77684464e1b3cfb0ca253f87150623b0e4910dfa210070542414245010166676ab20f06d5602d07d02514e9bebcad186272743fd1ac3d2d44460e98e34cfe5b167acdeb2ca63d6fa1304d5c1d868f49330d52adc89337aba2d1d3b2c589087049f70c419a7fcedd14538caf500e0b8748b39494182dc19189a2d03948b6b6dae801c5a3a190d7633b3bd3bbcdc1d4d45bafcd75d8c204e7e3ed9513162c1c9f0aa917cf0ea9e88c42d31f6d10e95a85a297fb44dad50d0d5fbc2225886c589700e3080642414245b501038f000000b2b922100000000028bce9118e0f29a25dd3ceef611b36e43d5fbeabada36b51569f4bce8018a4529afbc67e179f22615f9fed2dfacfbe0b5cf20940c726a25e278d73646018d2082f60223f10706553c5fed5b5c405556b50da65ba92fc6b57f2c637b80bfc7c0005424142450101ecb177460f859dad420754b4a08b64924ed599432ee90df231a81808352c4a3af23bd45bb5c16729f4d6f4f631e24834d9ebffc323a1aea4c9b409b6880ab4805b8b039dc8b8168069693dd0dbd64189bda99190b01f6116b61500b65b7dc52abadae801c23507dc19e8bfa547e4966c5a776fb15609c254612c621a8465deb83c0f914706ed9c96ddcc398116ae3672259a60e669afb35f48b5f3554317e5e0da7c0474080642414245b5010384020000b3b9221000000000288c75edfd0fab5bcc5a198f94e112e1436ec3db4f6f1dc4ac7f2ac83c77ef72c77123b719e15ecb48ff65ed085afa6831275d08a18ccef1921ef14768acc80d9ccc42a11425c174a3ff2796825e343c20c55feb82044a4679276150b85de3070542414245010106d57002d4176c4904eefae1339e463efc7d3f34055f2ac7b0606d4f59aae06e0d575e5f6736def7c863d9eae27aa55f37ead863b8972c7096d419f2d3b526833c2063ebd8f6a5fc812440c1cb6963f3c1d9dade14e982e88975a5fed8ac7c50bedae80131ded32067b34c8ca1aa2e97357250869b6a24c409dda6d949b566174c4be065b84f631014ab27352de6078b5589628a49d701ae8ef74b802d913d30cb42019a080642414245b501039d000000b4b92210000000008cb66d6fdb6680c9cfd727047befc43a54b36dc11924d7494fb0a9aa1eb153739798a302c5786bb30615043f55dbcd7137768169fb593243a69563b1451eac0cfbe862489d3f1bb54185d236ac16e4f29cf30d365199e0ba88742df23c56460705424142450101a60abcd576827a3f5c030f25c2c6c1e7c054d3e13b9a0af65ca80be95189ee5f143805aad0a87f242f1fd8593be5031ea9668d438c8b6c686781d0e73e300e8fb061909c52348343c847a08920395a26b93e8ef7744a0f77146ce7736f5a5b79c2dae801a177c76de4649c84d4d3792e3fd9a0be42107724d78cac9a4f1a8687879c4001d4a64564fb3d2a552e696deeecc27e6451469909df613f9bd318d0f6966897f2080642414245b50103d9000000b5b9221000000000c2ba53320de3d3c485ec45349853db4953518aa15025160993306bfefdfd1525e3f2c8841b688184a28c1943be6461af96c9197d9688bf6557b6f4e41af7e40f80497a2ac838d4e5f26c011727c13a458916d6c9e636b7a4a7a71d586acb9400054241424501017e33487a3a3cd054885a38e71ed665984c03dc46824048b44b246ce0f33a051f70a49266116fdd802b86736243df43d8dd6f265c5ed779f40a8d954517e5d285538c66229b6e5321461fd712901ca7e524e9887663fec84ce0f1a35cde3a2ea9c6dae8017f3de48506a332c05b8e1fba3af81146271410836d44629352af614eb1717be4433cfe8dc8e9f8bb2b4a453d7f5ce3563fcdd63908277e2795eb34c291f08665080642414245b5010104020000b6b9221000000000a43ea015abf5abcced101c608fbc4617bd7f99815e428da0b3b8bccd48a62c38c65c4b77984f59f65c3001e7c3f7bf68f60bacb9bd15a5aa14a561e0c8a0f1044999d5c76409ad1b36730451c25c3f9b569d063173f9082d19c296ef13fbe60d05424142450101ae89c4eff52e411994fec8b9e669136cde321e3573f40c56b93dd804f867fe23117d0d88aee38fab36fb37c9163a6e5e0a3d0abdcc10f2fab67cb92310c6928cfb3228c07a027afd70fa6dd70a7ff1674435fb3b6d7cb37db965f80364f36363cadae8019fba6de8a1c1ffa85df7efd94a3f3b2b878ffd42bc7afbb3043d5dd69c6af75494a6179c9987ba96672a9a3ba88746094d3f0d818a331eafa7aa240decc6697f080642414245b5010345030000b7b9221000000000960cb8597ed51bd4a6b8730ff0adaf5e05ce64011eddf5ea6731eeba1df64363b426f49148eeb83c71e949a7fd5b1702bdff2572237da030be6b9594701b9a038fb3625d206581a77aefb9482017b1efc5b1366348414fe8b1d682b11f03a00a054241424501018ae0542519a2992d29ca8b07f4035d24885a49932b3cc599ba7713fe5615b205361c808a13ba686ca7a9ed50669730b4eb5d7c61bfc512b3dc1756c242cfac828ad0f40a0644214aff6bbebd2037e2cee2e9d43fea4254d2c5ed27bebaa007dccedae801084b09d7ee44c850b9e16c6665966b0860c6d560543a4bb1cedad956e18dcca021711da661cffdf02e775e10d1a24406e36136f121f7f52d0750e36249ed80f3080642414245b501018e000000b8b9221000000000a02771508b1eaa4455da5ad9a3a6685c7675bb6f036357784022a3175a2ecd044b93f871c222265008b5c9c2be11915168eb9d29ee2a728599f57a58c75915091ddeaf7b900cec27df08261d36e6a3aad40d15a16182e088c496ed625d35840605424142450101a0bed037f25639d6535cc5252b6e39f50b1a347385695a3be30af98939a6e32fcc94493c192f0c80738381d5afb55f79fbf4dc97a191cf417dfc179076bd02801b6b1e179d20f2ff7fb08ec4c659e39c4bd22eeea908b6f5244f4ef2a2b5b4c4d2dae801d1924aad1ad8e50eacaa958b6975ef7f6860da038c2f255ecaee2f7c1c9072ec813443fb98bd3ba7fe648527760c3de48010e463a6f2128613336c5c8b5b527f080642414245b5010308020000b9b922100000000010c155904d31a9e922196985de76f84461d25befc095bf5064402019f901dd52fff46804e5135f6da072a6105ff864554829b5b4e8a01d41a599c908496ea00ee53735e6eb4cf7aa9dd8c5c9ad249e201145289f033660d479266ceeb573ea0d05424142450101308db83cdf458867b5eb24952a2ad2d13ce20c0f664e1c7a86be8a87c14a694267db180d7003962fc229c5c2986e571c0701e5af75b8cf3615fd745b447b3f8cc2d14bfa5a93c4cb9dbf4e49b7865cd47efe036e6bd41c6c38c11ab83e62fb01d6dae8016af4c67193cc6cc566225da7c0809769afb18b05618d65fdd0a63c723c653afdf8420880bf1a3ff868ec93fe66ea83d1efeb71808c8af3886d4f31f24d76f71c080642414245b5010344020000bab92210000000001e8322bb5cf60629bc712d549543627d3691c98dd631124b5f698db2d438427c08be0a40b847a3b0496723b715036234761f63d0bcaea45eaddf184b1b6b0e0f321be998343dc1b36c491691a3173656ae7cd6ab19472dcd499039d30179890a05424142450101623efaf5981dedd26f57ad00d07731e0b20c397ad77b907291d29a3637d9955a0ff7d6e72d89aaf4ca9d50b05fa7469af819134073661741277383ee566ed68ae8820fb2890cf5eb963a2649ead4e1e4d86e5896f7d1077d3a0257b05ef02261dadae801f1bc07c3ce967d2abb96c8eae0ce28371c0ab142ea3846a94c3b29743fb0b37ecc807a9dc49de422fa0265ebb22411173ffd4ed320dc45642c4bdb0c6657e513080642414245b5010333000000bbb9221000000000a25de56093399f900c8cd0b9cfdc6112ad115c3bfb6fa384fccf625ae0841d5b20056a8acab45dd9029579039a749ee5d33d6c30863075938b5f2d419ce6b80cb3dd3d38f2122c1c902c566d939a34335e131ebd6cd2148e912be9f5bed49f070542414245010140882c761ed376ec1cd4bcfafe4fe7d02ff1bc4ad3beb9442abbe26fc128dd33ed5e7c986b35694713a7a08f4d03b32fc4cb91275b98f5c413f1f8b8340f0688d63789cf73f175b1e9f53748d2de89098bed006ea4670968b6ce8b338e69d9e5dedae8016a335f94e5795021e0e8359e171d217c39b087d059aaca37359ce4ef2f30db14308c27e671b5d8a5513687a9b2bcf6dcb85b3a7d8d620847b6e61bef6bd48aca080642414245b5010340000000bcb9221000000000eaaa177043393951a510117454bcc81e856461fa99d0335bf64c1255990dbd7e34fc3947ccb0a857d8b46641a1fd6965a942e1d04a8c0512b9908bc0dc7fdf04d33173829dd1f9c529577e843d18fb00e781254d1c132802a72e3b0cd38ab20305424142450101f29070524b87b5a9bc782405278e35a80b4080295b9f6805726dc5e0e53256643b187b8119b3b31af02e2bd2f18f6c4318766293bf18d2e0ac36d7744b3043806c395ea0271fdbd9f1b95b79766ddb0d2cfa3c3aca90526ef6566615152ae5abe2dae8016cd82c762eb7f49109ed4be1d8b0610e39f404fca98c42c626bf21fecf05926810445f87154a0fe15a31dd964dd18390930765d22d164e1499af78c450a7f3c1080642414245b501036c000000bdb92210000000009cc2a9dbddac2ba466af06080a1cad80d21c9091b3913a08c3a6f6c3ce650b488481229939b6e6c0ebf0e2df2fd4647d3fe21152d8354eb78301eec156340c013d2be1b0028cedcdbc1ce07a625d308bad2e9779e8f8a6e11a9a34b82e680508054241424501011c07e7a4225daca6af4690d5cddbadd886addc51ee2ec7fd887d89c9c61c344d38878d83fb234c3603fcffab52ee1e467fe42c7a798c4b535392b1b5e92ed38135ba071b2f71a8aaddce3fb54cfe994250bedbab3bde8aaaaee9ad78334189fae6dae801bbf035a59cda5a70defbe748f1590da95ed008be16c6b3d92206e2deeaf95111b395721fe217565971411797616ad00c06fe94154d2dfe7fce5d7f52d6672fcb080642414245b5010378010000beb92210000000008260037c7bf0c08ebdc97ed0a36e8258151eb981f797b7ed62160879bfdf9c5c1687ddd301159f33622512f7ef687a3fb7657272bf416d14640ae3ac6d2f990e85db6724f7286fe8bca9b3044dfbf35200eebfb034f42ed70317d79fc411ab0d05424142450101f65ea0e885c916b4a9f01f9d4fc2a3a17907c8375f0145a497f836467fe58b7364648091da7b7211ae6622394e3f816dead76198cabe2819742315b92f8d4885ebbe9ad3719cb08eaa185a9ab17df3cd9b4e87f398b105d8c285699a82ce10dbeadae8010e186b5b2c3b90e8512d44beea24c1d1792e1689f6b78c5917c6bcb881cca81e1ef3edc272a1383e469da396f31315097fd4caedd77fdfc7ddb426e41955ada7080642414245b5010159020000bfb9221000000000504f58fed53e91428f4e81cb46ece9e3377b763307015b182e4d5f974ced9d39aed01e0060d36f6ffd073456eaff12eea97bb29f81c28497262ba195b74ea807b1ebdf612dcba6fd4c2d4abd928c979bdaa72040ae0445a37a00cbd8bf70ee07054241424501018a2b01fbf16913f910a907532f8850f9560fef265bca6f93a61be984d088923c087af967024dd8d8dd34060e5259bf22db1fa02a57f61d55d2a08e26c0eed281ccce5f2c0eab93e83d0cf29dc8166de3d47df1b9d11fe0b1a9b8757382cc602eeedae80111507a1b473476c8ce2a1f6b698d45085a9879fc420d6691428d1a358278274a7897fe8dfb698254e9bc1ebed88ecd270b3c12bb74f6e710cc57f728ab2841d2080642414245b5010178020000c0b9221000000000aa5c51f005108390e5a45b480820d8f2e17a8aee9e7e5cc03acaed495675f315f9400df5b9d68d2f0c5256449235e428827b22c4fdaf745c70c6f5e3dcc6c10b8d4e605a33ac5dbc7e4f2741f8b601da9e8fa758a6b33d4fd8a48b032ab8f40b0542414245010150020305a7bee3fab7eee2b1c4d787bfb39d53d6bb55b401e42c7d273a508b3a66896f0ff5ec2f3788b3455ff2de0ef228cdb7f9aee4bee854abfbe2a5229984a028b62bd42df92551f6233538bb458d33a1729a84034cdfa306fce0ee70fc32f2dae8010a186375c880e19897f569c57b7cb6c14d65b42132265d3a905f94f8a61513308f8d5dd43fe20f874d55f9b952904f56883c53edec8122a046a89461019b4e34080642414245b501031a030000c1b9221000000000cc8e454502d2522ebbb917cfe9cdbff86a9f9a21194c2265dd6f67ff883d7b17cfae6a88b0bb73027c991611b378e92b8196da52d5bdad0293896bb9e6d842030655967e11b184e4990e77290f27037b4beecc9e8cffce07b71b956bdcf9e00e05424142450101449c3e23bbdc7fc2bbaccc8f5dedb32332aa91fa4c7c510623a27fe2703abb49aa4d7627f16e16703e06d39601e4c1af3f3c90f9547c7092e812a25745ea488abe3c7e70e9e16e7e087a8af6f40e04a27b39639d66715e985db6665ad9f85c01f6dae801ce833dffacb6e7f35d91dd6bae2be0280bbb9d16f389bbc316f78731832e554f1fbb4767b97c3fafaf6cb3dfc7412321f40845273bcf82797f8c62246e15cf78080642414245b501039c010000c2b92210000000004435d734cb8c5337845c126bf4bef7db2fc23eb0b53990077fa3ac93bd3a4b43f7f62bbe170b20f906030a5d36ef0d7c204d6d74cd158507165caf326da5a70f13f62788cf6d024ec6d9b87eccec735f622f8f9473a8dc1484a8e6c0c062c00905424142450101ec54c8dcdfb66107ca448f5e1130d321c22ef77433b1d82e5d857857a82ff90c5177434ca535d921184ecf4b4fa503b8a37aff545124ce7a57991cedae123e8dac84958ca542ece34059224779ca34d6bfbe58d6c14e04c55015af3e25656071fadae80161bba3a1ab249cfc973f22f43752de0a258d70fedfd02943ade8732043dafce3af005dad00d865b4d133762ed72f0ec5ff317cf11095c0d64a242568e65bcd94080642414245b5010304020000c3b922100000000070726c7910915cd79c23f7923f0ffe05434ebc6a2beb3b5dee8b3070007bae3a6a7d23614edcd7442dc15d807085eb1fb432b55807731638c33134e23e9b130917f2905573281f659832f8ae1490caea3aa8ee0c3a5f0bee9e9af7c510503a0405424142450101922cf55e579ab8635899f76eac1f05783f08439ca6c0e586cb2cd4e3764c0a7d5c2b084214cd2a3e4ea21c70af71b597fe2bb33fd70cdbca32c304933ab2e18a5585ce08c801a6f157544c2f732f6559c5f2b0f3f23d5f9058e051738a47c8cdfedae8015012a6ba5d736b18d516237f8278820c7000873d4043e03b488edc98dcb59eb097a5dfb2b40bcff95921f478bdb279768171b626749253d4afed0c569d0932f8080642414245b501036b000000c4b9221000000000109b4f8bbf20a01687244932c6dc395d1e35c5fb20e26931d115f5dfb59da9277f87f5b42bf2935463fce4f2732739a43a5f2ff757be534d73e596f34ab4a20ba782cf6656d17cb1a9cc0c6f724766542ef7cc741c66310ef41866e261433a0f0542414245010108f62a1c0e66deaed83d86cb607f63bab17fa9007e148210a9917df2028ebb654fdd114794baac3de00cc2443dda37c682d4574bb35becb96a05e9dcec201382ce62dcd9484482982be91e102f26a997c87e9bcfc2be11c1e5fce3334084ea7d02dbe8019f8d31b57ba4f24c201be859760a4f1f1d5290dfc68de0d360c2b7a0741f1ac82d52309d564d454ccbcc74be5faf220ffbf4935f92d90fb704f7fcdb371f49d7080642414245b5010154020000c5b922100000000070375a6195e1438ad7bdfb2c2fcc339437e2913ec973490b7d0d6ae312253f04c322d5d9d871c78f29b6cdf3fbfd7eed8ca4f7e565c0db320f191a0491019503f6ea22997b8b0df2e51cac2f4e564de1e432d776506fd9b778dab7262c78ec0405424142450101b00fb25939f2eecddad7fed061a1ab6c162b4267c886edaba72afa9430f84950cfc6460e13b2f0556eccf8a7a1e776a03351d23cf2e9c05145fb957b4862e68831d2f2d812bca55f4818529e076998947f5eeb235c9700469f953d9ae3b0d28506dbe801c891392b34f376280a9384ba6a826b66874db5d3167ef3e8acc3797bd9a80ab1725a26449eb7e62a455be85abccf17ff5ce6f7a2fda715549d7e8e7f14e2ff97080642414245b5010335010000c6b9221000000000d0bca51e872bf10f6223dc65639b11c5bfcbefc298eb45241cc4aa37c31f451a21dfb0c7fe45c8f76e1a8f4960cdd08cf3d074003019d75738569d6600255e077d286061ab2c7fbbd4891e7ebae923e5f3a61fb88ce28a18566ea2451b58e10905424142450101c627ebd74744dcf54558dea414805b683baaaea9ca16bb2396ed2e88d5ae606e3ba6cdf5d8ef471148797a3cdfb3559ee7e83f27c8b4026aa377523a0770b08eadb6523da83d001ef6230f772a6b001bb6ffae60d50dfd5192d1b1d25f8181a20adbe801eab34fb4d54f5d3a43cf9344d8d5c2cc3103da72ee1759b6209fec01b4f1110dd2166e7b103516814ba3ea237b4c78153b1cb07ee0a2ebe5bac3b2a0f90433ce080642414245b501012c030000c7b92210000000004ea5b32c1e9b3f2388012bb1f2e6637b6d38c8de24544c697dd6d178cd534003d7b5f4404de5de9a7743a83cfe9056b5eec72af09b09463454a66921132c6f00a04b3c113ae00239f12a900fd4001e6924e11734397221dca3497408e4968d0205424142450101ce56f0f3c1f286cf7eb9afa2806799591e3f11d0dd798b6c3bc580ef9b90313b355500d9f452c881a679fb61a7a2d1e36cbd65d4cc4b06bddf7e6b62b3d1108c4f4c280bb00a8828e9bfc3e790e2b819799a799c9dcc059bfab567275696db8d0edbe8015c924be7cb7d499e41d9bea6919d189d832e3497a7d05122fde5115489f664b15bebf22276d83bf0df86162502b20c67d70bb599b2c713107c644b10a0918c2b080642414245b501038b010000c8b9221000000000b8c7bf7ab5a6074c96f43ec6bb3897067e223886a1a1dcd13e7d30be3928454d3acb0d9d6eb25ad2b9985837dcb20684731400568c0a193ae22e36916b411c0db4fecf90c8af56c429819e065bf106ea23354d0239d5f35b5f58bbe2bc101c02054241424501014835553cf1ab17c43ea31c78b0dcd60ca824c9f616b0bfd291907c89eae8846129f74b7c0779bcec4efa25841a0957332212612648fe9c4412e0765a9ced7d8006cc1db845ee29fdce9022bcfa8fa1372dcdbef5621c08375261ade6e23e57b512dbe80167786abc7c2a7a85ce5231db734091ee8a21d8459683d8a5e2add56961f540006abbe639703cbc09314a3264e4e313db15d00ab0bfb54012fad9f85d0d7e8009080642414245b5010302020000c9b9221000000000060b1209f3a45242ff3a3cb99afe8fc550f09cf5df8904466de933fbc3a4d5455467c11e364ad473b5b9f180e93e7ea05871ae5cf049007f057bd5afe6fe5c0a8243c1cd022401ec3a323668e3324da84b5db6dfa7455bf770375e1dbf584700054241424501018a6d22c2d3a8c130e8c99a042012c1ec9694e54ac7dc2482b31dcc6976197f4ca02a0150b1d32399a08ac5263c3935bfeefe2d8e4b3d8987b4468f7db924588867f4dbb2eecc2ba8f642a18e6b8caa12efac8c81feb71e4ba4e9d0df1a0f319816dbe801b7f113dd367b5a39ece63b73584342873c1b80709cbfbb92eff98189f53ed03d467434cf6b6305e6bfc01b833322b9b16a2317b70a512c6b728428381fd2088a080642414245b5010383010000cab92210000000007c80faff72444582e105145556b9d9dcc8116c45886d182556090cc30459607861282af42c13415f0bc8911f54d8087259efe1c4acb34d0a1f0fbae0c8db820c15c4f5442ee2c32940d2a08663c1060e87b6a90b4f146cfaba8a7019edd2a60d05424142450101be03b1f04bcecf4e63109ed2721f7c2a9aed0cf511e1e88ee5e8c60b19b959088a0e9b435824b5341afa7a17ce6c4259b6723c7c507749431f41c295446b7887c6d92afa2235c999eed2aa09c7a1a6eae0ecbc6949b67b39f2f6759516ef31731adbe8014a427ee1b462270eafe86ca5f40c04e7382856ad53fb63deaea25f06fa94010d49d4645c1bcbd4c5784893c4a75842a5fd3f8f5be1d73e62f3b490ba65309f4f080642414245b5010126010000cbb92210000000000eeccd6da0e6cef08b156d377afd4b0236f48a3be38549f18e31063858f4420999b26b8507b33a61d9b4828940fbe70e71bef602297b9f7c8c8c14f43193b300af76403aeb96657a63dc3981a6c858f42206af2c5b99c857621706d66766ae04054241424501012a3987fa7de7100950c6b1a6a04c57a09056962c816c43094de61814c961fc62009108188271032bce911a51bed18e2baacd952e44efc2e3d9e84d99e5ad7d82eb8ed1c7454008fb0463f9a0abe2a7be743dc639d4e0496c5be5c1f8b31633021edbe8013dd5ed4d71ed1e4e2ba39551a8a5e74b53c9509ac2b97eaec6d5bc9ac742a105b8ebe3c23d6d9a1d813b4f351878f514ad95b396200dcba7bf73c86fdb962b27080642414245b50103eb010000ccb9221000000000be77e78d5932ee37002b52cb293b2e9c150f8ce3336b264926a08d280eca020266fa26d947fa42a38b42f7291c54f510fd8e1edec7a22c9d2e90d9c2892c5f031d3b0e921820ec94395f5649dea3cb233cb3772ec2d48ed00a2db62808000a0b05424142450101bee09e4e286bab03febb68c73737a83a923638533f260ce5e0011a52a1b265574d613b269995b1d80eca1fa47b8ed4acddb09ea2e8226391381940c46b3fbb8bb7125db7e11774a785ae6b303dbd48d806bc362ed23c1196503fd3c1292c889222dbe80112b622a8b1c6a7e18e61271c501bc4f1fc0a4522f8ab34519fdf27f04bc828eaef594e004a3eb3c9fa38096368bb7ab1cc7222540ba3b8dd5bb5282b8d8a5163080642414245b5010119020000cdb92210000000007ac178df8fe56f4cabf485dbb5a0a0e3af614b8dcc99ea20f8f24f70aa78491963dd7cfc2b5bf35aca6c97baca5c803430495b3b994a929f2d34eb3ecc20da03e5d8a56ec783648194375f3d1ac62be1ad1b5e970d2e56bb5db98d971bc2cb0c05424142450101acfb8443ed4c27993fbf903dd9d45cbec6231b06fde23ba4b8a574fe7e047a7b6e342da435a45474ea7a81474deebc3b65857d19f2269de2c48eae0eca214787d7b91036bfd9554788d134475f4f007e510d821bfc779a9b56a5a8888747666426dbe80150f682f27e3bc62b20f160d616c766c554767f5ca93f9fb8b8c1209edbc23ca56889d64f0eaa5456578ad12ab69f5328f89ff5e5074e60012f545ec65f90bc8b080642414245b501034d030000ceb92210000000009e5a49ab756d00d1b684e6eb27923022f70650ce2c4b57d04f7de1108876b3024be7f0ca5ff52ef10182193322477b42bd973d1f2aefbbfc916c900ac33f200e16bb49d412d55f65ca4c67414ba21ee8e137072dae4e4fc510ab065eaadace08054241424501011c2c83c68032a03062d54e71b492cb1ae16a6d32fc0b514864fd65a36a313e050c89339f030cbc442c40b57667f4f04477994d1d9997c2bb8e38894a2329dc8358efce044dd6ccaa6fe201616e8fa0ed5cb643b3a13cafaca193e9999c8da69e2adbe801499037904f5a65430e03b5131afd921e16534cdff42297196ff446184551c869a73f86995afba7919dd1e31769d6a4fd4e741ad254e0f07718ea4ef7dc53dd28080642414245b5010309020000cfb9221000000000884eca4dbf19cd1f0e9ed1a67a4fa40f76968b47b1a65b663045fcc90684c7559eaa6ce659eafd949b5480919ba06c9ca3edf1e3b5da1697c30794a0c472c60b3ab1a9705d646f92f8a6d121666855c03388fa33d719bb5ce7cad332525ac30f05424142450101de145d732c496118863e9fae064e7aa3dcef0c631fa7cabd3c76549b8961704393eded976678f6927489581665601dcc12e464f53e1e334bb1400ffe6ac27e858f7f135c2d8664e737f12488eac758b8c370ff59aa6ec84ee2dafbfdc4bb263c2edbe8017fd706618a6d4e60641df604a336c555a06dfb2cdc35e88d3849379066b8a27a1997ac4e5123f2e94d712c5bf8e99c0a1876cb30cc24c6cc6962efc2deeffa39080642414245b5010304020000d0b9221000000000f2f204255e6f30061de41fb4a1fdebc7bdd96255205bb3585c15b9bb3e7bcc75b5d12a2516d5f33556c7f9160d3a021b4868b7546b4588a82da7ad4f0d241e0bb9a9b92c314b0e88f5f9143243c9afc08419fe65b130e29149357374946b560a054241424501013c62a97e1fa3e8674cec7a50a5c284ed8a8f6e676fc467447603fcec333015279e745b571445429da85445c49c579d9a2074d4e648e6b2ce8316836e9e43aa8c7b0af865925e8a3a3f40c56cbce315e0c22c922876c1e8c22c4441c2a5f8a41732dbe80190c42e7dedd27b1961a6ac762a26e19b5cc46bbcd650f8750c37211130c18027f3a40e35c459e677aa6ccfc19be1967e2359fd5d8e3449e3675cb401662b9447080642414245b501011c010000d1b92210000000008ec4d7001c51fce1a525f5c4126205f6b191c3799c6d8789e0c490b090269821ff21e2c1197eb721fb52f59854d33754f80c3da03c60ec54eb823fe728c98c0d8af5cc37bf1e72fb3d0fd63a264645cbd17af67af659976299c656a6f21e240405424142450101dc6d1432fe6befb5ca0b00ea890dbe1c80c44ca795572bbb7d7f1c141c83a209343168bf76f5aef1c8304368eeb557aebfd39436de84d45e8aef462a6f465e88b9d4e9dd7e57750f0e7a0d5c0a2bc3a16af655ec9a40a3fdf8b70f5d4c2d5bc736dbe801ca6894d95fa5bad89e9decd2572105ed910264163a49533027ae21e6dd1a42489d06d4dbccf47e5638699f225d6d1076377c803521d0a3c4072d20c527234b58080642414245b5010304010000d2b9221000000000ccd0f8232201346beddb6e51679cd320b6e61cfdc5af29734183a74ce7be7b0c46d9e8d7048f9ae3cc51802cc2cf704b1da63db7928c13d5cacb5c33e87aba06662d85e7ad49f0e43c6dc278ce2987633063889fbeae63e4a375d9d04635b4060542414245010134ecced3f7ba1b212173715f1bb37e8273222eea27caf494864884ca045f02352587e1500ae7c603eb20fd674283ca9dd715b0acf2221da0974add657245918ef938b41620cb5a2c39fcc65b535c1a9135946362c9dbe448ab6c187167aad5bc3adbe8010f1ddb75712416253690ea1117de3ea3110dd1e43651183ab1af6139283d6e2727702519a5545687f727c9fb4a8e9e318dd4042d31da8c682ac15c2fac990da6080642414245b5010307020000d3b92210000000002aae94ab7106fbea0f13bd5374fc9ced659276812684033777d8efab6a8f665975ffba8ef4726ea476cee7591ab489a3321ba3e47060f1f72eccd24d99ee2008b9a627ffbc171263c436bb6d326c46d6630065c3165af236743d0f7943a3130b05424142450101a46d476556da81bb06931a761a37f9fe01a74f49c6159f511ad37c51cbdf096da8d098fe6a0e9cc82fcaf544e0e13f86b4a76172d172e053775d50169e99d48455f58604b69a2328c01639107d8f5aa47bfe366be468deb2522eca2dc59eccb23edbe8010aafe9ee484fa5b65973b85cbf71d792e9c4a9b7ee9004620aa61cd00084329bb8c3a01027b2d661e50b2962df8a53ef2a4fc1b8f92451a5d62fc0c6f0820224080642414245b501037c010000d4b92210000000001ce8c53e7fc093d370c7af1c42697d82724a2f7e107af5320ec77dfde8cd353ff95d87b3b9e7bc2bbfba43d56038f6ddf193a716bf83d82acdcddddbfa70fa0b1973f148c1f2e2a4439f87397b93f611882bebeaf617d5e5d3e35f05b3ea920205424142450101f887560d2b29ec3cc189dff656c1e4db1b4436ab26b12715b9145f87b4d1a8664dc2684ef0f1077408ccddec5be684c8a43047f011df06b6c0aa1555a4b27189fc9a6c90162308550831a3e2eaf051d0cb70c2e18bcff51910e2961c4f87db8842dbe801dd7b3d4a52fde76050b3cf23ca765c03c4a70a9e8f2a690984dba4f61a16aae3a198306580d9ca4e8afb4630117c273803b7bf3b167b361cdf7c165caa04cbcd080642414245b50103a7000000d5b922100000000008ba5d2f9e9516503680c719f5b01bcde45186be0d595c7814e1cf1bf5a18a3412ae2b34cd49c56fa6b3878db01d5466ba31417588811f877014c0e54003100ce91d448e88ca4fa2c953037c9b99c5a268c20488d858328e3285c8eb3ed9340a05424142450101385f3c0132a73387bc8c2f4186d38cb61eb63c26054fd43c293b1c97ba78f23ffb738591cd5c9e71ac1f109cea8ee1765bf4d163543e98b3e0ed6b168e3d768480d3faa66308fe07cb4321efa665c0560527cd6743a6460fec3680e9dc2e57cd46dbe80124982f97e4d6f011e098dd05d5c4a820bbe7b4e27d8f224c6b8114cfae44c5822ce8e607278b34a9e3255c222473ee406f77c112fb7900ca78c7b5457dab44f2080642414245b5010348020000d6b92210000000001039e503392c3dac47a173f6f72a0586b91fe51a903d9d009e3afda98fa7fe387296967f8f0b462285349d8b6ce9e474678bb80bbed54069dbfcf7cc913ca902e2578bf33e5c444f70e9c0c4e8b82103f14697e2e7bb0a90153340606e8ef10c054241424501018a63baba94431d19f94c0dc9ed5e150b908e83519c367395c414233b2c024d221f9d89f0de4a7f2f591f6259fb48407b6a494114513607cca9a3ae711ea2998a139075d129fd1afe5894e769d39efc4cd80b4ff08336f05d7aad1e958d55a3704adbe80124f66f43e5d0f03177bb71b99e4a0315ccf798ff6a5fc6793bc0cc631476b443ee3c6ca1a4a3f37e5e1f9bf49ed94208150c24f28fa9130e1932cd0f4593da78080642414245b50103c1000000d7b9221000000000004546a5e908acccbeb1cc64b25a1c14090b690a79cda802924d8550af34f14595a5c2fd16949089d1fc4178dae2d674b3cfd076520202fd0dddbbb81f594f0c837cb54b2678c0b7559ca30957d0bf9cc6c562d88a01fd2b462a137508f43c0005424142450101ca64a0fb8256f7562b2d7e4436fd1ffdf2161893bebae2436774513bb18f1b0b380cbdc2e573aaa2280be3e374c00e7730529e1936e01cb1a736d051f28f568d961cb20051cc44ee7a474f783401f6ade4d6c394a95a5e537cea5e48bd4137254edbe801e16f583bb8c29c64b3ce6639c62aad6beb991bd0ce54bf135705aaee77b9501f80f497aee7ee475b9982f1ea98bfa575973a1071e332f4b78c2e5a4fcbdecaaa080642414245b50103c5010000d8b9221000000000bacb4042a62cedbcf80d86a1925756cf11ffe4753551ec56e88930d7ef7e000ac9e28191b1dab106a43cf5fbb68eed9d3cc94e73e81f1889f3614ee01c42ac088d450fa3ceca09cbd564db5ebbac3e6b7222158f3c0e0013ea0b3a629619cc0b054241424501013e9ac0aa423769a83d8f346c30dbac09a2198549e8c4803540325ca62478e4405db76f2fddcf13e0da5caa08d6045f88cddf588834bd0115fda5b537836cf68d0058af47a1087365724f4fff61d86ce7f1132ac81cd9e55544774b0d7027e27952dbe801457e0053326a8749aa4e5b77d5ce4630b130594ec2dc681d76aef19d492c2ba4266a36f9fd26e80561795cbd7c117ef260dd6ba2947799cb45f93ba296bc367e080642414245b5010320010000d9b92210000000007483f8178406a1922abdb7d275b048d9ff93e5b2c5a9059a4c2d59bd36ed063d3c3dba609376f4fe4ee2b01348ba852fea8107420188c631b71f32be272e5405b432a284a9d1b2096f164e4efe451dd4d34cfbcb3cd6289496902e22c021f60d05424142450101f83a19fddbf72ae39b4669096fcec803749d8521d3007263a8ed53326b95be174cb6d6916507da47e350e80646846c53b59843aecd2098410335b4effc6cc1817f1778eb6b97d3df5a037583e8ea722a72da265c00e56da348f73e4c396bda2d56dbe80173fa4ed40accb9d7f8d735504ff6e1d80d32daba4f07a193de2dd954d5010c84e15a5d1e3f2129f0a9ae8e608db96cc69473399feb6d5153df4e5181fe733be2080642414245b5010307010000dab922100000000068849955f01b45315e383c869943ad1ae395d8cd69543f629c2caf0ff310582f89572eda02206d165e3dd45cd89c4db14bc41c9360ca5372891dabc53613010e2ee03920f93a1482537078a3d122231f42c8a05c5076b09d3efdf11dca96d20c054241424501017e6822d584355a719ec0ab440a32492137a11f87b9d6938a29d3aae6e339442a0eded3c85e932133c28e015a710e3be04c46f70f3f40bcd1b075b87cdf7dbc8c6bb05090ed1c2a5db262fcee7d1888cc61308d8c6b65f231b3df33a5c3c68afb5adbe8014883031c512af778eb72401c1bbd5415bb050329da284613a20b8f4687e0d9f78523dedb535b22433526eeb042b5dfa667f12a9d1807fd24c51ad0821e1fdcf7080642414245b5010368030000dbb922100000000026850af4f6c2d5a246d48ff363a134ea3cd06af4980ed6a31cb66b66bfda4e1f7317f673751e827301e4e7ecf22b57f8eacdd7580d150d5ada751745a6e79007a5315803fafd9b70466e5c083cfce502c097cf1a36b0e581c24ef51f402ab70705424142450101747b3321b879b027bd40f936a644b71b4454148c20e70e534048a3f1409f696a72d51e74ef3f7fe72cafaf08284ab9d54f3f34e5fe2632563630b4406a007185cf0eb4039af4b4b36be4c8c070e2230a8c291618c59a2a6b23515d2ea35719735edbe8014aaa5963b6038edc345fe221cd267fad6a82cf5fc87255f8d13d76b887f1fa4ef3e937f073f9f01a118537d72918e32022c5f655bd5dd3099c03644c04ddc2aa080642414245b5010307000000dcb9221000000000ea1c016e3ee1a293a9371ec4fae71bcb3c772d9462cd3ccdfbef117b1e076d7d4fa9734014b5de76b44a3c6f9b9e816bb25078060c35df7c577bcdca3138e10f146f236695daa13d434abcfe1c66ff0903208ba29ba1d7df33813d583710470705424142450101d0c1cb5a9ece615cbae94c1c258c1fba3cd75851ac18eea3937f6305507f6c59cc9991ff27ef9513d6d3f39208a7ac016d01f5dd3f2b2179e1624513b370a88e047381dcd9e96e93ac6abcd674e533d9e8cbe168e1ad1ac834cf398b18b8bb2762dbe80179502b9872c39a7e6d8836eab80e22b2e2806487ea21c44792073e82196f48b0617f94bdc3167622bf44faffd08c2af11bacabae290bbe3982dcebb33116ce32080642414245b501012e020000ddb92210000000007ea529f303b025e743a5fd1a36cff10b3bf84123cd721e3d28242bba272c5d58127b4df6afafd3f9548f6923564a47ce5ebc5fdf5fedb697eebc4c257db37c0f6248f014819ba770f480b5f14b71ba1f51981ab0d1f44f91c5b2401b18b3580905424142450101281da23e1a73c5f17350538b6a87a62aba4d20dcb844fc1f49b9e04536eb412d058d9931c36def758df6c79660aa389de29abf77a35400da3f2bef78adeb9082df37bb8e933d5ccffd7c27b618cf3aeac6cd8752f57ffbcf9f2a68bda33225b466dbe80191abda23c45874658255bf8049914289f2ea77da78e52e783677d20f423ae1ef5857078e9a133377347e08aeb4dcaab35cd7d9865a083c1055f39b8c70b78809080642414245b50103fc000000deb922100000000034b97be4deeca53a7187c2cca760989a87f8a5754f65b4d7365e4c8d489ffb3d7c2d378e002abb7b86ad3ae5887969b07553b3f7d1733e7b6e69ec0df49432096a9b8c743bfa264144b9164256c60c95ef02defa1f45949fd67ac787ccae070e054241424501016ca31aab8f503509663ab3ed85b83d6b684403ecd431091e7da4ac21fd9e7766e02240b5026f1ae0d83bc9641a64961d60934d1d454213c4ec753c2374dc9a8e54f2975ed0b7c5596070fb6748e1d7920309f232315ee1b089a7ecc9d2d5f4076adbe801d6a5acc7e966e24eb5e7b8c148288e8fe21a701ecfe8091bf7e84258fe0f279abaf82e74b09e1f904523767cf0120eee952b318e21007d43810a3569443a24f4080642414245b50103fd020000dfb92210000000005869446a159e8d3d3e314b3c22117f0d8b4bb503a21396706923446df4982f16edd3060bb458504089e01eb8d762986809b004aade34f1c8594eaafe4762f5088fde8d41f4a2c607131c2c50a0de6ceb5de8e51ecad35431f432d4b93db6380405424142450101d42dcf5c3e948116cef869a50a048896dfd4931ad1dcaa75909cedb5b9184522a9dd38ee0e92372432991d3842cb943883cafcf07400c67d3bf261c3a75ac38c0e3b37b5f9e24c34450d66d43ca20747f45abe19ac1000a39ed1e81a321ce97e6edbe8018758754614feb3c35982c5c29a84d725ed799df8e08bb1d2d1a322880012e6b183ad58180ed67dc883ca03ff3652e2abab46ad2ee008e52a050f575b637038da080642414245b5010362010000e0b922100000000056a69a59e3d7cfc2c067f51dbaac61ca4c26d6f00602048dc2b7046b8ea489347c17af99928bf10c597806c67718123034cd60ea2b2d5924cbe6935092cda90eeb1d15d7b106f3ab70690ada8c78d160862cd403b31f0d0c8f4a1a498d238603054241424501018a3ff838fed60c123a41b8f2fd49c0c96929582c679f3b9de422a215b423e409b6a5ef618530c4e8f72faf823a8946cff2f2f43f89011623fe7e5efd86183b80e194b41a7af812b5423f4593f0c48b97eebfe3f1a7d2d74feb6452bde638d76d72dbe801cb2ca1deecf1e73b26bafe56bfec6d0f14daeabc5d2eaa34f2b7a1b5f118a29afd5e98bbc8b3e38b5a97ef440258eb38985f712a0788803fcb9c1e88376aa478080642414245b501035c000000e1b9221000000000969a89c0e56547943132486a7c0bb6f3a4a2aebbf28d8a1a8cf76677f3593973e292453ecb6308808f1f6aad014e1e2e471a13e506dee5d2bb5393acbc1cec0f723dfbfbce42a6f402c00af64d42a3db9549d30e031da6b3789e1cff1416e80205424142450101aa5f46d29f853a2cc06fb2391e25171200af1727ae95e95db6dc8786f24b1269ed3bd397d6dffe068cbc572b64e3cc10272fccc6a63594255c7d4558febea38428eaeda0b7fca2b96953b873a7c890145af1b89fe8464bae1e18ac1e17e0d92d76dbe80171eac05d985002f27c63d5e31d88f35f458505f03f4b70c251e2a7b857316ea9899e61d151fa3ef5ce65a41cef7d919009394d38e28ddc5ce5f36abf7acf83f1080642414245b50101f1010000e2b9221000000000200a37ebdb570f2fd95101dd5ce7d97cdce1679f13dc6e654de0904a0b6cda509ff7a0b89c69aedfba72705e8367ac8eb81bdefdc4fff5511ed690985a93c307d13674ef42f1b14ecaea3ea25c4ade9f68e83a311c4e971b6f7ab761a48dc40c05424142450101482899493f0493df49e3f299a181a77d272f4cf87473ea318c9b8ac947555e38bf799e19c787d7a8bdfca95d17cb54fa558d119b96e2525d95f9af062939a98d63570b1329144191c9685be003b151e739b273d708f071764c86796d6a2916167adbe8012dffa561155593044c937de792e3e8a9d36eae7e68de2ed488259f22a3f2d70197975c2b7c2efb3dd035af2ba938622a74ea59abc03c8fc9abe382d54a9880be080642414245b5010191000000e3b922100000000040a297681b317ef75afbde24131d1ff33a06794ac1aed5273205b35ea41e1f3425a92a75872a64181b9c8c50f86dfd9a54ca3e8984e408b0ec3b7c03452c970f03ee704f6626c184cae752dad6b1966a61f933fe8d05395d3184d02b2cd36e08054241424501012696a32b5181cc7d9a0f7aa9332d77b3907429916c7cc2d48cd1acb10cde5206c38430168cbc3cb890296c9b90378d3dbe38e451099e2222b09fb8ee04ea9581072042df24f79182a7e10bbc8094decbe22a19cb163ed7681dc32b649f6c073a7edbe80116413d2cf669c0923621cbe36fe90dae4d96860bb5ae74b3bc23b079ed3c0a66d6b7c34ee13225812b313d12ceb5c07505d23879655e7bb27a3d33e988ecda87080642414245b501032e020000e4b9221000000000e6d3cede3f8fffe23b77acd6a60bd4d42fe8b704e3d8106c1b77d04c02e0104d70d4225608e96b9cbd6f631ba769a71177d59fd21e457b6e1b811f3485924002cf1c0c7b87ad815c9b26d397b50c9da2df4a6625693168e1ce1a34bc15c1400c05424142450101808dc87b96ea61f3b6b95b738c43d463e83009e5bd31a57139ceddf50fae406f48ae3575112a698f2ef80dbbee1876f244367edb95384b120e3b810e6733398350f09636cbc5af13c66a9c64eba8ed1ac310b1e4f60915c53db5fa630301be2982dbe801c1ab54df9e80ed4558ca44fe162c0f71fa96967470f11828ed7aec205eb1589d3b79d493bf37eaafe2bc13430b1ca65ed2e29b73d66425d95dad33bd7bd950c0080642414245b501035e030000e5b92210000000008686aed60853d8ece580daa5e83ce9bce5dba1e00a2c708e7fa2782a345b0218895ad9afbd8bb6f5f1789d4e8acd09914b224383a2c588c599489aaa07c3c8029d72d7ccd8d40d0924097205791d78b07d7a010ad383fc65384aee6f88b37d0a05424142450101ea637168e1926b1a20593517e10db6b0ba5962771c1898ad388afb75dc77d16057641db6b81407fdc885f90ddefb1dbdef9fc4ba2e7131454fc728f9929f9982c237fddbc35cb90e7385b8cc111b368937217875eea12e8df2bc9fc769710c7e86dbe801cdff6edc7f44a5d009270a1068fadc772d1d298cd13a6e7fe6f49a1205833c9a7d45d636b0e489c47948dd11874ad2d791babaa5a4143c292a99ca2d86d3ffa8080642414245b501031b020000e6b922100000000050194352a4879a55d7cbe19ea6bffe8af2d2f1395e57e849a8ff86b2b3d6631063c94a8f512a2b3dfd59d83ab0ff597d1dabd7a5f0de7cdd4b05eeac80cd59083dbcb8313cdaf16235a0d26868a294dd3db414bb9341027872f6104d57b299020542414245010184ce9aac44b23fe9daed343d83d41483c280df214f12c08d6022c4a823a656181004dab73b9315346ce41813a532e45c6d4d65d949139a906ff01ebb78c27f8fa309a2e487d8f8ed486b2251334d357d742f74cb1bc9e96e9095dfe5fd787ffc8adbe80164467f2c2de62e32bd86c794c84204da8b07eaaf67251c429d594f71927149578eb7c42a5ae617ee662daf04be74611b86c8791b914c060129f7e118b8e7359f080642414245b50103bc020000e7b92210000000000446fe88e82cf429832d6738dec29f569af567b2acf3588c2ddef34f3fe0954afd4eda349eff1a6c707440430f023f892216f68181db892d9c9b28e620258404259354d5a072bd8ee164f77bd904269c7eeeb01a561950f0fd8d8ae7cecfb005054241424501016a9f6d6e071d94e6edea9a35d8e7bdaf04776730c2333009fa7d84d28a6d256f60a7cc9aa6404dee1ec18be4b38e4be633d622ee4591a92c59e12fc1a61cf8867ed17e7abe5c08369a96e1f9bd7745c06227a3fc49315376e196c5ed2151e55b8edbe801f0fb70460fa2140d9d53b5a9597a1a15e1a73fb27687b2ed450a42ae36821857dff64cc4ae211a3eaff8200bcd493bf0432cfdec1534f2a7d98648a43aa206b2080642414245b501039f000000e8b922100000000074f1f9318c3f2da58fbf24104c341ec0e4461358e0c11883f7e57432dc6154364e5873708385b09e66717826a36b2712f8e44d10981329f38f8fbcd811653405066c7ba744f8c8b0cfdee9ddfddc61fa081d3b75f344c9ee5c9d7c96fcec360b05424142450101242efef228427600f9dcfd80e3d6ec27981fa403582fb442ba82d57522ec5e4f24788b430ec1d4f77aa74a5b83ee91f94d6f27cb20d8bb37d6096c47bef1fc85b19557fd09ec5bf9811d4217d09c3f158c46a7976d1d9a1db2602a078a8df0ad92dbe801092cb48bf2f23ea72ce67f683808381af6d16c37c9714f11b36f5d5995d08a5a31ba2087c00111a3d3d700d615f6769dffe2de99696e0af035df41dd17359c27080642414245b5010323020000e9b922100000000060e562994afc817f9c27d25a0ab9449a90c1ce08e282f65b097863f19197de3184f2f1587a6d8e56406002c11cd2151a94160835af34c85ff39aeb24b72521040ef2ac0e4a3486ffefec2844309eb1b10d9e3fa2d60258d79aa3c32e3befd80605424142450101f4868b898586f6720395e536c039165019229342513fb75434d951ce7f8e0a57ad99f62d6678b105be1f55b6c05fd00d1dac5efdcd31581f955ba877ea99e4852e379e45623d2cbefd5f9994d894061494b427f303fa7fd306c76d31d1a0e9f096dbe80184856a6cdcdd65717c3e0558bf819edbcb2ac938d8121f9a11734b315e21b3d6c13bdeeae21f41e80b13be727e9854d604f6c6404994b91950979589fba6ac5b080642414245b5010106030000eab9221000000000ec5b2cc3c5a309352cb1b2d8ea7cffd69e978495dfd6d5d71278b6b6bebb4057dc7ca085ea9d22394b37b2001f8705d635dfc2ef4d37f0ddb47d59ee30476103e9b19cc4f3be6af700c9e9091615e9049fe79ca77fa3d8b2b0ea8ccb22118c00054241424501019e569a546ca8fe3dfb4b16be0f46655dd6236be7521a6c08708a3dfc64199442f96cc474cb0fa22f5e81765d146cafdac01510164f55567cd6b05e7219118380ae4d34dcebb260242a727232b74106a679c81e3acb6182422d153d9f576c2de99adbe801596f11cbbbfbb5801548c8dcb4f7179e44500b269d0236bc018c626348a3c327a49859ebea11ee60e2f026ae979b48bd09c97d3813bd4bad61c6d7f4d2cd5734080642414245b50103ea020000ebb92210000000008e4c08c59d90fea9fe1481f94454c37f8b57535647d492114a19fe3f1308ef0002020844663d5c916b9de1173db631e04395d2d6c2fc7ffe128c0ada75033a09576fc0f73ffb50d82a1b07f70123cebfd781ef9cc2f1a5c70b44a208464b1c050542414245010100d8d763fd02d15bca5fd8e14bec43b6ae68d59b0745ae0ed646c2046f3c0f37e4a8d4175be5b20275b18288b0b532aa137c2849f4433c188798071311463585067410ce1a2096ccfc97ed3a70f04cecb5195a0e1979ac7f20bdff5f1bbdfb139edbe80109928c0700e40421ff64e0c48972f49564663d87335ca39f2c073e7c0d3362b62b13880820ba7fac54b41c92a3ade8bd0a469568e1a49e3bb8e79c5c3a1cd5d7080642414245b501037e020000ecb9221000000000143e2b81f8eccce9e906bdb48a36943408de4655a08865c3fa818cebd2ca272dcea65e35a1febfb584a9bb8cb2eedae75325d16232c620d014f405bfe1d0c7077f98a5bc2e4d6dd18e8d45f437dd4ebcc732368aa8e062c5a4ed1aabfef8dd0d0542414245010190f2bc55179007ec052eb55c6cd2ab74fafecc3abc43f6dd587facd286abb57117aecb5aa7687bd3354579992bdcd32ed62e8388fbed6c978aabc6e543186f8a783aab078cd42dd39d6500d2dbdbdc9071339c286afe8769441a527dde650e6ba2dbe801c1f86da50f7b9d4b875ff7f1ca3ad2e2f624025ee96a012c049d7ae61d99b3a1fce304139ae166830c6ed45131d09dff72a2b975d509d11c60e082f6d5e23102080642414245b501031e020000edb9221000000000d2709b8f343f70479dadda6e4a9527d4ed7f81d9b93703bb787e5615ce02b861420e44338f9d2560dc0fc85877a7dded50e32b608c6f68cedaef1915fe3a3404037da3ce44d935f2f1d4a913eec0089db9e0f4366ce253582bac858dda50c10e05424142450101f6ebb8d81fa7593e3372e2b8765d90492b06cf37eab574eaa41de39eb51dc150b7fd0303576de1c1959fa54cb0aa8ef08ab34bc3291e0cafad73fb3dfcf80f8042021bad11bb5f15d8c1a87182324206bda7cf180cefb164cc764740e5e49753a6dbe8019a42db34a81ec9e3b1d89fcd3fc9368993f6a8bdbd4c36a2ae8e9cf2bc354814d5395253c9750dca4ab2eb6c04a40c014ed4006d82c1d217b9bc2b690b523ad1080642414245b5010323000000eeb92210000000009672dfc6bfbb0689d824cff6653d42d21edf5708a9f8487fefe15d576bf5f24e87d387d6c680d519d4804910ea0205bfb77039a5ef3cd628fd93e2c4297ab502990f64b4450ea060f080f604d6f78fcbc78096776bcadfb7e334567dd016ec0d05424142450101762fb240319df49db48d32060b9d614fe11710f891f098447e39ebc7f18134785d66ca6e473a916a51a705c1b7537b7452f30f558ef5488db5e28fd342569d8affd5ce265a7a29ae9d02b7244c292fa302fdaea0dd0599b1f84c5558e4157f25aadbe801ef143430abfd9715f186f426a574d3373356796ad8764f6e8bbac294aafb892aac936b4f21ab35fb7cb2678f8fb6894b5bb97275c5356e6744429515f3d654230c0642414245b501033e020000efb9221000000000586ec216480a3b91b3aa0c22827f41aa56106509fa187d8804ab5d4f1688d736b4e7ba8dc52804874aa42309aacd4473142c8ee5695ce1381ad657a99788250081f40a2b0433a7f58fc847df8a33e65c64156f857b70ebdc52c6137a96d2000004424142450e33020001110ec434cc142de3e83f6c5d32526fdb84efeb826c7441298ae84621479a717ddd2601000000000000009a4419c9fa76e2a85014f2604f3acbe29d052d7f8ebe31373f213cfeb3633f3901000000000000005004ada038dbeeb636a58c61857a2b2f64a467b929128d118889a390a81739400100000000000000b681933791fbbedbb24bbcb2eaa48411c7c27711875b942537fa2d247c51122e0100000000000000240a8579b9c6cfce8e9ffa06e49a0df2767744abed4a3b3458b01fe92d44b4540100000000000000ae8ef962e1fb879eeeae9f78e8d57fbe6c0fc2a5b404c93a65f5dd8c982b51230100000000000000863a39f3310812dcfd9725d1ab3cece6dc763e7a43eb5ca7c54f7ed5ba1ec3190100000000000000f44df689a3d5ace5b6a109e35ac63d1e62b52e3a1a582ce0fa0e220fc8b671570100000000000000124f6cb9120882340f4289c1e4ee09fcad3ed8930ec634feda536628bcb4fd55010000000000000080e41ac5dc4ac06088d9c560d193b6f7b7ac4d86e0e9bb517c21df9453efcf4f0100000000000000487888f977bf72f61ddfb9ca54897c22fdd73acea696eb009b5401dd7101130f01000000000000002ccd992d7a9f1650e3fcd531b307d9651c104df6d5facd2cbab688d3fc334c440100000000000000d043cceb4e7a4daaff5be2f8fa11cd425af051654571a146f254afc04ffde42a0100000000000000ca204674c4aa968ae4cbe115db05e478b6ee03533038eaa9cfb95ba1b265653f0100000000000000361fab81129e1e3ac7e7c9b4aea10a218991b824c6dddc55414a8770e84f7e6101000000000000003652dc7e781bf25e8bb4b1d25de1bd9e9a7fd67a8ec75b479ae370ed9bfb2c5a01000000000000001873d87e3d21ad568f5cdb255f9870015002606921c6a3a905ce146abd77a2340100000000000000fe585a6b3636068a9009b18083cd6242c78da6f5f57dc0a7571b65a36d354c2901000000000000004ca1067f6f4f8fc7c9432818d0225242e98796be516fcb04ee272799a66dd23a0100000000000000c441ccc085cca761a998a310a1f074495af3e839f8c71de12615f22a7ea1073501000000000000008e747ea3ea05bfccecc9595e9cefe7fdeab4a39fc7e847f56b04135fc4a012710100000000000000fa5557d061b27db6b1bce99a95c830029fc60ac3430d55af30e94edb3a77a25101000000000000001ad6ddc62c61923290e127bfc7708c944c8d6a012805b14c70156109d83986400100000000000000285c8ef3c7445f4ff0ce912cffb2e521044aebb62620963df624201c5e4dc7700100000000000000947ff774965431ec727966c6dd539cd70d15e9181b192ad47b74b1c85745b8480100000000000000e479230ab2921c9ede58fe6211d8fc8e8a3ed3ac8065a7dc6ac50ed63763607a01000000000000000ed017fc562bf08519533de0279f2f7588ec4ea382d48a63edf1df2c807fbd1f0100000000000000d8d8296101fc3a99c56bd85c4078c2e389c071f731e18cf6be7c00aa9f4e2e6b0100000000000000845f63a15c8afdf9737eae1b91666268894eaa6996079ac7297490544137e80101000000000000007c3c62b725f20653adaa44243e4b3d8485c66c310e57178a3d3940f8f780043a010000000000000070e65e0e42d42db5509f35fd338047df8c7a6879ddaf62465c3947b929eda6210100000000000000e415160504f666dadd30265cfcf02b9d10136ddcd69958d9174da69329f5f8490100000000000000da8f660f8f4b90fff45ff6fbaa4df167a0f94baa1daac048346c50698fd285690100000000000000de6317d82c9feea35a44cc73086d6e73d0c7287728bd16c63933f913b7f27a11010000000000000028db15599a7fec3c93c26f62014fd30c214358c4443dc6bd515ef09a113070760100000000000000141f7c5f1baab0cdc3e2b10fa7380084c25f20252d96578d81700414f9eb7b7f010000000000000020b32e50928e8148a1b3d137e6cacc80f2a1ee5a3730e8880bdb0cd5d6b07f630100000000000000a406fa3390513c131099c9ea276472163db611174ca44e3b5c3e52aa3b47f94e010000000000000094c8b55eb88a7e3764116702ef768950c3e59336623aca8260ff44291ff9f66101000000000000003e7c3c81fac56951ff05ff47ffb49285750f6f55619ef8d0e11a388d92020f550100000000000000f840d40d3f38ea72cc1598f7e2012808584c0b90ce7546cdb82250f6b049972a0100000000000000962c3b7413c205f80b072ba3d8b9a828b10e42be559fbc07e9c597ea72cbe56d0100000000000000f2a70fe0286602a06633c7aed149fa7a7bd542c97bd6ae90cb1a941e684518760100000000000000b6cc496a16c32cca3993d385b995f38a6ba1df00baa71632dbe8f750ecb9b2300100000000000000d625a53dae5c0f3fa7af221c68c96fdaa1a498f941e10a1751a93378c4f4d04a0100000000000000d236b78d518725dc6332642273c5f6375be4494ae1e8e6e8b42bd1a19af14c4501000000000000006464efe71ac4e34c699a088b19ea08801d9e394ec50d56c7cddcc3dcec103d4101000000000000009c6f977636e4989f241a12728b5d2635effc4fcacddb1d2cbb45a739504c827601000000000000000652cc6a94406c8d2bd9c9f9f09768125cad75244c0f495373af649b0c82295301000000000000009455588e090f0f98a3a77dbe74d336e08abe7a1b6e30c1ea6ba7d73687ce721201000000000000004ad8792f5556e979d62743cb45b3098c23c003000131eff8b41de0dceb8e053a0100000000000000e0888da4a5a14218fc2c38b8d18d21a4acbd0a912f39cbe8276be83305f1797a01000000000000000678705af1f738381114b82ee5b8a14757515185f69afa15b02f40ba9adfe4530100000000000000ee4343f61d67fe213365a0495087acbea7b000429f2fbb80af14d32d3244bd4201000000000000006844faca3ef4bb5a7ba212d2a2914ab99eba84f93bcf37abf62db3a9f19fe0050100000000000000145ec81be8208098bcefaafbd43e887c772685225a803c19730d25580814bf640100000000000000d0146ca7b1e2653d0127dbc6b46398f6421cedbfe73fcc796af22317c6aca71d0100000000000000aabd30681e440dd836c894b44d5af8e890cd99599899bc0195125e742fbe184b0100000000000000ee768be474ba6200704b6bb75d6a0526263047da6fbdcb9344ccb35268361603010000000000000082f72bb1cc00b5f96c95de38d2698ba9c64a7ccb4d58a077538ce1ccb55efb570100000000000000a0674ddb6f130eae680532c6ff0b05a317cb3840c8906c3a326f26ce8544f82e0100000000000000ac2bf96630b117699e7fd6c6c717890510e00fb6db3f8a0a5c461a9309ccb26801000000000000005afb98d27d68e67f6e8eb81c6d3375425a5dc323bfac122963eeb580cbde5f430100000000000000f614bac5b188d32a7041ae91c9decb77f68d21c8ca76e06ad9205eb838056714010000000000000068bf63365fd8ca42dde5e1adbfb98817f88f205e17c1a38e07f8620fa19eb909010000000000000098dda42a2ae6079331599ec904ddc18cf94fee85ff96e1fd3e7b6953db8fc70901000000000000005869c7315c16568345efb6241e1c0e9ca9d279c8c7b398d32ae7c7323a93092a01000000000000007a043f552a7cdc9de8641b15b0369b677badda49d8082fb71c7eea3344159e390100000000000000a2b759bd4c7dea8909c86f2f570eff3a3b56e71d50ec8893eb66abc945a7212b01000000000000004e9fb9675233d8de1b78adc3f27ec87e2cb1bb838354172d1fd9c93b318e582301000000000000004a7a434d9bc1105ae7df912b7d64c32ff70ed5e531f464447b7d3188c06736700100000000000000b04c669e6deec9b1f47db449905e1bfdfea3bbbff5d4b4fb9c85dbfcc5a0546d0100000000000000baa5986e9b5b50de5febb4ac70500028e71046fd84c8083c0f62ff6fe31d1e0e01000000000000009ccba303b66ee95ad28150a987a672273809107589fe2f7d29665fc943ca62660100000000000000c28524b5af98c5c4f13ea78f14ea81e91df1afdd4500333f9423a80ff331de0a0100000000000000e83c8269c658801c3ac9014fc5a3c8309d1769a8dfdfa272ebedecb1a2a1e4290100000000000000ae05ab8b2f6db5296f2766087549efc6169b3499dde3d6374f7c772085f65d2e01000000000000002083ee72d69233ce625e5f1c3629c331bd6c83385c73722e846752018c64e4580100000000000000586418c4ad73c32f19381e4c32d8f2d082671af513b0a2b528dcb24a4230281d0100000000000000863f88d41e8128af4882262a1d55cfac41539436f280b567385da2cfdda040260100000000000000e077c9be8e48279abeb236ba07e011b082f2bd09528aae59180c7eab0619e60e01000000000000002ad69fcc5342b7b7e11595eba5a00d20ba35d6f081cfe395bfa1dcce4a290a0a0100000000000000900733f798d51d2dc54005ba53eeb9c3d63909d4f7e7ed32dce66b7259552f6e01000000000000002c605db889f693e3f6f832512bf4d6ddd8570ad3c71db7a9b57bd5689b44a73601000000000000009aa7c8266d57e70714e5757cd2e187e287c63afe88ca3a4dd42b3f403737f6760100000000000000705af57ca737e78669fc625f5fc98b16d120ba871b488ec79a9d12a1ed72f2390100000000000000183be67e3f80e946e21d9a53db35eb2d9d53b227de1d371e61ecf0f7b5e6b87201000000000000000e2162fc65a7a09a88b48781078143933638f7f40c98048b0a9b87694e34d7130100000000000000ac72ebbb90dcc166bfba1257e42b8b58574704ceae439f2c19ac085b61d40a6201000000000000003a04567e101ebb28329de2600817729f6f3c17c5e5789f036a52ad49b99ddb23010000000000000088dc2749fd6af775f8d9f3a1dcfe548d60037cdb8ec1db422165cacf9c96970501000000000000004af471b98c45055a8487a042c44567060000e421e7908d4ee86169d4f36d4e0601000000000000004221e0d01a623d31f3f7afb312e0ae6158db5edf68319a8b6ff98d623b0b1b03010000000000000068bc9e681587d349c5ac904a296ab9a4481c7de5d6b585621c5ed2670bfebb5f010000000000000016d1f436ee6064b05ea10ff906a7b6baf9cae49431489a8afbefae088bdfca1d01000000000000009231b7f5d1f17d7dd8791204288f265060d3469292edee45533267a7a3976b780100000000000000aabeff206862500ce4d4d54491712448e3823ab909919cd759937759a086a10801000000000000007e4ea4d47bf58ce719af34f30ac79ad8515ff9cfd2574abe5309a21ef0b17d5c01000000000000004c230bfd3b60ba5597597b154fd95829385acf1bd740607b9ee07262c994106a0100000000000000f6b74d3139ee7543b6821c342b061af75b609c2728c9eef724066fd35abdfe5f010000000000000018aa6738aae287470270a2213f8644d31150682adff4cf3b7b2da19c473ac8390100000000000000fc81c16f4d0dc4b27d1e0dc381ef15781f8e0ee41fbe0586bd9afe15ed13173c0100000000000000249f2c086a0c22a7cd3189282875e2398b6dc28a2b9a8a0ac9eb3a53de73b43b0100000000000000b266ee40cfa61107e9ec12f162cd72c6518af1df74002c2004c5a98b476947130100000000000000aa42949770f022aa18616f18f02917b25ea0182b6aa062ab117f9268183d9d3d0100000000000000886edfab02c7ab5f4f62f26edbf5dda9868c8a413a5c379b1b63d72a2121cd36010000000000000076cc698d58e5e4938fde51916c8c320e0c3f71822b4664254fbfce6cd4dfc03c0100000000000000ee4b50b0921a9e1f4e5f22cefe6bb00d6a4357c8570636a39b26e7344b3f66240100000000000000e844261de8b691e753a7ac7132c821469d83f25ea8e323ec1cec4dd478a7e6530100000000000000925864a0fb6058ba04b0d1425df158e708ba37cfd877f126796e2bd7970c1d4f0100000000000000ceb4d80222ff1340bbf953176e38bf2a8d40a4c6bca020beacbcac1f1ebe7702010000000000000060606066af08f9fb9f9afa87fb07ad0baab01233516511c5e6947ab8f3524c7a0100000000000000ce3ece9acf75ed3be5d3558b5cbe3b5703ec326647668458e82e42cdb5a948010100000000000000661e5553fb5d358cbddb07dbe15fcf7826a27e2c30847b9548d0d8f8f8048d7d0100000000000000248769c8acc11f3a3dede5b15035433729ded2ad8af740f2e7d3340d9d40ef0d01000000000000001e97263763dc6b55363fa51306709f7e0243af1910630253381a8592de97f00401000000000000006c5a12a06624fc5084d48b6eeea4b0ba00f87cf57986fc6110124c44e7e9542901000000000000003a1a122ffc962253073ab844867308704f6d92ab8f6e318e734f3b18b7e3ba3301000000000000006040a8414acddd0b30dcb1fbe083cd72889055353bb9f64c6a4875b861a4295e0100000000000000e06380c0e44c490844857db7ce6269e52272daf3db8c9c98f38b24c79c1eaf560100000000000000667ae5fddf7c7a58681fd75da15d319dda30921d6c659db6d0bf982830cfa06c010000000000000036b1387f3674856f0fb22a876ef12bdf2ec21fdcc849f200acc609e5f33279470100000000000000ec2f04aa8bd2ce4717e5747c506be32db5db1cda05e3875fba658505ad2e373501000000000000008e9fbb58a96118053914ff5c36348f8bc82d7e0051e8183315a4746d77d5af000100000000000000f419d56e18e14258c7eb351d1951a0c091194aae5c55ab1ac4cbaf795ecff17f010000000000000092dcfaa7fc0a16bb4a2e197db3179d207663971a4ec18c5a78594d579d02fd7301000000000000002ab8ea26eb951dfa831de87f69aa6608b4de76df2914cbcddb163beb5ce21b7b01000000000000002c2a0dd67d2550d4231d79efee8891dcc1cc103c007935aa49e6a2157c11fd2a0100000000000000f8106e64f3900bdcefc9dee82dcb471571bc421ca0b0b60ae2d9fc0f482328630100000000000000d88f551dbc4416e3ee46cd89c683eb51054fc9242037ff19a52af87b30402d2c010000000000000056091588f3ee5b9fe15d20fc336d68541a08e823e20d691a41613ce2c154196201000000000000006e2fc806b2626507a907566aebc2a6fb1039a533df8e7a8c9d9a287f3e7bfc3d0100000000000000e45c8c0d6a5aded1d938d8efdc41e5ab6b289c605cc6c19c3d7a12b384ffc81a0100000000000000a0fe04881e8cb6865fda23d69a35ce7f28574bdad4fe3b85a9925f7aebc6620e01000000000000002a11c0724e4fb460dc5513aa71488c58d8cbe2b5af25a62a4363e51d23fbb60701000000000000004e263edd08343be113e4234765496cf31e3bb0068d66e4782209171df67b2c3f0100000000000000265680af1a1392be7d96ba0fd2e32b5fcc11f2c394bae83dd49a29e4bba585000100000000000000c284219802cb0e56397b42e6434eae1cc49b1f67b0d05b2f13ef175c8e0aee5a0100000000000000cec30d319a93c00b7044b2f11165f3a5679d82ff4db6cb2783fefa42b236b8220100000000000000a88f2dfe51d3fa0370ca543711ee20e76ac172768abc40759ee95d0f99bf364c0100000000000000a88a466b1f2b4b7b6c497f85d1d1c3e8c13517217f2f2cd00c8ce300406d311a010000000000000064326ea393fc2fb9fd9102b08971b19ddbc878d77d0f53978be7f3ab87ed826c0100000000000000b4e7de600ce05dff305c9e1ead59c7dcd2b27f05b71c71387583e886504fc453010000000000000086899c0ce665034c2d701b5429944f115e3c74ae858ff9e7355dbddf27a4e70e01000000000000000e71bed03fe04edd6d1e28e7bcf8cbbff881734408b49f7c3e87e0aec6489f4401000000000000009a64638ddd657bedde38872091a93d700c6a29facdabec86f916c51266375f570100000000000000187d6e92ae950ca3e83ea4b5def1553313c9f732e865113c24a5dd1075ce2f7e01000000000000006432baca82539fea5029b23f67c912ccd9a652fe58c95bde1646d21e2b02fc6201000000000000004607caa83c3a777629226b92ecfd37cfbf53fd266e6cea36042b5366cb7676270100000000000000a4cf6a4ed9ce95da9df37128939a4bd909771b726e5901be83669fd30ceb9f1c0100000000000000c6003d828b9b0c61a99e86053843c22cbd9fdd734219f449e6200f3602c04a7701000000000000009ee91399c765031a73adf234280853022bdafa455fc539ca3920838855b11659010000000000000070f27022b5b79b964b7084d664d1fb56899b3c00295962bd8e7f330b55ecbd3901000000000000000062420d348b1b6663b61c0078995d5c0ce88659271b202b9d15c6597cbf760d0100000000000000f61ff7a1acb727894f81bb4e328a7f64ddbca4fe5477b60a72fe491536f97b550100000000000000341699a2305af27ee4e032aae91b4779218a2be3173500502d6644dddd1e653801000000000000001c0927ab80ed3e549a848e77c86fd30b448d4d188e0de7bd66370c05569d19790100000000000000444488d1b3e45c7aa8e35cf71dc80b41639412dcaed88f71743811c40abadd61010000000000000040639e72e9c4806d47f4bb7519ee7ff613ac108b17d3be775d72a173f0fc691601000000000000004c8ae5be1fefb1169156bdad8365d33f6bbd91b77a3e3897cbe0d5c4d7f7e3620100000000000000284b3a6ef1ead33f5fac01a077dddc179190daec59fe99586b99c0e35340ca72010000000000000092ef51762ec663f4b099fe6a72a74107d3844913df78f0b80d626fc4e20b1e0c010000000000000008ff6a7d89b6e1936b595c141ffbda304e6d40e36ac264d411615faf8049ee110100000000000000ecab81c08817d3365ffb7e7f3a7d847a701947a1db288bd2177868251da5a56b010000000000000038ddd57c995be34d71d938d2d1cb188a0e8b827da6d23ccd08ecd936850e9e520100000000000000149dd20740e21748698d0d48fcfe3e97e3ec1de3fef8730d5ce5f2fc110590220100000000000000542b657db2973a25f1f076f7b15ad6cdccff6e9687e7a4b5c0129bc77c847c26010000000000000046659edf4ece89d3ba4e4539a2a4466334f4107f099c1a9fbd8bfba22de09d7a0100000000000000ec9afce67bf970d2eec61a3f309f52c860732b69b156205775af8eef041872720100000000000000045fe14e3b9bfef6b174bb0624f9ca0f897e4cf6168ea5952124c15b5b2597500100000000000000a8c43807924e0b4efdf713a557ac34056545de254fa3745fa6cab97df913c46e01000000000000001afa773e53d051ca10aaa2e7d76b01beaa90c8c2d6d4a9c36d6dace0fcbb88230100000000000000f40475fa28c0b67b56058df22178c5baab25a28d153dc3277e0dbac6c094b25401000000000000002ef10a06d765808d2d44b2034edee5654fb27218398f0e79731b318678ac07110100000000000000500b779c0cf43579b8927d8ab50ea09f3d540ef36911b7a7ae99e2fbf4e137570100000000000000ca1f7eb42dabd6595813c3b7404cab648a2467fa9e31752949e07090b286a36b01000000000000007e80e157841d2bd8452224800a02e3c4b561a27f1731f0125f1291c359b4d4130100000000000000b0d74c7b304195803ba0b25627cba2c79293acff064d41b010a32b6d3ce1060501000000000000008cce5eb3755f181c945b8de1c6a3bcb7454557ab4f2d8d3cb9f7d137c2e50b570100000000000000fc320d8d5e575097cfffdab4641c3feba3bc195d7ec01121e2729ade54fbdd0801000000000000002a8cb7211931f3477b5731b23c2d973280b64b509ff16096da574f0648c9275201000000000000001a5d6fa531d4845ce8e8b33fe5e37f7f6c25593f92edba3ed34689d15bd8db0d0100000000000000ec4b66140f558efc29047fa4cadc629c72756a4d70727bc00b7461b74baad96d0100000000000000c4a4b433dd005332b2ff4af2534cae3ebc0a696340d8add1a7399e95bb0ffd7301000000000000009c1233cf95166c9255e9a6c5e0e927b8663b1b8a5a179b6bca90aaa1608705520100000000000000100f8e7d113d41416cd80214599a75f85b3b19e96cd2da7039c471de2006a64101000000000000009c1b9343afd827544ee40e6f45af9f75d1d4488dd9f479510ee8cf47c188f92e01000000000000006cdaa7dad71fe0ac92388b99fe1dbdc5425b39eeaf06a022ea8d8976720d914c0100000000000000c870afa85f2324fb5d56a923fb5a481e606bf27a3660a674ecac23d81be6cd2a0100000000000000e293330fcbd554eee59e0561b517231bde9f1bd1c64cf8530525f997eae2690901000000000000008a40b5186b72111670500b8cc2794555e4c67408a703f89984e957e9dd9d532b0100000000000000e2898e69209f2ba56c07e0c3f037fb20a97e5980ab3bf5f19c840b8f5d14641c0100000000000000a44074d35bfb2169d9f8bdae3caf80bcbe66f8bbb20dac30c29393f2eaf4967d0100000000000000062b28feb8046eff2268a58ade1d5c30db94ac566c33ff5a99a60541703431570100000000000000a62c0a8c58b06086dbd00a76e81ce32688c54f510a13dc5a287bff4303f41b4001000000000000002aefd1ba05d4f7c13ead2b20b4d868e8d787b8fda35b182040a2aa9a8a163c2a0100000000000000beaacb072ff2347efd4169fc7993ae758ceb56ea6f6897cf9a25ff71cd41cb02010000000000000012541487fb284062dc65881d5a326f3c963924b68ff36efb87efa1520c816d060100000000000000a82f7ab0942f27c701a45054359b3658662c80c0c8b1ccafe8271c5d3489ee0d010000000000000034a21abcc93598500f0d1304dbb4df783cd4faa9ab063e4caae4dfdc09899c570100000000000000a47830f3a4a794fba6e4ab624dada5b5630a82f1d61eb6dc406e01319c1a2c2501000000000000001840550329e05c67e05813a375fbba9a2ec82d1934388ad0292bfac2b45bc1030100000000000000ae2f6e59604ff1c1492face76604bddc0c48209485eca50f8d82cf67c727e37201000000000000005af885b0d311bd032ff14efbbf42adfae2a8ff2088f1e2259394bb03409dc4330100000000000000d6d45addae162f1bb235d16821fcf1136568b644721367bd3dad6a9ba9dba6660100000000000000c248107abc3ab176f7952a645a89b3cbe1303b3af53b184a32360cdaca6c400701000000000000009e51e504c5eb7a27f36c064c1eb1373d66a4e11397ed215dc8909db7fe523d5e0100000000000000da5f327512e71306ffe1905a70e12a259d0f0ef611dc7f56be8f0ca30940033e01000000000000000e6bdcaea0c05306fe727b5593d281bfa2eb7ff3aeb4bbd965af01396807d6100100000000000000f69d3157e535952e2155d65b90f2059bcadeb8a7b898e0443dc2f719e5f0841a0100000000000000d678254fe840948c178fb1f90f58fe093d1aaeb45b4919089f52c7faf222f5000100000000000000eede1bef0c43d1266c31905705140c0aaf136887ae4446bbc94cd5440d1970660100000000000000868dcd842b9e0304ac20a855f4374fedddad8781be75eabdf4f1f3d638593d1b010000000000000066c9290ae33d9153a0635c8b00685f11f9a89dbb3e9e907f8bf56c7b1d797e670100000000000000bc7567b86703c8aaf553fbb6add4de6c65ace7cf1816e31db07c6b4056ee7a580100000000000000e0c2d0e86ba6e4484856ec5a68dcce77175b15ed87d436bfc5abb5a01f38c01801000000000000008e7d9341619a28dd6a2733adef62b13f8cee0733de46494549fc38ae52f02d6d0100000000000000c43d0c516a2377bff6712585e03422e226f99014c9565da7d61bfddcc60ba2230100000000000000c6b1190ae760998a1286b3e6b9dda502a335c56f868f442b971fb891f361f75501000000000000003c611bcf0d259f8b57865f3250a1a09ddf562089bc322eee54be703ad422fb340100000000000000dc0e6a184863bce89bfee06c3c046a7cccf040d0d235865c6f0c56a75790244e0100000000000000500f6ef080819e095e751b2d13e5980446df95cc3997438f3b5ffdfa6ea0ec5a0100000000000000ba94bfa261dfe0b32d2697c88f97b9b1a3e603d69f916023dd3afa9d8f3715260100000000000000d4d43a66ffc7bb3c0a543a75fa1ba8c1b25eb338c2165bd0ad9aab3ef367fc17010000000000000060a92cd57e6ccdb5667307b0a0c9daa782758ece051899c00a7cf0578c1f131d0100000000000000d064fcab2c3f2b380af1b6ad48f31c17df5ef9ff46ffb7bdbc1908c7af5ad26a01000000000000000244515309e58b54210661c190fc86d5b4568eb4e85c578318729b50cbdbbe190100000000000000c4d7221426175dcdca951d8e377fe9c314972a9667550d2980d184dc7713244601000000000000002e2473679e557a5ab4b5f53f3775458dca1ecb9780a69a12d491e06e7b9c4d7d0100000000000000666758dc7096f360265acd0446e0e68d4d2c6787ac5d2a5d5ca76ded97805f2f0100000000000000ea4a297a0d3ee9491ac96d774dff2da3c675b3d7e4dbb9fa4e76c047b093880101000000000000005aecc7d9d96f48094caee4d395c8891fdc90529b74d835dae0d52123e5d2386b01000000000000005e092b5458ab949391f3323be3b09b90f02c9f2cf57c99e61bf5111a293c245f0100000000000000285f4d091e0564aded8101aa98e417e47008261ff729ef856003c798fe62725201000000000000000abdcf937b0f368b81ed8e41a7060a9d7917e6339c48b6abc6d8517b00620f7e0100000000000000f25c49e92258e740a9a79f938d3ba8249438b07884ee48a4973bd15e6fc52e1901000000000000006233f94f49a8fde5ca1b30460fabf5f1537bf4eb800caef619fe1c5a0572353401000000000000001845051fe44100fab50a4621b65b7c189083c3701b8366e0d853e139f60a45470100000000000000a41bbd69c636b6c12fc0512d2b016808851770a22c97337acc5fc8240bb82c0b01000000000000006ac55a703d9f7b20d49bd2387ee9fd6f1c6447a3401f793ac7be2064f3b6f464010000000000000092061d3be62cbed8f429d95dc1eac9d9bea8e23c12499426704ca64ecc28675201000000000000002c44ca2a77214e408a8ebbc6ea0e31cc4c01f1e9477afc11d9903a47777b345801000000000000000c34bc418f0ce9002c060ddf2f2b4b6942b1163f4dacd5608c0bb6d2159af135010000000000000092a908b3b5f108df9401ff0c6213ebdf5eac412295be6fbcae76a9320f12231401000000000000001cbf8a564c0beda112e06ce28226c1775b350a506fcc9133f1c87594a655e02e010000000000000030ad59b9fc56fe3d392dfcab57fc9b874707c1fb3d59ee78463f8d74042ac3040100000000000000361601767f088b9ffd46588d7049d46d33f1acaa4ea4561219c4709510c1cf10010000000000000074111e0c15602d7fe0c6eeb12065b07c83d890b94029c8815db2443a01b0eb2701000000000000005ceb546da5983b4c431d196e5bd32aa4803dfb513b792066f83546f63dc7534d01000000000000004a16b874d8591d53c896cff1d280d5c4e5197ebd894804efd727e4bb37bde7060100000000000000141bd9a074f86b6b6851e89bab648df0e45972c23acc7ccd9d5bec4adbea3a190100000000000000365850afeaa8058c89017ceea5cc3dd218f4955d2a4567f8a964ef389078c54d0100000000000000608e3ec03a52c67c8b0f637cfac7345fa1c2b4b61d09e50bc7b79a2644ad053001000000000000005a7d2d2e52a9a7f01b1ef3b252202b4c96af120e867141675c0820fbb67738700100000000000000baaf82c13c70e24dd09199d2fabe38d3712470ad7c8235bb9c47df20870d301c01000000000000002c64e7353c81c2b4ee434edc4cb8ee6d7a484371540875c546f6c9b42f6dc2300100000000000000c2a1109372991310a96d78b8da314163f9385399aeea368ed7e7c37f201893340100000000000000040b8138245dea143b0e2cb70875ea60ee74ad3df50b916db931db5e583d5b0f0100000000000000f6d9021798d518b38d24a74035499871973f7e0e67a3bc5ba37c5d4893e1d9480100000000000000ce87603414deb87155f1df0a5bce17ab2fcf11302407b96b7b73ea33169a69670100000000000000f045461d722b94b6c0928af47a0d8ed3b78d646b2f4f754b1c90196465d7f92b01000000000000005ea61dfeccf577b54216c24d8580984b5a948fc2e0cb15cf75d3ec89ef00470f01000000000000004eefad32d3c3dd9b713031d2d3412b110a5997283e12e1573f9523bc479e77220100000000000000949ab1dab46139ac9df94d74a3a91b263a98da335b9127a2fc7369476bca3e41010000000000000080cf2f4b8c6f71647d087c97a83df3f3eb230bbaa5bf86299a2d3c8ff2f2664701000000000000004a85979f5a5af886d6808c63ca7f266f7a55bbf652e039b72f1fdaa9c90deb4501000000000000005878e7553def1229480d89260e485b7f3a5280d16b9c1f1163151dfe472e247c01000000000000000869d1174ea5bfe5b56385c981fc3d8b58424426b8a826f44bc7fb6e2aeead7d0100000000000000d876abe03feb4a8bcfdefda30228ed6820b1a1fcc5954148b0ab36c88fd1ef2f0100000000000000167fcf03f83b30dbddcdfb8e4854b03645d77a3be1268d674e65ff2085615e0701000000000000004e3ab53a4117874a7fc193a4e51a4b50efb808cc0f26a3c3616cecf4974079660100000000000000502537a2d009d0bc18752420e46bcf3ca17091bc04458ab50188240361ef633a01000000000000004e3ab9c976dd36a1fde5ba6c85e91f24b0e0f1a4101b2abd92f8eb6953593243010000000000000072febf0d8454db0a0c9287d7b22f739ba0154b6319111cb5104130557b6fc3670100000000000000e036c3ff765103769a69a0015caee3b83fdccadce2a269a3df376d0dd4001d4e0100000000000000e64945c884e8e1f126c36899bd7540a6866ddc58aae678cd0415e240dd3610220100000000000000744488e84dbbc73174838a2fe49f231ee82448487e5e52c5268f9e659be8e95f0100000000000000e6dfae0c3ed3754930c79a74e58d09583dae0bec35363be7c1bab1c3f25bcd480100000000000000408fc6ba61cad770bfd6555973b079db6ad49880fe3335c4561eba5ee649372a01000000000000001031987c29702b10623bcc65912a080416d2101ac6e204a2075d9a66dea5752601000000000000001c85862b279bcf4a725b66e8fdbf4150e253a52affacd77f2b52400a2209fb430100000000000000c648a1f7aef735b686df2634b86d91c94428cab340393aea3a5494d02cb57a4f0100000000000000181ad1bad2d424abe7f915a584f39a6953a87fafc1ac0b9942ea2c64406d4d510100000000000000a0668c4c43e71d99ad863e6f10e887b2a1f2bf32778d1d4d6eab57f28ba5425d01000000000000006a3f450101fe066e048ef87e288d88e0635b3677ea222f17ebbe87d4fb92575301000000000000003847ea56b040d92b53331bd3db1cd353b1001bb6678bf29cb12414eb5ed03178010000000000000092049218a50b3a59059599fa127bc3719546531c80ecb68d8ca8f5a0671c915a01000000000000002e1c9f1141ac38310b771a3b1477ec4456a02000abf09c4d0fe505cbd8cc0e2f0100000000000000acb79942c9051fe2047ba6a2f0c73ac71bfa033ff1b76d84332361573dbe8a1a0100000000000000848667087ecc48b5e6ce7a424eb24090c6b597cf121591e3d648bbde53393e79010000000000000048f1908f2c135d1c47c7dc79f63d6c4154a56ebbb46b039db014d8b444cfc1380100000000000000f01fba66537030d59b1e78a3246fed5a25c5322734ff422a9609e78eb0b6bb230100000000000000000ea8eddba18d4da471cc88fa5ebf59aa7e0cdbbcc7ab13643e2f9d2a1e120701000000000000004ce0e117bccbd3903add7cfecf3546010867b1a5fe6ba3ebd189e4cf9586a224010000000000000042f67b46c75b06dbebb389f8cf054f7c82187a85ed234f3920b919a161046e2401000000000000000a5aa5bb5d80bbf7ffad004fe82a73d66db8edad93ff6ce0d8df78d3e0e6fe1c0100000000000000acef4f49f13788a7356755512b74656cdf11fa80d7fe7223e250a7478d2a8d3a0100000000000000b4acebdb8857e0261b2229a1c6848cbf75001ab181e0504826d6ef04ca04e92101000000000000008cf03fcba2760c34855ce798becfa293b37a78b79820144da5ecb526f633cb60010000000000000082c55569a2c6861ffa4f116b34d3c6e12b5fb075f048e7d6ce35ceacfa40a93c0100000000000000da124054c707782430358583f3fd98aa1d4dd90968e8b4f3d8683ea3f27aeb1f0100000000000000f2bafbef8d77c9facf5a04d9c079d847bf793bbb1b659b4ea4c7679557007d7d010000000000000092ba571f52063897a993dc9818bf50cf039213409b550794ec32151fa089d31b0100000000000000da088ee3658a5e9587f0e2e7327bce5f20d5cc98cd986a5bb817d9b9a587567f0100000000000000dea6ca7823dcb0837f8533d8da27fd1a1411b1b48df4a8706b2f1201baf23b330100000000000000366e377084ded70d1065bcfa6e0629d9df8b01def4ff4620432dbd1ce70a6a55010000000000000098755d5f5fe724fa2f0116ddb92c0d9cfac4da3eeeb51e3ecf0356cb2966a71401000000000000007642f0c1c4d20c54338f0e3fce9b66576b392cf5ad165368c9924e1773c8f0280100000000000000ac198b700e0e627e2642654d791a9102e19bded7085332a1b76f543f0d94a1700100000000000000380ecd43a50457d037678d57a799cd411c1f56265ef38e0f777790e7ade53e7301000000000000000a42164a9749a911c34ff4a8ebdfc5d004de3fd7f06891fe5c882d2b56c2e6340100000000000000c2ebf281e96ead69e96276de04407f5d89a6626b5cf25bc97eab4d35b1b50a580100000000000000b64acae010e2e85df0d36ce79d919a14c43f0dbaba232ee96ed1e5708ceda94b01000000000000005ab857faaed9e6c7aed2d200aa81404e546160140fd0e5f314df885f6cb2c31c01000000000000000c7f753c4216d22f5e9a56f145f2dce74230a875c77c15ebb57bbc672a84bc5a010000000000000076f35f08d6050caca1bc42f1bd55d6d300580caf671f4a3b3bfa14b23b8019400100000000000000e8a3d82b64794602d009b8aa44a922a6c4aeb9c19c36720026b9b010e4880a420100000000000000442b8aea3303213b7f5e5b592ee0ad9a2043c34b62b8bbeb61958eddfb70912e0100000000000000c6daea6e872ae2760484c874d425703ce31f238748821e5fa7689e529d8931380100000000000000a2d5d4f254b43ef338ae46aeb6c96293c9e80b6508a4f9e3534ebc3786c3137b0100000000000000d00d343b5b85e47a6494a75fe67e3524841029026739ff98f7614b269c7e10440100000000000000e434239ced7867561ec1c186d12742bcacc9e69a19f6677540ab902f0e450a66010000000000000050a9d209313d30fc0decd461fffd3f46248364ab3a7e34932ec3b1e4ed720334010000000000000058ef7a28606a9d21571e962f3ef8b8adbf9d3acc41517bc9313985759ff1933b010000000000000016e5bb9cd97e2054f662f70325ed8440637c299e77736d3c31795e1225746c5e0100000000000000e03b6d54dd7017cd4ebcc6642ab9c23c62e2d846e610c7ee2e2d3cd71337b55e0100000000000000e6801825bed5f4cce30bde6abb75623a845bb633ff2e14a6d696a85e92690e490100000000000000f6c65ba1b18a790cc704f8e3a1bf3177ff51e7eb0081df2cb69173bf44a4f71c0100000000000000fe84d61179f6f25c0f3b84b4867b3dd0989aa22a2d9bb709ef67a8095008414201000000000000003231e6a8c5c5e47509eeb593347ec53cad6f05a0b30938af3861075488f2b03f01000000000000000e78888c098cf42dfe016c9ee1cfeab499eb765ad64cac48b28892a0a41ac20f01000000000000004c8d2e09ef8bb646de84fc38205dd2055f7ae3a56ec41b9d8d3b45a15f8b69200100000000000000d896c042b016100c440efe8a5ada9d0fe30f4eec18ba67992bbd1d5e513d147f01000000000000005a2e0cace9c852e709ab431b7a2ce9bfab90fddb0676af571452b8373756f63e0100000000000000de32e1c268482c89301b141116a85c438b6ce208513c71a12fd81777f144033701000000000000006211919c6d1b11584fcd6f3f3fef54d0d72f23ea821685ba235675bce10504290100000000000000e21d26c4a79725351bdc30e3825afaf4ec037fc82890c9eff8eab6c5dbe42f0b01000000000000009e25cf7dc9db334d01aa6215a0a9e27468a5bd09ad742bfe68b302c3975c31510100000000000000eeae3f5142c5fb5b16632f97c3885fce76a7969df551a3daa26c0690c610716701000000000000005adb6c8cced26392d5bf6151b5118a54c8c83d4a6b3bc26c2a623eeb3b2ad3190100000000000000ac91d2d8a78bb7d759742bd920741ed92c02115c7218d39642dea17086de3d3a0100000000000000d263e15b1310b82d6ab02f9f77b307fa234ea4a139b1135739602ec3c97e3a6f0100000000000000c20ac319e9cc300bed0ac95df0fd200751ce194400a66775ba8b68523b3f895e01000000000000008890f167e5966fb28d63bf02377a8b064b2466930fd947e54adaf5bbe6019c540100000000000000f6e92ca7e76a3ead20727d91199658a84ac784f496f078a71da0ab09e16a6c1a0100000000000000f65d2e46fa483be5f4d052b2f03563fdf7621ddcdedec753a4ef9508782e106f01000000000000004ca7ddc4814c4452f6d1c98371c6d2ba26ccf94841249edb7fd120fed311262b0100000000000000380a3f04ad89f508b389352994ac011d1845187c479281320ff77f4352ec0f200100000000000000a2c51949aef85bd6d86dc1d699c453fd89f19e171c580a43244b37ac32db775801000000000000008cfe7abfb01580f1d3a9633b11b9229b37f979e440073d885ecccdb32eb64e4d0100000000000000cae3883e0f4806a85ebdf81669fb03f002d699c95f5cf6299d678ec16edb5f6301000000000000001e2d439fee3ab0966bcfe0569144e399d8c1e431528b04a9a103a0d5d76bcd5b01000000000000008cef7f6ca0d96d48c03771e33900c28541bc7d5ee15689e697cd160aff14797001000000000000008c63ab0ca01ed8b358bb5eaa2582a0866fbbb68d170a03f2ec98e9eeffccf85a0100000000000000dc8fc9b4d3b7bb540e9f12c606e4b0d784dbf8542db625f56e686a2d2fcfcc25010000000000000040fbfdbe5c13e3a46a731b8191f92cf01734f42cf7293fd56d664a732c14ec5601000000000000000ef24b61e18983aa285f70ff8e361397b82a15a41c6742541ce7b92da9f8931101000000000000007854ee17d89a8b19b9756fbb60653d788f6d261fad13a56933e0640c674a3b3e0100000000000000e4a3a09d9e9b63295de7b3cb2284d87af269ee20b02de0b66fcddaddededd0070100000000000000e2b114eb3572e899237c85d50df9e64d4f29901220e9de1d7a8e04d96d2ef24b0100000000000000a42e70c4530076b877035b43c2e41672dbb02b95bf1755e1f808e3cbe23369630100000000000000d8df9dac3ccd81b28470ca51de56a4ce7c637a0ffe4b73a826cf7fd33b7820040100000000000000fcff2cb776b86e8a3aeea98f908c3195e4ce5db676d36bc3a7af3608698ef0690100000000000000b684476641cb59f5820a10f788f088080a953b907f990df22cc7943dc3a46d3f0100000000000000c0a1d3aa25d5150e5c38220dde8f6049268270e41242d623989357654078362c01000000000000001431ee16d3a18ea78d6d22801c6a25fe304172dc5febbe550026a1c0929864500100000000000000fafbb4dff5111e01f4ec36533c57a92f691edc68cf3e86a2a51ff16113728b290100000000000000a01d73a031bb8f4aba71d271be16a28896c85cfb9e2dda4fa3b44936207a2b290100000000000000eab3d83726de7218ba112c82a45c033830114b31870c16d0a49d9070621e012a01000000000000006c1eed8b9700d423e00bf57ddb3af32d21d8f0a104dc650e871ff044fd754708010000000000000004ad9d2a4b586292d19f935a31d89b90df9c99c74592adf2d29e64a5e79ab0120100000000000000e6dd828a602eb81c7522b98e53ceee9e22d0cb5a6f7cb0e3b00a7006f0e0770201000000000000006c7642774bd973c6114e6f0c3558c8f166cc9c8e980ec1d7ec3cc0276426c77b01000000000000001c3d79dc60c89a9873f2c5379ee71f4f298165e07109d4d47bef6d993a084e1a01000000000000001287be7a45554529c73b9243a51a201100a37a2c2509a9fa3acaa4dc04b5093c0100000000000000325f3840c5a9642addc7b6648fdcc21495999e5c00b368420b23e8173f5b9d570100000000000000820409942da35bb5ab23f20f62db3f47c5f87492b13f030734b7e7fbcb05312f010000000000000088ae601d5eca42f99686b6d19b3889fa79e32551d065f1cd650badb2fdc9450a01000000000000002eafead8f602b2ebe286db17142e60a41962e071346b4f1064fcc70f2e19552e0100000000000000de53a86b00a4690bb292c096b97dd222fa82e28a833bb5969aa9ad4a273d31630100000000000000f0efe1c1c20dcc2667571aa3375be67362e7f28a49bbd9195f4b5d71e99fc1650100000000000000545b0afc9dd9b56d8da46d35f40723548fc4c13033912da4fa1da2e41265047c0100000000000000904d79ac4481c639afe07fa078bacba2d3a890b3f8e133cbbf744e7fc5297558010000000000000036e5fcdf6e878fee49aedc20f21dd56bb41ffc148d32c34ade94e6fe40a947250100000000000000bc615a1317f7bcb567f078dcf0fb173bc02efaaaab4751c1d50c45b9d1db891f01000000000000001c808827ef3bf3e9de1e02128c8dfad7c13bca0a657b416dfb2352b48b02db3b0100000000000000ccdf3a11292657bec36bca11a4d5be787cefd82cac4440d0682b5cbe52af6c5e0100000000000000904c675598403fcaa16f8f3da9ffd6fa995046782c87590a7c715f2cd590543101000000000000008cb90a65a771000542944e389890ad0abbd2e2abae26dff9c389dbdb20f525440100000000000000b6ebe42548778f3fbb2fb24dab7aef418391da6fb9a07dbf8b889e338ca825010100000000000000fab692e1a2e6397aaaff6fc06302935eb2dedc68917fab400c049df1ce790e2d010000000000000022aff5aa2b5c2f1b1ae4ae3855f3ead541c2f0c9093a946178e127dbec92a04801000000000000008056a44b805cb78adca43e77724ea222b5cf6c25d6b314d38ba7c169576b0e1b010000000000000096169e83fd02bc9ed28c6c832a3240e0e01bffd7f8e5db4f2b85521077212a620100000000000000c848b06bb3577ba4fbf042fde0779bb9a7eeb2978fcde090dc5733ed36043e2c0100000000000000faa30132a968c6ad5db9938a74cfc8f2a878349fdce5776577bdeb3db7a96a13010000000000000026b3a3fb30b16dd2a07b9b6887998c789f2e0b2b50a4e26b792d16d9410215790100000000000000e68f1824cfe5dcc175d3796ab3ab85442bdb76f9210ecc09e01db47291f7e5110100000000000000b84d61ad8579f5930af27feb2c48e63831bd2929ef86c3145e1bb065da6b1c4f0100000000000000f4ca3b31c69d7a198e30c876e1bbb904ca8c5b038e5a811667d13e391e13207f0100000000000000fe922a6bcbb95646bd14a97d96c31cf7a4bc599b1ca7a53f39eb603db40d5e6901000000000000008ec82c3594fa26941692a378722e80fe77a45b83bd1c6adaf06ad48bff228423010000000000000032143326782c6df4957093bbd84260679d1373bb0a62bf12db89e2a7c6969d710100000000000000a0cd6c7a7119abdf54904eb287334be70f7d9ed0b14eb82e4b453efdb7899519010000000000000092ea0b49900eb565e46e7fe6c88d93858a5908a48e13c5e073183df24054bf44010000000000000044479354ecf5fd25d70f527143ac61b7824743dd51426d1eb89964e05489af02010000000000000016a58215c2c1c4a19db6a79ed12df45d1bd036ea02ca7fb81798fc28e80d054501000000000000007a9b8439f7407ffcb77f97f7bd878edefcef5c6c780bf4a49a5b4ecd9fe850190100000000000000e238e399e8aa6247525fe6e10f77ffe34e93465f6e2febe695ea74f90e3ec32d0100000000000000b45bbe08cc25b9b0e98a490833440b7932e4086201c82cc950376eb77a6ad94e010000000000000050b8e2a374c29f6a14cd27f72c3467663915b006599f220c40672bd080a0217401000000000000008ab52131785bc4db73448c72fd6bb6f55f6ef370e9293489e124d0eaa3215230010000000000000092aa82fc527b300d0365a62edab188ed0816aec9a68071f88d7d9fd78efbfd52010000000000000028bcc25f41f0efe2db27838a212f5f7abad9542345b3502ceb91226ef95d0e080100000000000000b2370f9da2faebcebb91db0f4acf2171e4667e1e88f6a011c1248df5e19d85310100000000000000e25536d123bb812892da8dca4cbe099a8b1c86ba9aa702cb5b95743935ab9d1601000000000000005467c16e538a310001c285235bf0ac5fe3a9caf9d82738451a9dc188c892cd5d0100000000000000f6b6e74ef97f7d14f4493ff14c9f0f6a76cec7a4f713d48fec5d008aaa830d4c010000000000000004e09dba366325cea9b0b8df63e0a00a917b53ce34aff64a8d1c3e76a15fe34b01000000000000005ec8bc4e1b32dd9e65c029e27494ea67fde94ff891b068f6f01a0ae33b17312c0100000000000000569605f7c34797a11605cec3b4950f8f5c690db51b24bbe366a1259105fbd1120100000000000000e80c0983f6e5a56461e1312af164e23f0bce9da93e1339df16b99e45a335434a01000000000000002afa459c5fb5a2e43b8c6162674b968cfede7f551acbffd9de8ce753643290400100000000000000c6ba3592ef9d1e79b36a3cb707372fd1e0c0994887600205f2265d370341c71b0100000000000000501a9f8a14e0f544bfa2dfa5793cd7f1549d45d213ac99a09f1dafd821cee6330100000000000000a4af5fed768cd4433ee90c534f86cc4a458795fa7bcc54b0f3ed8cab36cda46f0100000000000000048f25ba12ff8795ec8dc6a7ebda399cc18453accfdba1e58b111bd3c3db322701000000000000001628bdd8cc64a774215982d34698cc24cbf79a71c526e1253259c75c3ab8273e0100000000000000f86a955e179e71bf054104adf25dcfe05d6b4844c14a70b71818c6e0b1dfd81c010000000000000012a54ebefa1cbff0e621b59449bc24d94881d988c2439178c91ce7ffa374a2430100000000000000b2d01a3feedc72f78e575c8e77306f2f337f33a7d8a5a7d28d27dc58849a631e0100000000000000466e7c8220cbdbfbd1acf470b839b2c41f428ae619698a8bb640f10f0d9ebb6f010000000000000008427554054a0e5a7ce541a69a3b63c4734661eb39e303e3fed502435e5a73730100000000000000fc6000725023c95d259e5e405737c39a2965d6289843d3448fbe06a5165ac7390100000000000000789c23ffdcaa9fead87226e570e0ccacd11ef3659a0c7b5d960517d86797bf0b01000000000000005cdc38b8b8fcecdd85ed9c6ae897c0c6212d8fc55fc06ed61f503c157688831601000000000000000a790d1f76d6fafe4cf76f5086e16b4c0102e1f05bd92844034b342a69c2303801000000000000005404349feb5559d19fab0a2f924c4cc70dfb9cb28fc954322f184bee5026e10e010000000000000064ef00a9076e78d72baaa85d81238c5c02efafbfd43a0c07d8e57c19ef10b34401000000000000003e39bd391328e498ddc44d51f5ec01017790c2a0334210fce8239ee47487dc1a01000000000000004cc73eb33d62c0ec3e7f886195bcded7857aedb9ac161226bef22b0317582e63010000000000000004b263ecddcc9db4186f90e8b65bdceedcc5f786fb72698779f4d523a7d28b430100000000000000fe8fe762d7b321d00ca277b8c4aa433bbc8001c7d1d7a2846929c42e0dd6483b01000000000000003e6f47144b40eed6ebcca435d33d00d4609bdf3e7af96270c65e858094b309150100000000000000a0fdd556e5d6c1f8ced14627bdf01fdb4234118e85703d24274a265c45f92b4b0100000000000000522263467c287a18428c4a5a92f37d273699c9ebb0bdc21f5e9a49eee434dd360100000000000000c8073eef80dc2bbcea972a83302442249e5d2a5213e65297c60d56f4e9be86330100000000000000fc04c8e12ae94b0e3889ca36e2b4a09c32f9c6b7bfac999f56e5f2dc72badd1d010000000000000096c09ca3191f61bad04d1bfe86d042857d3df82ad457ea844caec645758c4d220100000000000000a04f6b14597d61c7600128326a1d75205fd239ade0c3b25cf359ee731c80c85201000000000000004e0a418c4c371c6e99b76781644eb38a63789b5d7c5263f949751e9714fa7c35010000000000000044f2675a02133f10999d3f5542ee2a6de9f29a95bbd0ffce8dd47adb1f87c7260100000000000000f4071d8ef295841a0bc13deb8799e23dea815e376eaa23c86f65a5083d9adb650100000000000000fa4086b525cf61d35736e736110859250992c443420778a90cadd8e8c88d8205010000000000000016dd18d2e8f3a507321ce87ca52145f9526c0fe35f7c868d95fba2b986f84169010000000000000040eec19a099c9ee6ff24e3eabd0a7bcf36e0e0130b4718d50106344044c0d6270100000000000000de220179c5aae1e95b3ccf3a989ca36ac76061bde0cf710670a6910d26564902010000000000000090c3500722dd6123a02fc74476a10b866118e9930e9c73d6e1d48cdb6d57e43201000000000000004246cd1eddf8b9255734a8503025d3d0a30995d71fb5645b1b16be777c78e901010000000000000070c83248401cd7b8780895793b6a0e568802f297a3bce0af7d51eb653d23304f01000000000000000854a35b9b536c8f18af98c90d6e8e639f855eb29bd76cf5105414c21a1ceb6f0100000000000000ae7c51f4c49c174b2400d057d61a1f43a8e13a6850abcfb079a909d136fe374d0100000000000000ca5b9515daf1925b3e200d73957dbc4aafe71160d33b5635ea57641eef8bfd7901000000000000001a43d2bff1c184ae4e83348c8b373df9dba72d90741f3e3c664ba6a2cc96e9460100000000000000225ddc65b656709973da9546280bb4b9921bda577344d3078f29a2a8d463131101000000000000001645c1277cd42f9d98b76f5b54b9c8e33550d31727f24e5c5c50acb1089f68600100000000000000b6e834b1c53a32fb3f8713bbc96bf812c6e208232c2d6f094805e8fdeb0c876d0100000000000000b60af9ed41b27a8e5667ed9c45cbe12bdccece1dbdcc096c0763c0f5046b7e1a0100000000000000f2118bc805ab00459fbbbf94668a8793c8fc142956a91a538b55797633728b4e0100000000000000e2d0e3cf521829f443aae687d4533647fbace9e641f5fe677a2c4bc3d7d3b142010000000000000034dcf88929109b300a7d0cb74fcf0ab261b1a273e09f3d9cde025e8a2b10d62001000000000000005eae7ec92e82f6fd8bf2b1f720a696d0256b35724406cc1c16fe4c071448311701000000000000000e28b34ec63d7e1e4dcf7a5136b63bced930bc9e02d416fd5aee66d248eaa2020100000000000000ce49383c47b78ad330a3e5899850092ed6c99012b59d57307de5168d0d75b423010000000000000098280d11d9c4bcdf35884c994da018af07701df1a3ac3da0fb8bfcc95b7aa05701000000000000005eab887073ebf35904cfc25021f4f15442d6f334488dee6a02b9117698f0d538010000000000000078dcc07cc7a2f8cce0384b4e14f9518d3f771960abea998292bb6a52f6bb88560100000000000000c8387a2ccdb4ec7443fd3cee75e8e559990acf2db6c54a88cd99f45fac1a66660100000000000000327e66f794b3379ce2ac64024813e987661fd4567c9f067d32438be273911f620100000000000000d25555083cf20fedd9b5aca0f26447b57bbc2788ccdcbf0af2efb0b010f0ef26010000000000000082693a174b02d3e8b8d6cd1574b5c782dc98edd92134854adc083ecb36c829250100000000000000fc18fad14af9aa7bdbc62e1fff9ed7078d63d1137a3c1e670bd3a3cf5a93d3720100000000000000b4765627822d1702513ff5648aa320cda597f54505757d630e96d66aa1f3c73401000000000000008a2c2b3a835311965eae34810e418fbd1bb858fb8840a850bf036fb7eeafdf7b01000000000000007abd57972d56585451d75240342fe7195758d6022e0120fb2cdbc9792bd2ec270100000000000000a2d8db3a4f3fb0687052b930bcefffc672c01a65538ee7b33ce9dab822bca1780100000000000000ba73d9161f8469269ece11dcfb9081eedb02df28643417622e258bf2a424a6460100000000000000084b56e7c7fbdd6b45d2db9e3e922a969b28318b35ff26692d88beca3a749b260100000000000000ecbd30a0be3dc9d3f8f4b29af83968bd76d0c62bea36505a114cff593e0b8b4d0100000000000000bec516da47d1cb28351ecaf868365c664aab9076816f1c536a24be5892426a150100000000000000688a95e2be5dd66a92c6b401a64dead0b35f90f517fab9673dcec192e1fa207001000000000000007cb3bd528db15070308159636ad9ca12c1378fa7f8adb5206ebee98c0986065b0100000000000000d8da588746cdbf28af1a52046bad4d5374c656a7111c4969094929548945df71010000000000000074c38c9e1867a292166ced2f98282cde342789a3b5d13c5f76f15998eba3e8510100000000000000485f591ac71f970ef4610ab2fd5f034fcddd9584d0213e46f3161611d141512f01000000000000003020c45aa46e4f75fd5fc42d966f1b99bdf1104a667722d91568f063bb2fd511010000000000000028c912c4d4ec34a97f6416c64424bf80494332248e6a7bb65784a6221f3fdc2b01000000000000000a3c03895ec10aa735b1e2eccb283242afe8ea6f17a675815112ce842ebfba0c0100000000000000e45e549d0f5e96b9c5b320e37828c80629fe7130e5ce695dccd3438dea5ae650010000000000000072f45f59c55d6206a31a1bc2361e096731b34a8745d0882ca2e77694b650134a01000000000000005405d5e7f3457fff778bad5558ace408e563e388edd901f3af12990afa7e8a0c01000000000000004e717a4d2c84dd7b159a91bfa677ff9dc57bc7fd3cd661523c8140a7680a27290100000000000000b280b7f37e7cc9a50d4a52e0b7fedcbcd3cae22ec16ed4b4b75ad460efc227160100000000000000282509d0ebf9a77532c60bbf28ba1509de4a0ccee5ee2f58ff267963b4e11c600100000000000000b85015643739ee03dba7b523f32db23cd192333ca1c2dc134be5b67c306f104d01000000000000004e8cf6fd4c64d23457b08b38b3a72a24279593b2aec4aec3294c67c92f4d801a01000000000000005c3a3673e42f0560adb44ee5b7be7948f8964eae967f80ec506c0147cc11ab020100000000000000d6451fc5d7809e480c3dd53853789b86678b890f4bea62024194b14a745370090100000000000000c0a1f13309be3124c2357c00f6a5545b843c70749fa8ece832dc77bbb7fb630601000000000000004c3b4d7a82e669e759b0cd7a6d6dfef7b94a00283be725f25b263d1b4d9a7f5301000000000000000c714d39ea91dc8f07a7b925a03895a148022b8f117d19fa2f29479189fe423a0100000000000000f697d527f96e9f3839e3502704ed69b500b765f6a64ce6dde88af24db12f14120100000000000000de63bd0d9626943917c561a484e93462de4c05d233bc4c1fd7c8b6833d29353a01000000000000001a4e01ffeb9f145ee8a17dcff9bef42b02b5c9b047e05742ac85de33639cb06c01000000000000002ad227e1e2b705e448702ad1d8744ba85d7d5756952b19976d3abe60d60fc70e0100000000000000907b44da2b726af53076263cde755c2723774fdc25ea52f46769d8f357ca9f01010000000000000078d57cef73882a2d361a28b7fd2ca671b3f726ddb22627d4b915217834b7f2390100000000000000082f296935a2a606bec1f195e6b8456f07cbf7b27e1e557b3887e1c46e4bc37d0100000000000000f8549151ec9c6dabfb6d844ba74728b04a8ff079d00e55f5ee55bc3b70604e0401000000000000001a12a4f27227ba6a04b5804f339b0ac61510b6c15abc85e3bd479d6536fffa7c0100000000000000baede105d1ba83fd42159a80f1c0969bd4cd6ee77700ebfa1aa3283a9d274a47010000000000000032e2a6ae50a3524184557db89a681b1de81e490b4066b4fd67364984da49931f01000000000000008ea9246c435f67a71e9708e1481478b762011e0965cb3ecc405155b4d2543078010000000000000048baca83f5efb426424d78dde70340fd86117f01108c84f4595deaddb22593420100000000000000d0d353277195e01a4236de8fdfe59b442f17af9028dd3f44967dcb17c2e01c5d010000000000000006978ce2bdeb4b02ac7d76a3086ef1bd09e3b9857e950083b50389a48871f3690100000000000000a6d6c761b15a3aa636f5254f6a838ab668421ba56e31430282b33a118ac9ff1e0100000000000000745f2d27e596483e2ebe6ee5750b0479f3c0d355af3498c570549a29b8ac730601000000000000007260ab80632d40fc1d7eeafeeeb99dd747a0f4800de34c2e82578786008c5248010000000000000064ba39c577fc90f2d2976ae979a7205a4918f4c8db532081f06bda711ffc3721010000000000000080ae71b506da12f8a73992372864dc19b64df51f1437fcb247c215a47a759220010000000000000032df009616a2b4d4f6bc9111f2ac419abf4890c99d6def4ce575f82fe7dd163501000000000000005a378e104d9df02d1dce1f9a344f3d4b6f9f088bd8904d0eefe08734a98b42710100000000000000b64c6d920385362820b9f7f9abe817a4bf64224c857fb65825376471d8c95e62010000000000000082b8b2c123aaccd04d02db43724f51bdb437265e59832714a2c2748ac83f2049010000000000000026a2889142114c9b4a07fc913e87a0e5283209088b6ba8cc0148a84f94f2cf4c010000000000000084ec16701285c3c5edc5ee9ae8172215873ec71af3abd53ed9d407c2bc029b3f01000000000000009c2328407d05c9c294c6f207f7c8829f1de10c13a77ae0707dba0dd9f40f8f6001000000000000005c450084e0a0ae90f805bb2e856bb5b46a43cbeda05a7c1e2cc09265e1791a500100000000000000e21ecb8f6b42f003d0d9d3496bb5dc1d57dc74e496acf2c4a3735940cd54231b01000000000000006aa3cfb637cdd3b960d2578823da2b6dac1083495e421c6c32c9e17ec0c905250100000000000000a25f7e57318e03aa832486e574815234404b22d1548f35cf77ffe26573da1f0d0100000000000000227bd022f3a23819559cefc5a323fe8347a3b75f74de5c7930db82cb7fecc8400100000000000000b4af11fd00750c08b33c22eb7445aa0d0fdd877bf701f0755c436d7907c1c27401000000000000002aee8f940e7da1699a6deb601858ec4aca5d597c50ecad1c3402dc97da339a0e0100000000000000d4738db14d68af6357fe1d33fe05635287c20b35a55a6df8018141ae26af9308010000000000000026ab3e54f192d9f8ddf020571780d10d92a0460284fd7b9ae94e74c926c2112b01000000000000008eb2e37eee463c14fe7855c6133aec6244ea3457ffb9b8e0f3a6ef5784548036010000000000000094d6f27dde80e42a7f95295a0f62492eec7be0b70478fe5f30f4c29e280cf92d0100000000000000d27d11dd2c3ab8a3e15a00507904b9fa81c0182176208f622543309788c68f050100000000000000f64491339e6b78127580800529b6ce624217adc82ce10f56d5c6911962e3135f01000000000000002808e5a217f5aa78b344d154002fefdfaf686ebfdee6b7deddfd1411b6a06d3d0100000000000000602f60398fba25dc09db2b4d8843ef9d96e0c60a01ab42ad976fceaca42195700100000000000000a0fc4378807ecdc482fe8d93dceebf78cc5d627c03f9412ce1c1d87d7b44090b01000000000000007008909e788793c04e57158251d1485884f3bb98741d889ebffca8ed9644141b010000000000000072dbf42dd0d9ec7a221912ba34b585d7fc6a0fa240d719332bc372180b3b636d01000000000000008a5716f1c004e49a930e1f0de54439f0a32145cf2565727dfafc11a024503659010000000000000030139a5482c8907486506234bbe2eed7359b6b7c0630468d46dbf2118fc5bd340100000000000000165d26b408a69bf67c63a945a49058f53250882485046f71375f9e3128aa9e52010000000000000060cf7d800df60f707be3b735805e13cd4a7eede0ac2ecbd7033a872b46403f2c01000000000000001872a4fa55daefa13b0b4e4f218cad38d08d7d39d42e47f7df5eaaaae02f0a370100000000000000bc800344f0bf1a71721fca1cbd7ccc47dc009987bd3524a883ebc1e925071f610100000000000000741b61fb1574f9989b1fcbfe4bdf72134ed5699983228a2cf27ca11d76c990160100000000000000ca2e3f3b6f3cf94b81b3bd4c55c730e41d8df97e97bd8084cf1cd24e68719401010000000000000060b1145a4451816b6d3ccc173fbc25b6c7b287fd270758152b92b30ae62eb7520100000000000000483e62e86514eee828c7144e945b7364598a01df7867f7f637cca09e702eb02201000000000000006aee8ec61df9edad0c5903feaf88f6297ea64efd82a52c692c5e937e744faa010100000000000000040d3137e8d771c19de798bd681da04262e8185db315bfe393cdb5cfaa79fe170100000000000000864b7adae776289119fd4da00567efe67846ede7f576955aa4ae889981276a680100000000000000ec52884a19e99c0df416f81fb211ef85aff4b0cbc220d9ff6cd34afe35bfc8790100000000000000b42d7f267a4e469af84c38da01808f135bd6853a7bd92f6cb44e7fcb952cba3d0100000000000000ac220a7798ad88df760c16cf8d18a42683c98528e854daefc7ee32bae85d646101000000000000003404a58d5e89fc3bbe715dd5921f5f0180059e74d9ae6196fabead7746cbc55a0100000000000000380702aac808412a0b886294725efbf4fc033b7bbb85d3b24c2876cc9e05d212010000000000000082af68c2e6079cea3474b2e9d76796b70b2acb3a0ce9fcc98503080f25e629230100000000000000345d63333cd7a9a17653f94374476eecf5eba6b1e4cc1c6e9ed215ba55e5cd000100000000000000960789a11e8dc46636aa94789c0985f9b8fe7fcea34abd0e56f92180c0ec773e0100000000000000a65039da8396aa3e559367c50a309f12e8747e4d16245ec6000d19a0af26f9470100000000000000680e8993ada64e3423fe61be3e70aa728ee73725fae78fa5bf8c57e8e1c54e44010000000000000052019b0e030fac4f642c31526c6308bf9501a297c9b6193a82d6915fb708ff7c010000000000000036b72e06ad058a7708948b66346a0350f8e61fa8c58b0e925600aa9bf1476025010000000000000068b06ac165f434922b8bcf48deb490e43b0d5497a2caa64654a1c673da36b83f01000000000000002654b04f0eeeb6c03a28fb4647305162a4e1112f631b66293ba16b563b971d42010000000000000052283740793ef867c77327902030e4f6c0deec494b5f4db2d680452880ba11040100000000000000785545f9357cc0a5e100f2ee283dbf8f5f9d4967b07aad4bca6ee866312430390100000000000000d60eeac98c4a07a3f08e3ed451c61174b367a787f720a6b7478178209e28334e0100000000000000843d6ad78e2665ef2b86f659fc0f12af29b11bc7f138e45f013e9afdc728624d01000000000000002e9d04dc6cc5425a6ba5e0f6d8b88623849a669542d42435c7c843aae0bdb7250100000000000000e6a422c3c7eb6cd9165fee529afd2209fab7f39b1ae491dc3d5c1320ccae1c680100000000000000c00470ddb02e84a7bc7500d90418dd5d133f4f8184ede2e15cfb43b881cb0434010000000000000092ef07386f9bdee89cc4cf8dcd1aa942134b21ace1bc2f121c261fb57388d278010000000000000026126b727ca3d359ad9aa85e71508ca3fb6a6fbe1416cec946bac3819fba9a590100000000000000d61549c459c94ab938a68b3d96ce644c8672effe9916ad3435324727eb18967c0100000000000000d6df798a261d38a33360d44a4d53e7a686562dec34fa12b7e5db309b736ca642010000000000000076f3274425ab752e21ddc0714e28e9651d4206841b736aac6db37aa0d0d4c937010000000000000098ffae7440754e3de87fca995a95250a59ea23cf595dfa1e53b1367ce20b455401000000000000001a089db458c31ac827e683803163d520ee1b08ed721dcd45b02c3d85855c8c2c0100000000000000267812fa98f9e8e92421e2cf5fc5de6a9423cb81f3b9242d57eac782b3cd09700100000000000000e4fae18a7a4c4f5196539556b85a450a0c1ecae4ef28bc52d1ff9cf3374c946f01000000000000001283692388ad47b5a1ed8b0d791c4f86a36e4422ef3857c912d68f5d425f34730100000000000000eae8a9a4316fe7ce6cb8f624a6615ea281f02082a5be9d40593db1048b234a1601000000000000008eaf59711472c3ef85e130855a4e34e0e3f2b8852af88ab561e9a49f206156280100000000000000dc6956dd35ff772cec5f85ce4aee9e6e6a4b9242495077d77df3e3c48bb26e50010000000000000080d7f21b01bbe9c1c6d89a7275a660af2c01570e22e9ca538b212a9577421d11010000000000000062c124e506bb86a27ceec1d2dc59e15802f14312b81e84f8998735ca13ee9a730100000000000000667ab5e18370d52b66a8c57cf9104d5732b22de108fd38eb14f8b7da2a29ec0c010000000000000032483a2e28748ea5b9d6b87256ae783ecbbdc3bab669542fc642d2c8a6e15d380100000000000000727f2cc64c33ac6bf5b433538f35548a996e80ca8fb8b1de84626882c7b6e1660100000000000000be210d84cead5ae1c0ebbefbfc0b35f33310d6aeecf4308529b86c2cf82c0f5f010000000000000058070bcbd7f8f06c03f9b7aaadc8678f182aa4b3672f3eab877525324f35774401000000000000008ced7755d2a8b17b2d9d157c1b9ab5b0ed63b25b9377d21230d346c7066c39520100000000000000caa319312d929a03892d06e2a524621cd9fbaef1f86d325501b4f1108e65a96e01000000000000004cc8f3fdf153ac8200029d319b8fea21e9b483bea1ada7cb9bd9265da69cb9350100000000000000346e685f148b3adfaed81980e47a61844871dfd86ce1d4f84a7b36998c66712b0100000000000000c497e8cc7d05d62aa5f59e9699962c395433b9ad38bbb84748f90c7d9f1f373301000000000000000ae2ca012bc830761e5afc8e1d81d62c6d160962524fdc3cff021ec49f34e123010000000000000018f128a7e5736dea14b3cde003c82a6ac8c922d2ef21b7e02e1aedc841033e3f0100000000000000a875111a42b538e568016c84c89f4107e37e5643d9a307aad904a7bad7fee76e010000000000000012082ef825e367087ea396f92c97868c24aea7ac2435c8acace02be51068b9760100000000000000a62c3f742cf861feabffd3f949f64703932d3f49aff5214e7ce5f7bb6966374501000000000000006a0e1ac35bef24f8e229f76ecf852ce3fcc87a884e8770c38e2677447e3c8773010000000000000004b06530357d34a7c37df9bee937d879dfe32d5506d1eac3fec50de7bb44dc5a01000000000000007afb0c23bd2d29c347a9b435a6db5b56e035a3e021f3fc9dcfcc47e52f3f0e1701000000000000009eac82acfe866c0d456b51e909c6b83dda9ea53d89b71945c3af1539f7b367740100000000000000dc514d2b1231ab63823ae49ff21fe0c250eed9b4d2e3e83d9cd5072b5f994719010000000000000078ab99c3d9ea7f380ebfe7db64dbc72ec4ec0516b143c2f66f8b06943cb49175010000000000000060bf5700590c273827ccd0dd56915f377fa2ff366ba72bd81942908b7f2a742d0100000000000000a013268290f12c0417675510df18855b1f1a923f0b513f7e7ddc1e763e27cf0a0100000000000000fe0446048559fda5d5d172dbe2ec70e54a5c3080394d055b0b15badf80b6af1501000000000000006ce1cc45c84f9394951284b950417355ff4a093946649a1ed9935cc650d3457e0100000000000000a253dd11fc5c488265b2a938adeff374961efab3789e0d9e637b566a317d71390100000000000000b0d22d2a52036e2e23ccf11b48ee7636021e88a63160754c8e161276e2bd9d520100000000000000744a5456dc956cb654fa881f1ae233b3fa4b245d357826acb8874669726a9a250100000000000000d8971835a1c90022cd05c0f259fc6407970d8fb59fc3ef0c515e07be6d4892390100000000000000aa22f189a8994b03920c2bf1d15d0699bb548c81c52809751cee4ccc794f5e0a010000000000000098f80bca2432755fc52885f1d8023db386ac20b33fe47dd516df4d9318f8ba6f0100000000000000a434f5d00ed6fdcebe637e8fdeefb417f9fa17c37970d1cb13e0ea4ac82e2d4d01000000000000009ee276b61fbded6deab6125d7b348e03c4b3aaffa7840190ed1cbcc4a07ee93101000000000000006ec37117872c92130592d0d0a6929d21e3411790f02e20cd313f09ff2cc5866b01000000000000004e4fe940a0fade721c4c3841d8d88788b100bc0964b65c53f1fc9de3f55d416801000000000000007e0ce7ca3586ea27861b81e7334e5dde29293682fa860c367b5a47e538e3760f0100000000000000549467ec60e011b6bc97752245e56aa4abb389ad87f13b914746bf88c3c93f0f010000000000000084a19d04d5544e82fb8f9f928af37029d73c82da314f437ca62339890b878e4a01000000000000002eff374cd044c1a795a453673fd050ac67510592d7e051ac432f9f1dc1080f6c0100000000000000d05a7baf5e5fe42f96c92c6243a6fe99cf7d5a2f314ea6c4672e5f28e23cd3220100000000000000b857af823b2fd4d88db51d82bbaf4d69b624e1edd3f4fc87afc632c34353971901000000000000001013f3ffe121ff83fd6c2c1f10175b3ad011d9e57c41e85926b1033277ecad5801000000000000003c68c21b23ad1e95d92b93146ddfae139e2ee2bc2c7a8b437c41e20b8a11d9280100000000000000ea8e718c93e3fcb04bbe362c8ed92207599394e6e06aac3145a3dbd6e068f04c0100000000000000e094c83c28dfb858b2cfda0bf6ac10b079e0c35b875365158201a60800d5ae6101000000000000009a14a73f6d4b1c9896ad4a10169452a5dfff45aebb946ea701a5ed94d23c470e01000000000000009e69adce8d1e72ba462dd7e226f1b34b91b038d63573cbfd2963948673045b7e0100000000000000045cebbcdc5e50d0a344d6e01b0a7d180c7fc14d8d007de35db199b31c026f2301000000000000006c1895ede65311cf2f00b5c57378fcc48a95a614d4aee03ea5fe0abf27d8d5580100000000000000003c355f55fe52ddd78f410db36294273478aa852a75c8eebffd3efa0ab2ca5001000000000000003ef792db76744a93838feb7da8b7f665689e6db4dc8a510578804a49320bfd6c01000000000000008a24331a5b0a512762ead3fdba639afeaf649b9d7ba5a939b502bae8d709640b0100000000000000b0951f5af1811973fa55ff157a38b0995e5700eea61ccff01c33096543b1b053010000000000000086e37259e697af7b4028a1cb533d906758ccbe3397c121010e5a094cbd10231201000000000000000ce0da1b140a5d9983ea7245ddc70a90a004d4aef4c5c056cd5fe3f271099a7501000000000000004017c942a56376173f9ab39d8ea985e21b703c3350280cdfb9e495795bbb995b0100000000000000c4fcf817d2c8c0c0c7adadfadb4ea60b6da2913587f69641ca43325516d864520100000000000000183a6a2a9ea985d9096c322b2ff3db841cbb158e4737965f5a98d3a53d0c551e01000000000000009e5d3be1837102ef421cf29a2c0ddbae40b42ad3e090307443f8d4af4da603750100000000000000eea459214eef62024f3fd784c5bdeb42e844e39bd272a92917d12fa66b62394801000000000000009a8f1dc9de51f2a09dada8a66c489135472c9d514cc9c9d17e8dbcd89a307c3a01000000000000000e4c162c806a25c08db000ed62e044cc8ca078f61352f0cc5c83f70cb514c15e0100000000000000100d6b2fc74287c9d030678b8ef8576c642d8ce76b9b6ce366c7ea5ad1bc4d7501000000000000006cc18e610d5c76a068185aa7f300215e2fa46ff5f1bb58307dc8f0df0b49112901000000000000003c26e68e3c41050e6603b62482ac1e6852f1076795ab0a9ec9d87bb4e76f1e210100000000000000568b52d575a8fec7299159b9d534b3268e50a59d3052fcbea17956d8c3e53f2d010000000000000008fac1a954a0e7a7702a7c90de2b1089ff57865e3181fb6e9ff7a1c34fd0f2650100000000000000b63e767df6a931fa235e41cc7bc47f939fb11dfa4e25698e443023bd150a437b0100000000000000d85c008dba8dcf0f67cae0e58ccd7e09aeda4d16e78ebfc94c0de3364bcb9e4201000000000000004e7f5c7b96db92ac862298a99bd047b0ca3f8356459c7e42bda56a2d80146f0b0100000000000000a6cfb4d5fb89fc4d3cf2ff522b7fd4267ccbe8ab89546bae641d1a583923b05e01000000000000000ed511c927dfe3a5d2ea0d858f16a008db97532b7a311fa82885fbcdd96e51160100000000000000ca1fd3a7d39bdfe8d47a70d6b1c82d19ccf792482181aade0e8d2903ee83a5130100000000000000cea92756fea9002431fe31980ac9fdd87f4139f22bf7436b182a0dd03e762c4401000000000000003c1a2bf3bc6b6c09a130633141343b91e3d84a9ee3572a00f5ce2a1bad23af7201000000000000003e7f4634849980d7f3ac6804336df6758ad393816afafa7c4932371c1d8797260100000000000000225b2615b66b0581bd26b9d2aa025900e83dde43819b4d94050de7c90d5aeb710100000000000000ee80790df6b6c2d1c29a053a364a5bab2536c77ec7c07080d5d6d1755a16e171010000000000000014fbfe3cfef628559b53f5cef7a3439af56553151254d5d9e8d07e85f10bef42010000000000000020a5bb4207395b9f205e128f899c3b37d265ff90531eedbf42170121029c392e01000000000000006e7b420de20353a979ff79c79672b543e70d70081eac397b039dc39fddb81e500100000000000000262510163ecdc49a4c74ec24e9a330d36f6ce31747090a05c4a264fe72ac3b220100000000000000bc45808b65d5f14bed3ffc08df2c647507893528e24e9470fac50962b60a3e5301000000000000006cc53152825109cab7a5e657c955cadfc846a338285027affe175e6e89d5c7560100000000000000e2ea16b3bfb7647e0bb6f5a8c01c0462662485630e4ceb1193ba0e17c52d5f2f010000000000000058af2d747b43f08004665cee97001fbdac2d7f8f3a90ba67d57de144d1be3621010000000000000050e17a1e724d93c69103ab8656f7f38022a83394a1b817100992205e0534927301000000000000003ab369c42e4d12dcc2b478b838aed3c279bc7c0043f6398c315c9f2d3c47055e0100000000000000a204e47fab483977b269636c625a6d568e6066d6a1dfcf06e8b20864e342987f0100000000000000a2fde5e21a95cf7fb0a6d25e2d1909492aebea34a623572b46ca9262d44aac2101000000000000009acd340a31e012f94cc096eb81b69913480341aabb48cb7a2451ca9e5bce3c51010000000000000026e8b6bb0f086aaf10d543e45ecc77511b08c7a5f61ac760b6321bc961cbb86f01000000000000005c17a2edbe07f27251213b7f4db4f4e4ccceb01b5f957a296ea89b83cd6b6d460100000000000000f2441030ec7780e75e43490ffe6a2918beb750128114c2a54f7af73eb015852f010000000000000024b9dcf73084abbacbffd70baf520f1edfe5e08877dc6dec17670be166d2820d01000000000000000c2ca9ea7ad31c8c17d4ad472d7520dc29b38fec6806b95d6f13c5f431a2fd0e010000000000000000b9289b629edbcb7691131095127580893d36bf86391a3497fa0642c58eff2e0100000000000000dabe3557e532052159d0eb6aed80464eff5ea60a7d4d11ea6f3bbd93a406fd7401000000000000005ce86e8ab8446889e7794b716d39f72772671300c4f286247c9b94b3971f3a6e01000000000000004cb6d487b23b0ff1c1dfe94c87adaf45ef8cc0b60b3506bf72284b8504d7ad7f010000000000000008e78bcca3d3e2ca7f2813f465a76cf048f628556415860d98c8997d10c4ab5801000000000000008658f3d380c23e829c50082e5393448d014928bb3c7432b1dcae95566f32fa0b01000000000000008ea53537ec9986bf2df5bcbb81af619d3301489bb12953949cc4d62d44b77a080100000000000000fe20cbd8720f46d2c16756fb7cf5942981e933366885167c3905d3213dbcdf6f01000000000000006c15f28efaac1e24c96bbbbece09d237532c1436ed316744249aa55700d5e4370100000000000000242be63827b1ed32640e6465378afbc28a6c83b54013b0b79cf671c7830b36510100000000000000e0dd1b9898a07d1e916784188bdf246ca60638be485f3d1a8407ae39355d644a0100000000000000642bac715fd971189538c56c5b2d81af2a5b86ca9512aae04490ef9a80c0736001000000000000009aec4750f312da2c844b78f64e6da58d6a682d4b7d3ab8ed0bdf3dec3efef06b0100000000000000765548435b4a773a2aad522351915ee05798f3a5873a52931df7caf8b615ea1c01000000000000001cdd4e72f7ce330b5f458d1988a481a6a518f81f98c6e2ee6e3cd14e247d19520100000000000000586b3b1fa70eb13fcaba8b11f82835084416ba431613f235af469cf144abcd1a0100000000000000503ec36c254ec34d47788344256e9feadf0336386b742992f38b21ed62a0677301000000000000002c1ee1c5edd89c3699d7e81bd67caab2351702c68a313ba1a1b1530025e14400010000000000000018bce81817ec3f23e2d5f45944dbf6c9e72569390fa8b002e560291bba469f7b0100000000000000e2e55d6ad3fe7564b653e1687684978568da1df5c07f89d3ef6fef6bacc3b41c010000000000000024be8c31c6082f993e6091302892a1d5e678ae890daa1eeefb002737799d512a0100000000000000147e76962a08ed7feda65a5559464c39cd32f64a7f5d2879b22d06120e01634b0100000000000000526322af40924e6aa848968ffd4cd1f64b6932a6af827e09d6087ea2a23b8e4401000000000000004e5103b8b919c61b36c3162a9c8d47c9a78a2f1bb849a9eb518bc2338a3e0f51010000000000000020494dfaa45734759c4aa32982246b29ea6e49f22e0ab07e8760372dec30fe7b01000000000000007a2d4c9c30b5ebcbe2ef461947979e4a70a2d94c3988e16670b1b51598af7f190100000000000000d8aacc82937ac337a930403e3dc610bd4ff7d241a0a4140044812e2c0b9ba16c010000000000000006924daf9bf6f8ba2317e0fd39d4cfc568409c67b2f0b584db8ce27e8e0bfa340100000000000000c23b317e2ac29311d399211cdbbbf116012f9577212cf68ead5301176574f467010000000000000060728068ea6f3d0f3622d47ca6ba9e54e49742d83c728c6c1f3c3db925c12d350100000000000000b0a42ec890cd378d66733e45c739319aa6817fb717f9c882f6646116c67c113e010000000000000066e62a41d32cf015685e4316f2c5d22026c358abbd815b590af151fc8f65256a01000000000000003cb5fcccbe5ac7fb6d86626c29fc5960a6ce0476d8cfb51e8338cc2b619c497d0100000000000000a098655547e92c7ac123facdd4b3cae305e330f764903eac5abe9af962c0ca050100000000000000a84e5174d0f7fa67088df644df104c96f66812907348626646e1d5de9ff8976d0100000000000000927bddfdb9c270ed770b442243ba58804de2a383b51198ac17afb48d4fca602e01000000000000005003dcea465f6654fc042d1d2e44e9b3491df630b39769e33df13977f081e175010000000000000062da836ff1c0f853f1e31c2cc30bcde8b0ea955eadfefad24fa6d2b4421447520100000000000000f0ed29463a12cca8b1bf44755b7fc4b271611be8322d125f6d6657acbf9866010100000000000000f672856259fab6a08e26157d63e00a8af36793f579358d2549fff83e845c607e0100000000000000ca6ce2b47137af4e77b2c050dd0fd5009dcd0da8ba2de762c20826d0a939307c01000000000000005861cdec6edbde92a2035ffa83f87b17acf09262ea38cf4729ba5a8071ebd24b0100000000000000fe9a0a40f1de6f902e0dc05b033a066eb3e82dbcd8b3542336e3119facf3332501000000000000000e43c4763bbd1595aa61e839d16f29b82bf65e942750eae7dda38e9546ff201e0100000000000000be223791af645d5d3ca38db90e6b59bf0632bbed9e3a3940a54bf80b112c4e1e01000000000000003458516691992e93fc2e1af1f445bb9f5a20883f2780ef854e77d2c1a94e57070100000000000000da6c5070748ea0655df06b4cd7b3670b182514cb2d1d0551225c3a7d5c8d62290100000000000000e2f27f21c4d6c72e3542c85e6acb1250107af219dcc1ec272f74e378ea5b6f280100000000000000fa11dea7f82cf0f0f478a16571cfff58242a1a766f510240a67dedd35a8ee82b010000000000000024e2216cc3180349a56347d56e164d38959b7e35cffd69eced79fc9b8caaf06e01000000000000005664b72ea779c079691d0e6135b1f2022e8c634c014b5b937b90f60e4e08042d01000000000000001e48cb51cbf5a72eb204d54a4689e849243adbff41d54d5f2dbe5d93a0e1cc350100000000000000ca6b5b26ee50944997b2061b81120b0ae368c5e32bc65cf8efdb47fb85bba3700100000000000000c01e3dcf36740aa070991e472d0d82d7f1904e76a049b957a6caf1564e46a32e01000000000000002e8e24430b51f6d5f8dac6024f87e451813ed4db1b3cb6b3e612ab4bdab8f267010000000000000086b1555953c5f9f44fcb3a2f0dfc8ca8331e02dee7116484884e0ac7ef2230770100000000000000009d14aa7f46f47424f9925f605599767e56f962960aa896008c9683cbc3401e010000000000000040506a20667b254d811877385dfe5663399f75dbe3e5a57f289b344b6fb3ef170100000000000000b02c947b1a2bc4d773ab0e28044e8293a875d7b8870e4d607500fcbfe649cf2d01000000000000002c52d329744a9d2458e129fc50094550facd4aeef04a717cd5c452774128cb2901000000000000008c35da616e607d118115aef27079b5702f9f496ca21f96d682d614928446b8440100000000000000daa83cfbc4d25a2336289f3261e6a4901030ed6b8dd063b824fa787a3f68126b01000000000000006e97825a19bcf21e0e299dad9a0594fac9d40bbc95c375eac3cd3824b3e7c62301000000000000001ebe00d3e01f16514c89161a99881d53753813cf680ddb3e3c6bc577829064110100000000000000d0c63d9905fefedf8cded1dc7c758793d257c74656fac1c2ea134d2c8306cb6f0100000000000000b0e486d3f6c244145d23032e222f807db5f3c553dba509dac3ab9ff4f37543760100000000000000e0dde9387f1c3fcae0bb2529e0cf3eeaeaa05e244be364a3bc316c9580d6e93f0100000000000000583e56856e4256b5b3cf4a254312d6dd09279a529d238ac9235d603ebcf0bf38010000000000000018ea5e9458304122e280ac00a656c32f68fe24f92fb51da1183949cc5045241c0100000000000000789b7f450bcbeaf7b6353015413c24a5feac5bfb72372b7085d2232146c9311301000000000000000c03563780cfbde0d9184f984939a14daa87754e9e5f74a23da0d015172ea35701000000000000003aebb093fe4e3d0bd38e9b040a6be249a10f680d4afee82111da0cce9596de05010000000000000062f15b56b585aca4194e0646c4f1003f1ec573c9563dd8409ee3b9e06de18a5201000000000000001284399e30f2a03778b05ab4c104570c30d929344d9785cbb412ea09668d7e4f0100000000000000eed0a44cce8ad788c707e7bc6454365652e7733133cf814e5d51b2ff95d553630100000000000000ea538b55ebf258d78fd7988d5a1c843c9f52ec8a519a977a9c835fd872346510010000000000000092b1ef5e4cf3ec9e8b011e3295349b53577943ffab41f1222b2091758b9cc15801000000000000008853f09259ccdb3d1210fceea0d14bcde576201f38f0fb7ae94930d4a65baa040100000000000000ca8079469e665cca6ac1dd47cdb0bdb56bacbd031813f710ea82e6e14a88b94801000000000000000061bd8f3b3ef9e9f391b0462ca53eb93fff4f3d58fff22e9f84061a53aa236d0100000000000000a086680178e3769f1d50fe93858b70fecf29743f7bbc87b11501ed64bb087b37010000000000000004f431a123a6d3061a18f9553340169faddbe7c5040bb490b4a064b1e0fa927401000000000000005a3d41d3610332f2f571541f984fe6fc38da4251f65a3d81276ed2452e4b1a100100000000000000b0f8da2a4b24053cd5999ad51d0240688269f87aa7ccba7714f46a027977f7080100000000000000bcb2b4974bcee94202bfa32a193ab42facfaf3d3e615c7d901e66d1db11ce6720100000000000000c66d20874fb3e00d049eb98daaf2aa549adea852f3adb625f905518c09c2ea1b0100000000000000705a9cb74d5adcac0f4fdd165d627e10d3aa2ff85fcafc33f2c5315454bd61090100000000000000ae3b8fe38c68695e5442fe037c7d32aa8466b10d500a5194a5708084647bf7380100000000000000de73d7b248f71d461a03af4cad901c83d5959f3e0b039dc987798cce072956590100000000000000a0b18475b63b6ac2080302eaeaf6284c78f69079a4246b9f15ad41691310d71401000000000000008621a861e5500d932d88ac38bbc44b3e5f674b0f97ff0691a9afa25a6863a32a0100000000000000c4089e51c06f4653ba21e1d7ef818b038c009df54cf37e0992d6584fa65fa92401000000000000006e94e70c8e78b8589c175e400a77f92544ded52fae33252482c902cdd20c9e0e0100000000000000e642c2865d5e506e56af1962d29ac53960c84d3bb39ea88a5c3664594313ea780100000000000000f2f4985003d63b08f627b236beb8e744d584ef3dbf5c82804b2473258bcf522701000000000000008a5d4c0de40939c7d94f99684174fab5e0a2ab8de50ff648a7a0b7b98749391e01000000000000005e480e3ab14cab90ac2d02aa586ebf827f06a4751c38b87e3e3860c06a16b80a0100000000000000a611a7e5eddc98ae160057f937968c8ac1f79a83aef44f680ef264c4f88c931601000000000000003a38fb488088adb8b7ea111068484073ce8a181cc6b76737080734cda341842d0100000000000000dac0f653da931dce514482b31b67bd3a7678aa3f8a5a173a87355aaacc4977700100000000000000fe3855e100f68bd31587a9565119777daa2b19e411fa16d9f41c86c55a947f7c0100000000000000e82aff6645589ca5801dcc40ef37a7ece665950cfd48cbd26723d7a2625ce67a0100000000000000801eeae66544febd9632341c6ef3aabed279740b6b28eeb1d1303db8ad0f566f0100000000000000f8f230459692d95fad1c21557d279d5d0bad1a3d43744f8312fd51b339f81b03010000000000000040e2c0bda3d579af7aa0c75a37cda7f8e6d580fc57df98c2e46c682b16ddb92a0100000000000000266916c0513f46eddd8b74fce853a00389345bd7f8e97783d83cbcc9d23fb266010000000000000060821cc864972a7f113473f9e0071b64c69584e77cc078cba367aba13efdfc580100000000000000f460f9d60c5456f75825a685b229aa6e887b5f19df3a7bb24073e09180b8b66f010000000000000044a2fd326ef1e0ca462550f91c247c613baa409868fc6592aa39ad478c85317f010000000000000036d0f298717ce466ec596541acbf95e460fb8c1b40d5e50980e047df4191786b01000000000000001c796f4302a0d1f0a32d1fb9162b6627ca7097d7a3a1b30adbf4ef21579ad36d0100000000000000b893761395022e6f6b8fdfa59f894bf51dac1cac6c4f52c6b8338a1dc19689300100000000000000a4f4f89481b6ee8d5ce2f50f9af9c49d1626966683a01f3e73c358576127381401000000000000005ae9062275202e049d0d4b8d7701ab0b3b70799c3f89d79fb25e79d10c33f92c0100000000000000dca27aec167e0fbb3b984e209d6a253fd1d0a9e4c19092bebed233c7452d530d0100000000000000403f4f38bc9d1481ade01182beb7e4b8b77a8ba08c1800b92b878512607ec2090100000000000000eee9adef21f4cf375f086327411732b51607179bef18acabaf37d4f92f3dc00d0100000000000000a066fc4292f0ac7db890846fbc944d62f13b9edbc0e6c1eb92e8ee916b94f61b0100000000000000641147da6ce96c1cfb49abc2f3445ba4dd8e00e90721e19ee6ba99c1dfb1836b0100000000000000ca4a99507a5f4391876a1a0be7c26716a17e9f65a96679f2c8b2228bdc29661601000000000000003c09c5079e597b9b4abe38fc0f67677609cc936eb0dbfbf03061da4bac95b53901000000000000003cc3e4c20c232b2ab4f442d8bfae67a34730b69f01b0bd2033d20b5b7e670d6f0100000000000000d4db4b7b7ab59840347e5fcea8f1cf0524089fdd64a349fe5b8f5aff6ad99d7e01000000000000006c2b5d66b96f0c473fa442e5626842993528592791e62875260cfa928c96fd410100000000000000bef4f75529d960a44d2878fae1824d0903d5052fa53f75358d03b0cd2f550d60010000000000000092b4b6988ed74ccf189bd2e16868543b853d38e4ca82c63afcd3f3ad656d0e1301000000000000009a75a1a445f93eab93aa0f3f328ddd64d5c25c32788289c42301fdf1ff8aff4c010000000000000042be82ddfb8da6ee77760664bc9c0dfe6a5b7bfa58dbe148b965284514d0715601000000000000000e8d5114e90194b8e11db6414e52c5f9c3c99a886c478263ab17e040e1689f6b01000000000000006864c42f3cbb918799d7626c63725fd96cda1134944ffdf2925dd1c73aaece1b01000000000000008cdd8b5db78d01220b9b747813806f33d6b1d8c19d9fa3fb82098a3f8d53c80f0100000000000000765aa204bc165c6f9595b2cf359a1794f42ee6bd273118a38dd7a45dff33406d010000000000000050478b2d5c6272ba095f2d3316f19aaa06ff22f948d727999bb32feaa873017601000000000000007476496c52a5cc5cb0d1475412135699072772af64302a5a4cbc384ff2b2ad2201000000000000006c6a5941354b9419623fd51e0747f8f467f31b6f4b1e38b465107f02cecbbe3b0100000000000000de09c9e8d3e9c785c9f51c407a17e14fbd6c3c3187dbd85ae23c23e88dee413f0100000000000000d6501868e1cf90df4f8bd798e6940f4121f0e986c899f5539138faf8ff50fe0d0100000000000000e28b8124bfb689408bc656ed979b101069c42adb4329a14f972c39a2a3e9784201000000000000009459d084cafd40dea98075177523fd137a8e6f35ab1068dbb420e616ac176a2701000000000000001262742c54cd31a33891c1650618ea0163c1dee7fc38b716d4ab05f487369d78010000000000000074cfdb0c8499cfe04a51d0cd85ac6ba602436e8cb00b77fb2c1ca01efdee565e0100000000000000e88db1672f41855dc8fb8089b8506989ab40a1f8566f4d564e9f82138cd5b56001000000000000005c223c2fe106334557847ca756505e801f3f798f3537a116f2890577be420d4d010000000000000048839721a668d391498431b5747d7935d55104a2eba9489077abd8777763dd46010000000000000086cb3131c5406660f6adbb0125dfed1efcf8b348126898732fe9e89df5aaa90901000000000000009256c366f4a4070d2905aace2d57295c36d1082a179b17c483f42e944851cb60010000000000000024e8ec732707e61646a06a013d1657f6a35819bada07714f7b57462a03246b070100000000000000323bb7505e17594b4bdbbc7df6a205843f1f7145aa1c40810b2bdae58a95da0f0100000000000000cc179fd48870861bf5cac6ede454991d4f354f986bcf36a3aafcf9041d6b22660100000000000000268a98a0b0182f23a1f51e5adf1e56274d10a977744e8d0fd0b6e10910ce1c48010000000000000040a09307c5938cbd5f5838199f5da3d56052449b745ea13986b5292e290da33201000000000000008eeb878fe73763486242835e2d2b3c65a1a78bbccaee2ce658fed20b793c02350100000000000000344ef75e232ec97983125b4cfd8dfc4ddb568ed357332adf06f209d0183c86790100000000000000ac1fbbd6bffd92d23bc663ffc22f2d056f04534f6fbffe6fdcc2418d88c17800010000000000000054e139e51d28832bb556dac1f9cc6bf8cbb0c729ffc0a2d1d1724c72edd6e62201000000000000001edf51c6e6bc501a13351031b28451af2a98085f614ade4f2b27113aa4473c3b0100000000000000023518b184a5c21ab39941bbe6019b68ab6f2cccf27a4caf0099b8d4cdbb920e0100000000000000e2c23fa5e675899b0de4ca207be07b8d6a436101c5cf4c023db0624d56a46c7b0100000000000000d03a71fb9750afc4e9d30f612b70cbe2e617816dbfef42e5c1ccb946099be154010000000000000082a7bebeda9f9133fbd5c9af9b470e1362eed16a603c28a209df52743262015601000000000000007e3d9d762911807fe95f9a79e45faccb182e612c98cae93e0d419f8af768982a01000000000000005cc4dde005b2344a81d2b52f427c60a1b447fe5fb50f0cdf59e4aba4f281911e0100000000000000e420d5946c2d0d4d99cac1e224b2cea6295490ee3b6a9803cd28cb03b9aff57c0100000000000000b8af11e910e884a06701a7f157684ee2d78dc205bf3d330cbe361586db733346010000000000000036c4bf964f566aa45d6a4581e2b56c64923944dceb6f9ca332a5b9faa374a1790100000000000000205e0711fb2e06eb53f561854a722e0aff2a8e3c82616ecd2c6b78cc56327e580100000000000000daa0ca1a484715341fedc1f74ce6f2d62764eef5a95a5fbf3225db0ba79b076601000000000000007a8c92d925792e50eba4c4d4ac50b8302ceb21967cd44e94a3b0a8f1f6e837710100000000000000a2751b4ac6489a8985a70ed6b3437e0649c909f3d6effce47f0a20ecc33daa2601000000000000004c0395b7d2f32dd6162ca1e2b117e8833b26cb181cfc4b6c997a8b73455f1135010000000000000032e994c3f4e677a6d4df3d65ca980595264dac5140f52b1968748216c92c75550100000000000000c6e3275fb29aa69fdbd35aa9b3c3161875f3946016e575aa2a95c9cc6ad575710100000000000000aef5d56e0a88f892ebb22a48771a10c72eaa3886e4609310cf613c08d8b74c20010000000000000018e2ffa37c2b51670e849d1ba370badc5d5abe3678c59d46e0bffdaa4ad49c28010000000000000072cf291be9f6b9330459b406d50a21ef4d13e68a645f93b80422be6b9d875a080100000000000000c2ffc531b154837795bdd573a00b6b27e177a872f311353de1640060d6c3c8570100000000000000422517e03ab4255a0870a7fc588ab1655061c7322065a1fca053d82be3d1cd660100000000000000eaabe802ed0951368cb8379e36daa67e6571aa3c365c850c124fd9567c4c9106010000000000000002cfcc84716cbd61fe5b3b4011701326a7bb865f73c99c2736fbe41a264d30130100000000000000aea9dc5a53103f390c8584cf892bb854f64ad845b382abb3220c2be0a70e492401000000000000002c2a070ca5392bec7c3f56b2ad808d2a4fc3fd8e857f0ee2d6496aabcc970a5e0100000000000000f4d8f4014d1bd8343aff7f5f1d4366ae6985885032ec8d6cb198a054d085360001000000000000007acfbfe9d17dcc8bb5cc4ce3b62c4149d51ba5ff74293ad82ead4e3d5c278f21010000000000000092d47173f0a1a3d8db3ac255c2dcdb9df605501a7fe33d78d504c4cece2ad12f010000000000000036487c3f8eead755b47b123b845418d12e16b590aa24338a885185809b85a02001000000000000005458c0189a88a6f5807d6aabfe020ee80115394a1b67c33bad109aa647fe3a3a0100000000000000946b15252e2d51056c52ac8024b47513bb9d08e406f86a7b7d4778028c4fbf770100000000000000aa1f85bf11a5565f36a54f17dffbc62fddfea81f4209796c120aa5bcbbfee25a0100000000000000464e26e781ecaf929baa74714f82d03a533bbfc07c95037032a46459f07d5a2901000000000000003867a37432ab13fcd7f9c7bc872c23e0e0e9cc20bc01b6783e3202a396cab370010000000000000092cca5d3fdc514fcff6906eee877af979b006474a4eda8cbffdd861dbbd33b02010000000000000020aab3fd9115b4f4385883164cd41e282f9c9d547a46f9610aea615d99de2656010000000000000098378caf3d7a44614084d31da9ce1d96f2f7aaef3989ce583dddee4d04b8252d0100000000000000980b477be76f9319f3ef5bb6f0de390d75f362890dd6124a0685ab06f4158b14010000000000000098347031396336c66a994bcb21b8be5906714403cfde0dd94615c082cf42ec6d0100000000000000700fb979b0f33bbc8a9a4ed3fb3474ed2b89cfbb7fa7d1c6bacf6d2f1b76025901000000000000007e33ec70a5a0f0f5b9b1a0587deb97d390b5d6bd8315ec4c69ff8599d8821c3501000000000000004af566dc40a9f1b9698d4d28977db99590560e378444a9592c519c3c7e0f00350100000000000000e6d63bfbb03afa14d919f48fe3a3ee4255e8c30f32b6ea6281ce7152ad694c4301000000000000007441e3435ec553639e310c014532cff4d2f237160d3c2d3e44566b072269c17f05424142450101fcb65c0828854d8d68fa394acddc7801037c3e9a0fcfa24217e74080d0b17b3933e245dd7957803b999d75b7fe3fc180a5b9922fdacdf163586bb1a9d1c0a984f347f6f9c53b15f332417ebbbe81f8bebabc38d3d6b5f2db73ea44316a57595aaedbe801b419c5d2d91f600851bf33bf85a6cfc61776c07614c33623faa04eeb254b6d4131391677c2735b8eed86090046a61ddc45ed0bc7961b912d00db31b345042123080642414245b5010382010000f0b9221000000000b449f8a1cbc6f35aa86aab38a12fa472fc0ee8dadf90318505f22372646a1061c0524f4d93cdbc866c238a584378b67e92e5680716671e886886ceada54ee708faa3a785bdad6b942cebd97d448faf81c95633d1a95a8b0e6f43a8004f6ba406054241424501010088ea5da64baa132b0c385a5d1ae085f1fe7e0eb7e4a88640eb70f97de85911e353b356de549ee35deb234e6904b8eeff4cd63b6d1483d02adffa5cf4001f8386235c2e823c8d05a8a5aff4904ec83009993a304830758f66b05cdcac14e4a5b2dbe801f3500d810074333ee6e0fcf880529bbb52dffe3e1ce4963c196dbc5c74a0af5f0cd31dd25a6f356360022fdd49b3439b31252515a0c3e1a422e2df1a4d79d526080642414245b5010123030000f1b9221000000000a206dc6f8139d633097678c461f849e6211641abf6cb70f38c08471a494a0c4bfc3c6e5aac6bb046c8a23fccca76437b3f72a33b74856fcf8f2cd1f444217b065822f448b19dcebc689486e2099a72121e0f7e92d57fd7b28e5a5a7ab7a5520c05424142450101f67d11d245bc556365ce126705e722cebdbfd0f3a21ff2a395e8efa909eadb19a13754f4e3f68f5e7561e6a89371ac874900690e8063c174e8318cdad2c6f584ed634b50de88e7555c8b72371fe7fa47d37add39e00db2003f27ed27849533a4b6dbe8011ab593c476109f6e5ad68c85837290575204ed3e635423c005cf584bd7a33f48c58195f757af8bd16d5c078543efa2012e3d1f30d10eb5eaa353bceb092916cb080642414245b501038b020000f2b92210000000000c0ef386fbae33c92bc2669dd39ae808f1da1762eeb8f7242dcad5f6ea17381cc3469fcf02fb288b82283e6d6e770da62425d5b29eff35f0115ed62139a8d002b02950e93730f7675d6bd22f4546e3ad249d20e74b6c3488bb1bb2c70a6efb00054241424501014cbfeab8c426160052bff4b1c8db7fbeb889cb018285cf0d9d663149fdaf98529f101c03d39c7c4df41d1ec720098dec539a7942b44d154d14a115fc1cb4b380d498dbe543dd66eb8588c31c80c965b6b7ab39bf6c19d9399d5300596ba2044ebadbe8016d2840a8ede769f479b0b46919990f9a109b048f9437f6be65070c120f4d95980bfa792bd4e3319bbb281b4be002fedfa96e23282750393399bc79c6ed44a3f9080642414245b5010396000000f3b9221000000000822998c131f461e567f7765c4926935bebc7ad95e6d837791fda93fe59d5d04d45333a7cd7462389bfd5129a94f2a67ff39f38e8256e54c864bcb488366996029011f11f5cd37b53799fdffa0cdc7e21f2766f4ee1ca40cd036b29b975c1700c05424142450101a40299cfa38f9cb599ce976a25535acd4fb154d80a0daa1d3c5a6fadc826ba034c745b4e35aa1cf066bb72b770efeed5489da7589fa8ed38005461e7844fdb82133a4b4217fa726ded9ddbbd507ca97767149d81958e69e7465c21c35a6599b5bedbe801aef0c088821867987280c08f12cce2d9d8b1f0f919896d3e7d864b9de1c023c4b8f9a9c5195fbb02aea3f498542feba21a53c9871edb0973e7bc01e2e5376a48080642414245b5010325000000f4b922100000000088c0fe8e33ec8ada13606e07fceb153eac10efaeabe61db9aece4b6de34d03046acc55fcdcb09032b5e385e806238a619ddc165f2155935c3a1fa47392aff8053e9296c3a95d8ab5aea1942de9ba16fb681e6e728de6bdd76922394472387803054241424501012a111634483d3896c09c603a2d6cbd862f24ebb663add625475ef52d18420420d5cdfc783e615347ca06c23672cdb9fdf7484acc44f0cba14a7377e84ee5ca850b6a8763bdbdc8c50adc515cc02bdcf548484077f1513ac6b0ced464cd0c062ac2dbe80193250e515a77aeefaa3ae93b9a2ff7d7370a2d6885d090a107bac10c9cec9f3823c00a155f4f619a5062213e377da65cd1858b116d0f46b36b4ab7f2d81d15bc080642414245b5010300010000f5b92210000000002c541066511a1baa76052d9df3162c186e8058517d52d19e6f8f0de906df26545dc3e3b3f587cd5b4b8db48182288e9592f730b95f98bab1314c71b81ccc5b0144156f00ea4a2542e9fbb92b4afaa8d2ee31db8bfed35ff8c7976a91b67b070e05424142450101e4e9fb02a5c4ead0e1620a2d9add8b06814415d96dcadab85c9581626131ab60af0c1284fd363bf9abd14f4dcb68b07be3c8e4b7340026aae3e5a2efa29e1b8885773ead3be43b42669b78ecd037978fb854b1e1e6b528f4908fff082488e21dc6dbe801f5e800fa7af0afe57f71b5fd923db0ab50dd8587369d703eb6c22a8e3cb88284845442c632beed022719cc9150d9c76837a81d00799a84cc55db0440894195d5080642414245b50103b6020000f6b9221000000000b4b8136e1cbf9605474a5f1fd2110d967b05822a6a3293a319e1b64c53c0c45cb3bae66fe28509111192b67b6b397b16bd71c47e0068c82bde97650eda5b800435cf45b74d9aeb9e87fa91e9370fbbfcca6286f7a3fadb12e12fe45db93a800b05424142450101141835ddcbef248d40fb078d9c90ec4e19dd0242b0f937a166b2ed55ec3eb41531613f64c9877bb6b21dc026d5cae47ed63c1743f0d701220140a407c19e8482255ce797d13f1f7dc0a7c0229773657fbda68d0fe26f0810140a8d026dc2c12dcadbe8018008a8a6ce9c332ee994122ef17d70e4b17d1c498f637a88e1f5686e9c40878868bfbcb828728aa6f48b735697d5ef08c42df7ba158e1ef75c58b1a096664622080642414245b50103ff000000f7b9221000000000b871c2b02c94e4ec5adb2e212d21e36827f13dbfc5ee193bf6339163ad5a24458f221ecb5ef8d76d636af5ba23a4199ef652136c191a449f0542d0d075902c000041b5e873722d41e5fe3a5ceb1b06759f82a23939ac454b061184e67ff6040d054241424501011a84480ac1ffc2e20c318e7f5419aedd9b423ecb322af9b2cce5019a37bd525fffb2caf57c2425eb9a1608a8249de2e01c4aefbd0e70c4252bc7cc07b8ca218473614e9818e722801c33dffd9cee463257b2cb8d52a67af6e8b213fd432881d9cedbe801a14024f4091a9a74e5b13ed06fabc41110ed90c8c07ef0057905f3ea876f350ba36bbdf574a7022189d8e880f0e9bff113ab366c2b37ab05d6652f0b760e2b92080642414245b5010365030000f8b9221000000000c0ebdc07bcc3b8963646cbccc1f55cf19c1c1039679cb2b2a05b74f440e1be0449118c830d435f061d6a87b105e665ce11d57191f338789d45301c31598b7a02584b31eb930d9080a0204570b993b407f0d086aab626553ad7db572fe9b3f904054241424501019a0b63cfc366422b3f56ffbe0e6210b34ac129fb3264c720e6da7ab0809f0b127347047aca5529cc309e8a0c754642970c36d20dda2017faaf5b19bf222a998d6c8a14d6413e0578532f5b7bdbae85c139a52116a7ff76ea06b14a5ba9a55c62d2dbe80126402192d7f13100df5ce535cf2ed43bfdeb0f1c72ef7069692085a6f4638525f4efb5256a7640d66ab757234c84b3e44033de8e284cb052a5b028515ff20567080642414245b5010149000000f9b922100000000058d33c31258f54b87b6082db00b9699fb50e6f5be9733046e01ff74484e47558d714a54929043427ba6d3d22e0896c33501b2e36ddabbfe47be6b62865feb90cd66ef0fcd94ae491243e80bf478ea1551ed6ac5158b64443c62da34595b22b0505424142450101a6b603bb3d8812d27b28b4e84e301d3e0550ae4c7dec5d227e273319687d1949771650e9a8091208557fbea0d65905ca46178b71fa524fe784976e199edc8686bdeeba53c7067dcf3abdbc15805f55f3183967a72983b12a5e7d4615621f0153d6dbe8018d07ad8e8cce8dfd4714d9fb97a5942e77023c72c5366cd1b4a1d951383c45b181c9342ae8cfb87435603f1cb06dc6e5ae8c317e8f20cdce36f266741fe73c6d080642414245b501019f000000fab9221000000000bc38154cfe908aa66825bda39792138056deba7c44f1ef5d95a8d8d010cd2a2080ad5fe31230413b8b55198baf3c2a4c33b000afcd7ab0e2e11f2933f9cdcd0961ff357b087f3fe05959a2610db8ced350725f481a8661be1916e952dde3d10505424142450101de7c60d2f41b555be0a5a1e0ff42a5c74756807217150b14ef757bd40ea30b1495c6dbd85862f442c63b78bebc7bfdca2a0df6ecd6a8a6d63279227bb685668713f3b1edea3350118e3706d57fe413d2b8329e82d422a1f9fe5b1ab9664fb9ecdadbe8018c481ceecf9438305b35ec76fd373eb2f2addf21e95de00a9deeb189bcf67738e4378e06e1da810a801d782a13af2aae9aafa4979670dff7bf664a9739bd7d1a080642414245b501039c010000fbb9221000000000c4e39e58914862d0f9c9fb00dafa4e622b62423e910fb74688d5040abb7b7e44363e9b6dfae9df33381070d8d2118999c0949cdd8330027ee9560d7094376601305025e8a949154922115ed93c143dfb73423b9143e5080105cc2515842e1d0905424142450101d8a0057ecfbf01d4ca819cf63f48422074028b25961633beded1998089de686a55997b5012e189d39c0557cbce7078080b9ddfaf22633b2dd47f03aafe075c88c9d1b627dbd6c669d39258e48b6df11dd3bd9a40fd1e0706c106049fa1b14ea2dedbe80112ef7623bc77bed78559aae715f0ace08b5593f903d581e35bb2374b7e786ba321e0dd109ec81c7538af9cc107239795ec5bbb676ba680af56e7a73c36821789080642414245b5010390000000fcb9221000000000224c7178cc2a6e5a508ba0e9a96fed3344142f8504c80105fcbdd58cd48e5b358f9785e68be2a3d5377f0e594a2be93732bfff0febc782730df960d8dc878e0c2784e894016cda2db1778b33432db0b3624caeca34f863bd1a0f7a6d1d35740a054241424501018817d9fb934884fd4e1e4e1083196c44e893aaa93417678612b37ac1b6900b28f7ee4a99f2b6919b3d6ab4d122eab1172a4eb858c287d8fb85f6cf0e5b51c380850cb31bdf291c9252d801f2593c1c67d393962dc525c145f2a1ee536f6dc6cae2dbe801f6646bef6155875bc4e9e1b21aaf8f063873f1db0c8b56a7c52850ecc142626891ff9a3a44413fd17427fd7a31e03aa8b1e19c8b432ba10ad60a5e7534725ee9080642414245b50103aa000000fdb92210000000003af76d8606ef0e9ecacc5925f885de2387c9dccd70285fbb9dbd9e5097747d4794a6eef4ebe95e63f9d680099bfe77cf27bbbb06e6507375d21155dafa12b405f0292b868230535b6f531ca3cbd1250feecd5db44410bf3dd228fedb84a6550905424142450101dcd66939f5d3df00612aea01c8358a671f48f8c0001f6de6d6c1b94e1da2f44e73079d991e098089e558c962b9e73345d67ff46daea1f676163ed68a26999e869e87860ed374b723009aafbfbf1431765eef1f2363337007765eb2cb358be884e6dbe80121351db3a1de7af029413d9f5f0545ba9c49f71cc028ac4bf260c17f74dcc34824b1702aaf7388672db859fcc958f01848b6911f005700f5b46c1d10f1e520a5080642414245b501038b020000feb92210000000005222f717b95f96513eea8f5e3e489a6266cfac7eabfbe827d1b0fac18f7b201ca1d6c0e009037b7272973fd49ee68110079590006bc80127762b2a81d2f7ca0bc9e66388a3a25abb862cd3b11a76ef0bea2bf16df1b79fb106b489c839d2540505424142450101aa100364d629b03b806fd077f347006bb40367bc5a39ef464a8c4102a3996e1874e1b60c521f804898a349aa92ed6097be467d0f95238aacb748753f01f36a8fd06c6afc27a357848b6ac56febb255b25d9fbc0a6b7edcb785b3ff25d54dea08eadbe80137c99df8925565d5ddecfa1e1e13bc23b81af459b54e4258f45deec54b4951965c3dc1f2c30a868435cc1feada966f6ea5eb77ebb1c493af769b80f529a8b7e1080642414245b5010187010000ffb9221000000000e62a7f0bb4805bb26d849ccc5512f705efb2d0fa1c2ee3115acda6547cf7ad4f1736596043661915dfacc19e5755b6cbbebeab6b5cd6585d3c39ccce7924e80a5d763f9834629e61b1051b40f7333af955c45c1ff96240eddf4eea2c9f919508054241424501018655af147e72b03d0024ca56561714e5fca2d5ea58f35c8f7b82adcc7dac3359b2e9e6137d1c23cdc8d884320a267129ab6983d580c2ebe4d652e6fe9c3e8884add1dc7495f773f69adac75bf0a1796f16cbb6691b4778b4601cc7c8795660c8eedbe80135f8f8bc94cad74e26713da43bd4e42b85d175e742616cc987a0d74ecfba5d24b94f19c09a784becb84f0e329769a08cb2c557dbd70777c6d5e388cff9917578080642414245b50103de02000000ba2210000000000a3ef805e9a491c138637fe918a44da443cee095e1ec6bf1ab5645df88731829ec1ae882c3cb14cd4c5f897d2bf31f5d183c481b6f25e5fc0a6c848cadf2ef056c16929b5c2c0737705ffe4ec207ab8df9dd2258af75fde0c82aa4f81286c40205424142450101127122bdb0fc7824e126ea7c3f25e460c2d15a3077e75cc190fb81de01dafb02f757359a8329cbd6bb05673a73e8fb23d5241dcc2be47f2d6d58f25bf830868f8d562e0fdc68bf9917104903df8584304e3e9fd9c88ba61319e961458076040df2dbe8011bdc815922437cc1ad88c4d991901b6758c024ed2293276002db71165ae5c4d921e31b7df4ae00e4a568224b820c903696af20cc4b5f37c07a817600aa28a4bc080642414245b50103c100000001ba2210000000005485d124d9a00a160e27f8ffc51d5486576ff86668559dec4c54ad371c99047cd7bbae4cea9636354cab72abc23ae4c448dc27484400e69629aa389492e8e70de7c53426795319aca86ca92ead4026f86d9f46c9aab3d6c00659d113cb614a0605424142450101667e91b822bd40a411e45843f40443ddf0527e3986b7d0da053568ad0d21b05b79f6ca242ec663e325809dc86171e9a174c98c3989e21dccb146de844b41d58aaf0759b8470789bcb3bc5fd95384059185f48931c911d8bcee74570611bdf00cf6dbe801121d77274982b1cfbdcea895e6940286fdbd4dd29b9bcd93b2270560feb37d87b74065d9b9d532c2e35bfa7c351d296bbf76886e71d4278be9f660752f370396080642414245b50103ec01000002ba221000000000cae2cf443fde5a9d8ca4cfcb18959fa21423678ca2dc287b41cfa0419e0cd352ce96f4f61cc90c1e319f0819efa6e180c11afe7b7a19cbaa28aa480bb80fbd0a60505cf90fb32785dc1ac4a651b1085ec4280c9eca2d7e6faeaf9b6e9e570b03054241424501010cdd56e98500511dbc8f884334aa0fb5c50c3c9fd13cdcd72a668c40928ede081eab11dd35d93a1a23c8e4cdae8843595392f2407950b2c2c92e8b28c03bad8eb85c8c5fb8de8fd70cfdcd51ab05e3a6ca86fd131c22412527e574e8fc3d8336fadbe801de514a92548a6600d09bb8ab595f4b9ecf2374a6a0f9ccb50723220a007a7e314f0b067810e8b1cdf822de31b3aeee4c299cc2a66669150d145c4c58a06eabfe080642414245b50103e302000003ba22100000000052ff126091e2a096b266066ef30408182b6d1d8059b3192c58c47a034ba1f664713f786922613f2d0130b5a323bbaa2c0dcba59469449056d589394f9b8593012e2790ccfb34eb2868f2638c59174baf4cf1cafd4e67cbdeacb44e128b3245060542414245010110ed1eca9d9387bacb025dc2a706d80b86351c2db50387a17500a3d2bf6586577047715067c274da68590ea4d660ce8ba7f4df38e93a3b7c010b532770ff9c8a5d2744d3b5efc770d0247692f0941f565f7ad21a9393fbd1d2ae1bac510a3da6fedbe8017cf79b9bcc3773514c6d1fa6d15dbed4de665ce6a15ae785f20167dc88fcb0b758147a222d9208fe2e6343405029f3d375b01766900aba235ddf563666c5f09b080642414245b501036c03000004ba221000000000746d779fc66c3405619b2dbe5e8028a14c059c972743995794aa912435bc4c737f97757cc9a39313ff0b4ebfbc594cd3aec8350dba94ec006d1afaf62fdb530d9cb5be0a6339c07e39694669bb9650a0c7385c8f9ea9471f3edaf14841983900054241424501017cba935f870d6935e7751b8e489ae073c88dfecc5f6c3feb43a64794bc9e052368ebca58449c15596a6833e1cdb8ef9498c68f0ef63d8e219f7403d1782dcb829bdcc13a2a8660cc1e857cfc879ae66277425e7d5bff951992dbd2fd5df6249102dce801062bf5fd0df604c185d0561ab07ba7428f8afa711ef811973ec2e3cc3964ba03cd79b7b1a63de837e01afa070499ad82884033108289d18ca577a7043efc0a50080642414245b50103fd01000005ba2210000000005a216e887210e8bb38edaad77c8cb4bd79a93b764a17de8ffe17ed73c5470730ca6754dc9608cfcf7245652c434adc7ccc5765df60be00393d6fd27e38f4ee03ba5664e350da535bf3d3192c9a75ee3bec9606b874c8c8d35b07e52997f959080542414245010152769e123595e9878a9d2c7c933f64dfb38d0b6321b1b6057264d90b1771407636f85ad8d90dfa5743bed7255effb906da62302d7f6de6f49d2087308f2f24806aad5a2b3e514a1a4d1b3e1662c42b132745cd6bc7e7aeeb9306468e9707114906dce8013d16396328030a23d5e2d01845d815525c5e047e7f620550a27c6de6dd825d76f0fa97a20c2d1858b04a6a33d27e952b80708f36b9da56390e50ac4252cac883080642414245b50103a901000006ba2210000000001253fbcb953e0f760b58525d30763719ed9ca81802e7a2ada288044bb5e42a19499b078b132625b0a87f2a1379ad87c2279ac093f0fac00638f9f5c63a01f60a02c3fa4efeea9c5a25f042e156b5ed8c2cdf85a82298664b3b2dcdb70ecfc50f054241424501015ab45595ccc45f5390c75e6097568d950970ff0604c98a7123c500644c49d0307c30d2db860e99ee3992109dcc845ec492bd919ef5720d7892fce47ff9079a847f553c08f8e249c6d72f9cb9469b9d4ff1d11658ae47e3eee163bf56de51ae210adce801440e59b4381eab556b803d68f056f249947154992c2c91b3980f3d1aacf818c2c991d3615d3c0596c4eadadeefc743454656a4daaf26a8ae798ee7ee0fac929e080642414245b501035503000007ba22100000000038241e1c5a0b3cb6a3039d512560b8dbe5406b54c718b6b4e16c061fb893cd02b7bba00d1ee7f26815365cc83f8ae2c6894854036d3b3659e58532694a37cc00f1746f4ca35ceca363a1d2966aa46357c08afbda1d21baeb736ac7431051ab0b05424142450101bad482e41d7df2b34fccd601de5f3f7c63b774f83d951252a2b7b554072bcf42a0b4348d6d34d1b0e22bca7981ad877d5e88dfcc229489d041ba45e62fa0788526f583087adb20070d8b764953085adc05efbc1dbdc6765c91f7f1bea13f86f70edce801a83499f6c3661f303919c1ca9911f5bdd856cc66b45af49316659c09a4f45b2a805fe7018e3e3667dab9a755630440bcdaa29689f9a73508b2496cd0e978c3ef080642414245b50103df00000008ba221000000000bce0c3c323fc619c579b599dbbba000012ad821b9cc9469c60f7f7c66314e25b9768ad277bbba11006bc281ccf02efd9ef78eb5a13175be98fbc51fbbe48340f03b67f0e7825cf953297a18f18928c621a4b17589e6cd2d57f3c82932bd8e30c05424142450101eac40a04ee4bae00ba2a617946659c785401c0f4a86726b5b8ace53ba15c897ee8d194be369abef05e5ea4a2c78593f3c47bb42191a2639b9ef377d6a44d8f8c9af2968f38ef42eadcd3632e96c888725e5ef32dec99fc562e0e77e27e492e3012dce80194f0e8b0af75510d2f991f5c96e1724ef73154546362dd39d8c1198537850c47dbb49182065ca794279f2f55dea0d803e3d32533c9c045888b4883360f985cc9080642414245b50103e901000009ba2210000000001c3c37a0d22f8ab69d1f477fc62659d57cc0e92544967c2b255b65b2919240472ebe0c201bf29d2107539be1c93a9787ff5a3e8a001c02bcebb0e951c6d5e007463dcacf84db2a70cc3757656e4d642184e645abc5ac0fe7a43453f2b741ac0d054241424501012ecb34d48c8978db56063d314808d8dbced009738fa1a19ba0e678d113031131cfbe32d7467594d79f62c883746e4876806bce9b4fb794cac96b16c042530b8f77db51ede7c667c74d5b5bacac547daa0e4504c9183b03c1849c4268feeb02c916dce801a11dafe14cd6b3119b6bf1a3241d931a4aba9dd6f8ee7a0d3f96e24cbd77f09729adc9582b77f399995b791f2ca589c1259a480e19d0d39bc39f1164a562e4b6080642414245b50101a20200000aba22100000000054753832bfd493e44bf26af5f4b66cd7f59a9aabb2f2343901d76a0d05b0e01ea93aeba1f01208e4210dc856a827342b6ee7ed394f09c0a0a9bf31b0c33c8c0137ee444fa1916e37074f39142d459e395748d9391ded61119a235287b8b2950705424142450101de7c6993bc3e90602a0b86b388b4a4db5e2a1061b93c6c15b0447c9f3754fa35c8d9f1b01c2725f7049a837cf4da13599abc715ca0e0899f4b4e13645ba25d8f517bf315d9f446e8f383afb767e20b8334653ec0b512fcd292006653e8373ca71adce801dc4c8dd31e201b355f10a3022876c934f9dd9fbd17c17694821087f152af2e838ff948e5f8681e77526db00c5a0eb7e8a7d1b87fe63d0002b7a77737ba9b8ab5080642414245b50103be0000000bba221000000000be2c3afb910bf223d5d6faa58473c38eb6086c03ce20d058ce05e3beb59b56667596eef7bdc2e7b1b22adcf20f2e86c51e926d21f0db46c3d9dd27090223aa085fed35d6c7d29b89c9d544c2d40dc1fb0c5284555c0e3319a1ab06909a445f0705424142450101be413cdd7580e1eee0009399d07920666afa5de54b403f85f96513e29f2c5436af88177c7c0950fdf5d9f7ad5d739ea07102ef879d304d63bc993998010ece81536240f5c776e18c234061a475d538ae568dc8aae0658f59132ee4ad6d8dee371edce801b88855e348c82cc2a592c25423ebb2cef21c5aa1a19aada796acff57ca570bbb4451189b7d6d5c78628189545c8f92e82854c7b69632458dd6e7251eb6ead7c0080642414245b501013b0100000cba2210000000001acc96bd5f1430e6de3ab25ea0c7dd3b2f596fee6b7317b6175fd373e3d0286055b26490fb48d90444ae1a4dcc90321bb6dd27d2ed8f4e67d1d466d9b9651f0cd65b145b03199132bd753f7a080ffcd2496d4ee064aab1ba3efd59058f089b050542414245010124c59d8524198817f376aac3a7b7dc5619e489ae0c70f6ff647539cc9c6e6d52a49760d6220da6ec38ed49686c47ef825ba63cfe9cd17cff25318742e58edb8254edf30574a28d245d704c9029d38e9ef3adf8a861fb96aedd9d024252e275af22dce8016e7430e0ba8c5c0e357fbaf2b87b6e35621af1ccc6920b4aa936bd44b1a4a3456d98e57796bb87532e26833c4c23de3c0a455a2ae21af94922b40baa7e3257bc080642414245b50101b00100000dba221000000000b48195de2918a4a6ff4d1cc84efacd3fb1656a7c5f64a99d170fdd89e5d9ab0338ab8ec4562bfb7fce6919625472bd96ed5cabf6e0806b09149000c1d25a9f06bd14e4a086ff4f8cd2ce647c7bb4d88d5872bd85adbba157e0a0f2b1adf39b07054241424501017670542277c9867148d6a053d9c1622277f0b9d151a0baa80a82c381a14b2d0387c46ab8dc348ed1288f7fc87061fa358c8a74f96a1b747c58157391dbdcf38f069673b857d59182eef181cd8a372cade4227e5f645fefefd8bc45a995ec3a1226dce80187bb851403d681fbed54789adb2108e7232a6f67881402b5d8fd2eea8fb34cfd4cca4216261741e2cbb2f6fbfe12f6738b87ee4a546085541317a333c6b03a8a080642414245b50103480000000eba221000000000021cb3f381371a4d40d4f7416f9d0ac0a57b159faf284331919b3d7638385b40b7b6369718cece7a0e3ee034e76a842a88ddc57a976bf306b0027bc88220510340aead701d8654af813605b6def17ce35d9e827d1b204caf267bece572c5790e0542414245010184dc88eb8d37a69067403d31bf8d0cd429c0315c88ad77c5ac7fc198604eb15b84ca908afe52b2a4e248b18e83527c731e15ad3a80781f50551ec02f6caec38b2c9a91903058107e43f3e5ce2b062b6828928e12f0a9f8fd93e5e2e07137c3482adce8010ce1145c65a07ecdfb97dc59a3bf7e3beefe78c8f18d0f9f514f0f7483189eff142bb0c51fe34723685d66f25cbbb9566f14b8d971dc7a459c19739eb72151cd080642414245b50103570300000fba2210000000009a0a78ecf076986347412ae52bfb5827204ac20943ea0aea9fd619af803a9843b3f62251c3a46b05796c8e0819fe1a176eda31674dc67bb749f6de98443897086731c29527e76d9e0cafda46e4a6abf0bb2462b056106d9085416503c10bba0a054241424501017c1cc976379bd2f5cc4333032e7f1aedc7c62da40d5697bb66d69c86a328613f379cfb39bc00c145416134b932ab9a67c8edd3e284ec0fb68d442c1ff176d182921f76836697ac88f7a7f31d907b70e6d52e2c3707775ec950d16e69292274412edce801495a148bb4a308914a3cf9814ad953c392f13da46ac3a674fc4f091098ea6d0af687a90c955ba8e80552a1dd61622cec7e3fe15711da243fb75fa1a5cbe1b84e080642414245b501015802000010ba22100000000014650effdf767dbed4e89950e9cc3e7869de93be21d854a548cbc38f1c1fe64654e7fbbf6c83c574e4baaa73f3b693258ca343619c49daf3ebfd19a8eb93fa009e9d7e33009545b429542240fe9bc46f20467cc9ca4b1747d4755fe0f1ad660e054241424501012288f53f3ed4b9d3557998310fcb81f6d0a66cdde8620896561028ab77421143da60c7d6524dc6b7f49625f211450016a03629a9882e5b61154a56473d2fde86b1123e733e02ab05f876e5f94084b0a86d86c80ed3c5a77a352475ca7d66912e32dce801b35ae85715ee8a612897b540333e49092c4048938574a8f73198e7c224987c60c2c838d1d470475b593e52c8bb2cfed33cde4e9e0545b318754690de789116a0080642414245b501019d00000011ba221000000000f0e2a857a37ab9c861a7dc6da989154b9e365cd518adb849a52120411cde4c369503b29bc52f5c7c0bc07e564b89b4aef6e042fbf4ac51b0615e4ec046a2f10d82a453e5b4bc040aa47ed7876564670f4c91bff71ffe9de571213240675d960805424142450101a8dfef370430be6caf6bc1f6592688c05946706d157d99416d644c7e80e2ea4a60f026ba9dfdd9afe90931376cd3163a38025e5d8c7f57c7d3e764d40e109c8927ba72d3a760a20afabfae55f0c24d38083aebd6ac2d809a1285fed8580104b636dce801e6adc0422022036ecca7a662abf6efedb0953fe91515c869c66fa827774e2d5b5ed63fdd0248170db82c5e44b529e558d04b2c1d6027e0349f03ff0b8e4a8bb4080642414245b501015401000012ba2210000000004838f2a299672a7a3be5f9f105b00c0723648627c25d8d208d8287d9d0d4260a7bf8b5c02ca9a889c26ad4f2bed42016023026396915bf905d3202ee6f8acb0629a52dad2ca823b8d74129b33e7b77ca6f8159e5a7964053fcd658918398b30205424142450101b0b38f27eed0515c13ee356d69a17675a065e02cda47f4f4bdf09d2bdffc8519b6198bff7a06063971833067de0548d88d8b09702729eee42d53966c7d49598dd9dc9ce8034ef54964be29e68f31be8fdf0459fa15659fecd07c8a829183a7203adce8014451d73cf74db58d1c367223b3ed65287d4b65d566520e7338c7aa91ba6c7e182cb10ed5f41c1cf6e8746cacd66671363c98d92cf0969a50b3483394f87213b7080642414245b501011903000013ba2210000000003c3245ec4b396b5d95b059f586f974310c3bd0563906ad1b805b1a761836c226ff15b1d1511e564ef57a672a4d66e3654f01066b2b35f6eb75f6f3cd0fdf680858c88144c11fb1940910782bd7e0f5b2916b831c6c1f4caea146130386a5630805424142450101ba36026eeeab9cf999c343a18c5347eb5e1cb764320bf9daf0e9477ca7a54927b6d16743250d9aa81aeb1ebd409b039db276a218e0052dbe963acd0101695f86935d08150a41b9d414e265ad3789859d13de5697b27d04a6cbb43028d845f6673edce80169cd0ab03b1108b55b48d50daf48895ccab50be095743a6d50dfed83920c1842b265d08f398c53725b7af493da230c7a32fb290a4f6dba7bbbc9978e6be20a84080642414245b501037003000014ba221000000000643842da483d8d5e7ff7e52446c77c18cf4a3b00bef9f37c732460323df6510f1816fa572dfabedae5dd7486a25118117a259734ac5e8b3e8b2c8d0a40a4300a550cdab213c9157dd52768241676f457881156547b8bcf8b67eaa63bb49aac0205424142450101bab9db73f6d6e09c05228d3fbe739ef8d1893402efa433f63ee20c754a6ea63c0be6c0b2ea28addfc81affa3a634628205c0c2c856d1dde63e3d34a73fea7982077e41fa60bb5b0cc98560bcfb64afb7850240742dd693da853b022b44d9b1be42dce8012ee3c370bf436c99e03f803636df8465f5657aacb996888534e11bcc02cb7724ff65c809ee409d0d747f550c50945afa91fc466e40b2734e54d900186127b435080642414245b50103b901000015ba2210000000000aef75ec485d52dc3fed7083ad532f41872820646ce1849528139cdef680356e55ea5164198819997b58ccec5d64ec508c69561e15f50278932a3bc89a64820619222a48759f30a206d0437abd4d1eaa62c7d1743740fee46cd84fed9eb7a207054241424501019834d7ec8f4cd801d71fd7502ee4b9e832525aa3dd60a8fda16c4cdb5088ff0821143f58d5a13cbd1be7d5b32e39a2bed26c6be9b793b4c6e70b1781393339837b4f715812a105f4909ecf088fa804485f47d05a086bf3d99906897aca2d83c746dce8017f4945ecc33112c24af2a635b1d0e1fec21dc8ac600f2ba5ee3bc95fe20e78270935c101a5331b46cbd8bd776d9f7a8a9dae561940e6940a0c4a0a82eb212cf2080642414245b501019901000016ba22100000000060ec5ad9922bbb733db6fed6fa353e2b7ecc6ab0f4a060cd5d682b95613b8d3fe0e8795be40c6b204e4334bae5b66e5a974703911ac95a7b2a658febb8c74a07ce9170a28b204c8e452f33c090f173f7b6123c63e49594fe12a08630ccf47c0d054241424501013480b57c69af151e009ffd71527b7e3f8cde57bb3ec3a3401d49375bc12c9e4166df279757d0a504324d02ce523e18c3c4abd9157e394e854c7598c319c7418b3478cf4da0e255bd231722714639f6981fd3938246572739268a53d4ee4a538a4adce801701b837662f9e55bb5cfcb03dfa59631bfd65840a397dd8fc8d852c9da8fe835aabbd674a580b77104abf11d3de40d8b0f9f6e62a83a94fb8cba2aaffc3b545a080642414245b501032101000017ba2210000000002a897893855019cca81d3c35c5c4f09e7cdf1d1718340248cf6cbf5025fb3f5a72e4edfd0f76faf1faf7beeb68d23c6e29583f1c1271f722a1d171e06ff8740dd79d6387ddb59801b6484d8d1e1d5766dc9e785944adb2d466b8feaa81a87606054241424501010c06e392289289ddeafddc9bc4df993b81d11796fce824482f7e9374b5bbb534d1298ecedcaf9a5bdd3a3425187fb1b8ce0d22253154cc4dbb7c53eb3d53da8780e0c9d8c2b4e38c395da004d899365d2e7c6c7d509ebc54856fb013bcefb8504edce801176295ce8a78a3ad9028356830724dd0102b36f0d7dda770940789cc22e4cdb6de997cf0dab340be2ff88a21133e12a22840df2d2d4b64f36e287c0fef36b49a080642414245b501034801000018ba221000000000389338e750594ea5666a8a33c0dfdeabf09c853f0f45ea11d0c6b1f5d556487764493c006ff4c0ba356abb7a266891acf40cf06d2aa0edeb74851b9187d9400d6f71059588110e30821f4030359392b105031c96a4777b3a0914a80e0d476802054241424501015cd7cc300a1c17d1967786cb02a00c323dbb494cd591832cd6a86a2723286d0f67e11d7ae80a36ec7a43a0cf145e1b61af208b3a2551c6ed1be6c939dd9138862b84a3d79317a86281c5e70b79075535d837b7f197b4b1bdcc53d2e32596097f52dce801e8cd74d0d77e235d068395f3059ccf749b876fa7a93807306a32aa1ef82ebe1c753e2fc79ed15af1801a46a68f38d7a508191292bfba3a4a13cadd19a852d716080642414245b50103a701000019ba22100000000018585b1fdcec726d7a8e4046af64bb6492832c06d914d33fdf1fb63d6ee88357591600e1dee5aff761357f26f0e5a9d36fc78bb74cf84024775c95066857ae054e33e99993aa3d89a392dc567e441b9e71b5145c338dd57e906362e1d62e3608054241424501015820ad2fb9a6f4bc64a8679550307cb8760876bfadcdffe5771c4035374d3913c54da0a17b1e51bdde6b1724e583544becd35c9b6ecfeba77de3e3243fd8c780825fe6e72c3587bc2f7e93e58accc7d03f4c4d24bfb45d406008a726dc25778a56dce8014a3b4cd6a1b0bfffac3f768ae3fe9bfc166c41fa12dc154512f5e4e6f0f53e7df0c7fe5b83366d69e5805282587b107998f3e98ad8769073e9dc4f5f8ff7dddc080642414245b50103450000001aba22100000000080336174794cfd989f72a3e27e7e51e949d82a143eaf72c45130e6c30775174859748814c78a6aec88402ef83103452b0e99dbea5e6962559e6fd4162add600055b7665710c89cf0922681d34f6c8088d6cd81ed635ad26ba45ea10406d5ad0f0542414245010180eea69ad4203da794fa5ccb1f2f26b40a398ffd73156563eaf042c59b2e862be69506398cb169b5a384d397cabf25d85b7bd58760d7588c66b08f0d75890f8640559c0dca3f83fb8455047551f6bb9a317a49d481017286ed4f26e4d1a72b205adce80160f0ba0f63c3f24af02d09d28b81076009b7fedd74797e22b5fb79d37dd0f9b7d9b6e49bb690151be02f55ebb1da58095ad1b2c3030d02e693563c1f5937a9c5080642414245b50103c70100001bba2210000000007ce8650b7801460803829bd2c4336f000ce4dac1daae180a68169fe6aba4e23a07cb156b69b46d87ee1cfdbb6d01feb2c7f0bcee9d31a4264fd70c91c0ce6f085ff9f4f4325bcf0aa9a28ac1c16cfcc709bf173c6ebc7342a869b2220dcdce0505424142450101e00f618b8ad84e8f1f3691f73531ae7f7c4253efa6abb21b2ed1fc0dc1da1865c2c227cfecf5532db07d4579d3244d8529b4c5e273e4016f71339ad705e72988388ca4f403c835af7386640b6aede79ee19a35b55e30bea0841de1dbbe8830b75edce801758ca1ecabe53884bdcb325073f2914b1ffa8591e9ef389a24ba476c5d9c7ab50f673dfaae66fa4d7c63f57f93a29a08f88d04dbb3b35db003cc0588ea6eb9fe080642414245b50103870200001cba2210000000004255230e045827c01093ca0b997f39a5886aba03a784300ea4a1904b54841411b53bfccb243875b25aab94a467dae65b60d1501c1e10d58a7996a46a679a7c087f9d4d8e7039e704ee901dd80540d4755d37e7fddf80a977096e4ea3672e900205424142450101c0f60c7b0cebd7386d498f1055fedb89b0d676ec8f60dc7d599fb0a150e4692601a7a6b0383fcb155c7ce5f21152c851d6dc6142e713108d6c4bd752c6c91987677cf927fe000565db07eb924cc39609696bab38de01e7bac0e66453d55a34a862dce801d176fb5661882b30a41ade213a4982ce68873656ea3d128e1f5a7387239ef6e80286bc4e16a65fb93669459f85c8f442abceba3be317b4bdffbefd005684c2b6080642414245b50103a50200001dba221000000000a64a8ff416f208d2bb9fd7f6f758a34e9522b132686679c59209d25feaa22a6ca1421d5606d4d653b0f4e0ab1d049525089d5c315e2e809fb43c43134d2c4e0ef6cdad95fc19d45f1716d79d2692a74d8d397618ce9defbca75456121bedb50e0542414245010162947362decd74124e4a1c8c8c1868b48d926a87803302c861f5e29648fb77013985c4cab93557bbc32d725da3e174a84583afe8a6a8698d588c526060163a8a27dccc76bb0fe8a9f18d9d1685b0eb42c8990eb121be1d237c6a6b72dc10a69966dce8019a918aecc8f7f7bfdfeab6b1a329fca29b8c291e12d68501ea5ac1790350ea755173bacada266eeb81648ba5ca1514adb88e8987c5df345d71de9350e4a59f04080642414245b501039d0000001eba22100000000076288cf9956be8af8414399049d92b715327998868250c7c2945f7ad26e23a3605469fd8604d23232c82fa920c1902a9dfc455f5c44d63dd0ac3e2b433f4b105961cd02343f73c4fdea086ab1fe6cf7c61f3c9c5769a97b884fbf806fc99e003054241424501010e3a257b85ecb1c3097b123f8feeeecc9ad3092857eacf2fc6184e4bb77ee06674eac7ec073218d8452c4fe22ce608e86546d5217550c4e95ed75fd0b2af3b83e6206c7feeadc689223dd62f52326129f7b30836b0ebabad1861ce3fb14540636adce801dd4ce04030602bd78870bfb26995cb7e0fed5852534e10eb4a0ae9dabc77af5f75dc3f5ca19b459f2f8752d9e213f2cf8335e8417422ae3fa304e7fb2b39a865080642414245b50101dd0100001fba221000000000f86aee073438f30092c6fc97549d0424d66761a729d3e34fd679ca04ecbe4b0ed962d2d393e46f7dd8150623e471144f06a36768fc74323793d19de010f68a01d641de91d498c6325c99d79ec05fbbd8b1a5978e233a494be76a8fc7cc0a45050542414245010172229ea029599b817b32696ff2553757d045e0f8be32ce444fb9f9d31a0d93044892db53e60d8f182c09373c5dff1ddc4ad736939ebebd40480e5d0a7ec1da8497a84ebe79901040c2cc66f8ea5f129e1d83dbb0b81242c8be23366ea7752ff56edce80154356df370eb1d027574bdd065f98c87625e312541792188cf8649d8e6c52bf3ac649488c3e5d09ebb74238de1a2c6e6a6955e924ca36f4b598cac7a3baafb66080642414245b501013700000020ba2210000000005ebf889a5d18472e448fe9a0886162334534cbe88f2d3a1e850789c0bff3d50fd43dc334cadf31cf65042c5a361f62b2b85d8f335aea8bf7cea1fc5f2ba2f40a8d3cf40463c71c592346008858d140167fc7f7dfd42aef95de5bc50cc928a00e05424142450101cecd52c38c0dbb6cb3c0395f8b30bfc34e6f8d06307e032fd5716da2a3ad394db3bb5244d9ef94c7455b5498f712615fe2d2b478e72802e7ce4f838a019ae48e28577f9e13ff5664c6799c5ff74b2f29a773404bdf4578f25302fc1de200cc9372dce801a1963361c1f021b289e35523645676a4243376b5bf75441ba0b202a4fae85c20171a785abce36e6cb15e8ab0ddbc6575140693a0e8fdcdc516223adfab236049080642414245b50101cc00000021ba2210000000003410b3c491b37fde406498dbf5bd074173c0a70dff902eaf870130b41d769128843570e86a49cbec13e2124acd58a125946303f75e5e54e01db008a3b990d20edd57531a7f258a2310d43a4a7ff44f084e7eae21499d76af4f71f6025c059e0705424142450101c6def4965f3b495465153e9631a888aab11d004d601d70cac08187dc1aa32850a15a7208b8dc5bcfcdb74332595bf1177a0498429331c95fc2ba96a98ccdfe87b4b21e38c38caff9906bb04dabf6d7868f3a280ee43abcdc8760c10e7ebde70a76dce80198faa03094346fc53debd536ae778ab1f561c8b63cd97dcf256e79f34d9334c71ce0e34b2e712ecbdf56c4f0e7faff10f7b51a6c98d4126ae169bfcda36aab64080642414245b501035d01000022ba2210000000008811e36c5b4a4bc22aa9c681462eacfb5f9e6b61cb6554ae7068a5a2a658fc457538b48746baeefd55083b100ae867344279dab925d594f3861406d0e973a902846d45debcd1cc1ebee4741296c618272dcfbe9300bc83baa0b087247edbf104054241424501011e2bf7e2e8f025a2896391de7db877179aef89be37136803a79cad4d7401a825c5d384ae51b948f8e18972d95a0aeb8a4b928fe4824300a921e895d1457a4089a3eadfdfb9ccbfca529e905ba817cef744d791aedb0a43a18f7250b9c13bc44d7adce8018fc0ddf0d1f8740901556a124f7199d9053cbc4d9bc6bb52032cffe58d01afac6e53f59bc4f67b7836ed57c413735d560d3548b1363c95e247ca712f57c59e9d080642414245b501016003000023ba22100000000018b9022427f5453c944ce632ee4fbe805aba17e48193d5eee3d0e993bcdc8434a6819e553dd4ad874f39ab16fe6ccd8ba402e6c9fe3f8130131846e35b83330554460c04d092c00430a88e4b15d0e8bf25ccb063f25191d058281acf2ce2b70d054241424501014cb6dc40586224119b67b18cc960eda75d6f188d567c4d8cdddca829be8b535f7d9a501c347454f8f684ba56b94acfc69f7f92dacb88d9c03aef6cef48dcde842528e304a0c38cffb25fedbc9eedf3bd709b8c16aad80120948484a0eb34d13c7edce801712ab7d002cbd42264e77888cd326ed34dc9b112f137f069cd7c97bcc87e0fd3df6d08bad7f2bb29289b4f9c305908837c28c0ebb8f2996ada91edcfd6f11f94080642414245b501017b00000024ba2210000000007817bda57689306d642dc24c3ca21606efdcb68641cb10bd216d0f894fde80609ab91bbabdc837a757158d12d3fd9e55a38630fd0bf8d6a8264ff72a511bb3088af42cd351681d8e7d7fb89b701214e3cad4d40141134c0116a9efd1517b3b0105424142450101068aece2db9abf4bd47f6df2f054a6af28845168998496184ce3cf36ec7cc2250e585812dfbb9302fee4527ab72aa1bdead203777b184d48d70ed3dca10f738be76a6095519e7987de1a20ed8c6a9efeec9fef8a3aadbd85fd039e5b55d9719182dce80105d4329e6217c9a27f07229aa43d8d6105daa43436140e7c043779e62b530dbe2b9767302b2b7720c83fd63d7198613cfe9e09f9e602cc33434817e149492e54080642414245b50103fe00000025ba2210000000001478839d631f5731e5c1f09fd1dea2aefc8f5d5b0c6d3a6746b5cd079d94331e7da5328108c8a513e1238c7f2c6fef63153cccbd5c46f18f3158c7c182aac103268cc6bb000311d74a14099d25c7293e93bbc04a8d9e7e9c567fc9acd3e77409054241424501010c6a9954f99b2cae1f91adccf5ba284ddcd3f6876566d41abb0bd46ab863e7728a0f8acc4590b140f7fa388f81b359ad13c0220bbc5225072ea9bbea84872c871b34e80a6966a5a3c76e1d1fa08b49151fae3fc429d2bce4c6282e67fbffe8af86dce801d4dde10d8ec24a59e71897510a21267a4cf0b106ae139620ced55b6057decc2a44fd8b408cb448f8f9d139c7d59c5402623be4873069da3b014840d22f8785b9080642414245b50103e401000026ba221000000000dc53841ea4289427e74b65994de64907e300810c5e3d4a8dba7ba5118f10575ae61aa748bd0796b1334e98346fbd496d5b7ef696a9b780c1cec48f07207230040994678b302ad5ce68c7230ba9122d5c567e88e871224d2b241ec30ef3c4f408054241424501017089d395c75a902c7408db4cb4a8650ce0fedf76da387c753cba1b741e70fb7aa16f18776d641c475d4427bb0b2ff6a447965d4257d8facd17d39bcb990aac898138c60b601f77a1a140db57108a983b5a6784a56ecab41563c58c65399906bf8adce8013dfbd7af99106f1b44e7e7c3d2f9f245591718b7aea795924a25f07681f6101e1221064471d4497a6a2c5eb2427ade6743c08eafddf550bc49aa0a391123b4b4080642414245b501036900000027ba22100000000014109d17fc8b544721e0f59bf6ef8931c713ea53088e5408e4bb42194d3f703a18539d5d0308b1756f4d6736c499248f9b38cd96890654042255502a50a56003a991b26e4fa5e6d2ec8fa0b7aae7b7ff8c1b147144e4dcab685876353cafdf020542414245010176c85fb3ce1e7076d08b7e8b176b513af0628a013910494c198a2dbeae6f7974b858c8ba7414122ef7b3c342e7c87ace52f5770165c207f63495930a1543998b4560bc3d6b3aad7d0ce699ce7edebc25beb3bea435bc317026d478b7eb95ca488edce801d7fbb85d9b1a2aa0f54696d58532b1b9bbb2fdd6b2f0e7af56bc919e34dacb765cd34d76400aeb2ca30c6db08d2e914196d3f357a5cb8f80626fb364a435a3fb080642414245b501014500000028ba221000000000842de4aa6bbc18e34f981dbd1e5f1664190f1ee95d904b96b22f84ebb3539556e4335939964fc2037ca6fb59aea5824ac10cb8e669f432a4847928045b2cb4067109933a81f0f92f151700fce6fce3054ddae051b9277682e51c12be88732c0c05424142450101b4f9a8c5ae1b3daf592ef1610f666ed8103fd46485165b0c16a43dcdad1dbd728fdb4a932328fe402aa621d12d7254c530c34672fcdd2e84967de04bb5690f80ba00b86a1ec825d6184e739bee7b90344db553080cae28a28cc7ce3b032a9cad92dce80158b88f7875233bbbd014cc0898ef1916e33d6b6f2b47774d4567278a5301153eca9e51e4ca4d3ab69d6efa98619793a021180302b1b0aa2dbfceff7ce50f59da080642414245b501038801000029ba2210000000002a18768345554e5fb70a048cfebbf2b24dcba34e117be79d104c54a3bf1bc609aae753577e0ca91fa28eb437391148432558154aabbd9a9e25d077bdda3f030eb3ddb4780358ec40e680f1b1bdddc912e70c0679797b2603ae7efb43d8127c09054241424501018af63f94f774bb1fb255bad9c7c541775bbdfa2592aa7a02c3e65ccc02a1af69878fe5fba6c9e21b62291a18642af70f185a005255d35a7f5f3cd4b466bbcd87251afd41160d2641070f50c93d375b553d5464072e7f1899fee89ce217f1491296dce80108e948873998c3daa65e74d7007f764b8d8a138f195d2bfcfe067d75d67a705bdfc25389ea07c1cf771069b41c3c0abe9b68cbc8f8bcf24a26beb8aabdc7ed9c080642414245b50103a80200002aba2210000000008cbbe3124642a42aafe22b6001d4caad4e26fb9b77235df06ec496a9e228b61ee0b039a8728c3a61c742a6739f05e31c173a70de3122a0ebd629fab56b88c8029192b63d2c4bcd140bafd69c653fd9ae997623f5e3770c8b2e7872d68027fa0905424142450101f4ef13026100ae98693507a6d661936b2824c38eedf15c29341cd51f60479b77a74b3ce2b9f16f1e9b440bb9087f774a4ce747389ad426f7228816571c075882e9f5a63ff54a441cbfbae63c8abb3258703f553a89beeaa5559557b2c97879269adce801073ef10573e3781d83fad5419e38ba16498dcc1c58a6b1cfd447a19130ad2c5c39973e30ab63c9e1ffe68b3ae0f7c03724381afa08f93085bc7c7f91bf756adf080642414245b501033c0100002bba221000000000b6bd4e0bcbf40c43e524f927bb57e3267545f916f277a872fac873c3f2acbe5a0e3d44aecac0ab3129478ffd20caac92d241b01c9d411fdcc8565b6db0eb220a31b1e2f93be2c8a4b696db23526462a91b18b770dce7c8a381ad59e16ec8d40405424142450101eaf0a969f70cc4ec4a7d6474b75d8674200a2cbc2a8667eb660ad4ddb2da852f10d54b44a7363df5f5d2fa5292fdc11d5fd726a58de814ea48b1d3dbff72a980f0a484a6a04acd680e761931d7686e04d85024537ef03a242da98620c4279ffc9edce801a3798d737c0dbb687ff957693dea54d35fe4a839b116226a4feefd83853cd10062ff71dfd779920223f4a9957b51c313d3b90964baa4e1c10722ca108f49f24f080642414245b50103d50100002cba221000000000b6c976265445ca54da78f4d76ded01ce90e77f399f156d1f5306cf33fb177b28ad78009529c76925b4f6b45b23de7432824ea0ce1be896ea2bcc3c9678b63c0036c234c9ac9fdb34a901b9b9a84c68a372ebcd1da14e90006e4a44c0de47630705424142450101bc6b5a07c7ba98506a4bed463fd6a0bff2c183613b1680af67876f28f3673c6cc7c297c1d7bb236e538abc533eaa0f847b9834dfbbe1f43b894eba22f0987887c4d4769cc9fc6fd062ca3504cbfc613481230f7af2ecd81b8528dacaa64e96aaa2dce801938ed3d7655e7cb10942ac114451cf4b1aff0727f292d56b5c3c79225bdfca42ab520ca48101dbd4970ffd6b6923773c09961e4aac14cb59cf9a36e67391dbc2080642414245b50101390300002dba2210000000000499c6af4b255a554fa03ee174ff694a60f4e351f6ac00defd63ba01dd097c29c6795910b50f823f1c8ba4cb41ee48a3f97ab162ea802e3ee675b2004ece3e07855874a1a9f8086103ef7a340c739c536e52cadbb43d1f4c474b3bb0394f930105424142450101b863b4f82b4fc3efba37fa8becce456307b8e4ecc6a98678c8f9993cc038d821a26211f9d041db9972f47e52c84689c663a2871781857da29bc9aa42bd26d288e9beebe25cd9efc18f18457c63df5360aa62ac9f7b6d556f7851caa81a5f3ce4a6dce80103698e44399e99f8d8b6a727599646b6af1ff6e62527366ec2661bf85949fba4efc05cd7ddd22b5a5229bb4a4823c9bbf838cb307b26f7b4213033642d4be1ee080642414245b50103090300002eba221000000000c8a68e749341ec39b7dfd6a94d6cfd2bff68135b8a1dbf5fc9f843520b5ca374e41eb10bfa9d6f9459186ec1298552b92d5f9901a6af311241c5259335193e0567098451afd593b5b8ffa62156b05d451f6a1f923001ec6598c1311d39d0cf0b05424142450101ba902e31875efd55236a0cf43056e2be7d02ad87d3ff4112c52515b210a9d91895da63fc16a5ce3e3563e24bb4fc9d6f75809ac1928e7eec2f2c5b6a06c0bb8fecad5ba91192129cdc4955b9cb5389d516b4e2d337e509df4dd95cdfdb4b1fa6aadce801e3c353f00d571dcbf240f16c0ff86ff17f623ade5bebe37a0583b3332e57b5dbb5eb2877b12659933c3bbd0ec43f8d054241e172679bb2fcb538cfda0bc327e2080642414245b50103150000002fba221000000000c8ad4ccc4a7b1af7202b4744e8e3d130db714506ee1e795fdb945676345cb32fb7bf6fe6acaa781737985d56abd2ed511caec08165dfb43bc0711c76b06271057becefa220739ad8a100580dc9d08f541d366c880a1666b9364a24ea89721b05054241424501019cce2fc93254179ae4aa4ef38535bfd50207f4e50b3b4b072c987c9e3b17b317c2c1a8e0ad82e1f47f67e73e6e2d9dc0bb95e5e9f2c87e34f13b9c9075ebe886fb73e583667be684dc03128b5e187dda562d9a41cb28809892d9e3274ded6ba1aedce801af5cc9d49f4bd58b2389db90e43c30a046a94a147060f4023a7b21ba5d8fed1908c785837f0435a180efa07faec41a1fd1c11b16c5b8f4a1065b31dba9209e7c080642414245b50103a600000030ba22100000000042c07f1034850de41699c0e457c62a5c34f3123c7f8597ec58c2b20bcce1bc50abe017a55ff99a8501d7d12eb9bfe6670fe236c83e684da6a7ac0f5b4f6a1306fc1b651cf0ad6187ad6600c233f442fcd2cb982c4808f5931c80c4ec67b13e0205424142450101b0bb3191ef4098e1508422573ee24294c5c919a2f2a82cf927b0e98092c4b158d54beefe099aaa0f641e06999b2d38fbb065f8b2d89e944c4ce4dacf9c5bc88674fe6f8c688f26878a4b357d77b9f8177f34513133037349989fb29e9f37394ab2dce801f5d89bc3b169c8474d6b2711c94f544488d653f93805881e3c6c588726f3cf1e1f4001048249144295255b456ff142bbd9db690b2184206b7c8d91203a45954e080642414245b501031503000031ba221000000000ccf269dd62440510aa64c0516db0e4292b986b4bbab0e40ee63b0d1cebc17c44eb43ec8e457135548ee201e742e07300bc4a1e582bae9ec9d52bb2c905adde02d34cfa615d6bedbe0005c1a63ec1aa66e049b52c887381add3c063fb483efa0005424142450101b6052267cdde6e67cc84017307675f1bce61adabb1ad002167bb1f35808b4c27d0695d9aa39cbfc6a9a61a2312717f9ace02f83ec39725d972e87a864ea9be8ec1e29724a3b3709d5fd941ffe891c7f8a17d653acead996ff70c0fe69ae28837b6dce801fa866c19a5c61dac7ecb7af7034d2aa67481818e8540c2bc470dab972d9d3dced8cd1d2771977f7471f61f3fa3261ebd4f9a713200ce07dfde186a18df2d8c20080642414245b50103e400000032ba2210000000004a65bad5fe814d9735b8c3cb7592c85dc2eaa8298e3dd29a5c962a9b1fba0a2e86d24007849c254b9c4de40f9ea4e8ddf8f3703799a54d6d9cbb2d90d91a130f04167dd3275a66b21b0e6dff4cbf8f3adb3a258b4f12c826ad8af833c25dbd0405424142450101be6badf91689258c6bb682230db21abc2f31bb5eaf65b14571b8ddfbddb3c061cb17bd3ec5da4b575161a8cd0941344a38932202e6d734e8e52a68ef24de8784eb26689eefaba0312da4e6220b850564fa52ce84fab8d3793fc50d1d13036ffdbadce801e41aded06080ca4bdd29b22b80187ad849bf737ed741a91fd2cf635b138ab53f99e026f74de14614e3815a2d9bfdae06402a0fa14768a15c1523628ef28e630b080642414245b501010602000033ba221000000000c248308860f16541aa9b8b41312e606152a28f4189631a24d182687efc218c1b16a397396571f6d3341c6a09cf4acc6ebe247104381648bbea21b1cc0cc0ff030a5ee82f091b794f0a8a9cf05f02d78e89dc3b019171047fec77435c83a0970205424142450101ee5849a0dbb6b1762268198b25a7ff410b4a957e811e5d171f10a14ad3c87f4979f1d47ca097661c0b6a043b981188ccc335bb0a01ffd84bc18910b72fdd618197c2b671eef3411fb05e1b8c978281dfa9d3765d7375f9b6a24ab1e5a5c5507abedce80187484b962caadbd09b4a553f43f2d626dbbaa36030333f6a9560630ecad1a150a1ddd61027b1e0421a237e0d8cde9cb5b163c9457c0f8ae75e10f7cb4d08d3cc080642414245b501032b02000034ba221000000000bc3797b583bde81f88cf4a7483e34fe20fc00f32b781f6a4327244480b85bf363a44ea1edd0d4d9aeac1a4c636d715bcd2a9e34194f0040d7c47eeeabe7ab409f3a61d9db35c70551e439939c3e17f8e755726310a5a95e4866e93d61dc0a80a054241424501016283afd8e6365dcc1214976c7ce46c70a14bf91e872ccdaf4d21952d6b69ab5019657a60d39cdabe62853728dd5d106e2c940c88af944e94a6253892a4692c833ee3e79e7ea8a11bd4795f5be5a6e249157f9c8aa1cd72c37061626b0994abeec2dce801ba14a425da4ae14bc5001e35ed3e13294799f34ed7bdb509b4f5b27cf337ad70e8c361212d36799cc8f9f502a2680c437662849b392ee2818408e6e3bacd2d79080642414245b501013900000035ba2210000000002ca77a099faae46f8c6a6cb94cd8d7665b40e8ea924f6a9cc393d0840967920ebb191cb64dabff04a71a28c8d7dfa65c476c0b22cab81de169537442a6c3d509c1cdf03871a32610e85716440b31522a0fa4e7a24fcfbbdd385bd708c9f577090542414245010190a75cdef490833212dea3db39d203ba36585f76311083c85f63b9dacb87da559a0d4ff36fabf66f2be457ecca4fd0a57291fa361ad8088f6190c2a1a859448f645ed10845fc04e3fd2b79676a03122d441460aac206a2b8f0f3412961e997f5c6dce801cf96a3ee7c249f5051eba66d4ce742cfa1f90eec1e2fe53bab9556c0a21e1991aabf506ee8165cb5e8b3623cbbd33ecef099db67941685901442cfef03a583ff080642414245b50103bf02000036ba221000000000221ab19fdade3d3fb8a4a66d9e4533687f4fd66b9725f7c879fb078f22ce3a2b611596b8fe76679f311901a3d2328bb8a452c6662949ae299b1f0aa98f7fd907599e9c1aab531426ca3bf8d6fb15d0b2fd6f49e9d5adcdd1e5e1b18a4140e000054241424501017ccfa0b2eb7c21958a06a0a2d30b0070667060097c2ab83ccc051b1504e78928d2a612a2c9f8ad28f6815e45a3d742d5e74a4ac3a0adbc75e7b21cdf80f673844d87598bae297ed2fc735f65436acce1aa3c5b4b461896646d3fd057e714b06ecadce801edfab073a5520818b1d4d9219036c8386c856440d57908662c23abc39bd6828dad54e412da7da6639fd383b17e4fbd49a93e1572da4ab896dac36be804a3a7ad080642414245b50103eb01000037ba221000000000e6a5d9327f079d6ba7fcacc5995e87e6696245cce8cacf227adec2090a60a1601f1b8bdaf062e4c5286d42f7cab0f1424ada393020bfdd53e8f82c0fc1294c022a963db1a5f99c78903a83840485520507172fb89990a655e32c6cc3ee35aa04054241424501015ef73bc6b763d4db6d2b52498682f73ebbab5e69b655b26030a7e5d5ffc97776ee764f1be20fafe98923aa91b8778f0ff850669b04e9ecb320a02ec49ea65e84526c73c048fd5009a1dc10e09392f1711b8fca7c50a16e74dcce995d076bb236cedce8015827ece4878427999bb420135f9e4312c489a0eac74a73834f35a924be215e5fdb25ee433b883e670f117abe6a698e352fb4559ed6a58358ced83ace8b37035d080642414245b50103ab01000038ba2210000000000ac3a367bab7c1b2a48a33f6ce9b6a28c94ef821d7cb8c5b3372d9b538ebc50ba79fa58f126a0bf15ee991cefceb766c7f7b2241c7564a15cc39d3c5187f100432654430a3d25c11c02b3944802a34420487e61106e26d5eabbaaf9b472d4b0505424142450101ae686ac2f9c3ef8e79ad073091d303ee872820a0fe8647d538ba339ed12dca3648dbd1a1e9a7696e1f756c1782a30b8f26e86f8329e2b551fbfea7d1ce9fcc80694125febf3d86002ccfc12c2fce0635bbf31e321337616e63aa5730fe02d2f3d2dce8012d7920b74dd44781e353c7cf0692298c607663eeed3c827dfc1b39107c4525845cf546078d0b4dd0f33b31559978e1d6cfa879133fc98695f1a9c8e2b340e2ad080642414245b501031c02000039ba22100000000014e31b4ac3c321c026f930e176a0f5cb0aada52920593b1d6277660f82e99c5e72766d9eb3163af9e5ad463c15b94a154a63ffe9b0a2a8c83af10fb4becb4300c2c135955b8803e152e64849a069a86db15f62492fcf96e25329218fb2f24d02054241424501018cba1d5d012f4bf572cc577467532241d8e78cfd02d737341626a388e9dc0040e1fdbfc79f9c34490be65a8c382e099f5ca57380882bd2fe894d3c2a74741d8a67a575d46d13e4ffba4f8e9a58811d13ee9a16084bd989160d946557c6600f25d6dce8010fc39549cd225f7d6aedb15d38b7dcacba4edea76e3a7543df2ac44ce62f443abcbbf87499426484c29c7b4fc1029ecfe00cd959d1e618b46dc881347c5f1ca1080642414245b501030b0200003aba221000000000faffad095a27ec1ed05e9a63dfee2b005376a8f243a6f97c5fe0c191fe451e44c762aa2fa42b8cc33fa034d0a0f66b1778a1d17163201fe9f0b216c2ef28350ac1da04cfe9b46045f4340f9765828a01adf7cd69b74097aea350f45d93acb50605424142450101328dc17c37c0f17aa3b5774e119d965c989a29832f02d80e8c92dda95f6a66316211ef90c322d170c1d888fdbc0e47f59d405123b210b73ea8cc3a3dda73e5801891b4bfee4b26ce3b20245d1019955d5a71f0c2007370826029af63fe41bf71dadce801b5eddba59cd7d0cef6cecf2ca1dad32386427544cc2bc2dc7bc8f3ce6cd7076a367ccb457615d900a996f0e040e2646a99307e095b50386db512b8fbee7d9441080642414245b50103480200003bba221000000000f6308f18c62641a2b2d93ff37a7d9dd4f4d518b2b956d3b4a698850a2719b930db226862a1e4ba3feb2ca9d18598097a58d0b9f58ea60bbd0b1a6c478e58eb00d73c5635d3ad54fdbd12cc2452f12f9296c8b2e462bd0bcac917c79382bd9b0b054241424501011c39328e2cf6ad74a4ca73de6b8cf4f54516b37df71bbbc7e04a017cf1138c13a2371db9646f7f344ec2c5b7f064943a00bc20a4e014483d1e60b2faace9308610c533b0fd85b7aa74fe7fd19be7af72d48270f4ea2e3fe6cebe7527756e4fe9dedce801b2162e3fa101997922477f2e46a604099d8f9128f7c04553115095bacc08648c7b68d78aff11cbc839be1533f5f9220983f67a6330fdd4029681514348f0efb6080642414245b50103ce0200003cba22100000000058e660f769d24fe7839c4639bb5005890d8f5cd7a17ca9d7f557d08028e8141923ce7a71e22465144a786f18e873f1a107b1d0d4ae25e69ff35634ad8eccc208398f67b06bb3a5c79fd5183ea30a8ec428fa2fa002a481af7d98d92f54a1c40205424142450101f6b3a7bb420a75dd8d4dcc300b91d9e630efa9a69d7588746b7107ab9b3d9e23acbb97db5d2c72a3dff882d955d0de479c6a11bfc24e99115a3d66b6cdd83988d8b5283f97e2bacccd950b63265a953cada22537a2b53ce25b8d38131bfd47e1e2dce80187b9791c3953edaffffdcba9324a0d82a3f347ccea9a7cceeaec6dd0ed17dcc8de0ed7989f316954972b574aa86741a1fb960d2b76a55d1ecbb404238f120c27080642414245b501037d0300003dba221000000000024b2fbfb9c6a23d5758c05964be488c3bc2f7058d45b3bb92616f52e5582a4c43f815b5b3f60bfa39d5cf7df349ecc643d0d9ac83fbc39d0de80f7933cf88039ce03a90f3a213976da316b0e47cae9c4e66b35e8f0ecd49c3c690bd08def10f05424142450101349b3f2a3fdc46e922543c05b6513ecce4f317805a74bf7334fda7b3c621f758cabd8a58c70f9b43d50b7f56d8bbf544230eccca27dc9bdc3916deff9a62ec8226bf5c2ddfa77e30de5e2577e25d92660cb5be6a54d63292779a5a7c91ab016ae6dce80165b7c8a113edb061dc3ba437318a6a66820981fa436c91891a844dd092cc28c3cf79a75acfbb31234b65f9cdbecebc4527200d01eead8181a10a10b46a2680aa080642414245b50101080100003eba221000000000267e9c644a373a1e6c67a6f15469d9d428e9d361f5b76d62f8431eecc1dc0d69d10d00d657af103307b169f84dc88d52812ee77590cd316b233cf1f37320490c4893363fabc684061d039c2e7f4c10433a2705b78c906c935471a5a34545bd0d05424142450101ba0f81c62c9706a649b624dd937ebcb4c8c50520cad6d8156613a9947439121b5a58e5e1014ab361e82f3d2a301c193d274323a3b706df5c4729164e7fc4ee81d2f80dc2a702bdb1ac25ace3560a07f820210e7c7ad57b24f7310094f18c52d9eadce8019a7765638a6416d0a8d05bde28e84316cbe94cebc0bac3324568949e1f9c68f034a8420c05d6a0dfd0c90702fa0c1f7d8542fda9c0e700fd3578619522f5d34a080642414245b50101d90100003fba2210000000000c8e8fd6e37185112838f69d2001614f37448e07c7e92ca7cf5ba482e6f0dc716afd6f2c1c665e673599217202329c9f6971c44e7565b680424782e11819f6012868b9d2494f5d14d263e0b0611aac04256ae2314d2a52e703f17c388c53e20f05424142450101b4e61c27831a0c5f66461cff0a1b5ce7250a7844957dc6bae1a55545a62cb53ad097d0d580d21ce0bfbd40e6674f19594005936e8ab927c782256c3ca67a3580c59adf55f0bbc4f5221c883319cb44cd4cee9d44ae580af5a2faf9014f8d1659eedce8011f2cb7164a27807148b6a1680142b4346937b8f3883c9f1f1dae1c2e10f2f7a1243c28d5805e9abd31a385490f8b86c1fffbf4bb7feaffeca8e3b79630bb7625080642414245b50103d001000040ba2210000000007afdef90e1d2edf39a081aa5cfbc4ec34a85f48c21697ce9e74aa33f384f910a660a50f68f0f2d54d97783e27db7b41982397dd88d82a96273a23a587c0211050774a65716fc79839b1cbbd1468a930b3fd0b48565d62680fd8bc1ec874c040505424142450101e8408c71e2026bdfb77de0b182c376477d897086f37aaaaa8868e88f3cd84f3b44ff344564be5da523937e8d281780be4dd0d1242963d715bebb8a21d9282b8f8ba624a7f205c4f7c59cdf8140cc64c53e605252e1357638ed110fdef6058d5ff2dce8010b5b7eac5653eeef0fbfe3086c33ff36b59a13299c4edc6863c049fe57816db11ad04417ff3e7543b7925ee448b5796f1351dd9f878a0f8a1e2084a798cdf072080642414245b501031301000041ba22100000000054cf51ed01c1fb0b1dfe6c4f3e5a1ae332ec8b22d728373d1af7337c2157d6697913673936fa8f9f9214bf6a006d9d242a282a509fb0664b21adffed504e6f0acad938af0333206b095bd1d121b4e9a5c6a2f52c66b878c29b9c8b00e942f6090542414245010100682349f10b4854d37bbd4c9b0ce9fc9c06ee2f9dcf30b688536e64b03adc591ec57dd5347c9ba3be739318225ef24e3205ea11e5e78eb0bd44ee6c25fd1e89e9867caf189dd06f5045cd909e1ed9407a204b82daee659ff30f1525ce69904af6dce8011049e145d04c044e5b60c78c3aca693e8b2f1ac223492de698d8d5279549d922ca179b638866c9c38b7505c1568ff696b00d9dd9de442f4d8d35f25e0765ace4080642414245b501035201000042ba2210000000008834b6ae5f49933000abaeacf1733942762dbd03fbb3c1b45814180bedb3d25d752e3c1c66ae23785883750e685b2df32d66bac0a1a1f3176be2aa3fa374cd0813b705e3952542645afaf635686b67f5b7987955832556026139fde1b050a8010542414245010132b4a93e2444c927a140daac555f89b912e2a4d362fdfcf4c38f401d347fc51ac9f6afeb3884e11f7374498490097abf128721f28f3823d657a4cade1f2e8488ec32ddc3feadf7a6e097c76d7cccc9d8b1c6de1407a1ee0c8aa63c9186835f05fadce801e3846e046c67c3897355060d87a073dbd1f9fdcdbef25fec3cd0665cda0064e2937b2f126f6c430bcede1a51dd39350a8d364128b4f1ecc6768d0d2f8b7510b5080642414245b501037500000043ba2210000000002e7d4ba358d718b2805fd0363e4bb9e1beae80a7c7515eee010d4a9822b7705fcd068ac1fb0540bb04e68d124ad7b6302c0d7eabd0d589eeab4c43e818247a0cfe294a2640dac4c0f3eadc90d15fc9617591bbe19ce548bbfc97283be3780200054241424501014aa731cf5e07deea12a6d2fd3d9747546e45d37e85eb99421765877b2af6cd4af102fc517a6aa4f3085eebb41f26f26512d61ad2b01200ef5e56651a17084d8360fd4f1ffac076d2422fc5dfeb14aa7bcd4fad04fe7d0d840dbbd536637e0173fedce801981f1a03b388c488e095b01841a09265e0a5e68088f1c9a6d7cadea5dd2371a694d688853ca8e4e7f167f18c66cf1dc87877ba0d9e992dc4ac174f51863e1fd4080642414245b501011b02000044ba221000000000b684eabc920d67553ba6c65ba5bca364bbf2d8223d43beb111fee870af549a640120459b4d267b240ebece6613eb236a71cfb899a920198fe0206ee4361c87059a8b016881ca120695ee2987024dd96e0133bf7b4b30d251f48080459d13c501054241424501012e67c4c513e3c405bc074cadedebcdddb154fbbf3922dc1b1d831fe5c1530c3122843566699449ead5611c73581935e8d552cf590534268195ab2d7de75d248a6108bec01a75ca3a09df9f26ed1702f1ca1046f6276fbd9d4961f094087510b702dde8012506cb527814ca29e02c475e699066910bdbd55d9a26a4f4c9d49e1fa25c0111ff8a5c86b5e9905103981d4f329fcfcac86f4bf8f286d4964fe54ca7f797c491080642414245b501035603000045ba221000000000246ba5db85e74a735d8e8eb78f580d1cd473a2bd3928633456c723e14356841974f78660980a13e82dc70a977571caf3710c90df67d70976e58f4f59aceb4b060e7f35adb1d111b49b20c2834b044928ac18d2cf2234a1969a1f0a1e287b93090542414245010122dffc7dd161fcd63bc73f50d0127b12fc8f0967fb109425a9d45eb6e4a78b1006b43a794bc595b781a90da87febf04f9e2df4355e1b756ffca795d2b5c1928d1bb0001d356be23b8b8d1b0c8146b61d5f08d74330f4cb4680f5ef977d97fa6e06dde801963945f351e1d08f8106df0a0f698afc4c847c63548c4ccc3096a01d5314e9cf97f093ed3ae84d17e53332bbbc666371d896e5380187ba4b1f20a75fb8de4171080642414245b501030200000046ba221000000000e43bfedea72d5ce41eb2bedeae29ae812430f82ace4a3c9fef074561408090512d4c05f8733c6725ecc612a13aca1d95c0990bceb40516e932798b3c6cac3a0769f6c2ed605b26986c090f8413fe0c1a35677a90837aa5c33b9cfafa611c7a0d05424142450101d6c42b1c414fc79595cc1c9721278ef2bcfaed45f3d728351cefb6dc9052010e809b15c5ee32910365d2929311ecb9fb04a0c11170e7b5a53847d56dbb640e85fb2e2bfcfeb64626a1d989bf080f51bfa39a0f3f04bf2e15e724f18775fe1c070adde8010d86afcf05435340809cce52ed86f85ab9e9407d64244d3dbd658164f3560f73369cda16b70a9a79ee9c32ddee30fd7d48967afbcaf20b68c45d0c52d079ca56080642414245b50103db02000047ba221000000000fedd8272ef1d4dc7913a1fc89fda4984ecfea33d40a2333c164f6a7c9a990b4545f6a0040ec30d803bc99d99fd41506cc3920303a72a91a1e22cc9cea927f70bc8cd07ac68a6999b352f35c55609ac45ab1e106925b5e819eac41c492cfc4f0b05424142450101dab5572e17a6a479a2c8059778c04b180950446ef762524fa6c4bddf384dd922bbe37608e0d2c59729df591e497b86527329cffffdab76a305a076cb83ece28dc3054e358710ee1e6ca678245945a77535f5a555ec2ee8a84fd6ed5f3d653a840edde8018d1352ad2585d17e9ac832f7c94c3ac600e0279edab3f426abefd0858d0ec2eff273c3d9ae83e8d8e7b62950c7038fe1a472b4fe483f63af782a54fbe8a5a342080642414245b501038c00000048ba2210000000000004a210687ea314f3b3f726c2269c8825c40913c62e4b35281d4a2405fde3777cb35f7161ca22bf24d537784d8bba12fcbb0178c70e92beaf199284dbc11a0672e8772993cdaeb4240745dd8340d8bc3993695a8f1b516c9d02349ddd0ac90205424142450101f020e0edac956c0bb772825bdfe9c06b6ad414f8d482f47dd991cf838dcd52136bb295cde16b5a4b7cf295776eff943dfcc699e6feb835e74e9016b94ebc3f89b113aca27499d0785484c3dafa7f88c0b73cfd9f705665e31e4f66e95ff39e3212dde8017c7574578b67a8d7eac83284257fe8cdf2e1f4db5c7988415a26992661482c134e0c3b53f6c9de9c45610b346bc5ff7c6ceec3d2f5b67cc6c9f3369fa70ed520080642414245b50103e100000049ba2210000000000ed23675e0cc9589c5691771d8fe2fd12651237b422022d009103742dcff1860da50bf9c096f436c6cec243eded0b9bfde1b11d4e3b3ceafa82b30512377c30b312d53d8903c82ea8e0a14a42022d41e03dfe63d4d6aeec6034186a08996ef03054241424501015e5f80ffcaf560db5d5448a99621905990f5de1e1dc4524481e49aeaf8450175017f0dd6d1ac6a94089a7ec5f22b925f786d7998f9c406d85fc28ea1f2c0d18672fdfbf687e5d2a81e58c7fe8f92478eb47fb66371bee411fa32009f58b175f216dde80140325c20c4a5981cba17fd8c91aa4721952c506d1b570869ccd992ec099eae0e4e90f3674dc2019aaacdc97e0fd0dc0edd0e7fbc53940a983c23c49251aa76cb080642414245b50103930000004aba221000000000b8ed08f9e562902cb588d14205861e562fc69bf7bb1faaa5266aa41efd08690cee409ec072d673f279fad5234c89e040ffac8b630336bb04c1e28a9f15e5fb0c1205e0baa84dc12e148347478169cb62c92b31c12eb70255ecc61c361ab67d07054241424501012014fe4dbc313fc95382a521a23d3ee18ec4e87d570fba0704a381ebf96fe43c88da6d3bd825f79246c48220bf3cfc53b319d50241529540ad7380581ffe118a37b4f2ad96986d422147897f04d6dae9f66f569d21568ec5ebf4af3caaa05add1adde801d063a81dd9b54398ad60664fbd2bbea314268271e39d5e5a74d1000f26353b28463c23a5d5c74bc5d1ed8c7c81ab23a4a9ff4f47f6e5ad510c868b17e5b98473080642414245b50103470100004bba221000000000be7edd62bcc170393cc63073c29a6da4000339f7aa9238d80c245a5eb36deb0995243f9d7dcb8edc59fdc4a960fa44a864d68fe99305c948224a1cf780aeae0bdbf81d300220e6902b8c158e618e881d8891c8ed70ed52a0f30f48d6b886ce0b05424142450101ec1389d1bef60aecd244afe1c7b1436228d7e1d128b37a45cf25a82c94106721ca759181dac8b37bb254938917241c1b2dbf3ec530715a78b4e222753ba37f85b37520ca873dd1b32492b1dada6e179b38da26124f7c45edc2e92da2773dd5381edde801cc59e7a620578d1f57dcae4298252b7868d31c0c17978c34d08e22af7d60d210079b88b8d219689fe89e36b7bf2bbdd016715a2ae278c9611fde529a5db938a6080642414245b50103970200004cba221000000000ce1a53e6aa2b33253c35dbdd0de30469be9da818146ae33ed317e2ba35ef7b618a28b377e97fc6fe21b5eeac7bad15ee328eef23872daea635242e9a28d66901e6bd00d2cf85ea5f8829814e2758bed0d86557cef537bfbcda69a12cab198c0a0542414245010174a09f1000a2141952945dcd016f961266647fdfefdbf31741de2d9e31274216c34ad1ed12243658147be6f9ad60c50bb94346e7dd67a9baf96c3d48765dc382206f982fbd62b89cfbf2d1137abb2051292375700ca8504bbf94d2677fba0de122dde801e543c53e46c631fc2979b719ff80fb61c3328999ea1dbd111805d47c098e9daeac0bd64dd83e3f8485f6806f15fdbbeaa47851a131002e4d2d8f183ebcb894d4080642414245b50103090200004dba22100000000044ff148be6582e43362f2b3d0f83300012925aab8b9be05a4b625474a195011ba8719458d573d1a6a2fa1d52599f0392d38bb868bb588b7add4ffd958c0a0f048b05ec540f98105063398bda122c5ce4da41ec0c924301227e851a2a4710860305424142450101a25a18136ef004845754f1f586cff394f6680a1c965019ee4fb5e950f4b9fb68b74488458ee545c240dbc110e6e55657c68e17375d803d2c1a2027474b824a88dfdac421026a482a677a7bf4fb1dcb948b7dd6eae40ab0a62c3fa5d3e034931a26dde8015a531d5fe6465c433b4852cdd780d31dbcfbfd976d40eaffee03d266fe752d18eefd9fd762f1b9eb2f532c91fb2c7997d432dfb60fe8b2e46509f9eca7e3890b080642414245b50101a00200004eba221000000000503b39cd0e1a38b8452336f7c02d375d93e6706e36ea88975defd9f9409350278c5a502a1abee9e45c41ad26b4411fe0db499c322d6978839c0fa9150769e203f774792f580a44a512bf959f080b9c397b722223d545139e7ed37335e9b8f00905424142450101b245c68fe35bde80947969570f68b8e40900411bf3a0cd4f0c6b619f1271fc0b65b75567f6b924c7cf5af235920eadfef9ccf15f403b4191e3ac09c1bf77db8f8f5c1330a36fd74673b3b57f9bddb1145e0d909a32a05d7e9173fdf1fa72d9ee2adde80126b31d2c714e22a15f5a2f0164451d708462ad811a2aafd67ed9d623f2e5e358851c65c4938ebfb4a6c2399e2dc0132a3c6c14ad10c73da00dd3a5e8bac12514080642414245b50103bd0000004fba221000000000a68cf00f35aae0088b56703952fb01ba79c56265911c2cd1b897b73372fa4a29d807036269c8ab503380e0e47c6e9004080e5f70d785ad9cdc1a62db6d7df00c75e2d8257884cff5bec1e4f4724da412ba9f2390414fe8e0cc511c8c7773b90605424142450101cebfbcbbfce2b8cbf968b6ba11e8785d59f5233f1fafd9ec385b215607c4707949201384687011863765d9ad7890bdc43802a5eaa4ad7d7bcf7f5e49afdbf58f1b6ca330b891822f68f0cf3ad0de5b6cb82bbfb78e5e002dcee658dfdb1d3f0f2edde8014c09a8e8e78288b3c97b7ad26dfc65b40d858198177b550909ab0b5c56af688eb3ec8156991ee1722b2134cc5f7790a2965b5fff8b4a14679f7b5c5f52e4e067080642414245b501032403000050ba221000000000b20d126e0b11423f51dfb137a8f5fd719494f13a0ed68f0f77294acecf348d0e18cb984ba0332bfa7e249906e412f61935ab28d42870f6bff536d4c58c98730a25fdec835be21b19bacc5ccc1f2c9744fe5c2ca806a845d25247cc2f2046060f0542414245010142d0477b177750bf0bc352e830d168989e04aa19fe940c3fdc7b63a70423937344b2ac8c808b530ef4063263c4128cd06f8aec1956bf92503eff97cd1b8ff782ab7fd5a639848396e3425543a5e5237855bc3f3cde03f2366c26da6c589f1b6332dde801ee21afb71eb0131c233911094b47306d023e2bd3dc21c72da965e6dd4b43fc5acd7b38204ee93255dc1f4a67f58e0c9dfa2678875bd5b1d5040e2a9abc8d6766080642414245b501019302000051ba221000000000780bf283c9c15ec93c299eb807db449c9a9767b250a87b875baf698a1e033166ebd4ff693a4489ab6546d2659a1488e90ad0eb3cd9435340ae6423d7f2080c070e168e346762c8521ce8b70d07c73916f0baed9bdb467a43ca7f6a01f828160a05424142450101c8c9ff470cced3e04e66f661a173b530a53b1cda47c1a32234c6a1cec0aba94f27e958e222c12368e8ef448058d6e8b28235438f7d37a4318421c4226860738c3b8ab5d3fcec3dbf6137bd0c4d213e4e49a610ea3a5243f676f477c5428f75eb36dde801090b76d22dc4116bf32b4ec8e5960c5bf71f709971a37336c11718f4f8d3d858e7ca6b96d8c6582cab2c9f8ddf97c32c1f6ca86f9a5e31b8dfc188d043706a88080642414245b50103a301000052ba2210000000007cacc56d53747336d9455d47a22e9de87764d5040e10f140e609b84a05d91f5eb12a0436e582acec5aac3d576016766d0b81517c7193c91ffefc73e1dc982f08d1fe3f31b0c1954e47f2eff889b69cb7befdf5f396ae22ea5bc031bcdd9adc0305424142450101b20052a81d486ded3ec7a2e84a14397ed4e7c85bde2fb640b49255f750f4c46b07a5e8524ef2c228a59cb3a5ac4cfff1537e87209a84acc1a3f4cfd2ce510d8f24350f5f445e23c15901630ae632f49a274b5a8e7b426c121159d7c0b1c82ed23adde801e5bf5bd0e3be338fc7fc4cee5616deaf3f34a7fa7a533ad242bcd70c89caf6feea381c2e15f3fd9acdeb09a2f87185cfb9cdd95468d5d72da8f91ff969b93b90080642414245b50101c002000053ba221000000000dce30e250d900907b122444c16f8b10f15f738166ee77b7414f2820579dbf809a1566369b1b98b44bc03db82e9fc88c363b60dd098806e6146e7f9faa61dab0f5e88a79ff4782aee7c114d3bb65c1237f35ee8833d47dc5303f5693f134b720e05424142450101c625ac5bf837844989799c6794a534c7ee34b85ca1a4a68b8b1518207e47ae1af2775647c74c719af75de5e0c9786d21deefba58043390f4019d8d70241e578ca7da7861b06ad75a0c14d1e45b83156cb93e114925700290d150abe57f103a753edde8015a8abd31d07b720f25a68bfbb21466eb88af212817cf558a6478404c67f72109ac766f95aafa7663505a7c7f8b8f3f6aea4c5a2eb80f04e36d2e7ac4fa3fc34e080642414245b501011503000054ba2210000000000e30e95c500cf923ff82aed1cca924510eb2e9377b6ba837eb326ccca309d82b2035afdecc779197c61ffc6b8a9504665886966e3161412f9839345cbc9dfe04dc02a21f68662cec0d81ad4153cda6843b7ba2019c22fcb97abb5f97ce629208054241424501013cb9ba1960a865fb9618e96f5e832bb11d5057cfe3a5e37d813bd750468afe7c476e7bc4e086adad85e418568bcc5dd6e5286b6c924af3eaacd2d8d694ea528cd1ce7571e2d2cabd4869473b2b4cdb33a73e094848e9a9ea33aa0db907adeeda42dde801b01c5130f34591f8fba8f05a2df4252ec5e70ebefa241dbeb28675e258f1dd25ba22f111ca7a0e417efb9a1e518b4ef3546c09b56abcdd72b8d9208f186d7750080642414245b50103be02000055ba221000000000d0d7256bee017a01e1cc91209ace3c296a602a1d35cbd69b97b9c4a075200c5d2c5088707e7ea5ee806d88bd9e09e2d5d6aca310ec24cba77da2bdef8a6c76029db6215cd95efa8d5b6b4e83de661127a4643983905559f4f169b115b2f0180e05424142450101e056b5dad914b704116c58b6b6135c843889b8f720843ba47cbff37085bc56626fae6855915800c2177fdb27a83d1afad0d759e43157c1844649ed2047d36d881c761776c19393e23d1d9be1b763c24f8f729181863815a52e9e3708ba43b61b46dde801b0aed266f005633416dd45e914ec5e67c05ebf80f06352e27a3c3254e2491fda1ffcb34c26a9ec144e4a2488d19ab873eb952c4c654fd9e36b32970522447ccd080642414245b501035b03000056ba221000000000a2720109d4642c971e85aa0288114fa15aa57f521cebe2479ddd432d10f9753ca800bfbe3b020f198fe1e25a23a19714e84cb21ba2b7a3701cbfc8f702581706f88cbc0e9ffa366f63970a261997cd16d6b59dd45d6a85c7bd5d65b0eb189c07054241424501015a9f50bf8ca6e925d65ab08f42a47475eaffb2131e421535485241f11541fc436eda940519106944fd9a837471fb92fac885327ff996a96c24297becf4b33a8ba601b1b2f55425a4f271297a379a25a9e4809c2185e0ec92c7a5e21efd988ba04adde801217df85869642b5d68e2386685aecb4547bb3d6b45279cbaa42fa91917dadb463f9a89cc42a64607a9b5b92789525f534f474d79be75f17a5b7562d44b140723080642414245b501012003000057ba22100000000034bf3b24d21b6a52b77a23ee6b6bef4d4d60d69e9d3d94d141311e80c7aebf264b1fa5f3e0ddac4f7706c0fbb927f9b26a946260fcd82a87e24c6d04557e15080c346ed1e08796b9f5ea882ad5a741d2a25cb1e227ba4c1f6b6d8fee1370720005424142450101483b6e6643458fa833d287248a57a797d9f3977e1362d610e47d91ad67d80c5ec93f61beb2b3a30305ec08864003bfedba03afbd08d1e4c331988cc0557f2c8cb1e6704053d7380142e44f88092c174ebb5f087dcbf6f2fdb3ae4d191c0bca5f4edde8011680328eeba78be88c20fb92ff10f5891dab6e0fffee4148e937e39b79d1fdee566bc73fb88a5faa4eebd98806683475e9aba78ccb11e2b48fe0e93e0cd51cb0080642414245b501030501000058ba221000000000b207f99239241eacc68e87d551b601e5efbd37eeacbd898dbd307497d245b82070359ff51294b7d387b3e2eac01c739e4daeb360b18a626d4fd20bb4b4b8ba009202da7ff1a195c0173112c7df95a4b9978751e5507dc836b902505c2786490e05424142450101680abce634d4ea1952a9c3a263e477c5fdae46b0e575d224547bccadffe43510fbcf62aca254840416e3a9a953e895805052d3e99888cb47cfb200bb0cb96f88c194ea6be3dcb5dd94a14130edbb363d2b991070b3a38959fcf836c77f5a903452dde801467e30c958b0d1b777a1db0347fb3b3b6360d2294c36879dd51b0abddead30e995c9babd5c470f57c620ed317034c80ab2bb1c1c8e8fe11542e0c6ebb6abf1af080642414245b50101c502000059ba2210000000002af5ad4602b803876ce470fe45c44ef3920d8b2bd4dfba6a1632396df1545e50b9f48e66880e87c5957eb20735f9b197818c4e97f35a19cb7bcf540365725204f5ee47c9a5b9082d2d446889b91976c817c30aa3c42a7557ef4901cc96b9230c05424142450101563853f2c6512634b3a0da000cd45ec1390258ed016dc11521cb5236a86ad2071a4ec84cd0f8b0a2a2d4fd4ae6d04d0811248a9e86de2deaa0f900499dd88d849a13b80c9c51eef1a10ffcb81274b2fa690dd7d21f59aeb1dbac6fb9907ce62356dde8014d39e5c9f7584d8ca417b2b4eb73eb2e1b8438a0fa075ebd5de549f17c3f3308ae0eec0dab2a798d5871078fdda2fe56cf104494b7caef506a986b2b0a2ed1e0080642414245b50101e40100005aba2210000000006ee24facdde92881bf24b312abf561472ca57ce2597e984765d89060e8975026f8f02e18ae786152d76d463130bd6377320a85d103667f14329e6991470abd0e2d82b9a9efbff42025a0203aad291be8f90086f776a87cf226bb5fc69d1c940905424142450101babd0b7f3ba6eb39ee17827a1291c6629995d9ab435d1592ba388179a84e341b18da3a1c7318aefadbb55bde00ade6f034d5b2a8581676b34a7dd4310e61bd892f1a34d0ba91965f2399990c5ae4c44425cb85fe26d84d60b1feb6aea9fb2eb25adde8010ca2c78e68f88c6c38212b3e87a8ba33dcea39729c65a63a4d60c781975aa120652e180ebb62b5ee0500421d486c37c8a4ceba63dda45920b25e90e93302716c080642414245b50103770000005bba221000000000ec17e850723c7abbe9d81861bc31f14ec12397b39a0a4efea0cbc349f0048f4532b8dadc3caac813a89c2568b34e8feef13a02c7084de10445ab08cfb25d980651c09d51b50ce7c90a13dbfcf9bfb60772a209547cd1eb0234e58084fd0b6003054241424501016cd2a612e906a030272bbcbf55dce0f50bc5c2853424f77efd9f82ac42e584733182394eecae74ee546cca3a36fc01bf22bd35eb6179cc895066dde443d98681027d62ec43b8aef03aa78cdec5073b0a853497f1e7cdb52fb8b026b59f95edd05edde801b779ff453864f5e9879fbc24bafaeacbd1d6f16f882e7e33cc427038e9574bf20e62de4b59fe0c554aa311f6192b4381f75feadb51b1eabc8c2f410c913439cd080642414245b50101f30000005cba221000000000a6fb0057fcbcb2cd14da8f69642c794b85f5bf940e8ee263877acc0709f83d7c46ae10bc5a6052733da256b29ca6f387de3848554e81cd2508c7dfe760df1d046d2d3df63df2a437ab1e905221a8f0bc7480b728b54e1b3fa5f9c8bf8b723b01054241424501017688fc57da0bcb47d7274a23d37c685d5a716f454f77a51940228b91baf53e7fd656c257af9a9f8dab3c00723967863d5f7a4b003c87f2a8b5a0cd9a346d1280f7fef6fe345243d6a232cef92cdefb583611eea24168415b586521a49dc9b72062dde801dde188b7aff29de55d785a0e21bdd817cbb456c7588ba948d56f3eb6554f501ba9823ecd8de4b6e306b35089d5df9e78f57ce2c72ccb02bf8f4b52b57ba4b191080642414245b50103890000005dba221000000000aeb01e99439ab385d8775ac36c075e630de16d036371761bc2ed0b195ef12a3304b390522449ca5e2b186c12dda5a08134cebb2039d3c16fe9885b6dc5e32903db3638a0f56580ef3cc3736ff3ced1bc5993df61929c9949b22a354bd316a000054241424501012ede9eef65039964b025d54dd44ae9aa920535546d03ba6f74ae49c9a15a9673f34cf79dc1f54dea5f594a7542c92df40aecb2a337ba7560997ef61e12f3038a695693a232f3f86e2fb5bcfb4555be3a17a834bb4743e7a4a427532e75e4bc5266dde80177dc16434b03b3cda1e24234480db6df39f583c60e39c7516176f9e245a0988afa017047df38333a30e8265cbbdfd9000f7fa5d55562a0db715aa6f2f58e12a0080642414245b50103f90100005eba2210000000008070e0b71a2536f4b00a81ddccf9f2f654187442aa12a2d13953f13e42d0cb3456b52a03c6e02415b2e3327bed668e4b4f93c57661ff9d0b7fb43c68cecd6f0f7284afb67fc74651a3c32d7df6ea5df1ed96c5a30624128e890a2f1163ec210c05424142450101e8f0d723388f2e76900ba20716159672622513352d4eb3547557720c1b013726b62f73b9433900ad0ee37a86817d822d97a4c009fa858360cb5b5b017b13118f215da72b78d2d2c8258cdeec0c334529c97297f71612c5b9fb596d1c0583d58b6adde801620f6047f87d9b78bfe29212574130be70bb59c68d45475a95af8f944058ea5ec5064fc44ad1b8333b31555f0c13d1ed1ac6f6112464cade378bd06296819948080642414245b50101570300005fba22100000000024cb740378effb60c673d52c2210096a372aac1f6392c01445de27428888e40104106f7c9b57de44062925b7789e8807fa7be648133944fca15f4749e40ff60ba3d43ee40d13e4b5eda5cd45cb83474cd5a1ab22f3686ca5f5709052b7f1d50b05424142450101a0604045e36bdb2d72a7db9b8012b2493048c9a1e3db72ffb22d56aab12b9633109dc392b8cfe5c2e189a53cd888c597eac098861ad48d035bca477e4648b98bfc5d894b3d3d850c75cde9da26dcc93ea63875f4e346da84b38f3132ad40f3eb6edde80193aa366cd628c05f36eb6e1814caf16cc5cf88a5619307801df8ddd6edd2600936a1fd18d34f68464148eaa5d7469bbeaea8ef220c0618a14ccc54bed580d064080642414245b501031602000060ba22100000000080e904f27d4499eb6d9e6d079d771fb56160b9c552cdab26df232cd8b83dfe3fe689d63960b0af7ad815d7eb32beed19684315fac22e2a9ad794a293982dd201ca6bd7ae592e1bba50f277b5ec66ae52392559ae09b8a43124ce565cc2cba80f05424142450101949f142640835cb3f0d79cb48cb47fd1e54f684ef477b08191b496ea11b6aa7022e1a909e0778362a5967f07e39c77795cb5d32f776557937427ebb74a3fe385a9a71de20b1e84355cd0ea534104f13f3df47b402a4f84995b4eaa50664a1eae72dde8011317e29d40103ac8c8590b43a17561e3542d890a879b88a4ebd41d03fafcfb79251ff6f33cafea4cc3ebbe7f901d32ff368a47a52fa96547833c15837e23c7e5080642414245b501011402000061ba221000000000cae6f0902b0017efb42317db49658936d7f08e658262d07e3778ac370b211a2566c651786c318a1944f216a0af5ac73d62f2de986abf494d5fd2b07fe839020868b08d294fde9daea5343dbcfcd30332fe669fd11fc089426c0fe6c1cdbaec0c05424142450101b01040c977d5eff82fce1ca1bbf9e10b5f93bd7bab623a703aac7c2e1e3601469b32230d451c4534e5b5843ed7cd382d85bfebeb33122960fc9b34d66008388f031b23f14933ac39ed4bfad91075696fa99cb8f3c795298f7247e396bab0ac8c76dde801b81ae2813f7e9a5f2587d758c219b5cad668695bd45bc33de3a80ffd905d21ab20ac8913ed4accb6d6ed82198561b315a02825cce69132adb9ad9976f46ef7c8080642414245b501037500000062ba2210000000007460c2b3db823af899b6d171d21df3fcc6e7373397a1af8f11659ad8b8a0f900ce1748dd4bda15480d5fdd775bdb80137d98d8eca4200887d760e4d527b59300545cd67e222979a64ce65319af28d9571fb68bc7d9d161c6816c53872d5145040542414245010160bb3a860ac44f94551b2c2576bffdcf0efadb22fb7981a5b4c04e89593b1420d2e794f2c2e86aecfd4a99a6215e528bbd824387f604204e1f0661ea267d6283b6c21c1092c1218261f4bc327686b30fe63683ea408594541cd41f664daf759e7adde801f01ee253f269f70702ece09e62ef1313b574b7def6603e9d230ff9f0efd6ac811cc7fdda36079cb5d5c66ca4c638c9d3a10cbbef5325dcab3a8e0e204d168138080642414245b501016900000063ba2210000000003a6536ba1d1108907f5569c86436f4e866f277a266b2ef6848493cc3b6eefb266c1ce26d6405a96bbc69c6104821b0f306041e3a88ed92f6e8fb204065df750c402cadd462f6463d60bc558f39aaa489ea3979f57ae79415b3bddfe2fea40e0105424142450101ee4c110f197c9494f00a95e7776874419aff9190b10ba5a7e65c65c7ca84850a7b4b36a82bedf15ab144b8f9e7b848922ba04ee6b920e2a8dd26a10f56f43380c397e79017b5a5d6a7def51c179904a41bebb0cb685de3054af80c836baf84007edde8010ef0b89223e176498d1e0cb09c88078fff533d624c1ffae2bc61e623e61a5b7bb3aba358b3e2a68f5b9228f0915d2e8c9a1f0e4a58e3e6b44596cdee6e396247080642414245b50101ba02000064ba221000000000f0cf91d13322eb1d8b2aae29131b99e07c306ef3ee95c0be482c83cd5ad613449b926eff2eb731f6353662479a2cf3baa942d4e6b46ee6693b3defa62649990ce8d5eab3f53e901508df55af1313a724439a0ab75da87d9fce875666d0f4dc0805424142450101549bd2638da994bea7818573bd018da5ad63dba88500d87aec87f15318db1d5c6f32bde501c6a763dcfa2aa812a81f992649d1b320e95c09e9ee4e30235d9d86c30b7c5d33c5aa7b50b86924d38d3e45beeef4fd1e366613845f03b3dfe6451d82dde8018c43e148d3b86e7b048950dca11f6ad6a225d9a615732311e1f204df1a9990931eefa7aca304fbe93342699ca43c347e30d9fcd68a59c75c589745dbbb5405d2080642414245b501032203000065ba221000000000aa497a7e2db04e5076ff29d5a31e4bb248b2f4fa4c31d07817bbd431b551fb10b76cc3e62f857bc8ea6ecdaa7322d4e97d56f9d910a2206c963c68efd1951c0c53ece712e3b428125ae6414a87de7abbbd128d618ba6d46e9a7aa0ee02651e0f05424142450101a2d7c153426fe95aa1d29ce6292f6006ac25f4e99123425ab32cb75304221b23b60f6a0acf5c5a20950140921a01cee6a326b88a9fe461ad63c093e884abeb8259391450f7bcf4dcef2994717b51906f4df66de88f37275cece2140173831a2c86dde801101be9fc03fc891abafb02cd88022b7ce36219912fed257b57ba041770fd423f792236aa6b39e91176902b26d6775d9058a4731485a4a28a90dca1e98aee1b40080642414245b501033400000066ba221000000000c8fca796ac0518b7339ed1076b0a08857a4383993959076ba2928babca8cd77fcecdbffbff4d3933ac48e3599aec68ac817cd82a9ec271fa9b9a42001233c908903e67c8624bd604622526d1332846db636d00567460a22170f79621e06a7f0c05424142450101689709996146990f843bd14bdd89f78ed10d73dc34f1b7461aca2cd46dee0e7c1957ed4501b75dfce390a4df0608e00e29bee9ebadcdeecb2078039f418ccd898dcec09ce32789b9af15a483aa528c4948a4f0e64cbd984095e4cad4b3e0f1048adde801b6cdfc6147b789f4aa0ef0301e0a2f22ff5e5dce67f240ae45b0e264f3e9b7df24c01629cf5b6f0d8b4ed0b85a5d06d1ac7690ddb02d40acbded7e2fb4ec66db080642414245b50103c002000067ba22100000000036556442b816a4d0fcd36600f01f95d142e3df2d26b1182a19c9a6eb56337e7f23f4449ad7f4f96077fec0ca9e1b44c0907b534bd5c090bfbfdec0cb13365b09ef3aa4e82950b1b7504287b947330a5a1a7dbbdded79455c227e378ef2665c05054241424501015228175b08d4a52e66131c1285bb8f32a3025eab1e25e4e374f28cbfc4e6a148997db9aeae128a296a7e0130fbf0317f8bd15a42ccf918b537788ff46046cc87a1e755b279bcc0a336bc2c4e34eca57752be423b2884de602d58f78dcf94a8ef8edde8016f4c7286271c8611f28dc4b5955cabdcd32200d433bf58e3efb6cef7d35aeb1fc34a9a3ca572eb65daf9d42a4ddc1814942f7742612bfb6ef95c7b36fb876188080642414245b50101ea02000068ba2210000000001e24b4e7a21656d3eeda78ebf45b8e29bb34c3339da6160166af5a1bdc5b307c1a8afe0dd2b1080abca447ffb9d58f487fe1e8e3a569cb75ddd51b1458734c039a0387e9a0b2d269658b9ea06a60e7b37318453c55f8548748e3dc1b91352c0005424142450101e4255fe528e0daa956719f0d03cac3b85562b1ceef0abcf077df7140ac4d2d71f056117a717a6efcd1874c1daabd6fbd920d27593b83be2a8c8efda3d8f9b18482d62b6950169d47cfc4a4b6e599877e5eb255c8fcc03413fc50f632427b57b992dde8019223b4268e7bd5c1bac22776444a02eda9066482d25e2cdfca7ca9fc7366c41a8aac89029a12ecb04cbeca287e885471a55cf694187828dee4c0cd8435b90f06080642414245b50103dd01000069ba22100000000074111cbaad03fd28dd241f81d6784b56499d16c365413808f3152314af43db6f0f74aeef4f7fccfb4ef36b13d0680cbc1c6c1537b8293a92896c6f1dfd66c40a34e6d668b8f6289da5876e3fc8c3a24714ccf508de384f8d09493384c7712c050542414245010180ed2aa97544f3be3fd5e29b358416aec96bb4e61b2bcf3c88f6ec1c4742ab1985611b663072bcdb5e08825251ee5c0e7dab46dedeae83ca6a17205df3712382f968a65b2806719409c63d6cb74b484b038dbc683b0c7dd434c0da04c853644f96dde801386ba0c80d7e72c8c9b300e7958ee07b6aa6ea246f1fa93cf0fbb7b8dd1f584f558e9b2bcdb3ac8a8b02329e4c8fb7594e017e5f3f0953b4a6a85de17beae4de080642414245b50103c20200006aba2210000000002257496c07bf76090ecb90b05ed67c3f3031449cc09356d299d7c109ebf31f41f28cbd8293a22b05e4a19abcab967a96e27829a12d4f8d002499aed540072e04a1e91300734a08158235fd1cd7ebd8095ec5652ff8fb61a89d5622bf8380170d054241424501010846914d7ca5576249614eec783ba1ebba4553aef24a7a948031e5365cfac26b7d23911a8fc3df6893c391cdc1f0aabb4657346671553bba8abd6fb19f077a8ef320521ade3bbcb2bfc88b49bbd6b45d890c7159840ec214fd72927bc7fb64be9adde8014ff7ebbeab48a426362167f2bb2d5561fa0479b44f012e1894e60584b9b1368c0b390b9ad393d97680cbbcb80e3304aa5fe4f9298332f4852e9f87d9333d075d080642414245b50103830100006bba221000000000c2282136cc35b59cc47fd08814adaae9e803ee98dd0a5232691b81b635457a3e31876d3e02cf82f94c1738f621426d9559fc97763a346f990a5e8b959a8a66063c4860ea0b4203594b92df4bbebc36627cfc7b4c5b2ac212b7cc8889af9e0601054241424501011e40d85b2c78e191c4b925632a80960af525c08827d6552ae2997efbba1d325740e7ee65ed1755c63823ecd0cd41b518d7bcb8995758a4d4eb2639d6882f4d8a8223a75fa292758fe405ed93f88c58a6a88a8a1ccbd8e6094705aebfcca3fee99edde80161515b4bc14ebd597b2adcda6347e4210f84e6bfba171dc9c8e0aba70fd305c1351c5beb8a1a019cf2c8d59b8eabbda83ce92a132094aac5aa158116a7dc01af080642414245b50101d10200006cba22100000000070b5da2c4cbee4e5c08e78bf4cd8ce24fbe586cc0663f9176555dcf98443b8134a1d1ca9e4c29f6c576d3296ef91b9bf2ce1779e89220d3a9dbe4398fa9f4509a87221e4421adb3dbe03142963aaf058556cf720cb49404f9a55aadecd42ce0905424142450101d2ee618b458202171103d961868c161d808a87d447589a541a9cd19420967813d69aa23fa889eeb2476a690dd9dd22685a05e5a86de12d96040fffda5ae59f8d8791823e7303e4a206d27f46730ca3017b696d823b9917523a5ec4f5c9e79b3ba2dde801127ad6f8925893d82ba290b402e7ff3bab4167e656341e410650dca28d0d05ece9d5ac93eb07b17d906869eb07a83ce980ba9467ebbf3ab815da6589b1fdb8ef080642414245b50103b90200006dba221000000000f8c8d1f12a5f25bac469557e8689c3253c3896e2a3e49aee787d0f9320f41e7f3b7b918a0eb0995e011956ccf41a301d73d4c6676c2fb5d5f2b6f720b31f04064a8cabe47c6e455643098230e57f90cb9360f6aad4ea412619956b8f40a2170605424142450101d461be14e69e0c29caa8eb1221bf759f3b0bf28f347fe38337a260db8e0283642a74f4ec04150924bce0fc1ade45f64c06b6f2d1326adac5645b45cfccf86880a350320baab205e368f375517b4d1b16bc41122f7dd2ef3668015c95e4c59b93a6dde801de303e923900c91c6281a9cda77ed6f3e280d73f65d4e07fcae441c4344b610ed59e8a38a3b9f8712dc01de51519a553092da4383f54d71b4e00ebe2126e9374080642414245b50103730200006eba221000000000521b1d1ed704c596246ae7c6e652018e370b1877ec0f294936441a21669851650ea358ae3a1acd432d7f49c9f95bd1a67b3f5ffa2122edace27aea139a04590c9997e4f80b4cfb2632dbcebc680d8143e0362d3a047706dec335d40b4a8e480b05424142450101643f5f39f3a15065339dd68c9633b1b5876f4d3b833fa7609cdcdf163d1f127dd62b65b3620a334532c7d76980896a6a4c5fa5589e17a99feb4250a080e3e886dec0057b04b9f6e571550016c44b17c107ba8d4f99b3b66e97194bd302847c51aadde8013ef1686cfdb764034c79ee92e4602c8a6ded6140ca596dd8a194f2eea99194fdee12f8840c91d75ceb3b36d23688d7006c1e80333da050ea5b46db2ade2b212d080642414245b50103c40200006fba221000000000ee467aa9a074529657521de27ab5e6153b64081e5b8b43d21d8ea8f8c9e6d23fdb14af0552fb44d2e1e09625a122e4d54dc00b56103682cd06e7f291af8ab70504bd6ece396a3b55e00b4c3758a4398b5bbef57bd66a4bd3916ab4cc909dec0805424142450101da174256b910c77cc71fbde251f04678f6875b3521e24cae8d0967da2d9ac47f7a7b257df350a287f312edf0416f05e1c186b7944f3eaead0d3c45f9c5a6608ef5b5b8c3b33dc7233796b5bb5c699de4ac833c78eb873d3dfca8254670e437b3aedde801728c1161b156172d864696e71755b4dbca7538c0344117064adb273113ccd3ec576603c11cf83be398706ad1b8a3748004007da82500dec051f7a9a885e138b9080642414245b501032302000070ba221000000000c0232f166c9419e2b9a501877cb5d9921aba9c766be45a4810bfb3490bd97c7f13710d77a09e4a39d048dd3688ba3c42f1919c4b6d6d40a6852f2474ed809c0f67102415349ac90e174b32b18892c897e316a2e713f4de481031389cadeba40d054241424501018c65ed7024870841ff47791d56bdc467d07632953657ad95e837a6525fbdc42320a5d92bb5f8bec7c2a4da533a1502264351d7a5b991ea46ca4d679914f9be895d19646196a29db108e04741e6b22c7c009f505cb231e98e3932b0f6c3fd792eb2dde8014556f41e2cac7ce55ade5e19278b0029097297ca135fcbd6647171d4f6d3d06e3fa0e1dba589a2b99e36a585d351ce71bb1837aa86911f92fac811368e8a3e1a080642414245b50103d501000071ba221000000000ae7508f7146006ad9b6c272ddc5358ec2bfe7783a6eeadbb13fc67bf5d0642340e6866290eba0fb69ec17765db8ca6871fb544364e7a99d2f7c0bf03db87a6004dcda29e1d1ea67af3ff6f5157fb53a3ad256c1f0c0c5f2434aff7177591580705424142450101dc87c7444cb459918b1fe959746343b79e09929742b6c6e7a316a54d0d38c4116b763ddad636b3e0688cbd0f8400004e860fd1ffe30ab3f9e5efa38330bad38ae0b8c55c473938ff222535f5775f00ca3deb15ed6f5286fdbe5e7718e4aba6a4b6dde8012438054672646c496b38c9d826077e5d61cce3fadfd49299eb177b7a2dd80802301d6b6432c5449135b29a0eef0cf496ea91ff18b614560092967ee7289b4a31080642414245b501031401000072ba22100000000088f99ed5e21156f1365f9261ddce1cbfc359036b85fea51361fad1735a8ca80a9397569f2644e262f55c0949355142c602bee20c04d039b5c5b4018071928c052d1c131d94105bb2bb785e917b4d7c60160252911f0db7468766ce219ba1b8080542414245010116ba9d6d73b8ce36477ba0c35e2a847abf1e88d5905288b86c377fa1d8cff13e2b0b676d6eb492e9f33630207c557bbf7c0da12095735243e50da1d1e358f2833df1a8188fff59f438c2da7c5b28f924596e6acf981438ee1db72e69231269cfbadde801789702a3195253a7bad2686bae74a9d8f59aab84eaf47d072215d18e19da609105af7b5a9820b2438fd5276879369eb6704b45b3522e8fb7f45b06f65176ed7f080642414245b501034e01000073ba22100000000090d0c410f415720d74374898da51f0b1083adadf5704415208f70ee4ec66772b12e811e16196e85cdd35e9b18713c5b7bf5559b469eb3d49eb8c1c531d21c9002a47baf39d4346cc3fa4a7e5afcc05cd32ffe7a7545dca8c80d79c119dadb503054241424501019c721b6a31a0ace4b6ab9f22c421681894cb4974fdf25803b5c753e469b4ea28c30a6df82de1a4540e54c0689945547f4b461e65ef605532f17946cf4708238560bb1e8c3b71fc361ca6a4f4917e846e7086dea0d7eba4367304392f5a861bb1bedde801b1436668e36c241428350aee3610725710b318153735858679c7786951b00260e50b74ec9574b1fe23b578f6d9e8c157d945cef4c3bbe8b3b304bcb926f83e37080642414245b501038103000074ba22100000000002c459efd7cc037ab002434cc5cb6cf0e92062e8f83db543daa15643aaebfb637aeb64e3b168fa1c20935ac7c3988e265f0989f2fed25bbf96a9c47e64bff9024d67cdda1f18a0d8afe40d5c586183b305580c51c8321baa48079b93ad454103054241424501015c34ca243f771ae9862ef8612111d8028c07e6e00a8d8146507799481b636a7761904cb40c95f26ae642de1c8a6aa68d5de340b7c52e6af92925b1db247a2680d6858a6254272e14c6c234de32ccef9ca122f018e664396f99790bb5a4dcaaa0c2dde8013a24151ec8bc95d0fd963c43a25b622ce16a6f90be552907926586d4181fc6f61c9443a403b3024ecf3972d3da0a9585c821e934e9731737275e1d7c704ac851080642414245b501039700000075ba2210000000004c529e3b1bafe9b7f9672aca70a86d5f0ec675aba5fbe45570fbc33a4efd0719c18b9aa7ea17b8d67bf9790dfee7ee6b05b7ac4d2e26175385eb8b6ad9b400077cefe847d393a50eaa5470ae1610168797267e05501c19f627f8337472774b0f05424142450101480b81353c6f24a5afc9bf943c625ad1ede49e2108f0de55a230ecbdd375615243bbcced3550f97ad5497b5b0242feac8ebdc2c90659c65a2df233bd0ec809877c9d0e4d91013053eea1b69f57a3b7e2f2d9f988d337c4d614d795287c5bf154c6dde8018b0b545eae46300a2d1f4f6eb13973724cd2c8473d196331676f5e2e32dd5ff3859bbe1f9ef21ad910978a52f172a909ccbf47da6afa4fe75e9aa99fdb1a91ce080642414245b501033e00000076ba221000000000de80c84fdee58fc99b57eec4795efbb6e4e1e3a7d75fa2af2090ee5b21c3651e9c04f16b4fbc844b3809a1923fe753bd5793b27cf792cd0b897c7438160b76041bdb7f801b06a3a80efb968de1e802f34fc64bbf4a2c4ae53a8dca899872040d05424142450101cc56f75c9ea3ca507b795e63a19f9515af7aacfc4b83729cc71bd44bd515c54522a9a7a599b588edcef7b47006d11aa666f52e8f4feabb9cecaf2831d916608789f24ad440ae2106cd555f08814b4ad84512f36a6e3b1c90b764d563947fc790cadde8010b9a7195728e2b7263579adae0c16a5b7c3917cff7d2238d68edf7f7ad6fc202fa812a9f64e78f0c2fa4206b35deae409913c0a79718f10b17c0670d76333119080642414245b501034502000077ba221000000000eeb90a8fc157d1f14f58dceb193a1115d11b0709bd2cadaf9523d618d763903589e7894b2c21d7e79c8a54cf1e3c32a50409c57d8c9a40abb9c060dc5e29d307b9a9745738444ff7a1cc128d96bd71c2e8433543fc8db8c42215c81bc218ab0305424142450101be3df85eda7d7592026db0c0b0f4fb55a4973599bbbc6632767b7d54e1a9f31ffcdbbfa3567bba43d08080556c204c472cfacd0792c1485f7fd90ac60da9dc876b860a3ea7623e5eed0beb155faf7b61cd384c52cec78217efbab07145d7d6d9cedde8014504c369a100a6d61c64a5e3048d04c091ed51a4cc084645ceb1ccb80699c172b9e850534de4544f920c1b241347c5707a576e647cff592148830b00292525b4080642414245b501036b03000078ba221000000000cc2da196288a538cc9dc3b9dc2553e1520c959e01661476161d5e278e9273130a518d4c52e6a587c0fc711662adb2b693d883d971ac4237f2db5a102e0b1170285fdf562aef9c801188698adf0eefa2aeac1c5602a2b4cf66c8d66d7a5321e010542414245010154790c3919ba9aee24edd6844a40ee5e17043528512aa91f0665dce8678384563923585f3fe299047ec4946371f90e1fc510e03d198cf456601d2e2af69247840c3372d456191be753d4b7d72c8514b55c5230687e0ad301768759674e90c8c1d2dde801048f7bf2ad4db298d80c0de86d23989fbecffcc75f662eaff5a23e61abbebc295bfd6305fe2e53be80190c8d42cb10d6fac642ee55326be3f6af0a03a98753c4080642414245b501014801000079ba221000000000e2e4e1bcfdcccf1fc456f080ddc284bc6f875753e986daf28843f414d600f618ccd934ebfdfd8e2d45ab742e7e4f5725d9619564a0d04a32da74caa8c49aea02e9d9531333caa9af6de6585ad6142ff3b20e60d2930cd0a6281198d6d8116d0d05424142450101f64998b93de35fdf2f88b22d83eb5b5ef0ac8816883244f39305290d64f7ea1907fdae042f6a49db73088ef48cb628781b9f2fcb47966cfb485951567ad017836370f93b438d013dd5581da3d867bc8bf6bc0874643b12e8d158c1ce60e17c3cd6dde8016c4f73b06d59a69a6c7a6d90c6c849077f47017e89585ca6a2ea714e761ae4ead271afb9254e3a893d811d15bbe616a758d6663ba069df81731782158a7bbdea080642414245b50103320000007aba2210000000008cb40cec16846b87eebed1cfdbbb27a7f4b31a3f3335b5ee11ee2be58d79f343f2852207e4248aa12370173c8ee0a5f05dce8e22f134a7a06c4a81fc9915440fe4f4d97803bb4b24f9e185180d5aca1a303f2e64851b293d639370c22ef04c070542414245010162d3b62e0cfc43ba581ab6cf8e95ba931e0a5065457158300efb99cb99267138e0effd4f07ea7af063ace8fe85b148c9f7ca65a66be5b205a3f883c7be97e08ee61852feeb0591a7e02ef95ffd492ab489b7aa2b429f4b51479afc319551e589dadde80164f71677a055b810b0ed236fa92182de2e7b72fcf0340a4ed25bbf081ffc610eb9286d6c3db85e8c609b0b39cdafea8d2f0dcd39dfcb0ac55ef927297d6aa57f080642414245b50103030000007bba221000000000da914f8de059b0309af5fad1f092cc279e299e7e1b8269ac9714dd4076f0c33d68d90515dda1ac74302389824bc524dc255bd3f7d6681d672aea748747a1350271f60732b20abfca40c4d77c0c69ca3980a730c3367d9ee746f298f1f059e503054241424501017e8b3430affd622835ffa94ba76706e0dc64029d6bfedca2b3cfaef9878a4364a5b4fd38ff2227f22f42df116c0c913a7bc2468f17c56ae3fd53f46b5cff098d9acb879071691a718c408e76c61c7d85da51f4828d44b6b965854d89dc0c3b1fdedde8019f7d8ff0fa05761659a1494cd2ec9bb25bb75d7d4b62eadd76d05d8b59ecdfb94c0df92a4252ec5b8b4333f69dbe0afe081727699e0d58603f203c8ea0e6e56d080642414245b50103740300007cba221000000000c4cb51577e6a6dbc792c786de4aa9f61d3b0681e66c1b81da33af3012ceac925cc7d0aa56aeed76db51762bd65fa894835d4eb27850de674af1a4e133cddbe04e110552b3a6ca9ca657586ee7f9b77100cc267cf10b0dd10e3dc0bd2bebe2d0605424142450101c645d115b9a7324a8273dd5392449edadbf57e1532ffd71949df2faf1bfb9a33da459a0520c95a24dfc0e1c353a3f57db5f75811ac32884ded71d7b49585808d09f1202448b6580a8f70930096604efd9549cf33d9ef10252b72b4e3a10af1ace2dde80199e54c41685c1b069c23d2436284e8ea70208ad3eeb408407edffb77b5ab150b3c5e2427e42def0ebd4a25095d780c2ef854b0e8fbd78dc5b6497676b814c843080642414245b50103940100007dba221000000000426e30f139a8d9132d6d93bed9fc1785a8b5c88dc6634b6208fcf5af78b74f3bce967ac470d4a51fedde54d2cea14021de1dc242a4166f1dd5427dc9acac5204e49bde3fd2a06f67c6af10e63ea64555d5bd0fa7c4f09fcbd8c3341948bcf50105424142450101b8740209afa48c3d0fe6d7c11937cf38f73cf2b417d5d76fef16d49d827f244f43d64b383b645ddfe49766485a16c7c0f94f8f36f6376e4d116d9561fafe2083b37d7754d531ab425ba3f657fb81267b350330e1df8e51e2e508455ca66e1023e6dde80193b81017993bc891538ada24b818f9701e946017947e4b355521093ebdf995e07bedc90a62b9d5f27eaf03933dca8ff507e85e2b9a7453d366a33da9889ac4e4080642414245b50101660300007eba2210000000003034471d8471586dd3fd178405bbb2829da5ef9d73be8cb37d807f565e2cbe3a62085877914c9c40e62c5dc516816ec6efb3ee3ad5040c84d88cd4a63283c1089e1b6e186ae637ab18e0556d90a9b29582960f5f03422d217d5cb7167dd6220105424142450101129894e6784f458dfc9dafa365392ef7ee330086a1c1dcd7a2ed608cccd4cb08a8cdd842f15562c974dfd4eaf42d778c4512bd9e657dd1a8f81bc52125d51f88180d72e91ac833f6cf8f5c2f355b0ccf8f3e21865bde1f667293694a5865a915eadde80172999548bbfae6f93d1902bcf8f7b69ce82cfa1a99b1878ca865b1056e80dd69082717004fb6abdc810a4db70173163bd38a16310491b45d762ab3b8a6c84bf2080642414245b50103810200007fba221000000000526c7ca399fe80ac1872a4bc01b8c949062782eb00ede4dd1b3ad8000bfa3833298085e9fb60ee917165bddccdd9abe4546225e50335ea35f3622f2ddef4b201db80a8de362ce0fae9169428fdda4b09cb6a97da25632feeb0752e83b3c9f00c05424142450101c65995d5a874394488e61d3dea2e174104940dc9cb953c9195af009eee64c66463fcec439b2210c68e6235f647f0fd894fa10dba0d59711c7a49416a22db13811a2fef05846930458bf9305fe201a9c316ca616d3dfa75eee0fb5fd36cd84a31eedde801d61acece646b40f9575a1ee5a249eaed326a6142b925566e02c35540ff2d60256fd37bdd19aae3edb965fa98d9bab103bc50ee7fdc6f0a772e43d6a8b13a53bc080642414245b501013b00000080ba221000000000543fd07458a04795fcc24fcb0922c72cbf9952c9255d2c8da4eff1a35084ff7eb81be80cb4e02556c00232c3b57926e15b2c0bea6257ea753f425d0a635002089327381ddf5d2dccbe7e966a7f652f3ab8847acd009aad103c47131338764b0c054241424501011e3198f173fd52a276db848272d1b2b8a12ea45b8678354d3de9278935677f318a67811b086f660dfcd1c9983e410c499ceebaef35dadd50d67b3a5437974980ece91ccead31f2fb002744bd9d8a1ee17dbd16e3e8a944a40bda53188eb68e8ff2dde8014ab0265f7768769c2d1fb3e57c7aeb7021b4adb479bf3e6dcd9cc3b3f820df52db40a4f82f95c65bd90401887b8e32fe60c2263b87369f9882a8c76cbeb15a27080642414245b501037501000081ba2210000000009a9dee1ca1d60fdd3172b266938b169c18c71e0a738a82f78b5dd7965b2e5c217542071776e612e7478cbd57621d79aaac6dd0d7e28195290d8c7be5af6aa101ea4fbd610d6936e8d4f186534f2bf9665d985f44df0f241105ae648ece99420b054241424501018e4f85f1c48725aab534a9a808fa2488072934ce9bc4d2ff393d6a3b1eab83080599d7a1dec1c0d13c60c15c61a413612eada3a7fefdd288a5d0a1c668009d8ad8dc27ea99de7cfd23dccb3ee7525f40a482b64c467845dadae2e64dd857f21ef6dde801e598a7deb33931bc4cdbda863616a1a970f0b87f95360d8fbc6f71b3f057d6979c46ddae8f5a6d0bdb5f5dd17dc42fd5b27faf780a25e1a01ff0a6c8ad3792f1080642414245b501034601000082ba221000000000d8be515691223cbc36af18d9a0adff0fbc2bc960d997fabb7c25d0c254e0d93cf93b1317ca9b679bfa458eb10f03f013be97ab613cf32e0d3c359140a1a3870f09a80c6a3e15ddb1ed1f9015f50e421ac3347678e7411c601920f7f97edc460e054241424501012c7196b0494d16e01cbb60d39bb1088303a7b748d6e9fa23e2fd3cfdc29f5d657e9910d8ffa5168ab104f3e35521d79467118a0ac0dcfc63df2494623e95a68d885257cb0756e15f69549d3c868420dfe13e9faaa88130b66c6545f721507d5ffadde80124c7598a2204ecbdf2d0140c44c9bfd7ba559ab88c3916f0df7cd2dd94e0e4e7310f0e5639cf7a858728e8b3367a344e9db3d898bb8d27b216dfce3d04735b0b080642414245b50103f501000083ba2210000000002c39e8cc62d065326b96eda68f53cb717f4dd762be8873d79514488234e12247bc437e49dc117b363cbccd281e2a7dd3a9124444555c7d15101b48385406e90cd6df41faf2f35b402c6472d3e935e053b23399e8b1e182b6932ab0f9b662e7070542414245010162a1a614e74f0a6394ac7f90b58f26f0a4a23b9b29fa2647e907afedbad9785b326eb9fd4003fdc6de2b9dbf345cd6b1a86d23e5fc80fd8123707b8eae60968e7a4a2c0d3fa3af2ce9bb5df95a06d46325098cb67cb0e737c1d6f89085bbde4dfedde8010a604c9f3ae5ac5a6d1af507c0df99b7956a770aa14da5ad77726de285e3317b62612b4158aa5d859fcc6dc542c7cb9f75de343720d3fcea06d2b0fa7d2657e1080642414245b50101ce02000084ba2210000000000a37e09d5629a971cb9013354aabe9a750b1825d5e73b8f1e045c910c49f147450729d20f889710be1f47cc8908c4326e99bf61780bf805dee8fbb4e0035360146b43f7871ce4f761d5534aae5f0376c32fbc6231e33df92c89ed7f31abcc40105424142450101ec34b91d6572835610ef31905e2cbc9b2f1942c47b922c57dab539e462513b53b6a8bbf949b680c6385061cb201f7a6c3244182d35021093b2ed24873d59d8839c471962988a9856f9ca89d7f82fc5714e088d30c144b6fc6a9a79a64231b35b02dee8018e1d8b4186ae5e00fee25c7a9e527d8567742384f9e6d5b4d9373b9b0ad195fada0fc1145b777803d64bec4404de6b2829c73e4b3ed5c450a861fa7c053d19af080642414245b501030a01000085ba22100000000018324518703d28786ea7bb0ab068456ecf8f673ed5079d238d125a6c5eeb3232233532936075006ed6f6640b151af82613c747f8bcf1d15dde0ed16dd636ad0df625442d0760338b70c962689b0b21e28d114f7477b36cac100acb42db6b800b05424142450101584d92a4624400fc1e43f1d0df9a221043708496d2045c16f34222e6fc37ab5fecc6521249897c39ec74117fb4efca3f792934643be32a1a80a74ccff670ef89fe174f7a8fc0791185ca59545600c951c3f23af5367aeb40d3dc8cf626c3eef706dee80194c9c1032f53bd0647631f7cab09baf3e26e1a98f6ef9d50e90e94bc171fa355f05a3f2d1c37dd72bfe619b066386a03529d2ef1a7ac9ecff1a41a53bf97272c080642414245b501033302000086ba2210000000009ac7b9cbce7cfac684761e1e3728a8026b6e9af580500d3be0bbd8878020eb326ca06cf034d17e9f544dd21aad19429f8eda2d4219e718e0cf595a126721540a261a37c2a16475fd1557c868185bd300c9a1719dc5a5539321605939cb1ccf0f05424142450101be66865d320eaaae9efbf858a7cbf9c5d3a16575b11a8a40234799c8a0e488775013ac300ad985f49106e109a34057f8c1371ed6a65a3584964d6d0f227a5284fd1db347b8b5ded76cf991ec4156a63e81e8f5ab0b33e1d372d8afcc652e41230adee80176ac63a28c864e3fd3e0af080b40af3ff1acf6069151199dcc47a3700f16554eaf2e7eb347c08b53056373b7efed5beff8264404dd72c4163131c230eeba19b2080642414245b50101a502000087ba22100000000048f868a27782112a64b400bf9438e734ac1cdbf077f220a9df20dfe75a0ed11368c1a968dd465dee052d717a4d0d7566a2d48a879bf712f7e7e4dc2f3381400af19f9ac4f000af802ad40cec6abdf1e9e586111351ee6ce4efc961bca1c58e07054241424501016c95f5a7afdca045e123d19f9e6230a9b014bf77892c2093c14c7278399d9d7d0c4558eb8c1708d463a0bb073cf9e1001989310d09c2c334bba20d1a8788108a33273622e571a5b0963b0b2b2eb883efb10209e49932b58a3cd6bb1ada66d48d0edee801b0bee79fa71a0e58e33f217044256111dec3254cc74acbcbfed7e7c9fd02170350d1948a1c2e56105c4c7252e494baec8a98987d2ef531f5cc23436d59ae6af4080642414245b501037402000088ba221000000000b64e4c9f88e692fad9335f928a37bc203d3c9ea70d87b1b94ea0d20bc7656f4678c39699b654fcdd81b2e3e77e5802716bb836ff2336f865eeb1ee2ebda2b20745fb8b70439d081dbae3cac96eec88287bdbfb0abffb5a87acb1fdc8db05070705424142450101b20324c028032b469f12e3f20c62f6c459b1023a2638d333b63adc3e8806e41fb510f0cf49cf8f4465b7c727014bd2cfc30d116a0ad0c1b11771a71a849a3b8525a33ee1737f6cf837293d75c96a8b3ff2b1cd8f6649f482620039b01f644f1212dee80138398c3e20fd1c06865222801dbd78c69314dc9da67ce120c1a020a2c17333992fb79c75094ec6a2e4a6c0909bceee7f06bb233c1a4ab1f99c5aabcf514f1a57080642414245b50103e201000089ba22100000000060214a5e83c5e45e12f6f16a5d16554c057c62db9d0af41615594dc78bd6fd31178477a7c6043722056e3fd20b7707af3c3549f59850684191e53234e001ae02a6dfb9826bf1f9bde71a88241049299ebe1ba2fe4349bb6b9e5b0d0d970b060105424142450101f819b6a0517f80957089c6ae7b3ba87a68cdb7337e27485226e5013d942c4d58510c1612510bcdf4a6d78fb92be0ea2200ae892a05fe5b5df55d63b7c08d7e879e6f43588127656c2dabbcd97c4e717f5818d8c4088e7a01051d5a0fb53320f216dee80135a35ab4fc776950900c8287a99f0bfd4c9c0437452959d82e15b6be08bba1aa20132b92269ddc8e6efecaf958c5f0465fe2e1c921bd055905b8ea487d4815f3080642414245b50103dd0000008aba221000000000caba497ccdd554239506e86901331d437052b01522e5feda2cb269f4d72302604a653ccd8964ad2fdec141e366166b84bf658e5530bb68f16915d853caffcf0baac03543c4ddd860a23a4cc84b3aa22440e53b748c6cda9b4b3887a7e987f80b05424142450101c4aa1cfa87f688ab84d61020645312e22666b04998187fb9e226e36e5d9dbe218a135cb0a47098b9956faa33ff52bcce43cf4ed3dd43e1c39be181811bf02c87b78bd3a4092e41ae78fb1a8bbb644926561e43cdf81d1736e4cd03a11e438f441adee801e0906c51e6f90d9d9a24e97ada2cafa4a81470cbd7cdc3691def0c6612bfdec6f97cf79a0af7549536219ddbd0d191eeaa48d968228b8874ec494d030bd6510c080642414245b50103330200008bba22100000000052ce8c56beadb690a0b01351c03f1fa85e6124de07f17414067589c703836d65b2cca64ded02529a0ca908c85211534973b1422cfd0179b368a2eb188c741500d46a124711d12b41f84992d4f20cf03a0543298725c890fd5a676609770b0b0c05424142450101e6171e38d861f5eb328db4ba332ffe9becd2fb523dc86a7f43c97f03109f9d003e78d42ba108da52305884b1a45b0ef9b4c1c0eae2c7c35c2b23c46cdf8a72821ded393684963563a0790be156df97d2cf36bcdd729850385739d55c0b6edb4d1edee8015fb28d74bd574ed20c994a061ee64e1e073864265a4cbfe7d7a0e58e616ad953f25ecbd95cca20bdee66a08d421d8c4b08af170101e8ee5a5afb05d8ba008277080642414245b501031d0200008cba221000000000c2b5bc166a432ac812996ca8fd111176918393c9e1a165cbeb8f2a3508d02c5ad98e02ff105fd1c06b74081da34ebb86fa7b5746e61ff6210e9d57c86ff99b0d037acb8f903076fff89d0f3b5ba530b950fb7fc56e372738c101816836157b0f05424142450101c29077ca259afa25ecbe585f60e32a6604db72109433ac75dd70cfa5c16e693029fa1f171122aad4d995206cb907edc19f564be0086713a70e4f35169f371e861810b721992af2b7c64674a10189bcae828e70773c433414ceb4a64da32398db22dee80103ea5d2802730f4691aaa9819c3aa2c0d29f47e536737428ce89ec0caab4d71ec23450b73fba514e6d6ebcdda02962bd5c68f080330a113fbd4b20e67804d86e080642414245b501034d0300008dba2210000000009035e8a432a880b3880eb603383d68447ef69ac6402c3492686d0bfa97b105026672ab7cbcb0847668f9d5c273f67f3b7b14bc8bb5e944f47e6a4ae646ade50d09391727bc3e2aa0b12cced73dc0d2207a672615657146ac35fe953a84cfe00e054241424501018849c824f8a14a2dc6de7395f3466c4fb36296fdb8cf251f58a906658c72d44525b3a8c6afa458a2250ff2331f4c9213beaf080870ee489dc9ecf73db5b0418c9048a756f3e3d8d49c08e37c9dfc08b2b05ae2e9340727e294bade6ff0ed7c6526dee80171980f68623191df3b1a9c4620413a69baec0ca08f8630cd382a0432cb893f491e04f97c9b94af94c9e362017557d271a58b3454d32cdca310af7e4bc3afdf23080642414245b50103670100008eba2210000000005ed4ce7d598316d1d8485a3db7a2fc4bf4c28994b265a1f14b69bf26badf3b149f887e4552208c978290b06b14900eb4e9d26783960f0a0a4b266159ed84ee06b88611a03f8885960b7e814f7008a00888b32d25a4f4b90a694992b576c1d3020542414245010124423bcd4e4295ddefbdf6a74e62d52305eae952c8f6f5bb992102d9ce98970fe6791b37c1b61603029e6931716f101b3c4441c4da25f1c61f7277e62207c1890698b1e347cfdd870a8f806855a57b2d8198ee7c8456f34b4137422c3ed6d0362adee8010296b5d522980c3dfdad7c58d68453a287c51b5ec9ed3f7c4735db6d9850a57a11c1def0b14be08a88c1f251f4ab3f2e183cf41ddba5cbc78d40e56d0985976b080642414245b50103060100008fba221000000000a04599ee3d2356dfbad0178e53c3835fb9b3b3bc07d16a43d7b396a401c5856a3f65f2115f6733f2c0ac2438dd215254c2571b977e6f5021134cf36649151100b6f593305a22ef160876b1174cb64cb4fa016f196e9bce86eaf6df830b343c01054241424501018af6ca0a70726decd8faf9a3b7dcf8e827ed9a3fe97e3aad1c9954573c85aa17ad4c82b3903efa95f880d054a4039d471edfbc1a33bc9a82904080a533f3cf8445bed8250c6838a5c5bfccab85a81420210eec60702d576b200652fa54ec8aef2edee801a80ec0de5b4cb04e38ebb64432a55e3ec8137636dc55e5b2f73292fab84f9fc608a618819eb27de849d878b5942502738496866cb0acac77f7601cbe9a34d63b080642414245b501031900000090ba221000000000f444e8339454527c8bff43f11d92900ebba67ee6dffe96021480908ee6446b1c29ccd67cf4e23be82154a3b5e15e65628ad37638256ec71226d41b73f77f330284bad66cef35a71b4f4cc1f532f62919c1b24b01d533feaba6d930471b979d0f0542414245010150755a8f1933ee0c4b5c0c11e043c16cd7b8022ee7c291e96173262cb6ce8c0ce10a00750856539f66d2e0b00a0efda489beb7f38189f1b5d102442bbb97318d557677b1e4094375d898d159cb2207ffdabbe6bd93a6b34cce5437bc1eddc59532dee80159c36496ac3458332fde2c1aba5791f0ee36727cf5e2d7ac8770d61b400cef00772f17f6ceb6e6e6b676136ddff85d8be1a621c8edf0982dc562ceca440905df080642414245b501030e02000091ba221000000000308e82133f46e5b3a21c8a11b966ee66adf368820d4ce12f8357a23513000438cbe16a0af4ae3f1c441f7c22e985391671e12aa20166fe282569749947375500e4fe9cd97a3e2f90a2087521cc38404eb4e0fa9d80aff25da2facf0a5291f50705424142450101606f3952a936de2f2cc64fd689f95187aaea1b7f8d7e2074d2553d264c0fe303adcbbdf97d65140fb415e08467ca1eb83b0afbc1949d738f5039eb67b24d0a8c2add157d132579693c9828e3a740480e2f79acf58e0e8630c73fa8e88a08087f36dee801a9b80065e5f385c82c85fec843e82d8488521d7e24bd406ec02a60fd492872fc47daf456f4461b95621b32b4c6f6eba3cb85d685d3d271541fc3354fdb59e942080642414245b501033401000092ba2210000000009457f17713087fff99140b46ee95e9b4efa238ed7ce8f91046dbfa3930167236530c9c1db6722b7af142059a3321eb5351df10e8aadc6f533391b3da02f6590bae4c90208aee7a2bb9863cace4b7ef0bd9ec5a0020c58afc11b81f52c3c1930705424142450101c6d8cf9b203d5f6d526e847949aee817af4f865ed60b7972c7695e8c3287f47a09aa06827df14569013abb83749283cc27e4b510754c5ff0ccef6c16ba09bc8dfb8a6620f57cd9030970d7992240f5a636a8fc9da7c73215ecdf2a6f9ed61b653adee8017b91e357121c80d6cbd730fdb1ab4140b0e2c967cba5600e657a6951a294dd67e19059df960f435556a92f4f6a769a9910ce0c79c066bcf05d0c6d144497b477080642414245b501010001000093ba221000000000b0321ef1196d5d15b66bd1563dbe7abf7c42c7a5fa89a20e8a43f6ee8ffba41d4d599b93e848046fceed6f6ad43ee2fc5d85f275c095b286b2d19f6be141270dbd1a4e4a5d3cab1a37e6aedb54acd34d10fbaf30080c2ed6511d935f3492ab0205424142450101ecfedde8d841dc5a27a9a1d95fe681da268bdcfdff8854850bce21caa184a84e9026c3b702e2ad1f243cbe06c32e7265671e284e9b9ed0a8731ae286213ddf812990712233a9bb0b66e4350883946512223926154fe531030f03dc573949ee1a3edee801f99d16bbaec511d0e6ac76a4e33eefbabf1fb0c358879bfd0237c89dccd93d1a7a12f391207c061abf358371e365d79316526eeee17b7bd8ce3b0f6c65ce40d9080642414245b501039f00000094ba2210000000004205b9a5b68abaad9bbd26d7e2b0a6e09c253d3b77d63cf1276c2b007fea5d78fc4d284044b894a3d534a654f325a6bff9c220d2bfb1b91943da04f36d561a031fb9927eb99becfb7ab3a750efb9a44b78807aaabdf414cc2e7c76686f8f780f054241424501011c8a9cae65f2692c561d9e9a01a9a2478c7133620def1a558253e279c4f31e1268c172f0b3e1c6673f66e554077639b15239ac577b71e5728fa4f43a6acb6f86a579284f58b4175d32420bc4ee8774470f22c1c8a3b294a18e5f722a1c4eb8f342dee801c7e7da8bc9bf66adfd49cbe4c4d8acd06d9189096c32bbd9493bd63aaff07a2f3dce3949f765272c5716a6fd70f76da89830c6544707fed7e80847b46d670854080642414245b501038e00000095ba2210000000005416466180e0c3faf17a3deca1a788006a5970c868e98daf8714b9ea0bbb2e6016c6241c42bd81f34c2f10feeb2105d197aeed0c6d8f3c999600fff6f6aa5903548e3d253d90be5d18f8e1d179a40d772372602732024895c92d9384ffe9ba08054241424501019282d48d73c599b9ea734db8d5bec819e0fd67fb1f99e707fa29c0d7e821af2cc4ae417fa6c7e77baeadb729101680612ea00e1cb16c40f177227cc8b4d980811cd1bd4afb241b4218d0e8566c4c0f073d4c5590e0afe9697749830ba0ba681246dee8018bf76bb83c6f6ab612b96406b7475cd0dd9c0468187237a974f4c91b2606da676aa344ac9f0da432ada7ff9a26d5c1386bd3a2b6344bdb8da48cffc2235d965a080642414245b50103d901000096ba2210000000005a0ed90c03e94fc57ba1ccf077dbafab483bd8ca325a3897e23d5e17f3752856c043cc15f56e0b6461ad1fdc798e268e0394551388a697af5e60600405b083046573da1461b1b90fcc11d9797ef1c4482a43324b9eb8d62aa5d5f82ca90dd204054241424501013c96ab9cc689639509ebdfa836ee00b730c652127b026f52cb278bc7ee54f62ee7b6cbd8f4e1319dc7d71d6e9a384c1bc83aad709f97b7e84f62ad474ac04f8e2cef9cee0c06f584f3a24fdd83b9506356e96520be0d6f60daba7b9d1f7638b94adee80143a44b53243f15ff282fb7dcaf22b74fee4a9d13f1afdcacfe937e0e47d04ac01c2b4495399212de62144236778cd02cba73fc052d9d7301ce95035d1e4236ad080642414245b501036d02000097ba2210000000004463db3aa42472aff3769a7df4f359579bff1a2a4345dcd45f6faff79b971a620046b5fe7928862039ca5f5d072a6e54aca6d973d3e6399ffc9486cc2f80810718f67b1952ffbdffa4ef54725495988883e037c05f3f0c2fb074b4a4a19cde0805424142450101044a86fb42a6f78eb13c018f51086133d8f2d58035abb4463cc034a144b8bd022f8e01a85b165a7da8c9a93335c6289751fafd0e06b20e3494998fbde3dd3d8e14f96faefeb44f4bc3bc6517ef2e198e6b1b584e21808dcfbfc16df8671647dc4edee8017f3c6b34091cd1a8c15134cbce6bde79bb5ea3652293670151e05208bce74009f050a3a5ad3b48423d5922a68e379b6aca625b7208549acae3e758002f2d7b07080642414245b501031702000098ba2210000000004851f5bd13465d36c6152d0b5707c2994aa071528df3e8f1c262db2df77cd73bdeec1eac8a7de03902d6fc270d77a0cf5b3a1f16941fa703e2788a1ad4c6e80c29422aa1fa9873399b7db5a3d1f2a8380ef84e8081c9ad3030df14faf849d4020542414245010122b9c1d90ea13928692605285c2ee2f8e3e8d9a4a2bc3a5dfb2f44f33bb7e80e28c997efc4c200f2be7d15ab7fca2e309aad88a05a9205d37f19c437060f0a897d7dbd193bca101fb7b809744338076d479f85af09e9cfc45b5f92ab59c3458252dee80128dd32e902daed2f01ffe4eb54eb13dcd585ceefb31d5f894093182818ec24cb69ccfae899cc0c27e1d08c3852326a5ebbe330d6554d75054f334488c279e7be080642414245b501016f00000099ba221000000000b0d3ff33582ffaedd6c3b5b264a2fbb77ca34e58fcab91092d4064ad8abe3f7e677c81531d51ec857b4d9f7818e7e82f2b849442a074fb2434c3908b42501d0ffb4b4b91b284cd4d4040c15fd1d699a591ff013df48af3f1e34f593b9db93c0405424142450101ec5da226927cfccf60e237a47712e9b15e72c1e36fd14f6a3ceb6d18e3c729242c1cd9e6c4bb06781fb9ec86b92a3a0fefd168c7ce8942a223d036a67164bb8af4a1449d07437bb324cde677e6114d1392c59e01933d47f7f855f4ac522021dd56dee801a5a31ac9e998e4d9323f06fe1bad9d9feabe2d3f625ec20aed10c8cde245bdd8eb95847a2360847ea3bedf1f5299c6a345fd46405bb71cb38e81beb2323c1a51080642414245b501031f0000009aba22100000000088a50258cd8ea9b5e54bcbdaf0d231fb964302e82ca3efa4b106afccaee256426fb7d6176997fc7b6893300de3372859f977e17da8fdd42e5babad0696e8f70a15831b41335b0bd1f34ae41a1f5d65c658b2417f6cfa33b8ca197b0b3712130b05424142450101d0dea0349334b264f7ef236ea045e984266b200256cd015574a99d3bba67362ede4b9693e90e0dcde1a7a10e5d74ca3bb92445f3b1b8fb97154df5db25a39880aa9cdea45b0c35d87fc7375ed55345a7869550f1204b863e6cade334cf7297505adee80149348ced6fa7adf6b42a11cd503c6f9f4ea25fb97373ffc8d600aa3a53990536d8436749531776c507dfaa7af8e82f533b512e46ebae9220d06d32fa227ddc36080642414245b501035d0200009bba221000000000ea30c3ccd599d1969e11f9368c6aecc0bca01094f924858474a3924d96b47b76ba1f3c6342f3cbedcfe36e4fa10fa64ec93749cbb0406b33fe79c50d2ecae80ea8cb9f6ac71ef22fe5fbe7e59d45d164c3a93bc87693539c6c4dd189819d5d0b05424142450101187b4139bf72e3d8bbca1f31c6ce493f9966afb5bedb6bd86187cdac13fd3a52e12b20d5b5d5508ef17a32498ee82208042353839d45607f07fe07e4a3d1fa8850b8dea7f5f2a13a776cfe7d820f7960cfd87c8c5984a93508fb40aa8dd598e65edee801a387a7c02645b67fa4e41741ef6050428eab95f3a0d3b5367b6e00157d59d52c2170296040225c32406167e96cf048d5661ece7a6128c7f9971739679e0b8b29080642414245b50103f70200009cba2210000000004218767eb933c104b5fc48fb9a874c56b40e095d06bc32fa95fd5c7ddab98a2577af07f751757edef3b4e8ec6940c9e3c3c6ef7cbab633284f621b6f25b6110731acc27cfc0a2565bd7cfd1e226f49fbdce5be8a6967a05e8debb051ecdf710d054241424501010c1909b5a99586b0a35033d5c074d6ebd1ad06fd170201fb8186f19a30c1d44b690373a25df5ba5f876d3bfa78db31f8c66ed69047e0907663e9bd6d07c4818ec63d30d74aa7174d34292c41f65ee3c9996b0f4d2dc8b0b084cd01eb62aedc7462dee801a176240ac700e0932c140c43a88fa45f208c0029c6b0e0c2d6d0f849227f5938117010cab5e78c5d64619387e676639c81bbad4e584d54cdc8a474850417d0fb080642414245b50103a70000009dba2210000000009815e8c20be138ebe48eae07fb2851477efd7c099fe73e4ff9116f7f062fa936e83601d01d98f02703afafa4304e23c8eb7b47f5df38bdfceabf284b4b54720158cecd00d6cdb1313b61a9ad50fd2ffb67151cf143e18b2e6fca099a5cf4cb08054241424501016464744068daf59afb85408beb86285b7e2e2d50ca19e112fc348052323d4d792262d7c1dadc91a8dd7697518be9e895d64d8ada0fb7f6b73a91ae2048ae7987cca81f5cb23e8a824c1b62e9c3e114f5bda0c146f86a3980938a8f684f54f2a766dee801e1aaf3c3bae0608a50fbf681b69702b63cef4b8a5ad0d3b22a00d2398cf94a18e50f3b2c5473a075c6de97a5f9f87824ca4829a0337d2eb6b2693f20cbe969cf080642414245b50103900200009eba221000000000d4414fd2f40eea754fb0b9752d017b4c113a1772653036ab28f572139bfc6c65e0b0cb9a3c817e9e42a9c4a851aa43e609887dd7f3e2164bc4aafc47c3028e0749c091f30c71e2e9996f54c95322ef3017cb0f6bdcc812a0f15673010c53c90e054241424501017e109ee038f95636e8519bfbd985f0d9eac4f545d4c5356455025073be7e4d559a31a3bc4a34e38613da2b6d0c5699ebcf1aca5f07806947fecf4b38304e8f87a8770185e4e3afd7863a84ee8c9abb37c0bb90d24b35688bc51060b80e26f7a46adee801c3a69e219635c3d3ffc1cf67f6409d23fcbe40ac4df8b2711731085e666471cc56171c7757905d2afa5b678d4c20d19d3708859cc59536e0d1bc3413e581b3b7080642414245b50103de0200009fba221000000000309600c3c89e0f7423cf70b8d743385d0249a04410f11d918cdee5000802ba34b4ac5e6c4ad68570876538ebd81d7b05077d52b1a1e27c957b27731e2629640eb5c3b5f889eda0ae8c1c8793326841f122eae1b745dd2352364e54f2587b4f00054241424501011290648fbe40b5e39d72ce5ef7b582c4e1b691c86e31213a9c5df21fe6fea76eee1585f3be8fdcb46a7c78a4010830e47e8674acb9cd96634ca9fd2632bc2a85dfc2c87a74eacdc9ac20ce4839f57a18838263df61b67d906cac355a19f2b57c6edee8012458349780f3369c345c04a10122956d117bd4cc89d1f74d6c1154af8b54dd9e5528175e9219ab4cfb5c04173253fa2603b382adaa7bf0229666eda96cb6e91e080642414245b5010361030000a0ba221000000000acc952a3e5f4f65fe75df9f92e79ab567dc1f001b877936db22b658c6ea9fa54e2c88ed23c380773cf1abdd86bfd0ab71f15160474554b2cbb6ec46de09510066977e58fb822f8f908c0ec5e378d88b614635030d3e6118b2f33236428ab3e0605424142450101e456112a5267a8e6aa7a045bb227fc1862de7608f96468c917db51bc44dcf410ae19d308ba25b2913e3d55676959eca356d797f4995027fa96479cf5e8210f8996627442f868cd9ffb1902b38f0ee9654944d95968c910c59fcb64479a19757c72dee801135cd090b63f6cd2633d34e46ed5cd04b12a71234c684ba59799026ddcaa262e41188305286911c35fe24bab42c05d59ff5b2508073edda2c8c52fcca0b5aecd080642414245b50103e4020000a1ba221000000000363868a6525d9a613ede2dd4cb423445aa8d01974ec7946abafb17cd9270b50105fc348525a637fd140993942f211dbcc709060fd405ad6679fe80a48836e406a3632a4a6a6558604c5431f9b7c7e7e90b77bd0d7d77c0eccb9456e7c7fe39090542414245010188190945194f751cef43e3b7605ded6c11f9e2af43891dd4871cc6d094bba96a68b58955755cc965f39b4332f3de121c955480805fccfe6405420a3944505f84f241f5cb1cfd32326e9af83b441783f69037860b83a4089c94d8c7acf91b92de76dee8018059c0d808058b4932492c7fa89a0b732bca6e90ec8ee635cc4d392e77d231edaeb24c635ae79041054e5fcbfae824b502bcb139aa9cf2050c9ca2434e1d7a64080642414245b5010359010000a2ba2210000000004c6b4dbef45e0dd13e33a9efb4f8d634ee04fc70b45d4352f5899ab2f902311c6d86bf0731224edd572b52550c5d95090b3b780a36660081805efe5dce92c80bb7203b431b355eed619d52604d7aebf0ccef241f1a00df5acb8b01554011540e05424142450101a049258d8cf0b4038fb9ecd1a73ea97a0739af4f79dfaa6a0f7d6af4aa77c002cfbf952f3de4528a656ed23a7c46528b3afaf7fe07209c3a8f4a63da86d4b2838c2e04bb05edaebddde60761615518da57013fe34c2aa464c5ffdd6738dfa6467adee8019ac37f345408a7d31ed54ce745853b595ada0850438290f9a0241d1f7c1467c855c99ef3ae706d1c12a9e117beed1d2edd626af6cfb5fc6b83e2b1240738399e080642414245b5010167020000a3ba221000000000f25fd53317791fe271f3645e73146fbe46e2f1ec9fc7794440d426249738321da377c75d572dec6f8c96b98170867eb3ae4ada1fe92b2641daa11b1a412ee30e3385dab73359988b9a1b0f129d575d172e4d255f6b1fcdcfa67e5d4b21946c0d0542414245010150724db89353aac8abb9bcb8b358a52155d0b22472b8595399064ded0b73b61639ff06f8f1dcbc03e11618f77d6cc19601be59bf90475ae9308e4003f7449e83c83e1281a484bd8755e6d5c99301605e9c524c03c34f2e6f2b3cb4127a3150fe7edee80126a8f0034a8d6b71ebafb34ce6929c043fc9d0523f456cb7cd7429e8c498ba8da9ddbdeea100b3c270f66560668ebaca721eb017991b68740386b6763138bc84080642414245b5010346030000a4ba221000000000f0b2936336cd7ab24e92703784c194349f34adce5794b216b01f762ceee73460889d6bad542fdd741dbe73a9452f595595135b0a4cc404500d7ec8bcfcb6f4041c9cd5b766c7d53a8848fa21913432476c94e68e323a8da289dfe4fe025079030542414245010150ef8e0d9e4e434e0d9861b49f64343fa07bf0ed9d3ddcbf0633da935e528d717d54bdbdda8ab52ff262e3b1624cdf3626969b32f0a7f30a28c863f753b51c8d84d60451dcde09ed773c90990e1b9fd1e1183d32bfd05a27b7148e04dac0232682dee8010f0a6f40a746f6ed2569dc938d08fdf52b2907a022a3eaa96b99e0f1cd418ad2bcc42ecc3de185ee94e12537fb3a70d8b616e440235b8a0fe49b5f0b2588abe4080642414245b501010e000000a5ba221000000000984fd569c45ed27121107f6d47f288d24db11406bd2a05fa4b32bc0dceeac44700586cca5cd80b14c657e557cf3360c28a445ad987bc766378e8cf9799f22b058931b6f3176c092dbf3fea8f0632e30521c10e27b0898d60326de6c74508be0c05424142450101ca3297c807b9a5b8f89db7b92a0cb074fb086c424f3d7c43cc515e24adbf6f619f39cfc5b11680315b73ba8214fb99c2bdcb63940dc0d190a601cb561f31a58f815664a04d6da5278435d171ca28e2eefe7962ed8cdbad4ca37b0cb1a42b35f386dee80104f2a29c9ed71ed5bfc40f3c165f3893272b202f0ba5f2af3703488b5017f191ae85e9eb886a32cdeb4fceb67d1f1b3340745074a12d0a0b14e2d3351661e33b080642414245b5010324020000a6ba221000000000b6ef91b9482ad71119d853bc5b602cdfb4145883a9ee6bac280087b1260a0c33ada44d2325a73fb5eceb953ed44dddb5531b451fea98e234aa31b52a9720600045e83c48e0b50ec098b5f0246ddc03fe47e7780ce3d05d0c53eff8f5202fe20e054241424501014a6be8e8b320399e97da877139ce6fe4c7f804214064d8796b83111498f7044ae0d5147913697457a96be1b11e330bc2f45bdc9bfbf1b050d1139c316448ff8cc1fd7f91f87e6d4704d468d48a538328c0b9e80e327c8cedd3d13d4d6be54b588adee80123437a17c11e1ecc67ddb8312cc959495cc4020c0b0217729a712b8db14201613e4b29c1300610e4488af0217bfd80f19c1ca022615091aa5d8ea6088e22f175080642414245b5010365020000a7ba221000000000dc765b758195d4c0d0148285639984086997c89e48726cec3112c109bef9f47b79dbe24bec47c2450371b3b95bc3642fde82026b47c0b8558091e7cfdda88c01a0c618e8a732c801c2cbe311c1321220f2bcb834d15d4a515657d24eb580440205424142450101d231dbfeb10ca9512aec42f9a4f5673c6efd0194a1406bd229899234c02c1b478c0c1aa3a2169ee2ff7be2b3d479a90658879e9069ce6fc664dbac8f0a27b68881fc56c90b7e7544b07c391d1080d715dd32bc87fa89d3381617010c8b722e3f8edee801e99269293eaad912398cb515796fc3e28360313b86316e50f6734a6e9e4092440e69d17046eff8ddce3c0d2a76738ab30217a7075174dbe2d4bae64242bef28b080642414245b501036e000000a8ba221000000000a244154e76e2746100f16c7529dce3de7d39a9412aa3efb71891a974a8318b7e309b7ee8f11eff54ad49567b751523651f682141818b0e575075f3ea18f56c0194a744c21741274ec8a63fcb031c42b6311499b0f37391beeeed1816e89e850f05424142450101d20371dbc4605b95d439001be9ed36db600bcd940b840adac4dbae808ca13405692117e264d6f331e41cf6941adcfd38669cd8e6f3eb5e585e7d5a0487d69d8b4bf4d4ba834aa1b806fea0cde416bb094748be542825b4ed9dd1ca85b967e7d492dee8012813585209c328f991a6f8d15894bc76ce64dbd2c72247c9d83679de3229a7aa5f678723b1b9ae1008b94f9f8dbc442eab1487b434113eba7c010e8dc0589c66080642414245b501036d000000a9ba22100000000066577f9ff9364cc19a0258a6fae1758b5699ab9f6278064f953439c49f561f42cdcf0d326ffd4852c6696a60d0a1cd509bc24d6c14d918452cbc5e7ed21889013563c068cf24ed61e0415b9dbc6fae5b8b5f3b0553e9e87aa9d53738d2afa30005424142450101aea196dc46e9cb1db5b463da51e9eb4f0d1fbf3a2c3758ed13d9a463e005690dec9572075e56d76c76519e0e7509312b72f069db49d29359df3639f17f5c5485a4aa808a686aa6b787b832465959ed4cda71d4cc665ecc6ade3831f9e3a32cc596dee8012d9f0d0a1a96b2cfa0376aab44cea55acb8b3e4edf1ce5bf06a87ba703d6fb8d1704b84d110ffe2836bf8d79b64b2636e1121d29544e023c9f7617449206c233080642414245b5010378010000aaba221000000000522fb24e4be4dcf47f8552e041361df75a3dbd8403cd68aaa25511aa30213f09ae774c9a76ed0ecb38fa2834dda18d98dc75108d6b3d01fee05ab951d1e5fa0a35a52ae9ae6cd8e4abc90176abe73c4940606f577a49a31e9205924109a5d80205424142450101387a4be809e268400a107a6b5a3d7ec347e8a29c78edfd45bfe58b91201b7e1b75a12b91b10ea00b8a698ab1c642d96c6e47ca21aa36a4bd893c0af55c322788c00a03290318dac06f4f032c757072f0151ae5ee6916011b26b76748f2cb7b619adee8018d849113cddb75f51e310d8f99cdc270dffd29cfd721e32ae5bdd863e3f3425f9c12a156d83d5db1ca449a2bdbcd0d31d0d684cbde728f3ebe52b4db5116b584080642414245b501032a010000abba221000000000cae5adce461f6f646d26258bb199d635db8dc561a0132b1a3ecf02cb404ef937e6384a2687a27e2da01e527273e313437aeb10da51e02440abcd90a97e3ef706af2d5f5179907f579f581291d263d62f431b8ce028561d9bf20abbd6cab2ed0a05424142450101821eee410a9b3087c81c2b5ad08f1a5e8838b7a62aa31a7bc63efacc8fbb553fc06522df1ecf3856623365eb346b44379cf5e8dc8cc36b28d0b3add5a5c8bc8a94e56b91d1334f2798a6844394610ec4802a1d9ef535944a46b238af76c2d2a99edee8014e60489c7f7552bd9acc89cd15ca491571b9336747a4d19118176fbaed16ed2b259769e251f575ce17623b55485d7aeaa60e7c20631b30cd74b7272a756b5647080642414245b5010387000000acba221000000000d6631e25c8b9b7544b36c405ff0b9a92a747c97994dd9f827a7dee25962ee563abb164e045cc06ad731c92b71756eb3cd59bf779434db45db3ed43f1f097190c348c59b2f39b7fd6cc0b553cb6e58d6116cb770fcb558e7344e2e81d9f2cbd0f054241424501014cec39b40a9e1e784f7c91b7d8c962450752d9ad949dbe83ffd8c305fb33b249177482c57c7039adc89b9f1a7a4e0393c244b4c6799a5e0574744e5dd5f98583b7551a378704ada11f8c1f05b717e069b1630fdb10c7ccb04c37d40f09941ddda2dee801ab1d965bd336b11b4ab524c801967a58bb408a968dd5cee0925222f20da519683f6073470dcefd75ed88a7d81206c8a07fad6ffcc3b2bb357f8c3a9451b7266c080642414245b5010359020000adba2210000000005a81a4f7e2d50790248f6e70579b3fd81d7abc4c78c6daf619d4d14c65a0c87abf18f1cc371f68136f696cb7638a4e5c7bf388932207a5f9a39a78854ac209046e0d0586c3206aa09e7e641ee5cd088e9a7424dac57694a900b489d6f219860b05424142450101acff53ce9a76ae912967683638e5e3236f376bfff6fc69f5180c79b395b12243d5e28340ff8839caf83d36e737dd28c970457f62f7f1273e0cee636d8b6855893712a5f7544d1f689d8d9ab22bb71ab7374f084b7ab4c9f12c9a9886d07ce61fa6dee8010d0053a1e681f85f15f354568a3dca80cf90873ab0dd218366d5eb6be3e2ef845d30b8367b4e736ab7593bf413dbd3d588e7ae9a9738b0494655dfdf9a1c617c080642414245b5010394020000aeba2210000000001a2667c910c1f25e1b1480a671dc54eda6225d8083983e872c766af3c993136620361842347b055ac40b4d8b3e52e5d5d22f1e4a4132e6e89117c311d22ce802f861b917a62e3738d07e0977136c0ac298c8287f1b25cc5001b5c9cf87b377000542414245010160be148ebbe0140ee5aacfbb97f06fa45ca54247198b02ad5dd3f4fb70a32648ecbfd6a58b4c6e5294e6e75b73db3945e102110f7f61f0ed0471dc015d466d8440015511fe5424074b37d3e807a1eb549ffdf51ff11612003f6f6bc29102c145aadee8018c01cc00bc017b62ff3f3bbf56d3b281dc229915488a8e540024d18b3eb96255a47b1d98a2bb54f586a23537e298090dba97598b3e49ce98a3b5744a25e36ecb080642414245b50103a0010000afba221000000000a4b54ee2ef1f9a2dd890ed92c2b01a9f9f740209e22d554fa1e07115b07e275e7ead4edea4dda2ec1ac6cdbaef39a7b756522faabab4a598644fe24ce6a75307dfa7310cb23e8081775c52075c97815411825f1eb5084bf77fb03f6ddc26d0040542414245010118229a4315a02607353acfc9b3c9011facd1be1e07a7cbac67c53842efa9e1073cbe9b5ecea2aa69ab6e112d46fde978ce68945f41e3f733749b4d3f9e580f8599a9565b33e0a8aacc705afd1880708f7cf0304a2eb9450cc141214fdf6421d7aedee801e1dd5efd3cf8599e2cef2789480caf4683eb1d4442f1a8091da8aac772bde4560e8eb68124b6a4f33321510a5305fbf2ab422a6886a20846a34431e596d7843d080642414245b50103db000000b0ba221000000000386531f08d55a9f988411b81f301ffff8d019d97d9f3375c1043029ecf615850b7119fe0034d4c1a791ca40768927a68962d934ecc4bfd2266a0df1e5e62e209070a1691c36c68ce533eec3baba32fd9119dc32e566c19848574835af0da640005424142450101e23ed7fd5323b543ab102a7a1b42bdf5d1c4601735af81f1c5b34a7ec8584136237352c9f1ec3044d72f3a670bf57c1d3573bd06d2b4be580ebad1c412c7ae8b38c5504e9c2c35f3b18068bc9f922fc9253958fec40c1fb246da942fbbb501d6b2dee801a6b1f7ec863ce93099e9f7e1113e7a187270e91444adb19794d0399426bb26ff6811db53624ad43ed2873c62e0e8b200d7994dea6a9f9529740a1dbb393468c9080642414245b5010306010000b1ba22100000000084a80566129a46a9b22211009908919b1e9e7128e4ab66699a05e6311a45d4001aa00e1438f592bf3e58a88ed4f2d57ab8eea810e1bbd608a10076ab6e559707237b10b8e6f0379469b220d6c4e569b8fb6d4b77ffaedbd5cdc552adc0a84b0805424142450101caba9c3832405d96d6751f8c0371b12889ae002f0db3a67a3f57b4120db1d83b56b72592a092da9c4c248a2602c9bd1d591619ec87db3a98e7bf7ef4129acb845e400ad8d8ad1883930c5c90fd24370c7b28b85577725ef34cd7e3ae79ba3e9cb6dee80129d2d8d28dae0219542ab80b40a3f5f2ccd83dc7c24fc610e87e20607782c64fce9a2ae3ddad84316c82dbcab2c8759ade8c0ed983fb6897fb50d655cfed6b51080642414245b5010371030000b2ba2210000000006a6dee9dbd809c2553c5c9a017ad6acef6d7213adfeb87e184835756de8b9a1b819f7b6c6392b3cfd9760e1060e3bc94ef4a2616cf864933a134126ca2e89c01ffe2437042e4267be92be88296d830d1101fc680d0cdcb3c5a2408f3979c0509054241424501019c7610514a5c2b59b1a5121a2d217c187719dd312ad8335b206df0a36111e84beeb3c5d9d8b2a4dc9f63b3334ddece4b408e4ba123b120794ffd98383918468461761bf6a47c5429e24d3562294f5f4ddd8f3bea3162a435671b28ce012035c8badee801b39eda157003c695f807096635d9ca6ba26dcecf34b3d0a11f595f8b4f2d38658f38cd1ac612dab212f7bf78cdf4c160d7f1f70e5aab26d3ce646e558fdd91c0080642414245b5010313020000b3ba221000000000c64bfd694d7ab7b4b8764587b3360df7e490c0fc2f363680b89c4af804814935c805dc592a9357d904ebe371205c58985b442b4a67d5c67d99f228c5f6b501004fe4add51eac6fa350f647bb3213e0016647ca3469105bf6bfaf8816d41f9a0f054241424501011cb93bb23345bf8b544f5d0271fdd580062f89bb011f78761c2db1c7e4c458374236d2ec0e2a63b44c9994b8e564b071e98a9287c7fb5960a8b62fec20f3ad89ec8b6b5a22ed219ad2d98bfc75e5aa21095bcdbcf81160927e83f6a48a40fea9bedee801935b03e8a76dc2d386d85048caae29548f290a59e4f61f80862fda817309b16ae49c151dc433c8e2260e615559ce40064dcea452ef09510173c85b6324740d8d080642414245b5010332030000b4ba221000000000828f13fc43eeb6387b11bcaeb9176fc47a4323680cf63c640178538c3081672070ee91d4ffc93b94167c9162b453e99acc3e7fd4b95a9de1fe5713a3785aa80f46a94971a8df92780ef07a903652f99b06205bf4648001c0e6cc8b46a796ca06054241424501013e755acea31d3dd0762bd4d504146da8f0ec92c9e0c7c793b85c130065d1205f4d009c4136fcdcd6506533cff6f34f5e144b882d16c14dc309f333937cf2bb81368e1ed183ef6a14b3cd8ec06bcc3f869342043ef423165031fba947c5271dfbc2dee801952579fb2b5ed5ed789ff6b51aff8fa33e8e8469c13b34d18211b678f5501913589408ecd482fed006a0dec605e411d0a0a4e1fc0aed6e2ce43037b4c0066869080642414245b50103f1020000b5ba221000000000fcf6459760131a265596c01da82cb93b75c2f1c3459e3ad6507df811178c7d2038111c61aaa551f7130b2c8abddedb09726caf06c3c4ce18f00bbfab0ac1540946835ea0aa40ede557ba53f5ea7071f6e71dc34a3bcb92a57ce465901a8c910c0542414245010102f88043ce811875c8a0b0ac9d1c4dae5b0145aefdab5c758a84822a16ae7f2783f152746c25c5979c7bfea4e327eee9b2be8365253e859739bd37d7b7d1f0818f745336e9b82f78eec3ed68541b191978d6d45722051b753568987112195a1cc6dee8015bef9ca17a407780139c7af536b85142289a282fed7cc6760713b3be7ef49df1e1a7adb67168328b0367acb04c26108e0b4d4541b84f3d029e70c7a599805b3c080642414245b501033f000000b6ba2210000000003ad8868139a892d0d968c848252dac79ff6e0fc080fa0b4d2bf812e96b02336c740de5b3cf6191f7516de25e7ea1136fe83bce8585fe590c8775e4b9427d570297c385cb7f60f34191b24ce0e5f1368f58692509fc7b5d7565fbd081e5c6390105424142450101d47c3d5c7f082036250cf0f63f1705796f167d6d25ac86ac16bec42b26b60024097e38199a5223357b9164efac6ad0590b85a5f92982c37c57eb15263543998224dff4419289d6ccc5666463d8d8a6e51d5b80258e5ada7e0a7b92be2db67a0fcadee8018499330baf864b6abd1d383fe65ffc6eacab94b7181eeaeddfd5991eafba65fddb454acbe51416d100638646c2a53c399995bbcdf8328c721983d24baf8809ab080642414245b50103b8010000b7ba2210000000004ad435a5518b5f1db9cf5d62901b9a657dbeaf81563ddbf0626732114791727fd733be9fdb06608728fe16fb781db193c7d5f9b0887b5e553a6888bd182afd0977f081669e64e6e3ea8a34437e3900ca44b3cd8ec1d984b970610cba4fb6ed0d05424142450101ae4b315e0d99377e30ad6b1c2e456f0aeb98bd28b392aace2fe91b579c3bc63c5a75d8f50e42bf94b405661f04dd559199eca66a19bce640d04fcaeb5cd1ad81fb81ed2f9fee741268bedb358660dbfbcad5e084599c99c94ea8bab0e661948acedee80170a7a7b8442b07cca960ee42d9abd304889f0c2b725111708c9f7c444511d10bd3af73dd1f55b1708952e1aae3524c2c9443fb47c9f8aefe8b514484494d5c02080642414245b5010307010000b8ba221000000000226ec161b0ff64ca2eafffa3f1b3343ed869fad31a28e8e488249af4d290e957f50f27fdc83d8d9e200ee25d3f68ca7029c994b0b65787cabe47e9d85f73f4082746eda2c8915a6e14b1c3d33a5d60ad373f8a2fb2598259a417a49b29bcae0505424142450101f2fdb25273f9fdad07130807cf8d97c341b3cea0633bd403bee4a2a69ae7792ddba2f4924236a624e6307c739e19453c6019ffbf1988aeb0306ba88ea64b97803c9c74178e92cbf453c298f3559c105896db5020477735ae56f4d4fdd2a1e9b0d2dee8011341bc6bc79d042590f03ccc713e75ce2e85503bc5cfc31df7ea4d591ea2f2ca6708bb228564a6543656e68290f63bbfc3f83b4cd3232e5a3b3a7864aeded148080642414245b5010161020000b9ba221000000000a2e05fd05b107b7997bbf2488af041992b09f16851438376b04153c9c89b5250e8f406ed82fe780ddeca2be9b0557c1302b4b0969f6ad8a1ed8e49e08247d1053e92a43bf7cb9c50f09ef3acc7740391ce0f0047418c5a864cc65956f8876a0d05424142450101fe4ad523a3641c77d8ddebb5a4c999843c3248c0b64874b9b4eb052be7fe5c4b0c556da547a5989ccd4a7e86ecd03f885f08547b123f3935d8c304626479bb803ceaf482e93847cb07e786219f61b8fe8325bbe9fd983c7f07604a19700c392cd6dee801763663792928eeccb8c6b3f2723cd69e513944ab4be4ab19c95f866534db7f68803a8f9325ef50dc26938e6609b1f00cfabce54a1743f1c5df0032e08f6332ca080642414245b501030f000000baba2210000000002c8e8fd18236fbd21f7ee800f525014dd21d6432a2caeceedb0fa8d5c6b0a10b16236ee0afd81ee476e116cb0a9d7425bbb434bb729462f029df79964c49050bea3cf9e204ab3ba51fae83a62b5eaba7ac796c6c80520cac2d49ffc61c5f130a054241424501018e296f73b814e69034eac6c056c1bb489ab0fad595dc27287291dbf32798d00569cbaf3757cd0b122fcb562fa59c1bcdb7d491c0aac3c1430a970e9ee565128482e96455279c8961b10bb3e87b415234dd104af3f52f051abd04b02415b92833dadee8010912d64aeb372d83c05fe99fc022c0742af46618d090ebcb2130633fcd90492508740dd1866fd96002ff8bba97db12a7c6d8ca6e991c472bc5384f8aed6b5c8b080642414245b5010314010000bbba221000000000e2c7b1a93160992632a1ae2a13ab35ed62cf9785cd223c24d67535385c22e7545592fe39a717f4aa772dac9a55d9b18d268b84b5a9a4609fa8179d8ad351cf06b27b9e84e427f4230dfe86a25c3aea9fb1cf3ca2f774e70386578dc27752790705424142450101f8f631030473a5b027132f2962a43fe67ae73b28cb28296916ee35d2cc13402020f8381c286258bcc7ce74276e8a2f748008cf80c401790c71b618df885218870ca74ab9ebb79eef97511e510c4119b556497e68846f15815a2e09e8f0870bf1dedee801d05f2197d3a4d34a0f3011b4af1d2cb62128a96892977e37c22ea26d48fea2f7027692e4da6e4c034d5d27322d56fdaecc34bcd39761a6a71b116b88b503641f080642414245b5010398020000bcba22100000000098643816fb4350bff47e2722b2ddd963ae805925fa38caa7d5307c0e30433803c652a2fa0b139ab2a2a7b96f3ce3f110e0227810eab1804f95484202eec5e30f66ddfbc7dcc8251ef7705224ff4314c566f466e5f63e96f7f8adef884ec158020542414245010198eed533756c55cfaa73bfaf15c2694f5db28a9dceb85ed9bf92b15f7dbb5f65c7d5642758fe0d83074cd2f663d5753b44a7808bdbcced3e0c4ce4d9faa3208b3b2db0ee97bf50db660bb2979be8a3c888ccd8d66aedd2b9cacc4c77a7ebd24be2dee8017b7f9e6fd50b19054107db960d4833e511e2f9f5fb71ceeb09a69dae7e3b95e1d97c4faecb3852806f386ed3891bf1023c472fc442557de6747416ad264f1dff080642414245b50103ca010000bdba2210000000002850387927a4afb1a02d219ee1e6919dd2baf8fa8bfe006d89b43ae66b473c0a4d3d72077de473e2f820afc6a47c80d8dfcb6f9b12a1e493e0b7493ac2fe340d4e40aba832f4b48bfd7ba4157f3abb13bdd209c4ac40d9b14fa6afe1bd066507054241424501013a1ed6ca4adfd7e48c9512ad42d7854e28b9d679d15e130207edecf88b5a30091d48e240eaec1d0493cd1b7a4e437a6332f02b111808ca5ed66e9183e3e8258f47913626f5b0234a217b06ad8cd495ac5c1c65d38cb40f1da5cbd2b7d1a276b2e6dee801ced75e1493867e73b899a1d30923d69a0b311768c10f13e313cfe23b6bb1ae37a100ef5218e518d0b68c2875ad0e3ecb866b63d84d0d04f7b08ada286ac0d1a0080642414245b50103e5020000beba221000000000a0869fd7e370b0c8c73d097e1d3be87556de224f2dc7da24b32548f6593d6a1940014304e02be7c348a0b04cfa09982759ccd1e95faecff1223922be824ca4025c73f30c2b9667be71cd798202dadf4ea2a273668ccc4d9fda6934c12704030a05424142450101c4ea2babe3e4d6e13db85316b7d787da6ccbca2beb7b8ba0c56f49c442ef01613ef93e981f46a2de067b498df3e08b78fb433abcd0eda32734d763df195896804d2073e10ecad59461a7c52057503169d9a53cf5740a16c5b58a0a73d9be2d42eadee801ca7bfd961eeaeb8418b31c6a470c47c7f939974f4495c0539a12ee15ae1fab2ff86e20dd61ce9277b6b0bf95c33956fa8bb847a5d532f854ff0b3e7779c8387a080642414245b50103a4010000bfba221000000000347f0f7a03456b5b0a54a266bc30931688239033750cd098f9119269be5a6e2cc0d566ad684c92881803ee27f8df01200da94387036019477924fc6bc5998304938c50f01771df6026ebc9e48571b830fd3928a775038ce977fc04cf98ab070005424142450101dc20e2a8c43b0fffc1fb651421c03ef9b4fe1f37efc2fa874af9ffcea9f1f019880a752cfe8fe94c080f542bf09d1d9e678dd9ef11c241b464306324622b2a8d800c1d8c17a6b3da283fb4989e15963fc55d4c6409a2fe3f3977216d0f3e7c12eedee8010b96c0292c4e050e4e6c00548d944c61df200a48fa501d04055b09166d32dc94cfba9ba88b5d5f0d50397b63f3fd9fca02fc5cdde363337a14af093803219d85080642414245b50103c0010000c0ba2210000000009e53fe1ee53dc10ff2c7737ac6a7e5862538467495cdbf5203240fa2e125f903649312ceeb88be9ca1bcdcfea0d7f51f1666e4a1c486c1f612567773b721ef0f953d50160ef0638fd7447bac10d37e3cd29d9a1fdd4754b666bf29ae919c1a0a05424142450101983a7b0cd7f3494f7b0a6ca5e40026b67ac59d33fddd99b0c4fcda8458c536121277ea3ab5a96ec67e358a0d90988eb7e86f04ab931d1381f2a579fd05f8028f62ac02d717101d3dee3610ab75c3cc3b5aec5b4eea15f73eb7d913fbffc75ef7f2dee801970dfcb9a3be9e97bef3110b52f2b7a872dddccdc66b272451787ad741da11f65af45a1038f47856221c18ae3fcd2ec360e004b87cdd03659246ceb25f41d81b080642414245b50103a2000000c1ba2210000000008c392f4313803bc3406af281877cce5d1ef6d776fdcb815e2b36a13eb8490c69750c40876ba0444ab9e84f05f6134bf6ded40352a30791f28b17d5f05fa98c03977e82aa479393de0baa30d6cd84293aad9b788fd8da189943ed334c8f673b06054241424501018a3026a5faee2565403d5ffada277db3ea7d124ca4577182851166f929cb5175bdbf87cd59b21e99e2a5d4c0fa463f2c0863ca6681831356d486df117295288ef9b7a90e6ea552eaf5fbfd97d2b6a428c3facbfee34942ac110fcf4b73417c39f6dee801bef22d145dc430b98452aff41dcfc38bc0728e8f280b64122de3d05af26a57df62ff8ccf48e3b2072518b8c45e972500e9b80607609c5f38ea034fda695f868e080642414245b50101f6010000c2ba22100000000058cf59a6783ce11bc1120d5b7837c7e23f59fa5489526be4e2e648b761ecda1c585149f20c65488d89b1a49356038b4b079b6b28870f19eb5fa6285e925d000ca0539a852a1abfc67f421494d49c89be8fb0c365177eeddf881b394aaa16ef0a05424142450101b8a4f795532db9361003227bb9a524f10e2ca7df8a2f352a6f7dfc6ccfc58b14f69814c540a75e46dc5a2f69ec833f93438202a5e8d3f0da49a48c0b9622328f01cbc7b1a10fb655ad5f26f2526f2a54cf16592ac3e3269d5f2b45a845e0e7defadee80153e729cdeba2bbb7607f754c768bd3f3c9369ba7e68e1ca8cada3ae5dd503f397f6112f64a7ec26820fd5f3fb3ab9eca117f29430280f46b690d22272aa284b3080642414245b5010149030000c3ba22100000000040ebdd777c8c942eaa85a064921f701dbf1b206bfb404b638e05661569a9e65bc3f736cc1aaa257629c0e2b2dd418704454b62e331ffa6b6468adbb8f2401d08f429e148d2058fa204d04cff8610818617593b51fa546e60f0aa7c4f5d72fb0805424142450101a09e37b2ae7944bba38987e1dbb3f5f5e91e0ae719ddeafdec1af936a42756232811a93915984e333eed42bd7958025755bb0cb6dd7c83b17d2b8597684b7484db0ceeae516ba7d7dc9e6f311cfae27430b24851bf3e2dd7fde8f7d7eef05e6ffedee80164a2ef54a2dceee01800b791a12c1400a1ff505f239e530b86d4da7895b42f172d6c52d032fe93d37fc3b5778d38d5b7630c7a3b8bd7a37f3f924aea62709054080642414245b501037d010000c4ba22100000000016484448a6335cb986f84e3c0292bbdd56a7b1329ac16548cf8e66a091cbd9449f83b019326f50df3a254547ed104141c002e31b6119d03756491620dedbdc02d9fcfeba645961589939c08ef1093efb5b10eaa7b607cee0bd6cb6866a1d630c0542414245010154422f3bfe1d025598b5c2ac2041057074172690a593d57393572d492300723d20e3303c6aadc12efe75fd3c5c2340a728918ed7cc0f1bf6e5d6884f6c48798278f74e806b2b8445f92fd95fab46ddcc81bbf9d1617c38bebb03c4b94af79bf502dfe801d19f36912fe8b47665e6bed88ff02912760b26b1e6b2883527372c150f6830e407cbea1eae8424147e145c3a58f0a8e97b17c71cd2359380b0ece888ebd87bd0080642414245b501034d030000c5ba221000000000e8d4329d21ff9eae7a318e947634746d18f6457fab00cfbdc93a2656d9b09d365f60d4262832652d4ca61ba6f19b63acd36c23ce19af6aed7a39dd021202370d52645c7f9fd2885586a8bf9f23baabea73fac40eb74e7f90b416f528c6eb810705424142450101bc60e4c8b3ef1d80a8dbc4812dd02910d451af42eac78c525cd4f795d6e84a5dc3c4cfaa62286790f30be90a541084274ff3f8a56ed563f424758243949b2a8b6260a5f5fa032bd423537673dc1ff38065cc4bf6f2ed9a5142a5b83ef3d59c6406dfe801554bb0e40d08d0529cf3c128207728f739757b4947b1f532c2233c12c3638e725699476db9eceb150d22ea915eeae3af9e2502a5864f7061851d15f152657af3080642414245b5010357030000c6ba22100000000010e1815ea7161fecc378ab53b90a3e89504e7611a40396f2045ab03dfc6df73c0705298ab54ff69127b786198d187eedc943d78c6c11f493ee58be37025a9d06820ae298e5a8ead3e473554b4aa48f5f60128ad26f2fa895622c4500da6abf0a05424142450101e201f315ed777afa7413db619541fdd9084e1140fe63fd8a809f311a9ac6210cb481573c6149da412087d89b049312ef3b8763a82d455221a6c63e3b05ec308c4e54013d7f64c024181af45a610132ca00a4993a83e3ad274916377d5bd78ce00adfe8013865fc9376c34027c737402e82acac52094986df7731ec8401d6533f7bd9a03cfbe986c0f5d1ea8ba49c9559437de2b392bdf7dc3f5816562628cc2801da674e080642414245b50103d6000000c7ba221000000000e00549b7136f08aac53c5f069d43fdbd2f0cbb19309ac8482ab63533badd5d4c3d905aa9261c8e0b45911c7455beac4835a0b620abbd730f6c46833dbf92710a724a0898e09ed43bf91fbdc6c43617af98b6bd072a5c67c680f7bcb5bbdba60a05424142450101b0463de7de64728d22f04259cf57f6ada6b497bb05b2925cdb9f33d8b609a74b42b0e423141c2108b1de5fbfa9f6989303d0bec6ac41dc58de6897459fb18c8cd9edfae8dca656f972a90f261052b6974179cb646cf08f89d65d1a4b15d298da0edfe8019604f454b6c27f0a56bb1ac67339ee36080d3d4130bb5af61c540fd712d35dbe3347e20991263ffba72d46c5918040511210e1871249ab4dbfeecde103863e97080642414245b50101a3020000c8ba2210000000006a0df310470494962a9ace0e1c0db4760c6d3805007d8c810113ab97348f291d0fbad7e1b8453d74a5d4ca1126c7ac222da59eccd46b2112bb76339f7a68890b62ebbe545ccf0ef2b024db239f8353e42ed8038be3987ba57c3ecd0e771d750a05424142450101da2f2a81efbb7f2342a3c4df8e4eb1c816331dc4351139d7c280789d57fc76506afe4eabf1055bb08367703507ffeb3d530e1feff1e5a81a70a66bf9f1919481068a39398ea0bca07d15b91ca5546b45124838d72b15a18d332ab320e98c196412dfe8015729cccc720010939423ef1f898ac42e16676a2f32ac84ee5a44c16f717c3bdd2ae31e2b4e6b56ccac60479a996139b1ef48f7b75cd20db6bf4eddd890b4d0d6080642414245b5010365020000c9ba221000000000a286cee69f4f40996e2187413c10a3a605de19adbcb2cb4a2aff6118e979df1bc3115ac3930a634fcb4411381bda2fdc4f4a05ca8127b2fca94a244b44abda0196f28c63d4671754be7e668872fa3225c3bcd4ab55cebd2cdb0580e109c1190205424142450101369405aff9176b875cafb0590c584a6756b669ec5e58299c69fba6052a76547c10737cd14e94886a688de5044697721ca1b42dd3710ff83dcb8bea2c2419cc80f0af38e0a8f5916083734c4ab8d58ac712038f17fde5942b304d77a4d81560ec16dfe801fd6570bb4dffc60270dce8e445449cb7dd0351be29787a16831ec4aa7d1f8877d63df62a879d6de752cafd18a46ec79b54c844f30f20b048d7642060284faaef080642414245b5010339000000caba2210000000001e96aa6d0e8d70dfcc77a9f44370b2621fc64eb851838464d10f646e3b23d83fc8ae69140e110d6628e8d9f8f3cbddd94f075d03b5ca35650fc197b9cf3af40d671ed73f4c3146809e8bb1d913eec0aa41613b3eea9f030848bd076234f23509054241424501018ed9ec2311d327e5587d44417dc2ecb00cc03d4fb4ee6206d68217c57fa8f65047ba187535af856cd78deab1bf4dea17856d3847a7914d0bddac68b2fbce528b65160f337c753552152411afd51278309398e11841c83c6fcb641af2943841c21adfe8019c8200eacedb8175fcb5babcbaa77146b2add0930d0a1bd0c9d54c774de452abde2ca95d8c3f603772eeda9b9c944d098bd9bec105ac96db3ea854be4ffe8367080642414245b501036b010000cbba221000000000c655648a4a5ea9ce01e6a319c7d9fdc04c02655a0d33923a8a2a4845b6bd102d36d0fae982a47e1ab21c98368f472ff26c4f202d3ebd01edf44d278849b97107c5de310f20a6760efae26680a02321586933fbce77f9b7e30d2850ddf161b7020542414245010136cd45582c2da94a382b18af4b64996ce7d4aeb209bf02659126698eccd4f020b6d0f56f640e60e39d2f722754a121c04aa80de00dfc1a0cf535efb765e1e1850ffbd635a800dd60dff1b7e646fdfe20e98fe4869669782df636f04036fcb4401edfe801418f1fdce781b4bce37ff3486931736b324a6bb895fa39cb5b291367dcbaf41d636fcff4791004af5ed80816998cda5d003b0ee8ef2b05e52e768f8c4822f9c5080642414245b501032c030000ccba221000000000f0449aff339e9260f015b18baeaf67492380f2080c9d860c4b05f673448d4e2ef3b0376aa069a2818dbfc5f08297cfd0d79db033416d6cf90ca1e9d2a595760e8de5c9e7274d47bf905b869bfa1f62bde5f175e23c1e5bd519c2dc838c4e8a09054241424501010e41ec6247c5dad03d10d5cc1ce384b6775eec076d1e74a6c6be49d842d73d7d4f7bd9c6541f4b77865d25e33650f6f65ae2b9ce71e4c74745641e5c6a02358706b532e85b0b90f38193e58d54280479120f1315bbbea8168e86cd67c95ae28322dfe8014bede072f55edba3b1e8d18397e031add6690027933f0d9c673691dbb8e382143fbf63206f79e9440c49d8f5e2eb5e9020ddfa5ca8fca344260775b235998668080642414245b501013b010000cdba221000000000684199f8f6e20ce268b4a694e424aa68377e91c47870fa975622901cc701444dbfc510cc1bfd8d886ae8935c342188828a0e4d64b2417085c5b1bcbc8cfdef03f30d680fa65debe5be48484b6287043af7b8573d6100ed873684d2c0c970ad0f054241424501012c66a52d4ee4861549897b59d814b0694e64828a3fe63301a0f3eb1bbbc9684f7ed6ce6dbcfa836629cd55b673c29c68bccfa058100d6e8fb4db6f1984db1884f4d0ca7bb0873b2ccccd6a3eb74b9ddddd8d2302ac87b03632b2ef8247957cda26dfe801e8e0cbd90870c769d17ea8334a4ad33de813d5a80e6bb900d9e8ccfd182cf6b010f1edce21d6dd92621e3294dbaf8fe92ac3ed7857a53ce36926950c44a599e6080642414245b5010315000000ceba2210000000001c1802043bb9a8fa1709b1de1c7e01848618780ddf864644b6d683b9d559770e198463ddda77a40f80fb01d41d8b6e636fe5c31d046b7f1ecf9e7d203862f503d1f35a35ff719d36dc6fa71127225a86e3a2b1dde8ee6610b5a08d7b385e8a020542414245010152009c4adb6c67fd2e430ffd5d2c5405b915f1270c687f3b229e6abead068c3bdea327266cc9fb723199ff56b0a5a74b2eb303d8a463ff137227288fa074f18c20219de9f8b53b881d8b4ed823aedda9c9cf2ca40187ee3d6e6801173b6379502adfe80103020636c1fb7c3d08db791d09532c06d3895cdf6d0954e4492763c0f1ed49ecbdcaf2c758b0383da5c707b499c958cfa9055584fd736db743b7e5e8e14eb87a080642414245b501037b020000cfba2210000000002e351a31aedc1016de682f57004bd41fcc4d8760766b4ee19002d7fa6f247b7d72fac887b17599024852db1f98b881eb7cb931a5eb9603a4ea3a55b7f187090d195af9c3d849ea44d2874d14b36fbe221bd136216fd4b0656056705a09a84c0405424142450101de8a40c65d44c7421cb30f13d84368c8785f15e04aee6e7e22e11580a43ca85eb42706259eff98f1a0062d5c67fd7fbf75191fe0de19297966c886aaa0befd823dd3b35605513822331a6f83027528b1f905ec8d0ad90d7e3c4bc8257174c6f92edfe801121447f31e5b7c990e28dfcd7c2d8b66f670fb14a40fcab78739b13c9329591aec48e75dea96c30d084c609f03dbff62b703aa938ddb1723d5d36f2d9f4607c4080642414245b5010374030000d0ba22100000000092d1cc5ab5ebea7c3a954b344ee11bacda8d7c66f67206ed6bc314f77db7ea12f758895d3392d3e844c8da5636f252400e8f7e74985cc285d505f88823e8580068a6d8142a9af080461f06ac25a8169ee70088ada06c7303a0c218d743fee30805424142450101b84abfab2e1a0d776e8465b84ee51ff641d6a03349af0c73d14ebfee2e7c9c715309c97239ea2b9c5d7f87f85b725a9091cef9a885c5ac68ed91486db422e886de930fc85e4c98ca23fcd30a95eb7f1c0e48573ab930d6fdace7e85b1bf2031232dfe8015e769dd027880ea185c6aaf35f144b6b4cc6278cdbc2bbc43e9c3c3d4c8c177de21ffabca8d9e50d5e1e8e115b847f7b6f2ba6ccb886a7ee2cbd51e11a83251c080642414245b50103b0000000d1ba221000000000562d82ce95a0626c296556c41a805b49b5d96cf83ffef49d116472a93933ff484396cc6d03dfc7faf00ee4a743feedd11772122ea39c0918dd233318b06e6c081ab9c792bb6835b8f99c7ad739981862fd4e172891dcb43dfe7e167556e5c608054241424501011ac0c74a9da3aba7e8a2eacff85eaec7179d95e8ad9b914ea633f3b484d7c1459c67ff9d6c8358872d256e7aeff2978cbd31281519f34be0716afc9237e10a832f971e4b92366b66eeb1be51244082b9ada6d0ea04cf0b702387b6f6c38c82c836dfe80117b2bd861cc081f7953456fe81aee86f4bbf0c621b44b56d73d4ff3a9e61180ee00f02f5a6060bfdb7874ffadd0d34306e681d2d58f6c8e6a2dad208e0cdd840080642414245b5010196000000d2ba2210000000006cbead81aee5d0b09cbdbfb40286da261d5cb2cbcf547bbf7f778a774c8c66207415dfbd559fa812c579ced42fd1e212801d61def87b879a70d7ad6488d6740937c74593218c18be449dfeac78170b6cf65091423897dc7a527638f77235d80305424142450101943851b7e15bccc3e45553a1e0868f1be2b7c75d83ce163f326d80b935a3366a4e7e7d7ba764d091ac806998203e349f8c23e540bb6bc5f92c5de3526f15b4894a6feb5a7e4ddf1de1748bb89d66a0fa226424de72bc2fef4e99afaa2979fdf23adfe801d645d5517d443d26595606493d62a8d09762ebb86c33bc0c4fe5404dc5dceee518f54e00e67adb9963de01431d977db385d0a23a298fad0722b6bd33a0dc7ac3080642414245b50101fb010000d3ba22100000000068b36b2efbe13d5801c06c7fda06340f8ab08a3a84ff359d4a4545904f7a35652a83e018de9cd98ab9b554fb2b6a276a0a07cee05e1521d25b966a4c55ade90702bca840ec9d4cd5f172914c67ede94d1d35aeb3551b9cb5565199b9e21f9c0605424142450101c8903eeb30c83b96d58b7228265e756e2b0863022902091abafac682e6e01d1bdc6ef815159c24af6fd7916c5377835064e0f870107a99486ae0d10612e8718dfca70945a23a625e5c826a0b616f7c284ac133da6313917625dfc5954e9328cf3edfe8019286d64a08d3dfe0263f611d688ef9362a44ae72ecfb43daf0923ed78a417e830a04ec4ac7748690c02b614f115ddf440e7d12753032d4489b0659e0a786dd7c080642414245b50101dc020000d4ba2210000000007ea026456449c419fc38e5bff41e60425bc2e3d17b3688f2b91f4c310a70dc0b70db17b5a51dba0288caf1ef1c021cdb3a8bad09d37603a63e839821bf0b1b0b9a64b1f111d2ec211ebc8150fabc09ad7b460e920360363b4a8d0c6fa1d9910b05424142450101d8d909a67e35bf9e180d3dcdf9d0c9c9285de85ec1ff6b91db16f2efc1a5b45084d266f6fb3c7e39d610bf0d7f28f3834399aeff7c1c82f089600610fdb6668ee47e199196157b370366515ca7a73d53920a0afab7489f884e84bbe71a1d441542dfe8011941eaa7a447f2839f3ed0cf31cdd1c90c42ab8a3e41ebdbd06b02772e64a3ce48b2750638600c0139bfe2e42254dd76c161a338062302c0c77764f5257c681c080642414245b50103c3010000d5ba221000000000f622c6457d9b52816ee0781a6be186bb17ed96fd56f5dd7d57de99261ccd280eb9a20032d3c656e256b94cdfe2aaf592650cf8c96babfbe0bbe1108870b9f1035f68a9cca71741e15bdd7c2b4ccab27ca79d2c4fad90171ad191bd58b6824d0005424142450101dac85c0fbae3ddad4464dad20ffa7b4b5b2ada54aeb8bf123caf44cd75bf7e4c7c18f483095f76a9c04e7a7a354bae78d4dee40eb3b31b0b8d356670dc2c7b8bd1f505663d742bc5ea14595a13ccf02a3f51612d8fcfc53d21cd1f5a88c6ecaf46dfe801873aa6030037273f104ff93e92a70250011305318a30cafd4b28556c02e67815cc0a106f4cf4ea4fbd8c94174ad79ff4d449ddd0b4c593110c8b7b5a6812dbce080642414245b501034d030000d6ba221000000000b2471143329d8a9243cd9ca2317e391214bb30f9c5fe7a01b5a446065b811b7d8c7e463f4dde993f1dde10beb8ca5658c2e0874bd02ca9223f7a9070fa7c5c0f54b4ff58741b7dad16ee1bdcab4c241b2238e459908d0affa9621a29dbc8ae0505424142450101c2369953b80fa11221c0dbce55098cfbcd48ea642884fe6e075bf5471df4aa5df15f2ff938426d4b38b119cb59a3d8cb32f081a0c96e59e0516bad51a6fd3388d10282f53027ecc82fbe6a3591a3b23881a42b55d6a5aa05d2644590ad3646d14adfe801e204b6a2465135e438a621597c621525a5a23d7e6bd643770163a3a134b5fc1989ac29a6fbf4acb10a447486fe55338952bae7525aff42ea1226f996ad88585f080642414245b50103de000000d7ba2210000000004cb8e3261bfdedb2f4e0c6ed7cdd8f9dc7310fe147431cee72c14f0e465aa720c5b637959f4bb9e8bee0aa1a3f66699f4d5e2c895955aa8d90a4aac0c54e5e0605724a396d4d8bbd7c82659781ece6c2ad2223932fb66256fd3bc69a3011a80b05424142450101729393c0806dfb808647940ee3cd75937b0110c0f22c9d4dfeb8846031433c7596e46c3b5b85b5f4bfdcd3e59e16ac612f7cea3b906ac0572271d77641f4348eec3a1c6d35f1a8d1e140cdead364f33d274b81fb53f65a06d008a801088b45024edfe801684b1c08af9f3374a86906e991c5bee9843e22410852ccbec97af4cefc3d7dafa09ffde12cf84f94a212adf58be5483140ee6901ae6a10790ac7e6e4675e730b080642414245b5010152010000d8ba221000000000c8509bcb249dc0677a912813b739188be44f8f919f28af70ee61d15307b7c35aff8fb1106527e333c7811ecf96198fa9f852dd6729b14588bb731a00199683086287578300ef8f578eb9e7250e80f1c85c0fce56ee187a53258c886d9cb0ea09054241424501013e747fe8425ea19ffb2781c22fde1b4185b6c0212c8ccf5dd0da5635572c20235aca1873cb2a35bac0b6f3dcf1c67a81e24cfcf294d54970c8dae679244572872b06a8d6f985fb959755f8e55f7a2a96621c7f3d38051c58b10db71893a8df0652dfe801e695805fedf06c1042b183c422cbe7bdf507c4b6361788baa4f9514302ba63aa1d60b1ec795c2c9a682792d963faa94f50f531cbda474ecddddd76b2f71f99d6080642414245b5010325000000d9ba221000000000fc7ca9a2ca0bc1db4621afc3730b8d30e18b539eaa4eab94a07870ebd8088c454bf1069d0b6a3ffd2bc84d92628ffe68e8fed325d7fb3d9a79f105dc4fbaed01082ca154b964a6b8c94d61ffb1f1807e7b00cf6b347e88279dd397743c427508054241424501018ca36e721383b516ab7f1121a7268127e9657e087a7b3ae32e4e31af1f1d4c622bb2eb941f0fa56562b2f725eb64c4d7b60597aa4ba06a69aaa8d58c1c6b3d8fe4d332a864fe7a8f3696d9386de4a555e14d7cc997258d26eb12c04870964c6356dfe8014a1ed88c242649ffaa124d1d078f2f5979c8566aa2c0e9a64c6e99e51cbf2c7de5aa6ab1bd63d6f9a66e3f9e5fab2c7fce8403f9f1a9d8904e90a107836957c2080642414245b501015a000000daba221000000000f8eae4690098d396a68116948a9727c4bdb62ba6ccb1a7c960c686f595aba80d712cec216ed4b68e16c8e973a1e201611135534a285b95c330c9d7f32573b108be7bdd068e7cef4ebe22df63a0508d48a7dd22ad3da88e5ee2b416ffae147c0c054241424501016abca94850b3caa507c0aa968561fe5e37a03419868fbd202969e586f6a67352c8226952d78cdebe16264b3d42a73f4bdb824e24a6f83b61dd8ebf609ce5518e6ad7cc2223d1960edaf2df8dcadf58c3a888c7020b7504434729738f775ded4b5adfe801fb66e29e10b93fb685c122d2cb3a550bf0963ac872ff0d4699a78bed1a64406a90ff8d271eb269576bd111325660a78906366738a833452edbd84db39755da94080642414245b50103f2000000dbba221000000000be5dc3cdc815bffa7510d4cfc6927ca6116240cdf000d9d424eb941c6798644b23a44cc723d6c7a6b848baa9d2b1087aa1d1fe607dbbbe4ba54198459775d8047f9dcda9c7071738ab35bdd73de32ff8bfcb39e08fe8180f4a4dd94ce664570405424142450101f641bd0a4f4c454024076c98241869d267cef048a08c1b80c27a9843c1bddb238ea80b70c1c9b30b56114dc7c9a9ee71ecdf77443405488ee2aa1d8f5986658fa92e2710b7ba960541c26dd274870fd582010115c84f6f454203dbe69771c0d05edfe801b697b8da47ed53d9c6ec3b1d03161e472433a648ba8d4857af3b2e9a27e66acbfbdcc0490ea48166d20960b44ec32f4fa21ea6081fe56a39d172913174c8935c080642414245b501030a000000dcba221000000000c4a226ad7cb15828e37fffad363f443712a162990fe4b21ed66a18f103fbc36c6d5853859bd3c3e665dbe403848e01233a07761e092b1e98bed756ab3b2ad006c3fd2892d4513c4edf0002d2efc169283f561ee3e0edee9132a3bae92ce5d60605424142450101c2a0192320db130d53fc771c3523b11675ebc38ce2e78174209d71737082e26bb014280d763d075badfa52f53c040bd3b079603e6f0271a079bbf8813960cc8e1f4166b10ab2deb1e696a72642aaf24a4694c391c53cc669bca5e5fe84afe7ac62dfe801da7c56f9a60a8fe143c5a921166c20afe1c54bd31f86d903eca2043d41c8bccccd108fcba61fab1be0b1825e360c5271cb9fcf19623997c197746b30e28ff052080642414245b5010329000000ddba22100000000054d700298a3f0b4e2c1af59d1f51a15a0ef48ce1abcc1a1edbd9748da618eb7774d98cb5cea36059a6ff767115cb65a139a0ee27389eb95014ea96a0211b590c39fa9034b6ac65e6107f1ff0fffad0bfa2fd5a1f860547fc9f595b6e04f3770f05424142450101ae10368bd16f801a89e1620c386eb4b3c8f6e12593d1ad383e4cbf6a88318c2a0b781d65968077dc63734a9f253098c1c33ac03b76c0b3c4606ee70d91b7328073b6e8fc0aee1d3cd1c6baa8903c470666c36197595e992303ef8b582a14fbed66dfe80109b7c5ee5d8adbd28cedd1796514cc8ce3809425c5e1a3e5315522a6cb6804c81c45656fbb9c29422251a4e3aa9dcd64415bbc5a9b7858f8d86a6434ece4b7c9080642414245b50103a8000000deba22100000000046f6917b306c2765f0dc3ac86353e95a287382b9da2301807d04fd794b27245e5cf25bb9b3f8845eda8624406ff97605815f7322e58ed60472c6e97ae60c370d1b9bd50986b0160f735f4ddadd4fddb85625e37af8f5ca50dbe9908cc6f91e0b05424142450101f4c8fe63386a20da0ba4ad48078d9ad9a4a95678d47d1d46ea524ceb63280f25bb0e9707f6b8cc3aee55257f69df28de0d0b21baa175c9d7f6251d67e4bb058d8362ef04e1f1be1fba0dc97a20f1196e1e00bfa82a40c02234c99e8702b323a46adfe80165747f408dd3b6960514d618a1dc3fa149e7ad0ec70cac89e8948b16ed3e34e6ed2363192f71ab78540dd2e008ff2bd827a8d1a77ff500d6728e2dd720b4daa5080642414245b5010374000000dfba221000000000042663413d3829ec9231656986d2bc4cb54b35b56dbad6c786df95fa7ba7ac6f18fb792191339033d1a7e16b732efee95fc2829b9a7c87906d92a24a8468210fc2fa6679e106d01ad960b3add0d587fcb92573f23b6881438583b6fa7a6589060542414245010144c1e012eca4f91b45a2994602642deb0d6d44186b12e943e013f3bf9ecd044e7d3230e112fda9e83e4f47947c3235c42e35efec88a7756b6f22836a001eb5838cc9632cb19a8e4ebdba37e0fb7c6ca942860c7d893cf2ad806e733c6cff5c4a6edfe801f56b301177f1b3a30ec0c71a917ee92b04ef5625fe012fa05c1d0ba25df37e637ccb5932f8d3346c931a3432e10884ab637bdf9382929f2cfa6f2f7f6e9af298080642414245b501039b020000e0ba2210000000001082eb35f9257b3365acb943b095796e78bb3f19b4ec48d2d09f3c3fa2fa2a74ae9220564ef4fd728f0da72e5bdd6ea976e49b39e2a7acf005d6da850ca0540053c92c50808426dd971e68930d22feefc709e53012cf91c2ff0906e0eda62f0e054241424501010cdf05561c93e496a326e7fcd9c10533a8c76f2477c7994cf63bd50a08fe5c1f9d46b786617796e01699c0160d14e6408e2aa7f9d080a61947f37f2a905c778f88d0e6c088be8ba1cda032f2fbd10daae479cfea24c0e30c0680e9e782dd342672dfe801e7e0d1e5a49b078a80112a06293808648f7acf2a8bead494fea1668ed567522a11c9a5e4b23f1532816250ad2a2b9c5306ba97175c2ea863d156c40c22301f12080642414245b5010354010000e1ba2210000000008667925ca276366e781baed073f69b65d375298b90ae3873525e302f20dd137bff18996bbab3bb302cf12c01531537d6a08f776a22ee2f4a480722132ba2b2015e7da92c95ccd32f576b30fa1a608d299e5f2cc3d774a4da5a98f6e9f525060b054241424501015ee639c1911091269e9ccd1fea878aa49bab4af2d5bc869b04bc760cd6a35f5e41c96be78815766770866ddd911153d66b5ac99fd758e92bcd187e10901d3b8fe0e44f146af78fe2caf155bb0574cca058f00a918af9ed629375e00554cf19dd76dfe8015c9f3b45b03fa8f75b2f2063ea97f89443b9df1b7997e941139a30025def4d7d52a7bcf4def8df022070682167574ea91fa7d93f1473cbea68b96d0bff06a074080642414245b5010335030000e2ba221000000000d25660821bda804e540c13ce606dbe36d4597773ec33526afc67e58650f6b247db23530615289bef348f888e19b1696d3005c5a47c96fe29f47aae564daa9108dd6fbb3e0cf1bc1d970f6d3123f626af1d6e5853e654410d3a94dcbf650afa0b0542414245010166565df7e389ee5681e192cba351d8d0c31cf27f5993e1634f810870e0f1c5661748176c9a0834910deafe6d728fa2244b5bb304ed5ed04df5f0ebc4917f418037f3a0cef1b79b7ef764a02dc1aa26f72e24890dae020d4184c4be7b54a8177e7adfe80174dfae9a0d115a875720f3800b594a2921310a7e7aca57847c7bae5b25b3ac6d36d19738edcbe7b85226adad87e4246085b090e5c222fd4c682b03dfb443c096080642414245b5010317000000e3ba22100000000040aa83518393b802583e75bf86df26009c74eb6ead79c04cf524f8d0f88a77615c89ac6bbd7ee592a69a6d61deb007b8a77864458e75c21dce7cfd6192e2e50a7ddede8f68bc933e118a0b22f69be2a03971ab3a98e6095a0365e522d5a5d40a05424142450101e64ab26568370d3785a7e316227c9a61d1ebe763dd2ae7055ea4cbca15b306635be033974a42d24d9280229bbda0c368b10fb4db7837cfa53f56099a8296048cc344350edc73f5f3603da8464956d8b5669ccdc2f77a7b9162f60c34579ead5c7edfe801e854a26706df1cbf8673f4610b56474f49bdf2e002be5055313a35018b6ef97e86ccac3d9d812415b63621903ede9b7a7070bc3cc0d5867c912103fd9beb31b9080642414245b50103d8020000e4ba2210000000000cffbb16686092fca3a987935fb2b3e6ba6a363c5dbeb7161d24108dcec63012af8a27ef16c2cae19baf5f7785439621f801f215c3d8ec17fe0e6c00a366b3007094d488ccb1fdac2c377cfb23e32b6ce4b0cc6651cc51afe944e3360ce3e70605424142450101b01e41d121f6cca81a8ee355b47e5031986ba8dad8412681ffbd040f04ee2c2b3d4b4ccca9d497ca8c4811de28ab45191b23cee181a46f0b9d7fe614d9eb4480db3ce06637c0a70e02c83c50571df2a3c3de5dc5199e360860730107f0e4c60582dfe8016077d49d04445d68010130d8f16e014d757c63d7a8e483f3fa7964a73068f76429080361d8d3b105e7042a41e728ab89b4bec87f0fc280908fb2c52b75755130080642414245b5010184020000e5ba2210000000005a80ec9cca981a4d093e520cd49c5a9b2c103dc84a2595959d4aeb00179f314e550c27e61d6eabe4f3d64f2fb827138d8a0ad96c2b4e6c7fd580e284b36cb3057814edb289d61926e0e64f9ec47e60c9d8f0592ebbc2726150c59129d0e6640005424142450101fea739ca86b65c1986d1c308b58ce08be6598cf459e281cfa3ad223653cb52563c3e9c12a4291ab87f635c5b60e053848a7c775c29c0fd6e222cb1212f0fab8c7577e1436d69b5223906c92d3b3bd686e9aff5743cc6a624a9793c8d2a2023da86dfe8012c3143d60e3fa468eb6623e7dc313fc8fd0217699fb44dad527af13beef24605164aa76f7ab430f8e0991e83eb64117681076d4feb09f7d9f47acf9f32e06dd5080642414245b50103d5000000e6ba2210000000003eaef4a76e48319aff62fa77a4dada9b1013a52225841603820f669f4d3092120dcbb5284d5046cdbf238da45c98f801976080ccc5b03f8316048521e1bb2d0469ebd442d98b6079244d365670f66dcbbadfec1274780f9d27853922dbf9ac0c054241424501014223c397d6f8a9b3f91503ae79a1446f19798d04d10f803911a0b6b8edb86b30ee24d8e85c2f18c06847c2041f1bdefd1be0acb4abca0cbbbc2861d91f1d888d8df81d7105d47a6a49a021cd1bc177a37583227c7ee48c308439db4b8ad502378adfe8019b24fcc776f8ebbac1629b2f1f1dc70dae46dd82fc2e61b817b94ff649d4788ffb1ea928cd23325d2cae7efab6a380e330b1ba9fc2bee64a103a1b6799f65e10080642414245b5010399000000e7ba2210000000002ec169f3880680396c54b82ff782a4d15356c4604192e7e6f258e6f4085d822f7beb85103458cf232ff0fdc4523b84c626cbbc935b3e24a90b26b71cf203620b29c000ac592e47538b4eea9f07dae3ed865a4ae724b53f3601e669f40e07210205424142450101c6fd8175e861886897ea86af69366cf52ede8d06ab8e00b9a1815fc9c3bb5a3a3c37620bca39560dfd2b37bb8dda4aeeb0c8df90f55088bcb8b6b471584de18a5ae33039aa3225121af13d8e78fbe744699ed0c6a8a26cc85eda782948ac1c678edfe8017b3e0115bcebcf67faaad25c6e21ae526386c505e88723298e75ef1beb76a6c5280ed3f82a8c848c6fab6af9023f6689b5c95b449c017c2de16c6e6213519065080642414245b5010368030000e8ba22100000000002ed58e1acc0611dfc8cc1a736ee161c031b93e1cd39996ec00603fcbab3d17c2dbee3c20214c9bba24d8738297a86db78532f4bcc528cc3531ec40a2d6d110b821823f418e6e862c54b23f9cb627d0beaf141b1fb07986ae544a47374215f03054241424501010ef32fb93e1402b0a08f5a9715d9c8102e45bd6bdce4ee8ad1300eca0002b347628fc32eb2d30c249e387dd78eba71297bbf5537be794442962c4642b1a5498e1bea054030d76ee5ffbedc42d0fe8e8005bfb2c009528f7aab2d0735689d0b1592dfe8016f2de2ccbabd9e6ea66f2c970122971d65b423f6d578d21fcc5069c4dd9fda69ed1a7cd4002570bd547a4da3fe0bba36018b6984f5397c44518d289aa89d9938080642414245b501033d010000e9ba22100000000038abb86a01a206ae3b20848d741ae1a60e39c43874327019a4f55556c0e4322959c5edbd23f17585d802301faae9880c9c2454bef946b5dd9e047cbbb1cd1408ba0fdd4b2a2b2b0b781cde7b5fc29ac6ff7236e63bcfa3c1e887392088fc640905424142450101ba32ef7197d3e22b5727c3a89e213623aa5ccbaa851a1e0f2f7dcbfcbcd4a9577984b5df79a904e8880902a6511996939909e2110c7afe14aba8b43877d75c878826ffbbbfe311c6611ca771e169bb7f69714226d7c30164edb008cbfe8c833896dfe801a8cc4e117ce07675411bf5c2305b29b3d0629778ed7777e2dd0938450561f0945c034773078a993969ea4b0e26adfe50ffcfaf52b0034e93b1b68b0f0dc32f2c080642414245b5010349000000eaba221000000000d4248c4a03f35e60cdcccdc25253713c46338b3313d8ab99d4f8957f07c0500cd0bac448aab273920087dac32ef0bf1c18461b80b0029be9b667df42c9f84c0ef59a2285d308cf61819576e012ebea389ee847d2dea2f6fa2eae08b3ed450d00054241424501014693a40efe42f37d1025f3294a437c46edbe80027350232f674a486012b18021fd4a91f3e8af4971720f5ff480fa3569bb818ff8e26004a375888d447212a38f5f7a19eebc0647782593fc4819aa335050f6016443810e444d01da4e5400a7179adfe8012c3f74c5600111bcb57f7b12673cfcab66d781db479c5fb54f006bc42bb23e1a9252f03a4b46df81c739f95e258c03d17170572b54979866bc4f19bc0b927172080642414245b50101e6000000ebba221000000000ae43437fa501a5977028cf61e30bf1f4e85910be64e17f36881d522e8d29c456e6bd60a3bdf08c0d6b5e62467a74656e9df58d8aee2241f09e949a339ce50605caaed5818d4a0bf4915f47e2f9e451969ff5224407d6c6656621b8d6d6b0fd0605424142450101be7407ba86f2ee2974046cca4c41657c4290359b678f17739ef1a19dcb133f619bd61dd2ab99b78d69f79f44db80a21e7614959c405ff20be19d942b55c267819ee54ee577ddb68bea2af28e2e91d2ef298b832525e05064a088a884d9e5b8099edfe801b90f0f51914acc36551daf2b9636d259e02d7773448b86305376645f693a7b98ee872ceddfba6244dc1a95015a73b1cab2062b449c9fb57cb348c4aac93f5189080642414245b501015c010000ecba221000000000e81d38b08501f8c75f41ceec6b8c9d44d5ce64755977a5497a37c44def6aaf4feb20d9f5585bf869e3f946e8ac6f4bb6c7c5fb26d6068c034a95b196b903560314d491a8858ed0389f26a8eb7a8adfddba9382db61220a0847845c797adcb60e05424142450101b246f5600c865285fef75e89496a94e5f90abd759c4889bc85a687fc2916e81a76beb39c3c4843802ad85cf54b0f11b9f76068d5e5bb71a76be761bd22ce3a83a0c8adcf5a792dadf727857d5eed4118a3e8f42564cb1e22135afc84d4625df7a2dfe80171152e5f6000ff966e4886c62160e37942705efc189b207175b2fd768667cb084f6bf84284fe336c7e7d6c5ff3b0719d9fcf616eb30a1ebc310a3d52429027fe080642414245b50103b3000000edba22100000000048986494f3452d203f041c2e6b20b6819dc172b978c6add0dd95306d2f7f3c7dc0632e61567c51c83b7e73643a72d354eb7fe9b2bba5600fa50d1f5f0c20000e4e8c7ee6a0a54a615961da96c744cb2be080dd55ea1de3475bf28d7e28154f0505424142450101eaf0776a7bd33f7caf2b01ebb7b2f502dc384cbe4db57e6e9db561c315b78f6065edc7a6a78245c221052d1a4486392a3b1862fad9f0448fb39564c5b396cd8ec774e1d84b0e0375094c33c3951bafdd85de1245e46b0469d8fbca2a9ff1b9daa6dfe8017f13927f5596b7ce69a1192d3d53f928f1d8285ba8ce4ad69eec5b6667b4290107d566aa984d7cd79455fc351bd16cc6dba272858e42d65d07ca150aad633a2d080642414245b50101ce000000eeba2210000000002657939757c12abd17a1c2bbca331de1dffd587d121dbe7b00825cd365f700385d91f2caf8dab6c7ae6c651aa70138aec303f888faeb3947627d036686326a0c38d8e85f48bb879b3c32b7b69128219375254af5b900cf61211e639b82775a060542414245010108b66c24f191705548c54a721d3cc39e0736ba2eb273960fd0da84427bdae06602cf50b7c3766830f903bd31135391794bacf6e9e854d35e5f796ad1779de4872c89b431b6eb3c648f80ac8d597aeee2d51ba849e82bbbcf755458d52fcc9351aadfe80128ac8d774e993716ddfb61dbf736ab77a90540a0de15cbc7d675aa0f680a41e18ea2b9c333bb1e67a6beec7f70eee4c3225dc393f37a61d3489774cc4873c4e6080642414245b50103ee020000efba2210000000008256a1f6f8769809baa48de7a9879bb76ff36283058bfa3a2f00ede0a52a353eb49c17b3ca0b5364035d7bf10af2a0ba0573946823e0124567b9e4855027d406d2c7bfda086e2b90a681f05915a809d4d7c0198e307c767f315541de6210f20a05424142450101463dddfb00fd5017206aea475b6c821b23991ed32228ba6306c91ed8fe6f2b129c6eae4f1981ef47c4aa322e0e03eaf03e18bcdf371ed64dd11c5cfaab732e8976c6a9a35aa00495e2aae04028f3f5c38767b8090a201e6d28c1b3b88cc057ffaedfe801b218b3b7919b75894ecd1f97f14ea50457878d79191b7dbe6d50050cef0dbe61741d8cb75dac730c45ee5d759a1af3a0ea85491578319292ce369fc025243657080642414245b50103ae010000f0ba221000000000e8f46e0c8d0b97d4835663cab257db042580a8feee0e1fd1c73335b6774f6a3ec2a6dde13b018ca1011f026caa5234d60db6a063abbf636964bdc266f4257700aaaf5b29c78c2f1ff694566021e78d6ed7bcb6df1c11cfd8d9f29909e51e7d0b054241424501013218c42121984d55cd1d00ff7d8aa95272eca87e9e9b6288dc56fd106003fd3285cd541c73e7abeab1082f12920b2961df8a12bb88470f92185ed8743bc94e878e454de6ff0c031c34d334a91b1b707ab4447d54e9a375bb88a18293a6cf7e6bb2dfe8017fd85c37b5ab8865ae1cb56503a4a8cf8d03678c59cb70fe212defcdd3ae9d4a6ad0c879ba62441a5a15943be47f04d2bea23e2fd4a2bca7990e32037f787016080642414245b5010126010000f1ba2210000000008800436df08fb13c16bc5a541dae7d7f4b5ce57e696405e2c550e1b1abdc1e69dfff0ab13c8ccdc316aea745712aacafd11495bc4e890d30b02b444623b5fd01e649ce5680eeaf4d4b43512af46eb57aeba365424636c35c43c5244de5518b0e054241424501019e96d3c4908ea30e488195b6601a57350be039b86fba7a21c435f9213c1aa65ae2e968c9d4bb2c2a157aac0fed8f63217ad1d13bd1bde60eb14620529352ca8c84dfbcbc8cedcc3fa2eeb0ebaff4cc6ca2f45d4a0f7ad8037e3c478da57c0acab6dfe801d63f7c93b61438972509399be3738431a42b5c5cf450bb9ecfde00c7baa16450b2959f0a4fcee9b60ca1afd4e6f6e40106f68a3464053f02467afd8a2b68e471080642414245b5010357020000f2ba2210000000008094debd5f82fc6a1d388a6a3a3db4c09058eeb68bb7960526db2be60e4c9900efe7ac1c14b4d50514c852cec7101e64cf373ff35ccd5bfbf92d7878ac001002ccb5bfb8bc6f5ef52740cb75deab2d65d618d6f0ed1bb4d5841e022dccef6a04054241424501010271e0d939bae86459b1798e97d737677b1a20c93386c94503fb3559463a64182aefee2d83d2c04acb4d8ef773de8d07f157ef658e37b2fb02f378088a4e378ae1aea878f4fcceceb96fb7c7484def9497df0ee980bbc9e2c18ebda821717d16badfe801bcc591478c3a3cf90f04b263d703bd585fc75ffcd06e09e5f9ec22631ce21fbe33e6ec9126d3ed5453b8986a00430c837ee7f227debe0fd6e4c5464c76b69c51080642414245b501030a000000f3ba221000000000540edf91f14851c1e3e61b942d0aa680ccd999388073b023d688cc8a00e7fd17a21f752b7d7c020dd6ac1f07ad39bab3dbd820ff780d522021e1e040f12dd3011919954486ad1a766e111fa0d69951e67552e1565af6bb313db3ba8983d05d0105424142450101d80ab744e29c7aad7da12dac00ad6cd476c895c6ebcc9e3d515d2ea61da82a658f493fe6d8bfba054d445d31b14892847069d9e97d636e9218b973567d1ac98206929427fedaed00234ea9afe7709b0393fd99e2fac547357495a9bb6105a08dbedfe80148d578e7c97b6aaf9316d4d4630e3757f2915dc9290889a03dca82058a06974e26b8e462cb307fea9adf8354ae5af8504626ea24762aa45a8e8ff8958363d4d1080642414245b50101f6020000f4ba2210000000005cc9acee7e9fae1cb0b3a4169a7bfda06d2c06601ebf35dc5d11b7cf498f104d90d007cc9ab9b9e7b8b4c5bd10de5442a535cc55b9d3c13fa4b3a98d241fc90566d7723829f3867b9e5f4b31b680004ffad37531aa6481945556900f843ef80005424142450101be4434a9b9759e51670674e3509daf0b2d3673d8853be8ee33a1e55bd257bd75d720d1f1d2ad4bdd3007a98863c9659cea02a8e7e7c94723435b5dd12a1f3b8468c7bbe20d00c7504b73828155e10a236f6c1cb67588f887d862e9e7a72dbe3fc2dfe801e016f409a23310ad3114bf52ea89ab598d1d71c5aa131f673e04b9deb0da2b4bf6da99f734a9386495aaaae03c52e6697cade0aa821b6b5a594c9461b6559ae0080642414245b50101b7010000f5ba22100000000052a15551c2c85f9859ad25a5f353db334f80c18797c5d23b1c18e6d54c524257872760d56e98c7f11420681ac22c2701fc8a2617a6b4e212c312c6329e6f9c0d5487cd9f868548908ad8b88e3d0c353886a898b49755fb18af3c5eaef7d8c00c0542414245010174fe3b3a94a56f18effdd4e127c6d1cb4c1fbb3ee09fedd2bafed8f97f37cb74d4148a4a80680306419b34b3567d1c806e5750bf66d144b33dd0f75bb141d484b8d6b28c5b41e30fb2923e5dec44c324c3780fe75447f3d1986e50c6ff3d9ccfc6dfe80102381d7036b83ae5d9f460a1409420130c25866a6c78d1fc2d368fb1784b3f5c6359e3c6a6f3c421ab6fc0a94a975e696104af752dd55e76ca2a4795ea77b38c080642414245b5010108010000f6ba2210000000004694eac89e1eaf8267673787148904100bb6727d130daee3cefa1a3180911c08e83d714b877d80b40ff38ddd680d45a9fb5cb2626783074ad25f07b29fd71c07bde0caa109e548d0f85f2b343195efd8f0d996e8434270038a7700a728474d08054241424501014ae9581780b6155a33d32799c316728d50c901b2f55312c32097403da7af714afe9a3ea9dc6bfc868d5aa4f4300851fbdefdb88f9f5f0e27b0e7fcdc25dc1784f2c26001d5c49d6bc1df2780584e6b1c9e75161f54484125611ba92021cd6645cadfe80171b584c377c33912ed12523d4bc89fff6098333bd74d6e68f46457d65e8f5375cd9a53a1d0a9338fe66759241594b5a04e2217d8f892a3ba2b180e1101861437080642414245b5010337000000f7ba221000000000e4c9bd47926f067fbac0f9ca272826f8191220b7b0c06ba3471f331842944e79ffecd3fb27d334354d87465b4e3a6f887975e7ae1ca74633efd96ca5547c580a01786861570750d45be405fc844fb229be4e22b4c55a259e60be7ced2bc4850f05424142450101f4775942171067054a034e85b7ab5b9745581cae9019253ad5f4ed2952187c22ba537c6c3697966f0b9ffac5a0be42555982958e027a7c938f1f8437d3f8e58ab442a75f44dad10c8e3714c43acbfd5710284ef8419ccf601eaf78618e0eb9d9cedfe801d8e8ae39a4570073c15afea18a23a741dc3387f0a503efeb1bd1eb5350da8c7136cc329645c765d795477263dc26857db0fd1bc5b6760210ef54deb88d945845080642414245b5010339000000f8ba22100000000044c27baa35b953b8f3db5dbe5e0478b72348d00e1751440ad67bbfb3ab52e82a4342256abbd8a4da307cc78d4faa0ac170a59aa2dbca736d43f8822e78e55e0491e2050dd009a142d9362fb9f1b512f2995dd6bbad87dfe3f0bb90539d3303020542414245010174d2198a64522a71623cd6a97773866d4aad53f3c96a2da19da1d86f1918264321611bf7f372d392ba078a18980f9b008076180dd511517224f5cf31b0062f8cb40f58a668c426dc90de70d91e6c93a72525a819aab708597f0b92f723f98d34d2dfe8018c94c1c252213b32329712f91a2fbd020c50bf34b7c91bee71a93bd237af410afbc52d19b622e9a8acd436feb7322f62ed00ce0ad54e404dddad67934be4a0f8080642414245b50103ab020000f9ba221000000000e8ec784d3bb01cd157d85f09724e0fda002b4926ac0ca8045469af93a34b6c51b24f1974e0ca96f522c84d4efcf1fb0391649ba573392b547ec537ddbc6b2900e1693b44484fe2c79e53786d69c5cdb6f03b5e731df4f9e04f2f3405431caa0405424142450101aaa8b5d7767081de3dad9dc0fb1afabe350c600f0ad83a6f2be96d2f62fdfb127ca32714e6636f152180609b5ab4411292f3ed7a9b031e690ea87d6bd87bf186e32997631e1ae30606e092fd65c260e005b185e9f0f6a97744f5aa78fdb79272d6dfe8018a1d7ee041d3e1bcf9629aa6410bbaf473853982118f56640690061f374df823c48a29cdd17fe2ab282222dae426285f3ba8ccc977cbdca91ecb955912f04d78080642414245b5010362000000faba22100000000038237d5a56b98e3b47ca5dd3d3eac1304734aa26c460a0e126c2d13f76a08c2c33617a6cac8dcb96498967871c76a43914adfd27d55d7d6210a706f4c38cd103dd24fdb4a1a6952bc672f4619fa66f46f73a8b35d69fe097cb7cd7bd79270600054241424501015a3401cd0fe1aff02e1573d795a54c784b8e65cab8e0f599b1653c95fe69ab68aa5afcbcd168761ab18d7bc739dd08de439240ccab6b1798677552c7d932c3881e50b1fe3f04ff8f85863e7d44273d7562b69c3d5beb9e11a6db37d221cd784edadfe8010750634e47553e05ea045d07e42c8170d9f0ed251281a9ffa256dcc301e3e5eef87e502de3686615a4da271099689331bee3d58efe3c2f4470d4722f83114dc6080642414245b5010376010000fbba2210000000003e026cdd00643b6f6aaa3af443d47f05e83a06a490bf84e90005404729e65c2c1c77a344d07172115925b4296596dd7a4b73a7ce8d0a7d16b9511a8b5c1c740f1d1abd4a7f35c9e0541972a3841b7062bf7fe91a81df6147cee2f8d5fed1b10e054241424501010e34ad1b6b5aa714cbfc41dcb0f439f616ec36f078751ac77b6cf67ea26cf20499de270379774e0c5384f36a189d07f297806548213d34b4729bd6b6edccc584c041977330155c99cf656116b3afc3cf1fcd9f3218a7da50cf3824fcbb9a2baadedfe801e2cff6f9457766e28c92936c17f6a3edde37d8cb7f97efe63a1dc5a771ffd771535b66eaab3b16a555159579f7134f6187cbfa42ba6c36b3b955488af3d6534c080642414245b50101ea000000fcba221000000000162d3fd8fc59361f492e2cbd85a628f0306c92fe6a3f8af70d00f4840a6e3b51c777dac2fa5fedd091014b2409c8596ab00f46fd7e6c774051bd48ab71e1650c989275471cef0063e83d1d16e23d2502e0a9c06c344c50fd90c9c7183c9bcb01054241424501012c10043d8c7358b8b94a5f42796e279de49d1ff2f7da39eefddcc044a3279270829346832a28c38b35f991cbc2e382c4a5abf2117cda263f2eea6be9409cb58cf60667c9c0d814f145e2dadd825a008b382b2588395b40f3189e9ce13a4a6ecee2dfe80188602342669c18bcc70380b25aa00782a476ec322d83dc1f06a18a373a6cf2b7f323213dfb621436398add0c763a40b1f52d1ac041ba75375c8768347fa50543080642414245b5010101010000fdba2210000000009eddb8790cabe889c03c2d320e700057cdff2bbca0c92e86e3925037d084307306e29aec1f07fdd0333901721275265feba87c5ca2323b66787208f898027e0ba5fac0f129a767df040c461bad14a927089162dc6d7f22112898f320a91a330e05424142450101a8d5dfac0c1bfbd2b8ae7660473417656b909104e1dc85a80bd6c0a2b0a4d257f1d8c6fe442dbf4dace17930e49e766633cbb7cb66f4b87f04e2af0c4273848f87a70918ebc4c1246d0572b3807f919c678dad395171f9cb40d530bef3905b41e6dfe80162051f9e60e48f1637e2ded25137e351847c11db857cc8df889a742263abe2fdfd83edfa4c966ab034a975054fc69189ee9d2b572addce9c202d2fec69e3b5a3080642414245b50103e2000000feba2210000000002a2afa07f0d9cc5067bf2a9e0e018eb7b87c637f150bed7ba29618e83883c35f25622fdfa7aea78b2a7478ac8f326da14d082f6ab930603bc51a02f51783d70431df6821fc3620566c8fcdc5db12b5ef94fcc5c96b979f07c396f324b08a780105424142450101d45321d514dcd7269fdfcc6b7747c6a5f57483e93c63bb2f7edf82870054c90885d6bb3a6f7659169a4bd3b18881ae06441125c77d6958e40ac7c97d8371888f0a6100e2e2bdafb97852fa1ea08dd8544cc647028153dadaeca2ec30c29ebee1eadfe801cec477fec0f5e43a05fce30d99b039d6fb5ac40cd9549909e808f57e2bf09972d250781650118e63840b69f14bfbf17e6e1d28f2c8041e4f734e903f7e3b21ca080642414245b501031e020000ffba2210000000004c14e0e833dbd0a9ed250235c10b3cfaae04ac3f12de0ceb2140bf01ec0f9b0e772832411357ffe8474ac0720e0494fd3e611fc2a9c953fb8ca27a060cde0a02a3c6c8863a3ae8562880bf4a5d3c64e14188335840b7376f4ccd376a1ff99d0505424142450101cce550d128d8aa84e7d42637f9ba50c06d4f61eed931741fa6474a21fe59f25b773ba0d14e8a529b1269b5c28e73b8e38b014e1d16d448a1e7fea3456f069f8fc57bf2c49f17efbd4877f26287da53e6e534a879d3996f11c18cdad7c571e5e6eedfe801456bc9c889f0a532e1798a92120122115791cbe5e21edda3137aea2958494416f84d3fe09e32a5d9bdbc7d9c71b054b73e3db1b5de5db0a92d1a94ddbe5768cd080642414245b501032b01000000bb221000000000387d90501ec0435069f7fd66f7a17f6538fcd489b79889fd8be2698f671a7d5a77bb2714f19cc91cdcdf32f46543cbdeb15cb5e3aa856e1305880b655d2a6f08a0e3df2617de328be11aee5ad8eb912184eeb21c69d960bc2ad5478042fa570905424142450101aa3596f4a624790e2966f539fb41317d77454210b7f2455c07eb117fcad0dd33251b3f37087969074cb35cf27847b8cfe1cbd54b41737ddcd50c8af923defc8c2a50cd2bedd29fa13bcab6e52af33d9201c225924e90b3f822bf4d20abcfb74ef2dfe801e1e431f560182ddbfc98dfe0e8efe414aefbc21a01b4778440b599694060b797cc63253df9f83d15215ebd19215ae8083b8f7a0b9a98e801a31a1a80b7e68b8e080642414245b501036201000001bb22100000000064fad775b3c31d964845bda4753f0ba7b02f248b6d7761fd4b160be3fcb441384aa13d484161b3c3e0ddd58787f642a7703b3ce5c8427cbe0ce5eeab5298d70f3bba7aca64683c7c7193329695be3a17ecf21247ee05122bb372b851c018f00e05424142450101b40aeeb29b219606b149362343c443f3b8651d86c77f477565a59c16187c512df074f444d512dc690d67d96c3adaba92d909320b2f04760b2de31fcc29ab648186f50715637a6c250f6c6f6d68b7588d54e017abc4b0d4df5cfcc41c49543b47f6dfe801f6c4832b8e61b1645ff48d7126a822b8d26d5565755100800abbc16ddae4196ee9ef6edb223b9f1b40fb5ddf2d9ab4a6a9717a49debab95b6e8e8264fbc67a26080642414245b50103c102000002bb221000000000b220c5774e43273ddc1912c676566d46d7c5fb3c26cbfa132f27ba8d5921110d46e86975848c7d7d610b20fbdf0ce9107a309ea12dacdb3d4a2c62d577454b0efeaf90d3c01c45e8ec705fb6cae56266e9c3bc500f8cf4ba7bf41dd669593c04054241424501016857617d777de53cc8f13f4853dd10864c8bf87caaaa52fa36730c224f53a73310b7b45debda8fad07e3c85317f0d15f67fdc7e054c182d910d270fefba9e384ddbd5773f40f2f89e350bce5b59bf7401b31e3e61094cb94a80e7d03dc13bf86fadfe8011dd7e4bef85193c6701056563c32372bd4f6e2512923de25c032aa2a409d2e274444b7dd92984e17be01840b41657a3e3390077e2b1874e30837a99900f01275080642414245b50103d302000003bb221000000000a248c5c208255129fffe75dd20d7c67c33231329fa77673b448bf4ff5962b85aeabb4c987f5135a7e7ea6f706d8172528fbdfc9aa124f434811d8d0e433d8d0cd709dd3c11bfb7992b49dd804499fafc6495d83595b31a61cb0b5630c2f660080542414245010118c902454a68fcdc33d5afea8a891e55e19782f509320f30412d9559dae7f050fa0ade512c41109ffe252ae4b58f54df3f71724470c70182aef4c0cc0ae22e81b45febf032b5005bc35b0ef18d5f5a57a1ef7a313f66ba60f67386cdd07aad83fedfe80167306839f67f5267f19f523e81421afe086a46e209a5ccfe61978fa1b70690497106a1f463c08060e602f410230b14565fc9095ffbf2e10205d8d04badec8655080642414245b50103c102000004bb22100000000000153bfefe87083dd1147099e4de6990e0d94f943dff621700de0c54563f3e43b87ac6b42c2c3c4a88fde605989d383a1ecc8db914fa56806db7bc164e284d0fdfcdd7000e86f19161f0cd6e27205db35e4b014026837e8e8241fc3aca8af707054241424501018276105025d6adf4af8e34e83afe93082e79f1e84be1fd673627f3a98faba83b1e3abe724581a466ef509115b8699df948a5f0c84e9ba08e74b19e492ae2618c154f546ca5a1a53c08f291c04d1ad85d36609b5ee1f2cfb22860bdade32b016f02e0e801acfe509624e82367b59759109662c80523ea12bfafc9a2ae4ec7ac3cff2adffe4e745d0ece7c9fe1c0810abf68573a6af83122a4cc99543d8db75c58a0cbc5f6080642414245b50103d800000005bb2210000000004a14d375a3181b4970a9f19320bb499a778c07ea7ea10197462bc21ca7ec015213b912cdf6757bd8ac7e5b25469f016c35ac2d4aabeaf6fc876fd9b38ad78e075a65637e2b80b21078a3e71bf0cb68426ab1101c85bff6ae4cc0aedfa81ff60a05424142450101a2c6f8cf3a8e4546fb331e2fb584d6e74652da51d08b0d4c15837cfe03fc2c41e59ade5a120b2eadf34af86d3cfc03d9c99663646454cc6cc1dcb78af1397a89769aa7e685f03cea3b5c16ddd16c4404d38e09b1269f3fdc7593a614e39822b806e0e801496737b9c4a2e7679823c865edd5f37ac1c804f7fa3140690c71b898be768350a4d4063ed44a652278bf85d53076a138e5fc4ab625155d39988ef4cc9bb4c41f080642414245b501033900000006bb2210000000005acbfb671ce44479968273777c0e18032cbf5c4651fa88c0b58fb8c4cc33431b8980fa047dc9b1c089818baa5216d2043f6ff9a1e5dfc3b0aec5b55ebccc0d05d2eefcb2410cc612f2685b048c4741ca2dab1976ecd1d35c900ece2b2cd5ae0d05424142450101a20a1fb69285366d6399bab8e2e33a02edfd1aa1772f3ea57d6d17b4ae85f91901448f17fcb23579d48b9c0a14cd586d2340d280d706dc3853779136de62988bd8cd10eef9f013230066a650e01fca137d6cea776feee1eeda7558a90d5c77560ae0e801445f5e7f10c7ccabe0760558870852c8e255fcdee6c521d9ed6ecbcd09fa4c5577e41be998c294d03f0889b70ead0a2e7b052a68398301d52e6d3be57ab38ec3080642414245b50103e701000007bb22100000000042e2d26a4d31ebfecb996c6576dd8c245ab4f7336af11e5a48114a58084de4035ea58ad6425e95de20f6f11ba00d54707854f18ff7f33c4a8e69fd67361a430749432373abce4e2c595f0e0229343f51a9e919686a1b43a94447ef389a69e3060542414245010140e6b5a115f45831b0866dae03ffbe2266b19d53c3d230296467f367c95f8841e8eeaa4ed4210dc5990d782ab3af62a72d0d7cc6e50ea9d5f7d83639650f5b815c9294bfdf8a5dc2176b8b34f50e660ddd7db22952d25f95d89d5db046e4b7120ee0e801fc74f2d4ccfbb2982188761018ca5598c4a7a8c3e6fc05cca1fa15a66422c554836139cec215e2113b9f9e90cfb0a8064e5d1eadade6c49405f9c845acb060f0080642414245b50101d000000008bb2210000000005624830230ecdb0583063758bbdf6f3c02b1f4bffe7266f725ebed824d4bd56e0753a41da30f46e4d23443bd2b199f0b1c27bff6ed3355c84f6e363161935408f5e211388913f00e965a10cdbc439659b7fa199b9445a38079b9e574500c27040542414245010112cf11729cee8aea46bb65a44b20abd67dc48f2d703ca904165740171cdf242faebd8d38021cbae8d4f6cf409970aae23e066a1a7a6bda0bdad6eb48fcff238441b7f2c415eb3e482805fd41cf32c5e4bfc097c1331bd2efd4115bb2d95fece912e0e8015063c6d79fe71962fa076e173fdd58ddd13075c3fb29f224e470161d860fd7d284a6a7c18c0e16708c04f60ae0ed36bd10f92acb7712b04450ab1ddd745389f6080642414245b501031b01000009bb22100000000098f89dc39c3d75c134cf4cb8ce4fbfa3e608e8239ac47d9cfd0a39a53ae6073897ed83ce83960ebbaaa32d627b3a855a32d2f3a0a9fc878259d4586ee8a75a08838ca4418c43246810c82191239aaf3c4da77094745e7c79f53f05651f46810205424142450101ea88e26d54418d7303de3b4e72a6109474a3cd7514c722d75a5d696153e01b1381fdc2202dedb33889a41e3eac08b1f88e365e5aa27e1e240df1f0f5c1ed7381311466fe4e7f3f1a7481a2762fef6eb6701d9deaaf3f9bf308c0656e7da5cc9b16e0e801e9f01747fd456bc6dcc4fd9a424c7af8a096d9e1c482a5e0ecf723c0abf4995b89105981ca1625a11f738c1ae014cc935da750a329abe1eb3576cad24136a92e080642414245b501030a0100000abb221000000000a09375134a0c4b8514954782a4eadd92a2a2fc981f25cb5c1904a3d253d5f30ce3af0f17bd17bf13fc9b439b2a20f9958ff1c968ea0ca3cde4208e9c74ef73042d05e9faee8bc2a206125c50ee0c3027804c75864c30f9e7bd8c53ed947916010542414245010108ce921334df1fad8eaadc1b4a53dc76a006e446f983125a9a9095abfe63167e60e0631d9142b003955f8cfe36bafd8050ab1af100c59bd304fafa8f4a71338c35c62b9f730b16e39918c6c78c7c4a8ebaeaa1e75d95654ce385bb2bbb49590e1ae0e801fc1b77635bb8e469bca7132af08961941e906179f3c16220dc2f2d2de29734b6d18cba2f202026dd4f853b30488ef11bae8b81dca4b0a4106f9358b9899d0617080642414245b50103750100000bbb221000000000ee9f26b9c3688c107baced3d035115bda4a0166dc338c8ee57b343579fa6fc509edd9be8c6d73560eea56784fa639381321e2dcadc43f5515957bc852b2d6b0f5a2f7a634599d9b3a76957f7e221cf8f9c2cd9da3d636a690047e83832e6d905054241424501011ce5a6a03456f23ccac3bd59f51f0d0657297fa8215dccf317999db571673b375fdefa495c721eab840e6dd0203691a2260192bbc19ac7abe5d6d8cbabaa6c850ea044db6a1b79c7a531935ce68bc5410ee7aa44b77c027d3f923f29e7939df81ee0e80193a1800e408ff30aecf99026227d8821220e1665c1e902ea84d934359f9e2f70b42c3e5fab7b31b334a9bbcee6fe1df7bbe649db1a3abf6c113b32a27f6e5895080642414245b50103400300000cbb2210000000003ecad4315e41881b3179079e99fa3c440a6f4679230a6086ce836e7994df7419c1eb34789d29fb625fd7c53d57a5c5c40d84938ddec15aef542db2e43aeb6705de106b62a5096e975bde2a51ad5e478790d129c4b0e21c7ec7e1e4694bf6b20605424142450101c8146f39316830dc353ba034f6b0b1e794534931afe4c16dcdc799c64914a96af04c2031f37493a4ec21751b901d54f9b7c5cc22acf456285f6cbca3b52806826db9c25ef2bef6fcd81351fb12aacc887ba93974843700aa37a241a3937b2abc22e0e8016e1cb923b608ab9536fc786f1f1ae64d23ed0d51112b8f34b3d35fbb6d778f7e8f104a8e862a1415cb121c229724ac5f5ff0632422715b774f9ea333d0cf8ca4080642414245b50101000300000dbb2210000000006664dfb5bf2417d066a6188add235e883b65ba470663faa976000f8a46559f366f077c1c7de3a6e4738554a1a708758049d20539727b692bf8812ecce8504b022322764e9bbc8232c84fe9146daadfbab78dae13c6f53321c93e4105af312b0305424142450101ae20b66bdbb81c5ea53ea184e5b140920cbee29383d16313bb2c697f1974540c6ba0537e3fe02738ebef3f777a4629a3c9c86f1d66d1e094b2dff9004c7841837a8f50466eeb75ed045e7860d52af8b5a4b740016c700dab343094b1c035e39b26e0e801009222eb82724212533e84cbd357c3a10704df1048685e992f012f63ca817ccd5baea3ae4b6ea2538fdaa74421464d68ffe054880c9d6d92cab1f22176ab363a080642414245b50103d60100000ebb221000000000de72fbb4f1a994b6d33b7bf3a8df437ea44c2865f762f7dd42bc6ff642f47628e189eb22c9cbc28aea6f64c0cf70e763c16bf4e47993877617eadd610935ea08b1efec1ce2d314e3f6049b81b25d4d0693a2cd03da7b70bec06d0a956eb3ba0a054241424501014e413e4765f27c47e962ee6f7146cd8fc7426a3cf4b4c1fb18766ad1dca1411160005b15b01a61df18f6c6c8579367f5a37d86d624e80c7a07329ef033d82c8539454bd2f9457022bd042bbb245a7295e3238c3cb685a41b0c6aa0d60ac2d1432ae0e801fdd40ec57548f0442cfb423878445052bdb1139a776af1bed038a3c0a37e214f696703a8d216b1e7eb96079e133eae2f95ddeca22326c267813b564bcde12fb7080642414245b501013f0000000fbb221000000000aa0faae3af7dc53b0622d61c154ba88d94d2eca91ef9fb50d49281fb6335f13444337fb6dd17c37b594e4fb857a6e3855f62220351632b082936415c25d9ae09609c711a147f00b4fd3f73763375aba4368b118eaba859753c903daaa23c8a00054241424501016e01a96380ef3f2ce43a317a93e660b528c61025fa8c0f206d639b9e50d5bc53abf5e984b23060dd52e81dd420682bc3697aa431dae3a061f53bed47d0ae2e8e3c1089752b0362806ee2a3f7e0d8caa6760bf97271bd16ad9530e41e0805805a2ee0e8017a772287c45c369abb491f15deae66644070b362e807621d10d28992dd034760dcb885444d55f2961bec556b4692804aab557fe6c1c932c4d318498810ddeb64080642414245b501037f03000010bb2210000000009ee9ed7c75bece9fc10afa719673c2555447daf742a86b7961132ea8f2ed761aa01be860a713968fc2205971705d08356a18e0a8a215fe2fc1e069062d180103f003dbc34953479411f4fe4f6cf91eb06069c45c74eec31dd7663814c4e77a0c05424142450101c8ac19113abd819b63411ed64df5e7829cd90cae4288129c811c012e725ff171aaeeee08ffa9ab4371460553262320f62a17996d9c54accfbdac12fc6a487985bfc65852795f1994fc40889399cf8fc5ae3123c2f7f251fa49ed4df0eca7b6bb32e0e801e86a0fdd654bf9cecd0fbe0ddf1df71201755a997ff48cd50bb4ced8c108b9d8e670a9cedf0fdd64456bb1e939daa536bd403c1510787f86bad1a67b96403e37080642414245b501030c03000011bb221000000000466600c58f5f6385df9898ac9c91781b9486ca8163808b85867a2a7719c9f21d96e7ed933df94e779caadd9962a7cdab39e0275aebe09d617d1a49d771f9b6047f895a1779cf487e859cf2b00a52b2e271b700d6bee44c0f0814b39296a8dd0d0542414245010188864e700b5171a0129d6061f8b5eddd108d433f28fa741d5bb62c69f32c861db6dfa4c3a190d35f86ecc1d6acbe1c395b541ff0bb842b2b4cdcd43695bc988eb794c841c9074f42cb7f4536b1a5d90ae331424f838a7588e705bf35ed9341ca36e0e801e764dc1a3e3d40969cb47669cb8adffc843056eb6639640cfed48431ddb24a2b728504c8cb45639823092a0d6f51dfb348e77382e73f0fe8b17d1acffa0c0a28080642414245b501030102000012bb2210000000009cbab025ac02e160c57e3d757445be622b6dd5a992e3407ab114a9496dcfd44e72e0f0ad7bbdbfe02c4265d48579eef0698d5f6adb42a757e4436c31eb02d30abaa9bb9163ef80b820cf901be2f38f40f3e69f72c278930bdf11221c80c6290c054241424501012e9bb548b3bd87c1d14b7d8487af866ec882e99036b40d75018e2717b03ab407d3656958e58e40ccea5fa1978bccb44e0f4de8545311e9ec96060006ef53128469e0b80f315970a4e4baad49197e2e777a6255826af3f7a38f2bf064c1ce340f3ae0e8014c7f0873fdae9376d80ed8d728138fe3cfc4c7620e4eea0fce9586b2f9b46b0100df898d1246de04d763669245af1ca02638b6f28e388698f661eacc6325928a080642414245b501038701000013bb221000000000329c03062fa394fc158f7121a7604261d153f1b3dce8b06933e1571981c61367894a7f207f7c830e9602f97588d1deeb4d27f0c70e00be2a6986da6fe8cf3b0d23f258ceded535045e76979df9c8364b0cf272b109f193855e6a29102180820f05424142450101aad21a14898389ae4aa74b7b1e5005621d84c22042a85cb6c588d6e54499f27287f5fc58f3ed9bb7b9d1e454e3be5562958c82cf831ca8cd7adf99b07e00e383e2cc2cc387b0da61f07e4e9d61cdc0f9d3c8621760088976a2d64a148ceb768c3ee0e801eb8549c9318dcd14f266bdcbf8dd5c8d0c4186b443e1e1e3b764b1747a84909f9f5512d878d1beb2bd67f61e15d0ac1f9a92b82d0437771ed379d7bdad882e38080642414245b501017101000014bb221000000000b8086a0a672a63503e541476f23a6046731937f7cf09e490febff8616cce886314c61fc14105d986c21323ce4ab75169d5f62e2cab454b3e0f91b07330f025037e36d3a124a84b0631a4ed144604b368bf553de4f96e9ed2878f363a5e064d080542414245010172ee544ba19a3b4222e99fb6edcccb50b3864615820549387fffd7b01cfc4107f7372404902c5edd735c6812480bbc35c19f8cb6429728ab9909bae661196880fec269ac7aa23c0ae4e8a68c3a6a20aad01b548eaf6a080b796be21823fc283b42e0e8012fe64a967290b87a1a218eba8081c4b4c0f00b18326231f0670c5dc29260a507255b8185329cea232796abceed2446abc490f88ae19f22bf88e19f66d1ea1e3e080642414245b50103f401000015bb22100000000088049774d447f70216c7fe6eb33d41213b5fcd27b49a62cb4f1f0f3ea741a8476b3a2be9b76439e4230f5e0a30a48c0844d75f1d2a12e6eb6e4d6f21dc9483078cfb026133d946594c6b454b94afc49ff4ba335db6bf97f5fb196a8df21b670b054241424501014e871b4b82a3a700230c38a87455753571e93d67745b8959a09fe4788a2f501976a70044d096f00651f46eea5e506652481913846f15a64438d78a4969f56c8e35cc8768c89e67d5a51c271a93a73c7b263442c0534fb354582100e3c0721c4646e0e801d7eda8c13afa511f1f6189a878b2103b76a91477b6d0dd1a9d7f90369d9143d09c8ba941058a03e409049050658e64b01e412e0011bc29bbf54172504a81d604080642414245b501032b01000016bb221000000000f827d3ef9ec52810ce9d3e4903d3748d6384f13d64b66877298c49af2e845f428139806afe03e126d3e10f63bfb4a7dc006d3328af68d9016fb222b0d3a7bc08e81d75c68304d76a067da151c18e036f7028482f82fb9f00f00b0d4ffe02c104054241424501017856508e09a598be9110938b0bec4f071ef43736213f4bcf997edeefd7091139fff8d807a09ffb0e68bbbf484737ae4b6f32a7e1e37d3b1ceb06be3ff4ddaf8c55af100b1c9585894e29e7418ebd955e6d315d67f37904e104d02fb288374f534ae0e801e22001667d694248200f689111bcf29870a2330de15f37e38c50e366f5a19f9ac35f25940221659a7bb2d08bd353c691eedf2599a89f5880973b372ac2b3f594080642414245b501035b01000017bb22100000000036de61e79b373fd9c0619f04d174e1c0417173b55c0fde701007ceece7f30a1e0bbb3bcd7a93133a4bdc05823c45a955fc6dc3a9e61afbdc7807e107621d320a54ee4d77f696a61023bd9f59ddaa5e44025cb34666028baeb75c8b65f43f0c0b0542414245010140e3d26887b2a5de004eab5334e7bbadc6953f447a6d59c21fea94a4299e2f5670e45dbdabaa3ef9a4fe3732e527446a830b0f0298a4a4a17120c3d90411ca87ce8e0366030fae23b99084b9db2a419097b0890b8e2a428f6d941e32dd8c5f1d4ee0e80112ec7e36919fc5761e4a897d34573e3d6ec4980836a4aa626ac3c49cb184cb7b174b91739b22b79107f6c3cf6fd65ad565d6f9023e39e1121771e93175e749f1080642414245b50103bc02000018bb22100000000020a2babb19c9c2f8c72b27cd01821299bce37e93473052f41129c7ba4f00315380c1c86c8ece4e98650cdd9ef4637af604903f458cd25920e7e1c5879b33420f43d8aa40c7446fcea1f3b35536827835f2b7df226809bbd800900f861bd0120005424142450101e4881484c41224153eadd1171a58bc36a68709d56d525c7747ac1ad0ea93507c31cfeb4e8a75d186ccfab3c587d5dc182011435373ad6b124255edd4cd404d833507e93bb4006f17cf6dfb90f49c2d2b979e5fb4342bc2cd7e9053ffd9028a5a52e0e8015ee0be753c89c450863f561eb5e0a368ae5694f58463409845c9b8f50dc57dcd83d9e60c85d423dd75c9ebd6cc8b5efbebe985e8e00138a2606c4b7a46dd0b90080642414245b50101a300000019bb221000000000825d473f101048850d077584bc28d06cdb78e3615969b176d2a94d84c0afb478618fcb7bec0965570811888eb833b2c59797269c6f90872abb52bb12bf64c10508d4eac2eaa15ca96045af89767898b403747de765e9e9c451b802710b8dd80a05424142450101048f559414e3c1ecca3bd94425c0fa069f9c22d3aab8610994410d0a0aefca1a1b97262277507c0a8be5525f1137668090a864f71c719bc846351428aed66f86162be368ad871c2c622c74426dda5f8e2dde9236a15a8515a26041e4b64700b256e0e80195c6c3469c279b8f156aa204c49a477b545ad8c361a1657d75f0516d65d35953e3a63e8b7527623f50090c487f149bfe44f50d5b78d0f2e3aba2f0a8b2fcc448080642414245b50101eb0100001abb221000000000108bab879ce97c74de3028c6c8cdbbbef6c579b6b5a970eb0bfe1643e4a8673ff5c312655d9a7614df4fcee9e78038372e737775ecbd86ac3d1b20bedaa1a008c0c5cfae796db0155ed1840ca2db1b4e7c9c22ad356220c5145108205aca110f0542414245010174bc8d98cb85508da23b2cb1d4560a65c1b351942c396ef3af86e24d9f5c8d20e5591def8661365c7d65d2fae8a1cdb515081e7ee9cde3c73fd5333d52120b8836edf9447bfb780128161ed572a79f30cf95192897caf6335850d20e0730e1055ae0e80161d2f60417b08a303c88b8bb0898c7d272b0a2b7f8f355669e139681636e0c615fc0aec6ce035b9b731d64b94efbd86dfe1584f7f9496c0d0a71340e66ff6c25080642414245b50103850000001bbb221000000000f695aaeb07ed4f8c8b16a810d6cc87b3a773dd31febeabf257da6debcb20b7210fab31759e648d7a59d929cbb4f5e6e00667d644fad64aa8d6b3988280724d0dc78d9c34c4fc110c87a08612bd6af726987e97af93cac5f948659846bbf2200f05424142450101d8c7f1f1aa4b27676f6529d896ec88be4e24a446783832aa6edaebb0f1b8c757e03f9cf9eaf453e093c72d74bf2efbbd7dce3886dbc2a24235e984c992413c8245c1c41d7c72a831d5664e1a66503f14c2439a283fbe790cfd21a5eb265b93635ee0e80139becd1ac57d58007de8e093a671362222a18dc1b67df52688b7687a1662e4f7ee0851b0ee41b5aeb5db865568863b66f471cebd488849433ac74766800fc4fc080642414245b501039a0100001cbb22100000000002ba1f1d9be9622634ad8af6fdb101b510cfdf007aca685abbeaf59fe737fa1fb1c242390c9768317931842cb9bf81f19d8b149f6b6d1411f786a2fe4422a203136e85775ad621ed263fdc131b7ed40103ae724dce1271004eeb6e31dc16cc0205424142450101041dc6c9d42dd2bcf154011379c34326c91dc9587cfd0b6d86a98b755016be65b2274cb783c12e7e4d1f17e058e18d210844efe877346f4b2b3c00a7231fac827c3c08d695ce1b7f9146812402aa96464d7b25ad615f8b01aab0dd0f02be5fa462e0e801cfcaae76c62d38a1cf11a007639edc31199bcf48cdf9b96a843a91648bf209e263fc0eb7a6b774223eb78bf0acd81d4ed2d7a8dd04b8ffd0e08727aeab3c3d87080642414245b50101fa0200001dbb22100000000032d47fc393c7ea1cb97a820d28b16421f5875059b3e4e0435a1178af3099a01f702f357388f393c7f7fd9c917b040293bc25daa5b9a2ab6f372486c07f6e54043d2e6cf63dd11446f620336f7214b8defb858cc6dc2059fa04dbe893d484480e0542414245010158336c68e74685bc31bd8801dc1d3d4e33015893c465afa03670c84b6c8dde403c3b40ae64fbbee6b3a48adf3e7ed5b7c33c3b63e4a07604a33e06b97a10b789a391480a9c489c8be711b6ec30cf62b4adb40454475996675659adc39baf4a0566e0e801c708a43ffc2dbf3db8c7360b09f0b17073b1970301d8ceb18a319f287f406082e25263a233874b3f8cea9c77935553713c456c71457338182a1ae891c2c11782080642414245b50103010300001ebb221000000000569c882cc47caf1e58c7e0be5bb4cb8784c918f629196495853f985d262dcc6c8ad9efe887332923a6d4a5f847489b3d197633b407f1a3b87b88c75f7b94660c2c08fff7ad9858c382c6692de8ea74aa9e899e68fff8a5477e51e2651355d90e05424142450101c062a93542156c4c3512cfaf7b86af7dfe1d0faebb6980f8c2d02ea61d1e424ba6c0fb96eb2bb41ca959164ca676a4e181d9754ff7853023a26b3b8a8f44d58f5a4b63a74ffc319960a261316754a5f229301d8411a264dd9bc02b391d3f6f8e6ae0e8018132527df9b278aec1082db852a32e4040a3fa1a77b307b4c1501f32dac3e15574dd46ec152a651de34af841483c875ee22a09a611d4f1ce8f9f3646103849a8080642414245b50101240100001fbb2210000000003abd99fc40a0dbe5f33b51aa71ab8fe34c3a3b18fe49b13cf6c611418cffc627233dceb5cb6a20909b20a4fccb4843c98936b27b6d393ec6d94c01c2a550f101b8c2bce880f213db25609a8e5fa6d1b2456531fad55a4e53b4f62cf76aa07f0805424142450101045c9d5af31e7801115f1d6601783b6e597b5b9632216d9a822fe419f056d1674e883b4561b7c950436656d3cbc608099bdd8b5245495a540a31f0b4fdb71586b8d4ef899a49004beeb4a6715a53597d0cc9944988cba4150b9928d6269158ca6ee0e8015304d76de28fdcea55b169458c789908aea8fe4d962ab40dbb4c56f3bf118e470c3acc00cce60e38171152fc0086f49503ef269046e7662e8bdb6bc678b4f356080642414245b501037f00000020bb22100000000024da57595fc8f4276059160c71bbb37f15192c5cf18eb72cad0a76c00ba5d6375be9fed2cacc02f4006d70e5cb6c107dc77f5554fb874db1501c0252570c4b0aaa8d4fa08b323e8a29cbfefed85c495624b13a87a63ce7a5590ada66a4f1270e05424142450101ecea04b02f997f1a1b6f1a3f89946516c062068939eca6a16fb585446472130e3caeaf8523d065535d57dfbf2fe651871f98b237fd654b0e39c2906be9bb8086d1f2a23a92be446ebd8dcb0953eb84418ef6f34f0fb3b54a8199084da2936ee972e0e8012e89e1d15a4efee7f53f97528bfadf1d5fc19c1d0f7f294f37969d0cd8321ad8277e71a372e8cd7764f16b43f7db18c79849fc435a7c7ff3d7f56f75cd922b1e080642414245b501033901000021bb221000000000c28adde0d7e70aa97075fd5549bc49d9674cdbec731f248539bbaa99676db05075f58507c5927a67dbf5b3970abdf23bf1b0fa30dc6c84fbabec6305d6c20f013c0549aac61c0c345e1393fcd2a165296582dfcf444cb67c9e97e95876b7f40b054241424501013044180e3abde57c78f10f9cc4bc4950a9ab874e76c34b8d17ac09d8e71747250a8b5e3cbeb7660948368d58b86aad2966f0382e369045128a4abbe2a2c70c8479b55ff874b65edd6c0ec7397d8bb906619a80ec8c55f416d5c11311da374f1676e0e8019e37e41fe58def70c84068babcc25952e8f17640b5616933eac5e320637c3c8715b82bb36927a05fadd02c7c9fc5bfe206ef43d3b1fd635b6401e77050e85558080642414245b501035901000022bb221000000000fcb6687f755d22592b1d75b1dc8b09a7b0b1031f24e6a658e42f54def13a8d6b06e06fe5e59fc433287a0b2cf81f4dccecdaa37349c120adf730c58a77b26209c04a21ce8c54dfd0b03b09184179c88c99c55f876c419499b3f4d76ac15bd4090542414245010150abe329bcf97ee088f5890beabbbbb0d98bc75ede28df6fde50c90b5bf9650ba74f3d0e75bf60ce66000bc898e56bb83cb87dc4802012b3c1acc84a6933d08f3496eeaec790167613b96b61fc0c086db7be7e292ea603b2753a1e0f1a52b8ae7ae0e801f401c2d85c3f6a3e37a5c545d701d22a28346ee9f799f9a07397eea17f439b8406ede1013bc3b2db35a2d6b74d88e8809057497aad336e130c3b7c2b4b0582d1080642414245b50103d502000023bb2210000000003854d918c57cd6280cadcd41203850ab425a2624ac35a9b9c32179595f595b63cf3ef6da95c66138abf78a7197e22ac67f6d27979e3f15ab328cf031d642c40c2aa50c76fe74ada4de41a46515584385e5333c86c81f7d1ad1a1a7d377d72203054241424501010aa7073a78b7ca0f2adc9aaa6421b693ecdc53f6e16b5350f24ddfe7102b9829fab11f2c9220d269220e93ceebd2ab7a6cc1d8af243cb7f2fb9111cd4020c1828a1e114ab51f1167b97e9f1ac1252f13885e324e2114346e6567a7d67835f7b77ee0e8013d93b3eda68708f9fa55ab61b4448ce381582fd8b1404dfd53ea84c0cbd207a77dfbbedb772083ce88db83d903a39b67b984495e811c1a2bf6c827dd48e7969b080642414245b501038b01000024bb2210000000006c7982460277fbfac9492a25fb8f17fd9cf65fe44d4de12fcaf6d52b0c6ae74587a8c01caf2a7dd9dd90c47acf4db4f323c468d55244031fb37be60b45ac1309e71b5dce515a51e252abac86bff3e60355740bca886d1982dc2ee8a6be5e730105424142450101ae98342c0e7696dd4443576c1d4c65a6f4b06f46fd841321fe8096abb186806a5f8daab455c93ab0660255f18524f59ebfef9b0f35074cd108e7789b4fe5c88563522fd92bbef730993b610b50f48647687523e385eac5878bacb76aee61581b82e0e80143f3a3f81ef29666adafa20bd575fea9379c3281c9b501f2cfad7a7a5ea25bcf633964081d5e5683172b89d0431ba6d68e870165261540012542978b1ae8f0dd080642414245b501036901000025bb221000000000500cf9e9cebd993e5a5b0184701619720b3518005a19be987ff601a7e67e91431a97c76932714ba9572e614919621a5d3c908a91eee76047a461520a93d21600ca36f9a61c75539c0511d05586b78684d7588b4e80c56013727895175f6d8c07054241424501010237e9a52d8139a0b3e953748f034bae72fad3da4929e0a92457ee2915b67c1aa1915fd77bb6304d930f73818a674b966db97aa1689f2d12bf95f3352a188387d1d313f3d78a69e816c2ebd746e30a59b9bbf3512aa777d34b7e828381e648e186e0e8019b5c198f0e07d255ef2079c9797f07f58b315bc2f5d71b106177ca7b98d0ece5e679676c886eb513f4def85c50085b38447eb7c6c299394ffb5468249f5a46a6080642414245b501036103000026bb2210000000001e9c7f8a91761e5aa1eba5552fc0a4f24f755ed4b3c96414d0e1b5a9d61a160d3841dfb39be2db9495023a4fe584990e577b1f574e9c1946817b7824d44fd90499aadbb03cd0b153aa213a94427d370f0850c95fbed44aa41cc6c1d7589fa706054241424501017079b4cd89813691ba1d727fb0fbd3ef5227371b7a3a3f263b3c92c041e0bd466709db87b34866c47789a977fc05ccb47684f7a19ee01cf24e1a4758c2f1dd81390f8ae4f78c342bf89bfc18bcf237f0238eebec1a19496ffa1e503b8d9e9f9b8ae0e80129f7c6f3ca00cf642420ed258f9799f85d2a91c48cd2e013d6c7c26200ac555dadfd4db60ea417ec9f433d87a503667e076db61258895943daf16bf3f4606b17080642414245b501035a01000027bb2210000000006e79507375137f951c47ce5355f87d537f6a6593acc1a2b2ea951043316fce5a29317bf1b7d6c3bdabd5d4d32e96c6f28b6a0e534d247cc099cde7e15031900ef44f971fffcbc192428ab060642dbb8a1b1c9dfe7f71850881cd177dbb388d0405424142450101e4b8b888fdcf31e83567b9966c8990ae4d4582188780a53b810cccab2bd4b7609da67647a3e73a031f92663ef4b22072cdc4258f1056915ccc95ef3558fec78f03b104df8ef327f255561c89a59769aa094102a4a92a643fa6b5620338a417698ee0e80176dcaeb9d972f142e0182a343b3ec3ec5bb1a29b36b028e983781a8f54b0fa5bb070c0a2ee6440e121f19bcabbe40c864576148a5f470e24ed3803039a84081b080642414245b501037400000028bb2210000000006ac1617ec04a87ac3356af19f186598df432bab45fa7e86d8c5f6ae1c6f4c560dddd2b3413d8c118d14786d155bd1cd74f580241d60a063d67fbbe7546c96a07b6107f31636fc32c9531e0f79831f4c46533ab36f5df2ff2c42d4c60bbd33c0a0542414245010142a48db9f74b43a5c71595643c3fc343df7b86fd65efdccb964f0c4aa92320262e7c4a2813dc6c59d85cb9e112ede92cda6a8b323d75cc4a1e2ca9426ac7ce8c245f3f93ab72c927f0bc250c9bf75c9a1c2401443ddc699d7ab1ca90652f925b92e0e801e6ceb6cfefc8dfe3beee161983dfe4b7c8d86ba7f44a4da3c6885175778f125d0c17039022e9282bf1c9de0493dcc0389a46725ef7ff2f769d948664e0a15868080642414245b50101f100000029bb221000000000809d72db389f35f816a730597dcce660d15d6620aa36e0b596678409ab234a1aac7ca464d6935aa6905413ba2722e347d8952a951f8bffbd5c6cf791298b370a8de591b34c04d493d5c02c6f6d2a69d8f17f0de6a9c7870b763abcd9ee7a830105424142450101f44cc46ad92b5c20e3d29e4735f5ff49715af07355d197096266d915ef256774a0f064addeb5207cfb4e3e1bb13a53f6414709e22a1f80eace093647cffb608ee69af0ba81d3bc3dd17fc91ee1aeedde5d9097f5c33d779b16f30e54df2d014896e0e8012a50312deae247196674c10a81bc4437675857d9b9a6418187b66988cbbe1e5c287e7e553e2d27caa882651601e898951d0ef530c949e0a6f1ccbc884c86a9c9080642414245b50103750300002abb22100000000058817798f1613d046218e33ae81f1a96a550eb4b61f3d14de8a29f706efb317d8b30689e92d0e978348f6627ca0c4b4b3019dd39d1077a47e46bdf8d5ac40108cd0e3ba26658088eacf996b64b441300af79070c60f57cd5532598aed884930205424142450101c2d357a007ee080c899f2e4572ddfc038086885481bfe02fe322ad247d588f58f0f995c6ee69fbbe6a62767a89329dbf2cc05f87e41069a771baf1d93a286482d32081ce764f364a6ef9aee49e0b142f30b5c348e09cd9cbdb39ed8908fe59c09ae0e801368f885da8dcaa5debd1fb75f371d194c39fea299ea5766529874a748073d4edbdebb7721cd04d3ed730440f0a151033504b0bfcc6525912ab77622ed2936566080642414245b50101b40200002bbb221000000000d2f1c2c8c0c2353b2b98f38153ee2af54e249fbbf1c644d46eaf58a24dd8803de54cdeb863f2aff86d8e4264538db0a014e4f5cd7c0c42066e91a454b961b70b0ef1b2d30c9951e2075b8c7b99dc9926d9aa896550f89e9fa55cedbb907b760205424142450101cc3a0ce3a450425faba970358c1b3d875451f6e5d4d33a4301955db857d0ff5727dbdc2349800324cf607afd0c77aebba14b8ee43171d98522375eb43123c48d2423a6a940048c60e6e3217cd3269ce4cb5dbc443028721ae3cf52c777ab09679ee0e80128e7d74f8feb7dff060c2f4eb39b93beaa75fe4fde559dccc8171cc92a7432811a147ddebfbf201ea5bc4bf918a0c253f6d291eaa71a869bde960449739f81f3080642414245b50103fb0000002cbb221000000000dca4ad6dfd1c74e9ad39d4ca9ac6b394840ff108923d58b408087a327b72f568f9a97271ca28da8bbb166990b1ec3bb096d2d8b8b2f2f459de4ea3e725e3d00dc8c8495d2e1c0075bcc4a290c31bb02153022d3913b6bb5e9a1e836850c1690e05424142450101b46fbf0f86630bf5853a2a310dd428436f371293375f6c0a3eeeba7d7c77093a87c402d2e0196275edc1f66127fa67ddbf722c436217acef2216c5e194934a80ed417446a2316774db33463021451ca927928d7c8a3dc2ff27bbe0f66d50a28ea2e0e80191ff82460b25e601ebd06dce05b12429a8615ca21658041c6362fb9c5c6c693a40d1b01782cb5cfcf64f4fe81b89931295f4815b23e63d27b041a85447e442ac080642414245b50101150000002dbb221000000000f2be33631f0af6b2e64c6f4b6c6b74d460e4f0dd7889fe784acb136ab020a658e7d1a9bb736d97ca9e10b1f41b91d5e645df87b5207d1b87a2e78ef8ab38ff0262ef07f1fe8ff13e4e5c804998d4c2748aec670ff56293fd71d2569f3ae16e0b05424142450101a446d0822f04759d6b0853fd1d172a81245ae1abe66cf577fea249cda42b28358833ec133c605f476d2570d2e122af73a475782a8060949023ddfb29a8e4068a1c566245d014392c203fec991e9a76183be8371237c59c256555f83ce6883858a6e0e801df0f1f16f90f13b452dbef436b837ad0320d2d3348106974d25481b1a480c2da28c7ebd0fdc98d1177b33808e73e5fde6f9a525dd6cf59b73c0e0f351d702980080642414245b50103e90200002ebb22100000000040fb2de5db942fb5c7501ef8301b3cbdb1652d66782f63b087eeb3e1666e2217b57a5ac0c656f3ca82cffe8f7a6ef30563b31a1aeef0ffdd50b2d7dfcaf10f02364fc3f3933f05abc84ac80f964799d7b6a3f5913c67c56de6ec6fe1c45d640e054241424501015cec4c8c8546df9ebdca70c56b1f1b555f7b563e5182b4e34b45a20196d1a43585ff2a518af7090f41c777e5e0da90e759061d5e58c8c3eb0630faa67260118382f5287b2535b8a8ed7310d3c70142efb8d05356b9973e9c295b8b6f9b4d4c2daae0e8013b7ab975db14c3a8080bb3db5006201b0f6bee2c2d269d825d69071b09ed0a6bc10be336248e31d8c7ed87d8a4bb891b3f1b42266a5e9920fdffd9e9f6ba8f1b080642414245b50103b20000002fbb2210000000000c121a6f1f2f55933cec262bcfe365153fc958f376cefd02426cee161b52284d3151630d9520179cf58605fb9e95596b15d3a74e4103ef3fd168470dac5c200b88c4c9297bbe4c9c4af4bdf5395e59456db4b0375311304fef09d7330cdc970705424142450101de67f1eb951d626e2121a0a3a4214680c6778b9c7e271b78f75db8964abd2807b9cce6b1999a054c78afa411d82fcf43c16e9106fa6ec513910d4cfcc6d4d18f045e4d857e478f083be08c625d818bc3617e2d816e673034423d220f8c80f8f2aee0e8019c2a69912edbf18ac260cba810aa056749ae972fd65c1aeea661a99f38f4c4a2740903b9f83e5a5baba0f064c152c8260b00d5e0da538fb733d60d41c8e7fd65080642414245b50103b800000030bb2210000000004ad0b348f0b37ebdbb7fc150804d0104a03ef7e26f1b51e4973ba48fc80e534a6fd849931d3350141bf0cc59013b1a1e468275457756e141e86a7e9a7801600b48a3659290354026489c24d0c363bab71b3979055e134065329e5c8ca853d708054241424501018aa9fdbe9c7acbe0e4baa4f16f8c7f5d0dcf6efe0b45fa7e4a7dccba5a77f0080ea7d2fa9f1961c27962718cd1848e9e149557b3895984727fbdc036a3a5ca86cea5f2cfc19c7988bc6f0f3afec1bf390b1cacf25d5dded8d41c597e2a96b937b2e0e80123cca738e5cdc33867a01d63c0b908b86284a9afa8e4602a7ab9f3572aef83219eddd2e2ea05c9856f23422a714c1759d32dd14a7260bbe5cfec38e960c01eff080642414245b50103d302000031bb22100000000030434f77419febd19923f334605dfd65094f1fc24ca4d2bca901f8a06b9403508ecf796ca347bc60c622e43ff918a5034b1c76629446c3b40c91a5d4d60cf10a1723aab985e8c127934c7a6293622fddc90f7cb2e0f203610b8e766fdbd3b00b054241424501013257571f2baf3c32fc08f4cb70a2a918aa1dab39267eee9496549a01863e453073150d0821d7c61c41af8211a9ef3c68ec16cc21363f00b8957cff1e4abe048b3ff6215e2743121b4a01b53c75d42c5f3d84af19b3e01ebbd97825275a9fa797b6e0e8019dd7b20f597be6f00b4b6dc2c6aca5c5b92ca38ba08c28a3ca39e77f5716e39dda9ee616e481a499b4bd6551abde640c5ca585bdc4c34ff3af3e9c43d91bcaa8080642414245b501012e01000032bb2210000000004846f7e784a3189ad3128b947557b5be771a7d9dac11363a2c75299365fc464263f31ed51c4ca3b2b9d0bc84ae3fe19ce151b16b03aa77b5c3e6cc9558580205cf87ef72412bfcc9133bcbdf97c67c921cdd35f165d060207368525593377903054241424501010ee34b2a21d2736dc879372c000a059603da9c90f1c203f35fb00997b79be93475c423943c2d2cc4b595439ac9906891ee41fa438fad863605411de2520a3c8272aad27f94c33fc699bae349cb924c2715654e3b7d3f2d610bd5d08427bd22febae0e801a9d96fc362a7c610c839e36663c5830ea42ab19a86ce4285f9387a5b2d0a84c351a566ece96a1467463aa4527e0d25efd9cda275661db9b975e14e87b0ea496c080642414245b50103c502000033bb2210000000008ad0039c4177e17b44e1482715af48b24a2a29a418de820fceede1c576213022184d68cb5df0dfeeb8355cb40cf34b3550d656d1027c68023b9460c828f73b02c7eacf304f5d2ff35f3a622feea3ee143ed487c4e47f58d25148d288d3bc780f05424142450101aebb9dd52799cd5fb1cd878b2aedbee29364f963fbb93e29b86684eb528a23247603cbef52d0064d4556043c1b2b7507b3385777c7182ad38ae836a453960c840bbe189e8a9ad7e8844f69304e7db239ffec218de4ef43ae80bf4dd1a0c67ea0bee0e80132c9bfff675dc3c17ba30865de93754ad6b83d86081dc0b823ebdf284519dfdb2a49884dff3aa25f2b2a77bd1eb7d386164d90c70cfe60a9f5fbdb1587b66eb6080642414245b50101d102000034bb2210000000003238be538c856b5bbd0f2ebcea369818af3d799ebc443ebea2bbbe981758d27548879e6e25bd1792a25793077cd67353286bba6b8290713c54db3875477473018d3a6299567f948443bd7b9875df942a70a1140198892a6515d56b97ca3a060c054241424501014cb963b376b7956f64d0dba2a3dfd76f3b45585bd860bd1fd848363643ff0c74f7f6da4b43651a5814c8c613e6d4ab0cfd85db97573b736dfb927fc3b7e84e813c25c3716106b828998f95440fe01a74bfcab90e50ea595035b46f0b07e5986cc2e0e801145683c95f54ae92ae67b12a064056cfa9c89dd9205e74b1a50be0e687b0d3e21ca9f1a4425ca75cdd8b41bf413bcf7357bad06ddf606268e7be5c9d17d03aa8080642414245b501037303000035bb2210000000004268fde3e144c2129aa1c63ae4a7b12cfe87cbfe43cec97a93007b007cbfe659ae9457e79431b52fea42ef9a4795ef4b44aa87bed0d02a81f64baf51f3ba110780f9d0f375f6a318844a07e7340c501ead3442d5bf6995855fc03668b83aa501054241424501010492685857340bc22cedf65e1664756b06019d32c37c4e6600010d1f83ccc070f4e7f49002442d24aa897fee04c1f41b055406cdb9efb62970751aaa87185d88f140e43f155fb448d51aab322b6a683b4dbbada2f41990a6ee4b56c37f8d5d18c6e0e8016406ed109ed715ebb3c16cff68d97991826633be19576240e75ae3552111a2680767ba7b5cba56d650f9ef7032e7dcac2b38e3d215c385c41e8c970e6608f6e5080642414245b501037300000036bb221000000000bc5c3ea7de43b5d928549c7ba307322d04b22dac3e856598e77add8349f2fb19a21dd20ed242317d0a45bdc435c78815bf7fd32abc48e4de9e3448c775fef00e506feecfe726efdd210ccfa53533e0a1759a8ab9df52acabfa07f4ca979c4c0205424142450101ba8274e6ab638bc06b5d26cac21a106a02aef9afcad5f3cb87b5851913c5971745075a4305e4fb3fe8030dc6ba474200c017d723fff5e72047738154537c0d8afcdb2658dec33a6adb0983a15f3297b2e438076d71d1bb30613a52c8586f2947cae0e80162d3c3b97679ee93c92f42a917dce0a12f8237ecbb1efa17d25b7aa6f2b80b47f1ba1c5dc11f569496a7ceed0251ada62282e8600ab970b966104d32d5b749c8080642414245b50103f700000037bb2210000000003091287f624358d3b0605ca30686f3f3ee0dfa8a0a866bcbbac8521d7e92096f3225a3d18b9fa2f61a686d452e46cdd187e0cb5ac93b6c7d918d6c7c6bb3f00055674ba19c5aef68be512d86a1a77df4e957fb938a2ddac71c3bd8f9b639e80405424142450101860e8a39a35956a5aa5f51e06e3ebdcfb62eeac3b5c548f551c367e6cc27f83d26436f3b322b12fc78bbf27a47e793495ca4c72e16b4cc382c0b8df7d349808d73a5ccede70896d07b0e0ad8699c8bbe5d9d8227687d8e1484d8e4f5570b6c69cee0e801806a5631d4f8763bceabc23cf5a5520d998f2c04596e564b3cf3e97c38e7009024a25dba3b8398d54a6f72ac3568ff9f6ed3df4b79e20bdfb55a4173044ecf03080642414245b501038600000038bb221000000000ba5f5d6b7114915568dcc8f5bac1a5e06bb533cafab8a2851cd161f5332e760f0f91414a0135c45eb187c81472aa24adec173cc8ca8d6b5563046bb55b1b8c00b4146e48218d947dfc9a8ecfd5ef8f47ae3f7e861d1fcba61405e8f36ebb970d054241424501018a8a9bc6576c6f3ef330dcc432d7ae08f1b8cfc626f5828552f0109333739a0cccdb01b5ffae2b241a943c21fcccb221e8f3f819fc31ca88110bb99cf9fe6884b19220a88e301bcf09d587f837a75168fbb10baf091fe4df9fb598f0a6f640f5d2e0e801a58ccc255a3336d51fd325bec0b9cff2780622c81738aaffb6344efeaa3b4a5cbca80c06719890475fc9b6346c3012b9c4b253b0dc64c68b264ef5807f7891cd080642414245b501017900000039bb2210000000006247065a21700ce3636d23dad20925c7141091f6290107d808ffb594c1c9a310e74d97bd7efc2f97c4b80077fe6afb016a83943ae560ffcd3f8dda7f4dd21e02b8cf5135db9a0df5462f58b86a784cfaa447105609d225b79bc806b7bbacd90205424142450101dac26bad04108d3e4f06e01d9090af4b8340ea0646bafd754d41fdfdb626b53f804ca3f7cdbfb5dc76a210f65c6ac9325087dd7c2fcb3d541be8dd3f42749884ee96e41c802406521414de8ca04233b4bd94988851675deab011b41e9eb7361bd6e0e801fea5e76ebfaed355e06372dae10ea73215d8bd645671c2d5ce835708f7616efade6f5d84149e0772c20fea2dcdf3c19dc309b9da97a16a2040999198573a2a70080642414245b50103a10100003abb221000000000e4c3a577ece106b06a2e5c2ab1ca92b79b847e2d68f2056035a01ec21797fa35178eef5016531a3b964a71719b7a27a3f0da3801b99f242fe8ee4c7fc383860d7192fc7e3046d916f2845671d45239575688b8c584e565ce148e19fc289e6405054241424501018010bab5337dda709d7318f8b1977223ad8179ada7a05eb37b679de63997e6052995a60438000374d36cc710cb7033c6e11930f01b991387023bb19bae0be48816ce80b93071b56fa4671399ac80297320a53e029fb73dc939439a33a0a5cc2edae0e80196c23ed484664b3009c177f3a5b62ae5a293f5ed0d7fbae565a7bff1130da4bb57a2e75385c19d70fc343b40d23981f3cec78c1998a119c2b37a24c1dc83cecc080642414245b50103da0000003bbb221000000000a2f17210a4b9215f7be985c0ccb3fca93dfb9f7c7ed9cf41e3abd0eb99b9c8284e6b6d0694c2cb9878664367f3dd53036d49882cd7b40b51ebb38b58a1065000a6fd4a7d964dd5fbe9118b604f592d8c023f181ad567bdadf0a0a88d8d7ec20d0542414245010192abf7fb944a63972ef6baa65dff601196ae1fc0feeed4c743d999ebeafd0b67edefc281f4fbd3cd533b980d796f4ef518291a9fc3d2b6aff45133ee9088e282fa519416e08edd3d7e2aa43fac1e045d8ee97f8a3666b73d1a32c8a927fc51a0dee0e8011196d98a29187a36295eb062ef8464e75decffe6b50cd5dd2578c174b159e4dc89854f4b266887261538e85765060039f56627f42f1358446565b16659bc01a1080642414245b50103000200003cbb221000000000eee8498719ca96d2dad1c04f4e31a4faca7da5753c3dbae1c29b66eb8f3dcc59bed9be0f5772606b313ab23987c42a9b51a37808c0cb61da15134d1553aaae05cf87feab9db670c728d1a978f87fafaf3601cd0451672e1cdccfb24967becd050542414245010108a9835291ae99d17261cabd80ab9ff42ea16ae4d93320dc3d4e73bd473a5900ef0c5d50cdd5c99da356bfd610912bd6a68914caaade3aa0a2297609d6cc2b8aa6e54711c9a694779bbb232c4bbf1303c0291769c57995bcfbe704395a0d799de2e0e801419a2c84bd77564f9dc9e0c45218f15173e3c0ded1a26ae72f62fba54949e4f06755101ce8e17e89139115df34ebc712f603dba65e7bec661a54ab95123e07b9080642414245b50103150000003dbb2210000000006e93205d119dd659421c4f6cb895d22c093e61931db3d3283b7c23df51f9030648932a9e96f14f8dd048237b1e77cf40519c3852d5d73a73de28222cd992e203c03a84af636099f0ccbfb28a80d9851bc53040b805b166d814e0c5fa99008f0e054241424501019e2079639d7f8641adb9e14b3c48e6386c7007bcd1d4cd369f26f3027ab95d10b161e288349db84cb80c8ac79eb2e75686cce179db8799fae792f2cde3f2478b4358d7b2fa82e53158ccc0f5d41314dcf3e05db387c35c6d42834272fa667929e6e0e8019011940d72a814f2361c46c1b376edb456950fdf33d123d39ced90c43195b0e78bc975d6b2a79c7f82671505a4adf666d881c3b1c6d27f92f8178fb554851d47080642414245b50103b50100003ebb221000000000f6e687ea10037f6fac05ecfd8451bf7373968224194364291a1c74808e6e561e7737799d4820f8fd32ddf5fd697a49565c981f8fa2bb1bff0e21e238e42364013762b804b20effbd408eb5643df09fba2f63e9d64f9b52e812d4e9562aedb40d054241424501013eb888e0d636546146a01e5b88e9b8a7a407de04c8edbe2c93d104fa6f77f82b301e19139c0f738d26eec973bb48e37dc2579b53e20898e495281788c93169892caf29820d88a9f1aa7302ed1817f8312e8cf7e7c962afc6e3ea953dc224ab3beae0e801e729ed5198760b012ff18c3a2ffbcedd272ebc0de15ab0b7e1de49352795ebe4e9203ab84c53b925978255acea45b9ee75a2d8c73a8f7384dca28f317072ffa0080642414245b50103a20200003fbb221000000000ac1af475e052b526778aed425008fe9b56379a7ba2081f12bc092bdee7aca9344aa55928febaed3795d74608995e0b16f58fc56898c4794455d8c39db336c301093b2e53a3c73766c8c9d254b4596729f1b96731403d6e3ef3f0a30602093b0105424142450101744137a9b3dcf704312a2310721f59a47846ebb84e1a49fbd1a6849bff8657025dd5dad4a5ffcd85db5d8ae5013058af8967088dc31bb9e59f775ed698fea882ef7ecfaa2e7897f4f39b6f36a116049ba5738da4b587cb2055649d62b02956a7eee0e801e911d4a861a837c8a14f07822f45a56c18e4d159f99a392dd8dc5c5438884cc94158c2dc15393d3baab0b1a2c0e12ef32b776befb9344399d5750da9a6d3602c080642414245b501015f02000040bb22100000000070d2b5cfc20da888b954fdc4ff595fb8eb49b02c221078359171aec80a95865a90a39b5d56131f89f60a2563a699464a3742bc279a6247a799ed5689f78238059b0209850a7b79726f5aec5f6f35687349c5c990c9e0cbfc5ae94677d8841609054241424501011848250162a84805c1f4e1b908d7f72a213af330cbad57f58a8eb6f74ae47140b6701b39e8001e0c74e9902fd9c67c7547c7fe1160b11c867f1cfd9cc9d8298669a59ad9b3b4bed9e92686d23ad8246b8a9a1f6e610e7afee510ead5f303629cf2e0e801a7c41aeb98d8023497c4f0c035c7b8b20b44fa0f4e27ff7f531e995eca16426a67ecf879ed371952416f3cee8cce76942e0f3150b7630666e05df337a7c44159080642414245b501035001000041bb221000000000001c8b95155732d2c15f460b01e7721d96be023c56fbf6e1698a2743ea7f920ebfe7e52985705aaa0cb6dfc020a2e91cec48430d2f51697346492f95df4bda0c09776125cadfc242ecc987db74b4db2a5ad5cb7420e36d71ca588c00484bbc0d05424142450101b221ce0d9aec973f6577d35a02c3c608a14b4b5e55336aece0be1b56e371bd27dec0a825bbb9603b6b845c3ee9e69632f9ac02a301b6bb4157cef59441d9f88e61617fb7acd1159b740a7ba7443d694d6fdaca79d194b9851b878a995f652036f6e0e801999ebacbee77a6149b5efee4ccf7ca0cdba892663726637058e39782988e6a159b0fa19d6ec5b83c553fa603303d11fbc1b2d7df371704b05576d10a266168af080642414245b50103d600000042bb221000000000f2db1353be8cfe9348861c1359dcc529dbf72f97d01f0efe0260ec02d3f2da0b2aaefc33212bcc3bb33f7c78072ad2f613c72cf5eba78199c548836c7206ab0bcd8cb6a1e410e7f35b1dd6a4e6bd178e17f08691d7c1b366570f9f2c0824c409054241424501014401fb6152318d389d013c0c5276e556e351e14e3275d5ae5b9490f2416b601abb3bade2496d2d32a6fc5534dbf943337f253ac975ed9487fbcd8442ec476985381ce071ca44e564ca9851b39aeadc798b8d9ba2e2f8465793f91325d720b350fae0e8015b8048089ace72e2dd959a123befb7ee46f934a81d0e045ad5ef9505cffd0c0a82040c1b32a1e828e22691e65055edd800479b08d1551e896f19cde6c2e55c7b080642414245b501030403000043bb221000000000c2c7b3dc3702911f2890fc68faf2d7b8d2d7920fe3b7fab48be1e100be8e0058257d1c217593834bf36fadb9c422825b5520698dabee37f87d28842198545101d3c0d7b2d813891215f4cde27f710989f1f122f8e5d85736391ef842d6d1df0f05424142450101703c3372a584cbd41966e8eb54bbb597b8c708b257b86a82991fc53a01a85f2202a19d58e341643feef9b12addef9de99e5ce805640103795c907b0bba428685a63e7a0379f47231c72cea852d08639699074fcf004905f572d7f3e09a76e63cfee0e801463cb72991f11b8aaf3f773d3bbd931c0abbaca2b41fcb8de5cbae6649220eb2368740cbf2e0cd39bab88bec71822b10836a7e48a82818343c7eee88a892d356080642414245b501031d00000044bb221000000000a83122746acbd4c5a9854dd0fffae041edad9538f0551676b80d67c92c8fd43ce90083e899b596dbe8f766f8bd9ed1e4843ea50522bbee7846e855c02b35f309a8e7f05aad4fca8c349d801d969b8a7f23e6755f433bd1adf61d9b738942110805424142450101e633879a02b5c9e03f13d7161a50809f971958e907ceafcce95808ae83e4a2033ebf2d23fbc3262475c688f7224c5de995cc31190801334f85d177aabd44a38437d4fdd9ebdfb7deb75affe8f85fa9be55479d0490e779bd1729979b3ab43b5902e1e801e528b40c601e1d49a01f2aefc20fb3e81d1dfdc8a545fa5543282b825b0e1aed4dc5b86c8216cce8c51f3a22580ba19601f5ce17a75d1e8f52144a4127d12eb4080642414245b501037303000045bb221000000000862dddf31b057253500d5f5d1372d4d007c8a1bca0dc5ea8dc133d25236a34084a198319b1b8edb6a6d75c590c3ec330a639c69adc905f32e1e7f5506bd0220b384ec0901dfefe18122dc948ab71ac89f8d326b2bde609a0703cf1385c51440a05424142450101741f9535f70e4aeb58665c42264a06ac6707c813f6621950f416d277816b392b92c37f220cd2f6c54ac2f5a4c192843a3488dfe8be47fa87e8cc511d7c4d228eb16ebf167ac594561fc34cc64c18ddd831de493f0e989cfbe61284f81e6651ff06e1e8018bec60e9da31ebcdb7afad4efc4c9abe20dc96cc44cc906774868d905382c36c0c63e4a28d547e16753e97adf550ea79dd17add4462309e0ac5523b0e681c25b080642414245b50101a701000046bb221000000000c62c70c16da638911fd887b2c63ac175f9b4554da6a1f7c23694ddfdda34284ea63c738d9cdb607bf1e6b5e962ac1edf6841151fc98cc3fecc8187cbe514e10e9e540f0c49b69d577a715addad4636ca05333ac1244a1817382b7deb5cc4d9010542414245010194401065e5caf04c9804eb3ed306b128aeac932a4640d31c712f58d869e304171031b1b207fbc11d84d182f8e8bab661375e8b6196012f2e9dbc70bade0c208edb209d230f345e9f12dd6a0dc37e2d774c01998aed5de6ad7e1b72f59bb362980ae1e80143e3c12e36a71c54bce806091c38c65e9719021ca39a6e6e1a00f504b60d58b12d2e41d2b450ffa3f2c65209cedf6e3ab80058b611d529f9f4bcf1051673b195080642414245b501032300000047bb221000000000907922a07136688d467e3061b8ce7676ee2d0844cc32e3ed620ca63527d01e7bb45d9e8234cfd4f7ed0a23f336e2de7e99380a99a869ec442d94172babe946020ac733ba4da7c7c7b7a1efd81f6344799410cd5d99d7c5c989b9326f69379f0805424142450101949911a98c545299f175cd001825304a2d667a24dc9f937dd9caf9f78abe9d0f41b7e38257bb6b624d4b586c4c972876028690bca00e8badfe118ef3febf2a8c1b3cc5e68178c1437e82f577b953165d0301e8c2be5f91d3f24e86be98483bed0ee1e801d63fa50e004cdfcbb43223233ad235ab1e6fd6223e6a5908554a737be654713f11878d5466b6b451113e12f567eb954e2382bf233d71bd36e3046d3c90d086b2080642414245b501038702000048bb221000000000528c9605c1f68f3d236e3fe9a7cdc5a053ea18992fda299aef4c3369f3d3946cb94ce88d38c61bf422cce56c97810a5690b5a593c7edfe87df455ca70218650f5500cb4960002873f8f011ef7756bd12ad3f36fc18d185a77cee591a874a530605424142450101ee96ef38c2c1a46056ecc96b6335c716e8ced829e4f33155f83a3219abb06d1cdabd2a77c93c814ab34ef033fd3ac4c7a1b52bffd36666e87aebd6cfe085578947b3be5c02fcfd1e8c1efa7d257a31a276c5e2217c9c5ffd0602409419fc510e12e1e801a84f26c57a99032e6a01242698dfb008a0df202183bf143c9b094e96b06c5638bc4a2e671e4573317f05e00ebe7349275eae8b215a203135aaef09451dd438ca080642414245b501035901000049bb22100000000072db46d1f8f74198be29161f31a11ca6e7da72feb09e4e5c6ae8fbec56ee5857393bfb1dfcf7a735414e3ff3afe45c120e6a610b2b58d21247b83d6b1459ea06e6e762075f5cee24c1bb5e1017073bd3a2d0579dad84816deb555ab2d0bfc7060542414245010164cce8ebf0f1c62cc915853bfa8fbbb00760ff05545982e25090534d91b41b056468246d50b5d6755012634710f87c8597c9067bd70bd96f09c2a01abbbbb9812623ca357da5766a8cd1422c6cd712f80178fd23c4e463732d46852a5e81fb3516e1e8018acf0ac9d5fd1caaf43badd32683744eb9c1a2c4d3ecce74380bc2ae616d77a7fd22f78f6bbb13b2b274304a8149eb8a2a0b4b9306bfe0fa0bee80f7df7acc58080642414245b50101020000004abb221000000000f8ff4e9ad4d25c5a91f4ea0bef695743cb9e8e703d7e58f15b56e0df41fb2763392cbff5a000f1071ea5fe60de3f275bcab81fa779c2ed18912a8cf4fbdf5002e8aaf57a70f2a2b1f9ebf4153cc224f66b01cfd6e0407909f4dfb455a0160b0d05424142450101404348041f836d2d0440ad42f916f16115a07d87c64b7d60cd4323e733d5b36b8c42b76c0a8a060c76353f374d55f6fa35160030c84e56938fb01a2a02349c8baf31800897bf9c2782046ae0278a9457811a3d94ed4df22f82aa3ce1968380b11ae1e8019c8c6a9d67228c18b159bac2c2f45341f243ca123496518062895757f5a7a62119b0c59f844578fe316b008501e708968b06969d8999b540ece3ba2cb7e82f1b080642414245b501031f0200004bbb221000000000c8a66221c7f0e4b73a53d20ee88c450c0ff8729feab9b61b2dee85a9da07847d23e53204944ab560dbf565f846fb5aea4617e997582f8ae9072ef95ccbae470816a34347d326c68b63fa70dad01b67ab536a7634dd83d57d718db62931d1a00105424142450101428d4aa260ed076e73e1e0e800c5153bb9a4da79d5fdd3780bacbdcc4cdcbe67681b93fae7f26c2de1745e58a93b3b7c8930f29994f16747a60a4e50ea4fc18beef621d8c7af6b7d4e678247054b59746f9ea27db852c8c6aa9d205f9ea371b81ee1e8013822d59fe0e58c29c947b2cc630eac7cdeee11cd648ce19040a5ac531ad10ea766ddf4f3659e0d2b481ac3cd7f624018e126225b2cf93edb946a84eae0969b69080642414245b50103d70100004cbb2210000000002eecd8064e25b14e190d31884711ef054ce803ebcfa7b18bf4185dfce51d266d4c1c3576adb1897fc9d7a1b20af0aa4c02ede7ceb329d690c76042cd238ced0e841dfb87f50603a3b377b206a1b9843f0f5dfc8fced158eb7760c1c9c7e2440605424142450101567da6ee6102e2de8e1c7520a66688716029619cea0583294877add824579a2a649b9a6d04e59479dd169cd556eb4e310bb056a43da4922a0c520046a6dc328e9f2cf4ffa1405b9fcda541e1d916ea5e8971177ffd00e7ef5b64d34c2711046122e1e8012ee27fb86641fcdd16bed58892183e54edab4c4d29fa91ab0f80c83e9d14aeab1b02577ffebbebdf0b82039bdd447c76ba0b31701ddf097574693ead4e417770080642414245b50103100200004dbb221000000000ec196e1b0284cb7c31a122e9491a1d761beaf4425f24257e66bcee36497df20f41249ca3bc59efe462decd46611817228716db8d6e3a6b13cc10e9426a98760a82fbb06abfcdcef7c5fd1aa0bc1de59605fbe4fd440a79aec4736d6a4fb6d307054241424501013ad1f69626c7a93c41df07a188e2c5d95338b652b15ffaa536d880ea50cc7058c4fadd46f3a1deeb3aa79cdb60e8b1b93e22ae56387186b4fc3a6088d3eedf8dff0757f871c1b27c75247a495de7b082c7151eae6ac9665447d879059df58c0b26e1e8013ebe127d78e40eb35a1945e1e5f7d2320ba5933c771cfb78e9faedc2cb0aff2712335681ac49c417401276d01f367777e0ca2938fbdf28f810df7da61efbc160080642414245b501039c0100004ebb221000000000b293c7a43bc820015f3a774aea5f5f7dadbb74ef1ea128aaa9ed4c2b804f9e0d95c3881c243bfe54cd20c915d1a70fc8f2129a27260dc1efcdf19d40f7cc0c08ffa19fb74c8072202c2d1449d209d475c08c2f1585ff670314e3f0f3ef9d0303054241424501012c55be6c7e82bf114e40f67be68dd2d13c476edccca787e410a4c42fd5350f62cdee21130efe159e2a06652f0361df8219f9e34bbf792e6da2c56bccfbd0b9845cf7b2fd7397ce6c935ece25054a5e0514c9559b93223d57fd6f8a9a26a408512ae1e8017e4dd20adef92572f3a771fc6aa1fe5cf88f33d73c71c876b7472d3659aa2dceb1280e1f1f4ac8a426019cdef3a09fdbe6a2b234cb129f53b5659f4dd9c72d5a080642414245b50101750200004fbb221000000000e63919c2dfafd73897cfa67d6106fe1aebeca309a884c34b9c6b6f755b2efc01bf0b6f93ee96f439d51b7e489080824ab0fff4779ce0d177eb7fdbf73d0b070c4c8592037baf41de4f2fe35fee9b1851e8209c78e573f2dc2bde493342d94c00054241424501019c502d63054e1791ba0b6a55e2b82969d45cd9ea5cadb1c5cf10f0e80f92b570003247236eff38cda72084e4546c18b4685c0bf03468deef89a8a596f9febd8c7e45ad22857923da1d74c5960c7cbff19fd52369a04c001b47825a4502680aef2ee1e801b4a7a114bc7630e02052e6d53bd1d94da51e3e768d2a87ae862537b9cec8db74b0da6a738c7dfd41a3d2d3bd3e41bb8ed5b20f4e22a00b34dd1af4f297cb88fb080642414245b501033b00000050bb221000000000f43e9754be786b9911e3ec6a9426b1a159e1ff1b94efd1af85193a9865ddc238754ea74630b0c63dba5890fa8c2fa670518f76873ba2e3217d28e992f90b1200c82b16081e34004bafaaa2752e1b226496d4ed98e1aeb559c6fc31e86a0a260805424142450101d60f5eec18ff68c43f768f04fec903bfd25eb2853184a3bf8cd3c525e12cbe298a915289a17473fc0f0cf350644d4e3fc405cec2cb9fb8f37a93431e4bdd47819c25016eaef793bc1512210cd493d9f3fa2fad43bead1c0ed31436d4722ec11b32e1e8011f7c065edf8e10168afc14fc591f7cf7be0acbff5f0a06803ae4db8d4a473b4811c793a3a639a94d2694113dda586a73f87134c008f8b2a22eb9d1e3df844efb080642414245b50103f201000051bb221000000000f80b0c91648332726ce2dc393f2b9af15f9f452f73aa8177f8dc8244d738ad5299f61faed2629c44e4701876e768d3c187eb4c20857923156838b17e2dfbd90ccc0efae100b47f67e0ffccbf544c84ba3105883dd0a9cd878e369ddcd0c4b701054241424501012a48338e1ff5731103d04bf287dbbb3f77b11bc93b3ce6c602fcf8165658356e296e1e7100e8ca577ee5416003a31a70386dcb8c47db7adbf28d95a1558dd88931892f6aa772ea0caa464d81b46b16c192744e592fc5b8075dc855d69c23838236e1e801c1beeff0746ad48e55e8ce014623e486b5d53d6c7ba00748c09b1c97505554a4038dc6cf475562a36e542c26a0c754fa6ae42fed11f010e4773e61fadf1c5d28080642414245b50103ba02000052bb221000000000faa89a59130e5ae94853accd19b374767e71ed5c0ab926ac2aa9f7f3b2cec50042c48b5c0ea85124d4228cb25d8486af999b9342fca29c394288fb43b783f80c1d51c2972ace5fe722a588234f6aa22092e9f9628cb9c9c44f7d0e2488e6520205424142450101d29b64eb98740fdd5121499bc7c7584656e14a454c938ded71064a91fd71e9134e4d480b864f4aac700aa0f8f656a74bd76096255f30916d0f02eec3cded57814577f498398f88d202ac641d0477dfedaa47ea6eefca6488c1915ca94b7846533ae1e801d8f4c31a3a1a57480cb480e5967aa380524a7c7ceb21091d720fa401f52d16ec8a0b74d4927640b574a332e2c99597715fa09e3e1f98a4024136caf24a3ee18c080642414245b50103b502000053bb2210000000008e1778d9e45a61330d10ea9c5f5f85e34b5cbcbff8ab4097966ccea28d98604b9770a8698386ea79e333c1aa86ed716e49902ceaba75cdfe0abcb40e4e927c04a722ffa491538eb344479d24ded47f2091113949ddca0059c4439b419f68860f054241424501016454406c2bde6021aa7708fdba7c67ccb6a88a8ff4dc43fa815df10e4be9a92d0897e491413bbb4cbc979ac84bbbe1f7c0b1a7a6cd3f41b194072a7dfce46382657d51e84129e67c0a86f368ca76a94101c8dfc9356e40b4361bf1bc8951eb923ee1e801db56996fdbb4ce6bbcca06d694e383f522108a26e0b1acc29c8cc284a89d70e270ae8cab352f525d845d66bcdc926c637c3a75968951b518143734c43dc3eae7080642414245b501031d02000054bb221000000000d2862ae9ed1f3768733de8b251addf67eeb9f964465649ff585662d62faa8d4d9cb654a990b26142c86ce02352ea9f828b65b1b23e2bbb3de3b8fe27551d2d028cec9473aaf9aff2ed43419514b3754fec0557495f1fb5512ee2296ca747870f05424142450101ead0bc8073dcc5a9dcc8804e6108b30cb50a59345f755192da9d0785538d7e630e49422171b75f9f124f38dfd8875e735078a4936c57a27b6f9792afb8fb2384013d369d076ae17bbdc6563735d824f54f2a7a3d014005d5b3902464a68e42e642e1e801f6dc107efb9d5be1b99a2921cc50f68a1fb421ea65ae8f0b12707080cd22a64f7b30bf2d590ce6e401725af082c3d9f68e8e634ad70f94f7b7951a6001425bcf080642414245b50101a302000055bb221000000000e0a8767d6771c974aa119ab3ee605d678b74f7e11f93e56dbd51919c868c820a2a9d98a79e58e3215c03f7902d866be5b7b448371242a4176df3b147ef94370a9d34a44f232358147ed98d5b3055d4f8f18024e175e8132eaae06d9e1b69e005054241424501018a2681e0529f36512e09fabd7b29a4c1cf737a328116cd567a099afd8483e90d6cc7a1070bb2294104fb8bc25eb30b384ac261952a9d93001d778781c9d70f895047e0a691dc3b1fae191210fa09ce57015eb2d5403c8051e98dcca719059ce246e1e801a09b8f7f7eda34f9da4c0a389d92774daa689d466abf6b4a2c91f0547406a0edfa4140f756bdef0fc32342aef79d4670506cd5882e6283a435ba7665f28cad36080642414245b501033801000056bb2210000000002613f7639bba9b6e0ba55a1776bcb6f1588cd6a3bd53c8d29c56f4d3d9dad72ec5ff8aa5b1e0d2937dc93bb3154ca32dcf1ec9754b7519a178fe503294d3db08127b3e319525a03c24883218393efb7961434b37d4957a86d007412656f73e06054241424501016ce5891f0623346f9da8de1cd0db0e66c50a0edd60f006ea65b41749e3126f34cad311e52e38d2fce55e40d0deb786875078b15eb52ef32140e577dc29dda284179a6382bcb528de9474d9223a3ab0478e2683438dd64a07a47f5f59149038284ae1e801c9d5d40f3e37223af7ae4df4fd3f8284efe3adf96aac0e4b7964289078630420a022534064ea5a1d013948b8aff989c71a67f6e745990593d0d0d725f83a070f080642414245b501011e02000057bb2210000000003eab74daeae8611f6b8af7f065c82a1f852368edc5712ebd59f8ad4e6cebf76e8da4e01ad47ae58f43e7774af5206b2185035caee7befeb27d072bd0a7db5d0c3aa280c971ed26876d5d8c659aa17399faae1414bb50b2a7397cac5546a44a0005424142450101e2236b78c712fe22a7805337e8f04cd61b7aad57b2f5a11847976d6a186b7540f951d0dd9ea0602942e72c295fd432a2bcd32ef53b2ed0911f174e6f3b97098729d8b0284b3cff713f2d2e6fc34e180b234191a05e8e44cc90217fa21ed83f374ee1e8010009b588f198dd7b4464386b0a49c0e623dcab983a81bfa5fd12cf5621c72ac2a110348f13942e55a041b626b52d181c23f7c1ebf980ac9be5cae82297a2dc87080642414245b50103e601000058bb221000000000f8307155e7381db08ffa9fcd7fb959077f5b0eb640ffba147322ff2005324c6cf592e19edc2defbcdda9ac54cde16beca27a4871c42f8007c90434eb62f178069a842b2a62aa885c6bdc9d38a1e842df3a97eb7bcdb962f759fb45a3aaa34d0c05424142450101bab4696da63454c71ba7b95b10a80ffa6c21e82ae2026f4c988cee12f2925637e4d0a65db67d3ca2458b5912f4f4305b858bee3e8b340bd58adf2550fbcc2f8e2c81d1ae465ca92c454077e4f97b3cf4c3e032642f2da42d03605c9897e6b51552e1e801af288359404d39623f86052339407da8dcd0bb56b868782b9f2d991d3d2831d7f2b01752b3a1274d1bd2ed4b84daa5f4fcf1eaa19c8f8c907863c8f22e5e88c3080642414245b501033903000059bb221000000000784b1436891aa128251f23ad831277206f0157b82b02c6b6d18ea733d175700d8338ccceb609cf708ff2a34fb31d2b4e013f320bce51a37086bfda79be63f70cb9bed0ba99743aafdd06792d69cc300a704f05c456627687b6f9d01706368f0305424142450101fa8e3c8e4270f321ce67bc4ddb187f9acc8268300f580267c6d3ade4ebff4936a9172de95ed2523b560ed249fd2ffa11df13601b4395ddfacd4230b9f52e5b81bd76dff0e6dbab05bdd25ddc0bd9f2fa10ad68ec4974c63e4d692d32e0f3d81056e1e8016e81ac5bd4b3200bbde17d3b99247dbe8aa363876a4d0d4221c053b9e5c8e50fa52924197f958606ff0d9f005d5e31d9760ff3a482818756853800c803196a6e080642414245b50103590200005abb2210000000002ae910d9d8a0518fc8e4d126e4de7953910d2be859aaec55f995a27da68fa8234a0209796a909e0d2c06a364d3cf144d7002d2b451e4d3d27828c1efbae5be0322971dbd41512d83c6f06ab6c856b0568ec6b5efe58e66d198c73b264480b10c05424142450101ec8c1b192c1f25986c748fef0350fc608b97445499995f45a5d60fd7e690004f5b08ae78827f9672f11485820c5fa84fc5e8b358e7433beba6273baceedd078dd093584eab11e066859dc7e41af3ef8509a7ae7fc37a20f3fc324b5ca494f4aa5ae1e801def9035aaf90237d8105289ffbf263ac80e630a6f0d98801ea63a4ee65965be8b6d070488d24a328921a1c3b364b81e4c80994a0403c5b597d7f1bef80b6953e080642414245b50103810100005bbb2210000000007664b114a021136f17c9e38daa827fb141c43924f97a5b168fd9b4558132d40bd8087e3ed9a501da5c549b1f787daaf7c3075c0a066da5650287478969b72e0125974f5ee841c382b0c285ab3a9f3a1380c0bb29ece4207cb8a001e065935d0e054241424501018cdefaa9b5267598a8a0628787236914d4317e479272242483b880f46a783c02e8ef210ff0d9e7c1a746ce0ae1604f9b422aaff9fc0d3a5715dd5c1f61012b8801a0286dded8c8c5c319ca806d70d179f9d737e6413b859e4efe56608843eb345ee1e80153ca8d6ae6a3658939c8eb9d70c8bb7da53d9f4e6716357b285af62e77dafa8da858644772fc0a03aa1479f7bdd6b3e4bbb26797661d580844c3d21e2aafed19080642414245b50103960100005cbb221000000000d80a7115becb567be26928dad2c48432b6f00202273aaf8541fdc496d17b0332ca194b2867e1a7c479df8b3042cd74501142e04e7581d55198a97dabae25ea0563b0819d11c29fe19290f3d69da7740e914f8a2c401c29f69111a007f071f5020542414245010130bddb661999c9912bbd416103c013cb7bd863e1be3558cd1b98c2d2efdc9555d50d2d7ac3275a8df1b56339465407e0d40756eebbf47f06ccda5c41d90ac585caeca2b005b53d40d7e1837de3945d7f5772d00d9ca6ef89beaf47020f19a51062e1e801b9562a03b5ab8f44f74e6b35a78089f723777cca8f17bde6f3cd7f14738379819edec7659a4a1bd09aaa024c4bc6d65d70b6a3e6094e50518c6761bc84348007080642414245b50101490000005dbb221000000000768674241695209d7650c44299e0432333da50f34da2405c07ae97d9cf6d8f73fd5cb802446ce3e2683b6b6f6ecaf5d7ced90dd8cba2880fbaf0dffcea26510742bdf0393b23b52c9b4e75acb510bf5864cfeb97ccdf3ef8844115103e11c40005424142450101081bff5aabcefd476b1b0b2455055c214159f9c14bb1b4fa165b4ad61c7bba7136ac615f23c04d68619321842895d14a93bd078a91be3779b13444a3b68a9381455a1729a132e5fa5b306b5a5a239009d9e75cd9ad398739b5ad14df9e6fae8866e1e80123863dc777d000d3ebc545da03ae6f1ea86c8bfb246d709d8c7b20ef3101c861263cb2618c52136b217fb6704ff669507ed02507200afceae67b3d4eb4c6528a080642414245b50103140300005ebb221000000000ac27b5b4a248f45a3e038bdbb609cdcbb4642050a75e9648e3714ede2bc55d4a3f076faee57be6721836696c1e7c55cc229811962c509747a2afbb5b1446be07f8c184c00f6fb4311f3d80ed25dc56a88cdb021e9505a5bf1ede83c5b2e6880705424142450101a06baa56f888d25dda4a20778da428cee2fc8fc11659431ef10b73767e9271161829adc765588a2213d4274a903763f5beaf7ecc08f594affdcc14f333723f8b454bdd31c1c64ca6daddd3e5898d52e77d15255bafa8c084f351be6fb5b3ac4b6ae1e80106b0f6a40eb7aa85da0c3b68154519da23461ef439971fa6dfcda9345f9643aa131a07c85c18c117f9bec846a26ce29166af970ccf3dbb38fdc1ba355b863cc3080642414245b501016e0200005fbb22100000000088416969069db4d1a4f367c57b5d3681180b7b7dc5e28bf8900b20400a23c74b46de14fd78b94fc861bb8a55afe11dc0d9963cd33b68bac44c3deafd8c1f110e7457659e4c0fff0f9ad9039c910df678e5c8f910760894f3734bc33dc1261308054241424501018cf5996ae0f9197ebdf4009069cac5355a1b8bcb73b4a0237a22152ea12e135f2a9a9f0148c73d83435de8ef07a7ab382793e933415724391072a6c4fcc8f58781b2bfe3cc8af3c8dd0ffed6f7738f10f4ea56a257185504c782f6af2265ab4d6ee1e801a87b236f29ca45c25f1f43612d117a4e80c08183f89549f257e4430a0ce46d9756aa3d37d24d438d8c02eb4223e465eb19892e1aff4a4264c0699e5e797bb7fa080642414245b501032500000060bb221000000000baf78078dfba8373571c7c1282bade52d333b1a4feec380bbb7f0d280c0f646b8db5182cec29165314b81e6e8dadc37ed816ff24525e1053e2b2c3624eef1f08d8c66e399a22de96e911d082eadd00ddda34f845d3662d2cb4e1efe6d1e93b0205424142450101f237679981350bf33ca26979b0f4d238000f0596f928c10537c854025373673cda7b1013895c59b0aba7c3fe700914a3267b6d8bb4eee41b2d480d36509984897f42c0406e9a417de44da8c0e82a5780c30d0dc45f0f712227abad6190fe5a1472e1e801e89c0dc0990ec098c1b1e1340ee492026784db9ffb0d715f2970243255704c04fdeba7a05bd9ee034dc3f1aeaddc0d920d5d2e2f9c9985fbf12194cd50372c81080642414245b501036501000061bb221000000000aa5e88f20cf7ed1e1ac55f76270ec506fab7c50f57628272d4937ae162460004ae3d6b8104c42dd21071bb52f6b63342e63e1a5b1d51a46c1fb63532c838d30c820a6af06bfde26fb8fea34f6bb4ea92c9987bd974247be53f8a85465fca160c054241424501011ee974735df14137def236c1a18af10353fab28bb73d61b764898282d9ca266123fb0e92c9ec64052be5c7ae226d8a8ddaef85ddb568b9300c6e27c6fa601a8c107b19aecd4bd13735ff4db36f1d4a39cbacdc159b2602f4e0fddfdc27b0348176e1e801f57039f9fd72d9fa917aef70b3c99d332bf3c785c4971f0653da8f046fc782cd0dde3e4ba0b16d3da9587fb0b3367cf22b19ccf1b81f406b60d995cb871e1df1080642414245b501033103000062bb221000000000864617a293f2a69f0dbba56f33060474ff4fad6780cbda53ed42f5c22770156d163b9c5ffa3d6970e0543287d86942c2dd7347c72f22dc9a12a6425fdf51bc04134debec2ef7dae63167fe1b3b34a9a0ed5661d5db0705568af080d07f7b090d054241424501011896f6ca7f90bb9f47ed4ae387fd3419ad8478fed73734bc843830b765a5e024cd2660ab25bc8fc4de22b458172d762c9407c0226b9b5f5f2fdc4d0c27889c8542aa06884d4d955341f4ba038c5cec1a71ba72f0b98f4f13c8d1cd3bace4a8407ae1e8019ab6e0e03a2c2ca234380ffc496d19acca240ba01269b6366cdf8f22cb86ed3fba53a52b06d793f658a9bdd360d0c89c60eb4ca05c4938807e572346464a3d6a080642414245b50101d100000063bb221000000000ec557152063306909ea3980117a1f196a4eda7eebf8696bbfec54c7df6e1815a75acd46945f267ef2d0a281f4ca3e4ca6730687eeb7a9abe2678994bfef7c2059b0fe9bfbcc61af0717e83abbdae776f0ac4706acaf461b9571abe59c0523203054241424501011a383164b50989de9f58d0e59ef2afa5eaf1fd6c91a2ea0a30c21eaed623eb0d73245c2805942adeef08a9dc91236f0515baab7487e31fc6e2db8561f023b78ff5e501f2a47efd7a35791e1b87faacc6eb329c61c030aebf84506127ed7402847ee1e8014d0005bdb619c11208c7588d0efed603293f34fc7a0dd64ccedc2bc49bfba2e993e25c0a4274441f7d50612024580d9c3a07fb687ec21cb4a43300d23bc30705080642414245b501014600000064bb22100000000070ebfb2f0b6d56bda96dc5d0b84e33eb858270af6f85410314906ebb9778be22503d26e6de4b6909e1251803c12ca58290595f72021813dd5213279ec80e730592b7a1b0523f5efad3b4fdb951b56023263d03249329c55f6c57ef17c056890a054241424501017e13fd5017afa6c1ea6dc03c22c26c5b96b608c9f6579e4ff78e2a293156d07af2f32a7d2bd71303c6e98890bc6cafdec57bf37943e83a199791d1e2f3edbb8cee529ff65437899845805cebfa183a082eb13272b7d7d59208b2d34abd93793d82e1e801be96a3979f52dcd8b5a6149c6f25f307d9564027caf137e608c37a1552ceaae2e0f207748961d89a59e8f714f6294b64ed543df0ad71e2dbd385c95c54eb7ae8080642414245b501036d02000065bb221000000000b6c262595fdd4ed57f5c59778f965bff5ee9ffb75e50e52c575dd909dfd22e322969834d8023887e756456edbf483d737fd00b3ad1df804b6d807ec41da5690ad96288ca1eb62d051e89ebfbb7edb83b4bd2cd3d8dcdcb2c807798d35b4c610805424142450101f45fc882abeaa18a5ae7885847579fc9cefc182f25a7fd02a553257047358556b1bb1c413dc8280fa6d4bfc1fde645a5108c75f7de48b3536cf854f67e89f484ec76bac771e5f78dc913aa842db925c2750f507e852829febb57ff519c782a5b86e1e801015b455655c598fc8a65d60a013a882ac52c919255090a252988e423933ee3bacdbb6dbfa8f391d52503b76ae58974e980199df58fe602929fd937c2a4a623af080642414245b501031503000066bb2210000000000aba683cb45545993d3630f9c491cac9cf99a4080088f6ecf308dfde1fdb6f3eacecd044d59cb5374173b3456127ee155fe1cb6e81cade2eeef840e629b2de02a29fa7c5e6e5c0a9975fa07f784f697dcc7b05937d6d094ec6763d3d6f6d0103054241424501014679b6c34b21a7613c50c846ea819c8d058a9e8cc9250f191e235151ed28b25711b40ed6910f016823d5ea8d085644a91f66ce3488e2e35cc414c993fe48448ea4c6499a67af504351c65ed7660f3a298d0bcb1b25987c2129360a2fceab9bc78ae1e801889dbbffe6c106cb4485909234d76c591c2fda1a677a327ac0389b5518152f1255b43f047d879b30ce543a714ca1e87147a670778f87c7f11747fedd78f32350080642414245b50103ea00000067bb221000000000386cb0c842e813c3255ce3daebe97fdb9118571a2d63ca801bed6bdd25a3ff4c4cd6598bafbbdc1cc2d3cab9e327fd664cb04cacfe4799462ee514e7f68a1d02ec2ce5091fc1edb63d172ecd4a43314d3ad0043328f54e399ca74ee39a16fa0905424142450101b0025b6e2cbbd3e929dcdd66748074cf14fa43764f5d211859938adf6efe6e3011f726e4e6c4e8c24e4cc0a21f205bf7a572dcbd7b9b8e6459c1b0ab9c7a598c2768faa861e1e84ce2ac27804fea3d6cc7b1412a79131213bfd6481b09a35aa38ee1e80150b9f64361a1f62b9fa728e7ac8c17bbe140ce4aaa4bfbdc02f01f5e992bedf9b4b4cfbfab02ebc45d78e394058b14e92ee4c1836a3c5e1245fb37d4c264ee4e080642414245b501036801000068bb221000000000c69542a751aebfea83bfa2a8cd1d47cab94c67f0ed2fc4e010685a6dbfac2a1f3772b7020564385f102eac6c83d9aeee6acd1a3b62cf23beec0138d2ad244d057fb43197a7354ffe128ca015b9cf7c74aae571104110e2ceaca33a01ba413c0e05424142450101b2f1a97ef4bb3b23109cfdf6d7d087ffa1af495697e5ed7915b2b6d66f875b6cf5ca2905c826a0d2ef412718e8c5faddad77e6f2eb2fbdcb3c4dd358a6cbb683d6694cafa2245bfbb5fad73b683bb26d786e88deb9e49046b1e044473afbced692e1e80144aed6052c17debe9643d70c7f3b3c2c157af25031bdf1a79f9b167b2aee1f9314afc0ee85903d12443dd2f7f63bc6ce68649dd6e44320680753bc1d3c755507080642414245b501036d00000069bb221000000000c071dfc9f88ddf677ed63dd076e0fb20b71dcedf356052154e2367f8aa5259342cdd55743b2fb0dd6b50e4cf73ee8df8c7d5b98b404c72b2f8c0c72dcc77680b4b05c11d80afb418259382684fc41a2d22f1928c63e9f5c7334263eef45c330d054241424501014068e5cf298d91203526f9b59a8b1cef295f615d5aab9c8fa1d87b1c6439891178b3ef17d550e961c210c95eaf237ec7f70bcd84e98d902e314ea1cf466a328c243d39fa82b4a1fe5e9df9f9b758370cf8a720d5d19e019cbfe66286932bcd9d96e1e801ff7617d56e7898167691e675755d7b4558a80363df183fcf6e402595509765639782949426fc3d401de687df0b0032395d97bea9a4ae99dbccb8f2f69cbada73080642414245b50101280300006abb221000000000fe27dc084052479a1338c9976a2f0a48775a7f89530ba79ed163e7c05b3c113a1200b6b354fdba141a4bc9f70097870698132762c7b217aef71950cd27d92a0f635f6a5faa690ad2e6a1f0855ecd7284d4707b246185f2963441e9a8894c7a0f05424142450101925063c705f410c07b3046d3014b5f155e9a173b880a37f5224e63b18b77a144cf3fd9e29131baf3b9bf63ae2eb2847060a7442de56f0c3d7dc3ed82614cc48a7fb5a6e2bb8771f32c9f1b18f9a1af1a878da774b438de1fc5baf2a29b6829a79ae1e801f626b19dc83504c4d890510f8a354dec59b87de613eea64d4c964715eb5428bf619ff682cb8bba05108ec25a23491e0ece3919044228c71a9fe9389a4f91f869080642414245b50101c30100006bbb221000000000fc75daee6b94b2037d464cd72eeec192c9c62105026f5dd6899101336353696262b9ed572070f055fbebe81e378b468a89d33d43c54e876db7b43244f35f9704cd4b90623c42ecbc20364ba4d40ed2cd46e7c9f15f6a16f1552f2162edb18e0d05424142450101fc1cc1671aac7d690674aabb799f29743d0bd6e1cdca907d60b7250034f7c60b02eff856e2ca09e3b4c1dcf215b438db94d0f1ab05f19ddf166b238b406ab58be77d0d3633b5ba0d1b6f97a575dea6c783de6d9a2fb5f0d19c39f61bfe20ad849ee1e801cb53621d4afee1d6fd06ea814f85b6bd15324e98e1b1f6f5141ba002659da87298ad2d637d46edc9cfc78f5c6b664df44b364df4777b0b29e81ab8e0d1bc8a8d080642414245b50103170000006cbb221000000000147445e7903562632c4438e1a38c1e8b58ff6b0cff8983114d73b7059089961ebdcb1650e068fd0985b9ebeecc7e6b22674962d0b5d94d38489c7d2d912a5709daaf3df9f758a14126725a91abfbaa4df00b6c918f325940a3a36790a6966707054241424501011c801f7b2be57cd8028265f3a2236348015321c6d872265ba5cadd72513b501e7c0020448130ffd51ff926c9bb72cc6163ed4f526b30b30fb0ff800523ce5981d030ede23eae570928da2e41e7e5ed1e605333f28fc008d5b925788e5055460ba2e1e801af4881ff561b4e9c21ecb8c08d4119a996de960c7b590dee5919c9e0b5195109716672d3672dcf0b548d243e44f50ea68406e3a8bbf8850eacff00619df5bdd1080642414245b50101a30100006dbb2210000000007c9c65bc4b626497c170443f4e0bd650dcd7dfe23bdc3eb9d6e8995347cb6103b3ca1c57fe337663464c969b13627a3ee1e13b4926f7c89c7f2f5566f5aebc097a29d8a985607638a136682301c3bd406c05f9d7d892b57a599eb16a3887c30705424142450101165e429c0e68c810a785224edcd5ce32a967c46d4f968a2d31ad5c853cf73a418ed09a330da2c06f04b7c5dc903e41c0aced94de6dc813c57e48aefa270eab8ccd45d449840cf9da252f3c3ee5d917b7da06c534acf93c7cc5edf53a20b91543a6e1e801805b5b31d5a28b434679a25b8e4af3d5f89ab273abbdf2b04a93508cd50c31ccc739264b96e90787b86781e8152314af425325c7af028747bbc1f8e0549307db080642414245b50103280200006ebb2210000000004cd80aed2870678090efd95bea0b78c03f451f323a72d752b7be6957621d616a68c4a853a58acee92953498d9d1001dc92698ffa6b464aa3fd8b6f0cbd0c790b7a0d6881bd3536af9cf94d791cf35a075b681cb075f0a96016ae6c6821b3f30505424142450101a652a695608d1c2fa7703ba8d3b788724c4770c20b039050d842043ed149d06c3accafdc68aceb16bf5d779fdfdefbf21a68a0dd88112c4814f17bf9b189bd8abbc7a9bce37ed363e4c09d5be6392a07f9d0dc2fe2ac450b7fd627b2597b2ba7aae1e8012b8f75eb02a0f795a7cb5cd4b697f53eb60e220f0f615a2c493c0f92ee0c160707512d65fd3127391396302880e48ba9a9f801e6db9e980f0dbc7228ad039172080642414245b50103a40000006fbb221000000000b8f4ee4d709fb5933ab6af0f9cf17b5c73a2648d04be0d16521a438fb1e3a12861caad11bca4422fddd301b79d71ecb913f3c35261a5db8f87bc66f82349e00bdca867f3b975ac41892081cb6cf67371d5b664462bf9ed0516c3d63c598b94050542414245010190617f6b49a2678c0a2004897c83601c5108ed3165d26e245483b460dc4b032245b2fa1d2d872b0ebc4f019cc11299d8e623bf771e31181787dbefe5b9abcb828cc3656f2dacd3ed20567d9bbb2cf9f0c7b22caa208a602ef9c57f58336cf891aee1e801025f422c61d88e5146c02a19b9fc325bbeb8e236ce39289f464fe234cdf8411f382502b2bc41e9f5568253171ee4f20aa417af25e952f348b2d2a3eb20b1fc28080642414245b501030901000070bb2210000000002c109cae61e982a6c727219ee2f93094b69378db49af700678c0aafefabb776aecda777db161d538e86f71c65bf4a5112c0c6435a3dc297ac7cc8b4ce947bc04c284c7087b42033662355f0cc1815aac0bcc8597fc7fcaaa06742f64c347f0070542414245010144f993f55b5b971d3acd932ffce68405df525d76619d0ca0f205331d87fbfd003734b83612e5fdb6badc4e19b5f5e615625f1529b2e7c1b6b20c68e8abe7d381dd1b73d618b827e5b3da38681081098b1c6b54cf680ba4eef5fd210a5a8d0527b2e1e8012e19dbe48d56666f4d5d55259c2a573cbba6df29c83523b65db56e9b4d6521c613f5d9eb000e0b6f32b46d94324678538dd58d50cc1d835a3466280a71d6d990080642414245b50103c001000071bb2210000000001461e3f968b81196b224c0143bbaff93c066ee600c4bf76a3a745e6a6bb0760ad2057b596025a0f3ca3042da1f50e7c91f307b0ee3ddf6b227e31ed2bf4e4104c381f25a4f63472226f6f6766ae540409197083c9455494a4f228a14f29dfe0a05424142450101c81e358ea512f0783609de6f851bf45977e8003ec492e7e15160856a7ab67d628bc8ac00e306ac2b20151513c718a527728b52ccc7dea770233e15588a1f128bc7ac43d9ca5e514d5c3514bf86c7002f10578c46c367d7542f8c0cfac5737abcb6e1e8010903a5ad7b31cc51aa991e00a3552829fb337dc268abcec237f55fb71889fc2291b8c5f13b2aaab168bb1e8f1b6507738e65b75f6789b4586b64471f960b0e78080642414245b501010103000072bb221000000000a8d35bea9cba721e4a4f0e2c52179e7000e899fb26ba431ac45360851b8eaa01f8eaca8ffedc122e00e444ad56bc25757bc7b8d403a83b65d8569bf7b9647c07352a43c6e3e203853318e33b8aeb9801306ebcb7031e1144cb58bfd962d0680305424142450101f63b86286272b7b0d94acc8b0d76c563920ffafb31f05162a6b7de66eb985454f88e7efcb523e493e616a4ba5881ec42825bc6f881f7ef43cd35319afa17d980f066b1899c067a624b897afed0bedf33208ea3f7ca94191f01e684b77fcbad23bae1e801579b6c7edf6a85bde36b5b5045e0c7ded725c6f68711bbb0009f5dcce3fd0e3bdb5c443e5b21091bb8e6faec7b23263615ec96243876d9bb3c5d21467f672615080642414245b501033f02000073bb221000000000fa08a648faa7ff96d45b585818b70fc2286566f713bf296a66362affbdef591459484baf0ce67dfc70a7a9d9173b9596a641db4f231b2ff28661d922c7dfa70fff77633fea4027ab768dd64da78d3f0ae98baf7324814617766098df510e370205424142450101547833a5998e52cf9fe75c275c80d2d3c602acfa838bee08ac55e5b8609c885e32717e75dda2ff92f9257425a8b52df0fb165ad64d858947006f321511be2c87a9ef5ee5f04bd28d72bd6a779de8edda82f0720aef9ed5b052436972a3b97be8bee1e801295d50048a87389870fb66df6658c9270d2dd69acf8397e42b31909290ad0e75a22dc6d209b537cb509400be169bc324633f30313749ab9f79534e8c9598a72b080642414245b501031d02000074bb2210000000001cc5658bf9eeda1fe47fe6b8ccb1a98c4de3d837e0e4f5c27361e169d475a52a7d87388066776980deaff1265689ff13c9105c269cdcc4003e4d6087872610024d8048d485e124be473dacdae1edcddc9e4f4ddd3fe2597f3c75e6cc22cbd50f054241424501016653c65adda40144d1e79c480daa61c71770919e21c03463db80c065b57b6047cca7e83ca9274a757ce0d10d690677b06f32b125dd591ac1b335c0b2f48c04849b1e944cae337f29ae01dcb4c24ebafbb9e5d8704f0403eb1a733565318a4d2dc2e1e801c1bffb57c2f37eaa35790e68af86a3d8a43854c9e8fd1884fb8b35d94d7359187ef9c850a94e822f5db5598ebfbd8ca491e297721bd313e73caf1976d8d86bfd080642414245b50103f401000075bb221000000000da7b73cc3705b3f3e576eeffd8e3021055f52b21a304842d363da8081ce59f6a097bdcaf051ebc7c9d87076e0acd397dc22c74051e5b6bdb8c8ee88e58ca470ad22d0deeeefb4e82ad6916d8a4dfa06b0a88bf7c0181b3d508ab50331c7ac80c05424142450101bc08aa805ecd2daa80b3704f04a8a6d671894b7a440a910d93c883a12cb9d2626e857c73f7dd52813d7aeb05f4837ded2397b968761d26f9dfd1bff174bfde8855ed526eda90ca078a0e584c095f9b8e29b3c2f898a43ffc33d50433585d1bfbc6e1e8015dccf01dea59add00c4db6e14dc89875f50accf614caba21158e00b4a797ad2a25479302966b363c908eeea6205775d60b5d919e45cee0eb405564479008e028080642414245b501014302000076bb2210000000006ab26d84418d88fbd80f5b9cf9735a92a2c01665641ed0d99625e69d079fee68f2056f87463d0f8fa7fd2db96f8af1e746ef7355899fbdff87a0b6e1e8ab1d05aab0f8aaa90106ebde7fcea702e7f498067f26c9b655cd393025ff18557cd50d054241424501019aadd8a00bf425225d120b15b6b354517fe28ee120f28df0435c09ce2b78881ae57dcdbf4b6472e81f29d3f2d78abd7a0124c5cfcddbc25e9d9e7f8a2f7b708d870caf506cc155ebf4b72de3c1c72a6f2e569e295ed96b114e77107e1bdf4d25cae1e801de4b1e6af0228b236af0f1ac9dd3c6c5f944f8c4f69bb076a7bc3cfe1b6118ab7fb8fa9695f6fa9e47d3048e94af85d50458ab730c6f6cceb1258841f2781d70080642414245b501019802000077bb2210000000000cd523671aa28c4b5d35dedfbcc8a44374e9179fd7545ccbee5ba77d272ede6e526e76b1ee9c8c521205c13e98d5db07dcdd559e160488d913adf60860973503080a87724fc05c662fe6f28b5b9d680515953ea2e31ed2cc023474152f47cb03054241424501019a9b25ff9f84db655aa6b085229360f3fed42a905e5edffc231452af738a9d22e7b5097fe44fe3f41600f9c40c5514f80e7be755561115283dcd88abecef2b8d112dacb68f8ad943576c39f2e41a2a1db357c6ce1072749f7796b8e86b8e2915cee1e80147e84d7c9560228f145facf965eee18f203164dc6fb95ad56218dd538b157e671712eb19348041acd59e26a6d593b79cbe325a80b82e458f6a78ccd7009c9ae4080642414245b501039502000078bb2210000000000afde94cf0b4ec10840a5599b0bcc4810d18ecb3e160b9022a09cd78b335ba7f48fa78e3697822c0a766ce33ed0d0a774efc743d5c07bcbc33d322655e658d04928424ed7ef12b4255ec79bdee4146ac02da32bee77a4484de7b09c85048f509054241424501014c64ca85c4f1ed92ac9c9a83699a626fdb506bb5f52d7ef32344f6edaf5c9061b490ac8162c115f9b93c359a5a08c98cd0e7beb8193130f425faf2fcc9481082f10c3c4d7977571bcc7a2d703416da780f065e56f7d9f74384134511ca4c55b2d2e1e801551dbdcca10053e4e958b87b97d9ceeba7b89bcb95afe87af4da3d64ed3bcdedffccc190850bb4d5c2b9cab5315e7645fe4dd24e90608910ac51cc69bf84162e080642414245b501033e01000079bb2210000000006cec708c96a96e80c440ad71babdbfea6b33a5f43daf2a3aa1e21e34af867b2e2a71dcc02ba397e77f3c90185cff733551589a02c9b01a385ea622785931010c3db17fab92fd045696ab85dca48174b2b856b20f891d410f4b5afb8bcc50f10605424142450101c00e53b3973bcd553383f07ea236d15f4dda155c05a396d00f0b97e35ee5a817175a4bdd2dc1290f62170030add13cd5af32b3e3395f75dd21e0116b0f56358b3508e387bfa1ae3bb3d54af01e3488c233c5e503cb0b9027d0a50699ba4929a7d6e1e8011d8528c673331e1019729654b24d299a5e207d21d38811709ba20e256926d53ec702fe0e3833769f4dbd27f4443663d6050cad280a8cdae2c06adb733c0ab34a080642414245b50103220300007abb221000000000f08462c1c8dc0caaff1ce2acee3b211cd2aab77a86e396da870dd49fff1b8f6740baf6cc93d0c346bec1d6c983c0e4f1aed07b8cca019fbcd08ad7017c47be0c1a841b2a525002342777595e79d151fe2226341e6c11b6e26ab7af66aad2c00f054241424501012a56309bd85ab29cd6ce24476821f7f9cb2b70d94f38c4a8003e237542fe7c2a13261fbd68f928d640a0738a527575351a6703b52e8e8054b4a947579a652d8be8a93ef1aa5a295b02d85f37b6c3bae1403b79d6e1ceb741148f15a0e84a6c06dae1e801310f364f640bb0502d6cebd82ed660d85297b92f1117ea83e0fa9a40e7d08546af293873216251759694c6eb2da6160f22751e88f5d9190e17e60e54680e80a7080642414245b50103750300007bbb2210000000004edc53082ce7504dba1fe14ccf0f33c4769fe86c9fed6ae69145ea2b312bcd661b379f4b193aac1cd57dddc2a4c73bb3064af7ae7565c2e04b867af8e062440081a5191f95941080c641a7d084f6f1e6cfa612d8acc11e292ea67d0085ba2d0f0542414245010164ed7ab7e1cfb095bfb965129f29baf17e2179f44d031472c35ba4eaac1c7c05b414f2f033c3c14d41c20926245b4fc9634e32f10ae3760febe1a5c11e90838ceffff17915acee6bd1b30a3087a0f32fed875eae64b6681f4cbc4c3c5c58c84edee1e80165c2755d32a56163bfbf58903e19a24bed44f2439d0e7525ea1ee0826ad1edaa8f44d3eae75dcb9fd7366045e8e5ac5a0f0b553e89957d8ab95b1edeab88a89c080642414245b50103f80100007cbb221000000000aad4ddebdc912af30241d91de2e0ac1a69ce5a6e3b3a799ecd288b6fdaef82573aec4167cf01eda59f11039194bdaca9d9cfa848cf2486eaa44b2ce13eabc0001ddb238369cea82fdc8eb23715df59ea15039f9295cd2615a0de0888cda1d30305424142450101e24f128e326aa7673d7272afbc5f61307083cf508a62e67c72a5e85726969e5890247501af70d0f44a0324b2ef003c561aa2597aed58f8f87a8ac59ea2df3585c1795a315d3738e7b835bcd26bf687051cb543e7cd25ca1e633ff944e2f5bff2e2e1e80195aebab576a4f42d8f862124fd45967f0e36d448b0f63879d571371ce6cccb6d5ef2221baf8522adf068799d2cf9d7659b4be1d1535b187dd17b6d262c7b960d080642414245b50101cc0100007dbb22100000000046e86486cc98ef4ed7e0cf028fb1d57dd882c7b1d321d65ecf4dfd7f45011071cdd3ae78053bd59e661f72c3461c7696d39c7504def2ef1d0f477e8625c79a0f2a60059ddab9f542a6558196500372da3f65073b2fd4ac95d8788562c8de680f05424142450101f2191a84df7e74a8294527639363594b879aa577bee7d8add88df231962a77768fb95e667369f9161f79ee2348746b9216786c8c1eb669ed48965f3370e41c85a63fd3d4404cce0008c35f51ce94d4ccebb542e059c9b9e79952440ba965679ee6e1e80111cab2ce0900c883249ba900006938aa5330972c117e4ef42a63de4fc02d3792b9734826d382bff473e55d428df388756b5bc0f79712fe08d7fbb1d436c02905080642414245b501015f0200007ebb221000000000841085e9d67e1637f12d8de098958206efdbffcf309cfe59180fe25fc8519d6516fa93a332cacc31b5d22f4ff31a20fc1fa05174ea687bd1cb401ed63c455e0c1f1a8aa8158d0c481a0c3a07f07a87548eca4324aa07ab5a386e87230148e50e054241424501016000e377544a9b2a5287452d4e53301bd69a13ac1290359b31146bd4b9767c67f95c4d831ad30be066d15ca4aa81ad6c930dfb7af0f429e6f8a4999ad3a0da8c3431d6dba206a2a23eee02dc94a1e4044448e5f7e4e44b2ccfb1afe4f79767d6eae1e80131dea7f348d5efdbb14b3a185fb92e67bc287bf03a957020fb168ca288981e40cc7a1c9d33574c88de31912d232940235edd65aa074cd78f0e8c7dfc61e10a0b080642414245b50103040100007fbb2210000000002e480a7c0e5e99efee6518efb24427d30e86012eac52573b1248fabfdc704b7a8d9e718eb8e1d623ac5208c54f9879f1a62c4e2bb61887751f48831b03e7f005e183cfab241e0110fc5632abdf1523a0fc512832ed3ef3b67232c5fdaf9bc00f0542414245010150600e1b558f014674a2450f044c20b0a8ba2e5d9c66ff030be6e368d60a663257fe808fbfb4d0abb07cbc93137febcb0a8615b15f7c38179e9f0bce79ecc282e54e330a96e41fc91d405f2c2f3da9dfc485b12c3e9b39020b04197978e8b7b7eee1e80173ce9908c1aeee8f3a4bd414a7177a3697478e6cf0ad9f8ec1fe4f3cebba9af32070853704a20184d655339b25d0631c08c201f53a7127e46cc09e545078ea17080642414245b501033d00000080bb221000000000f09ff15e590ce6e7418549251840678a7314a46731bc6028ea400554c4dea414dd3a7dd1d56e80ffd1d605084a09f6be42114bbe69b68857c275afec78e3f30197634cdb12ced02b0881186f553c5ecfce481d4b2d14bcd2bc040817fd66f50905424142450101d2aa6710082ecbf921f8702f20db9aaf4bb94c282bb40a8f121da6a09c595674974147d5383705311b499276d65fc5066e4efc50e78bfd6b3d1f2a4d6e6133861ff6fd8ca99f3738a3cae7abec3793813c8410471768529130d9ad179d36bbedf2e1e8013acfc2015dc5f3c0fdee1379fbb4edf50482311dec521f9a84e495cd7c13ee713fabd4acbfa0be8d5a1e95cd2f20bcb52338f45ee28691808e26254279db9857080642414245b50101b500000081bb2210000000007af79f85209a691b37db4f2237ebe50f5dfe497458c37f61afaad5663cecfb2bab1118860e646d4f0922ec3b1432dc0d462571b69d5359597f4898d727229b04642f196fb35ab5034621e1abcaa506096e9c895929db2333fe8c6feb8a6fa50c05424142450101d4841071909100bca0accfbabdcb14a56ef222f4fec06384b625ed51d80d1339f15c337b4a20925870f57e4c4f97accb585d3194593429b7e0c8974820533b8c6d8e7a823ecaf7cedd1b87f06c39ee0907379bbb98ab2fcdaaaccf2a249591e0f6e1e801c4efdd37b70972638905744b066aa2891479e53752b8aa5864b477e15a2d90ce0825c13b3680e540f28c8a8afa88a4c2dd5efb1cfa765f7c10169515f8a2a835080642414245b501039d00000082bb221000000000c62f1a1ba0dcad2e94a049a32d0efd14d8e3a3a6592f0c1c61fbc5ab90ee012d8c93e0f8f28453e52d3196641a131865fb1b86064932092310fa58e1f20aad061025ef22d7194859035a97a1f00a7fd17fb35229a66bc144d0786651b648860005424142450101c297b552ba82edf80a8589cf0b8c5f1c050598bcefece09819ebc54476c1557c9eb60b594dabf050fdcd2c3d992bc8dbeb0ca6fa6017dc997189d47aa151ef8e66185bfb5e70dec3d7754a8d5f3d41fdf884a3f45d1cf8ee89d9f4891c10376dfae1e801444d2838ed94307ed8eb16b773c5a9d63798169d99654067626fce0a1897ac2efa2c4b9a1a1547318cac28738332126cb83b698da5db13b40ff84e8664775773080642414245b50103f101000083bb221000000000cccf8d02b65b803acdf669925235e6a3f3f8e6ab7843e0081e10a1cd4af2a755c519015dd072834409601e70e0480a8996f80ab4f223d62c43a2d72ca25ceb0855591ed911980baa490fd28a8db05fb83cc06c86b529f3e119a00a4adc9c190a054241424501011eaa029dbfa61c009e51d8131fd3797ab2a36fc8d51d6eca8e14e9fb5991fa056eeb7de0827d5be509525110aa499d2316f92d703ed22b6e5f85db1ab45caa8438c36e02f3b3ffed62ab9e94299254440bddc1e17715d68c8d8af74dcb817229fee1e80193a92667fdc7e68bb321647f3c98f96001a1d3889219c409597c189969374594bbd385c8ef313d91998a79d29f34e24ac97fa61be7afac02487b5c0fdc96d8b5080642414245b50103d302000084bb221000000000ba50c103ac7e61eae7bab0cecb99a9c2eab1b7723862733fe85d6a3b9f81fe31f651e7769319c8941023394c108c489e81927f0dd491f782143161fb95920f0192f7c18485313cb95dab2396e95d7091ccf55e7867c6693ce0f78b43b871e603054241424501018cecfa93fb63bed39e7e8ec668d5aa86b35077407aacaa0d596edf7d8e97064573d53af449d905f32323bbc987f90ac6464b7fec3ddbbb9c48f821e090a87b879aab293ed8c8d82f4aaea877da4c4588a7f41957987061a029a06d51ee85bb9202e2e801d35143389fac23bbc433d5fc85e3bbe297b3592b2a7b48f19b7e09123123fd8d2b6b99261e231e25e08602b9e826e10a56f5a18bf2d9c1175add8e2637227094080642414245b50101d902000085bb221000000000ea9fc244b5ebc54dea0fb71ad079c2562a4cb4689b0182a96f44a5d7fdb2934f6ed0ac9be89b8d760d5f2e0191ac9155ed812a48e0eeb57012a91b7c14370c0c3d94efdcaa66b3cc716f6a000898907cf0b689d86c404c7f4a26d4e68ad32b0505424142450101be89f86f62ab3185f4d3426bfc3bad81d36124247e86f1b7682260df2f840c1c9d18fc55e43c1a8c2765c616bfc4a9312c865a440109c4ab209952d194ef70883f8032dc9b7c8d130277c5ac77f56655d607358b9b6b17a859f8b1926b09368006e2e801c35767d29d320ff0ad45cb1c4a3f558b081174a6843f7f3dd2aca96cce6c44158d1e46717ee3a804643136481a4a10d8d776970880d31958ead3a8e8751a8e52080642414245b501032b01000086bb221000000000b435bdf2742676c45592361cf74d90142b4fff3bddc920f2f34fa3f9d7f7236062c78148b3a7ef6c6cfabde2e0f13cd2f1f1f96909efada5d7e6fb9fa596ea0b9a0881ee70b6d3ffbaaeffdac5705c6feabf3663d01fadeca2117f25b9aa650c0542414245010136a75308b8076d5cd47502b55644bfb204c2531ebdca32ed224a8529bcc8f41cb35ac215ec37131ff7aae58331992386f9b60264f235e054c64506d4c969368fd0a63419525d9570962f3415a88a497aee6630be4d17c448e9781a2a0701240f0ae2e80106f1609582094cb63f6778689fb77be243edd29e5b86a98a1dd23d43a8a9e324411fbe80736a2be88907960f2675ceba4d33d3babc6a25c03c1b3d287e5fd23b080642414245b501030b03000087bb221000000000fcdae1e407d22451caca02c89d28e486f8afe31e631e7b8b603c3ef5846ee361e41a69551176d756992acc75b8adc649d0ab05876eb34bd270cacf7a4df90c014a06443466d34c645835b8b80b71a026b0d54534fae27b7f97443ae291835d0305424142450101c6d0dcc3bc165619c80d42177b70a603b161d194ad9c9d5f04c80f9c33a1b533c397ee973e906b5db15ffc2807c4bce6919858f434cbd64a68ace184a7cf948a21151b4f2a5a1d12ff2cfa2f4f5a226a19187b06b4ca62b39f08bda548e188780ee2e801cba11eaef4347e38df97ffab0654952136661cbe689b1d1e4ffb75466f280506d333228f4e5a5730e0026f355ec69c76c5bc0019cd7fc8fe8b6a34bf90ded15c080642414245b501030a02000088bb221000000000f4d7aa6bae7f606164874a627bf886e31993c2ff39bed1a5caa599251604265d0de851c67fb9ba2a41349db2c7a46291e5bf5755a3ab710358eb57a53d91030e7fa89b10dbea41e1ce6112faa7065881a1e3e5234ca39d737a155ca16d791000054241424501016a2c4775f647c7f47e6dede57d81a192c66bb01ffe12897a0f379be2ad5a545707a4b4399d535d45b81aee0b5c9dabcb7fb39cc23027d54c23eb68b1e5a3d581a27c6b83049a468e52fa590cfa8a5cfc42e061f287558b58de33cb8112aa58cf12e2e80180c12312f29d00d3db7a3666c5df0d2d7d99a686e50b3b96708a53255f83b1a7d839f90a171148bdd5bbfc836ea885869f6c1ddcb0770c0f8f70e4162ca7fcc8080642414245b501015700000089bb2210000000005a75dc3a306765d454a43cf0814ec08a64584df0ac9f34449a2d925c63114358c6b6d84fe45decc58b5a91e4fcc7c9446e5e7b581a5e96ee9029b85c5d0e5b042951eb6086b49e2696251c05f1af806dcbcef9b035023f04bdebcbe13157a20d05424142450101b46d66fca30d506982c09982fbd8a83d71b7e0c89221a83eee6a5ba8e92ba45904972929696bfebd733d70736813624c15e1540c221d0fc088ee4e6df0511382980e6917c8b434ab50fc1679c28d2954e5bec9bbd5d7f725021b7f1f753667c216e2e80157415c9995cebb27271f7a0d2af27a56123c8eba8fe8672779e88b202173c5f3d09590c9b9cfd124ada3e284b7e236225aba567723d08f947d2923473a7b0f68080642414245b50101fc0200008abb221000000000fcf4a597c2dff0c25fc47b7e73e0fea98fb4c846fde5dce90708cc8040abb76652041487111a02094eaef295ca50e0b86fe516161d54614d0c96567417561e0328494087742d3484007d0a90d1921a2f17c11459a74cd5c725d1381e492c850905424142450101aa08fb76ebe608629f62a83ce6821c8d51d205780f9fc810ec0c0d5fc7efe03dac91ea5f9bea9d1f4a53174165673d3addb635ca246fd0216043fbef7caad782c44b7f4405cd1990598256e4eae5e39f0af3cefb1843aa488b252b75b28c53571ae2e801b8795ae8be06caa385a71177724446d22ae63fb723fb0060799f857b43bd003d158107774832dda32988703ca7e9b37b589c72d1a99717d30251be2869470b86080642414245b50103820200008bbb2210000000009aa3ae34aeab826cf685c6eb39e6521185f51e1642dd5dfd715e7d2664dba146a59396e1491f3b34247aa44aaa7afec4cedba1d990fd8530cfee1f2570480e0dfd8ab7cb150c841476c13f20178aa418f2dcfa7d2fb80b2a053d09ae0a40e00205424142450101e0d8605a6842690eb77567b5f29b44d5198aaaaa9b4afd8c757c156902ad8138df696ddb70a4550e6c001a8f759cda375bb23340368eaf3bbf1cbc0119dd688fa757c604b872cf81f991608b8d80ebfb4ee11f0003dffb172099053df1c6619d1ee2e801acd7012223ad0429789a53a50903500db71d23567206ad3c11bad3b0a46bb3a03738e8368c6d597ec17078410058b818605f3ba6b87d3053f4d83bccd513d4a5080642414245b50103dd0200008cbb221000000000a0047a281b2e053aeb5f5a3ef0fba94fc767a53aa0c26c07381f8731b238616e4f79b94f0fc3667956bf234f968d9d1f2fec3f8eecbd117c43004a7c7d92a90136623a93655f66c856c3f75ac0fe923d65bd04dabf72ba274e553f6ba0740e03054241424501013cf577891d705e20913fa0b63e86a396d576ed7ef6300dd7a5b242c72f41a778eaf5c302dea20945133df4b59a5e34f95fc43656a5fb2b78ebf2ee6d4a40358060665f94ed71d8ebfa1b789c253026e82cae5283b539198afb9d9931bc5d127522e2e8011cb3905e3508b9a36f819fbf2bc5eb01dcf91ab4683e07c55c8d3656307ded4779a632ed9cd957ee62fad1284567abed975b43ceef0ae5f4ac41366895c94648080642414245b501036c0300008dbb22100000000038b3600f2d3e9b7699be428778c64945b733fc0efee6fe0515ba651dc2092c765838d2529d5c1226e8193e5d6aa91d4ef719632e25ef7063b3506be19b14180731883b3ee8fa3dd882c1f077e8b3c4110852e4e8a86050ef46bf4da136fce20a05424142450101526ea2b72fb53e245665cfe954845ed70eaa7615103d770032d297221c05db2d61b5ea59b98cac9b39c3bbc60c360c36423b09994795c6938edd769af96beb87d34cb51b2b0c53d98e5a0a6142479322c3f9c2b5501f9155666820f5de71394626e2e801e0e8ec7bd78e245ea6906d2e0184835804942039161aa63f18d6e73405c8b9a648c37871388e232c8d26d2dc95ad10d1c0497311b9f1545efa5dcde0e7574c57080642414245b50103180300008ebb221000000000385e8ac5b949f49090db5c1b839d216e42bcaa4a7f294e24e0f7b2dba9af960f3cb0ad14bc691d56cec6446fc535281da83947fb3eef1f9b66b86bac5d9dc5060adb5f0e9396b5d144ce1983d5973397e9861624e4a2144f34aad8ba10195d03054241424501014ed83a25be28732d49e76ac64b0673fb3cca786fe41f999955c5f0d9643f02041942391e1ce2f6d66e400013eec06baf63a8524602ad51c25e23cefe4907f688894a4b21e8b21eb8bb80a165d004ba729aed7e18f709feea341e4a3c6af32b982ae2e801c962e46cbc43f8b6b9b6a0e6ed96458645190e32a66535cd87f72f5cb24c8c29d17ecef5bf0db3e8c82568f6041eca1c90deed82ed8edd948be4b04f08551c04080642414245b501037b0200008fbb221000000000e43fcfa3c61721dc08ce5cfb23b641b93e70796b72124a3e82373e1ada655d5fa1896e16352aa3f8f1afb9e17826571f5988ff3875a2003b2aa37b750682c9068ea4156e34b0a2ff0890f38efb27205ebc4f1dbb3c39793646d5ce6e27faa507054241424501018277ede785a01cfeb7e9253232d5872bafff115d76bcba0e051cb962550b38186f36b9fd12f9f1109d47174740f04f54f4e7b9c8dc03ed5617d3cc9cc1e6cf841a16596f559f0cfff7ff94f77a06606d018523c2705bf0da675b6c46d0f5b3cf2ee2e801b8e28a8769e2aea9a33bad8d920e83327a3cb674453160757afb6c9a25ac715a42734b4f5959f5d6c48707000c1fdb9809d96e9b43cab5ef7b1e2fc798139117080642414245b50103f501000090bb221000000000c052dda42e9802790dfd2026a7b8bef511de4c740754464ef653e6b62bc94b059e2004cd7eec4fe5cbc9ec46749088dd0c396500bd05063f603bf98300dbf400c854c9c00b027214552320ee0c7cf292adc81b306bb7327f031ac84360378f0405424142450101d256d3ba320ed238a1bc663f3aad473a0e11165c491afc70141d7a88cef38d04bb1ed29df963d1e7a780b160966bd898329763d8c0aac891486564890294d089474b5d033e00ee5f5acb3be0fae5c5b123dcfe5bfe93c768b7c8d402c547e3ef32e2e801da06cce07ec2187308da464ba247d4543f6e0c19244df5e3337db5abdf5ccdb5ffc81ae6405450a7131f1b20ff0df8df3775a26a4fb0bbe551145d4150c02598080642414245b501033f03000091bb22100000000086ab8a84cf240148c8d22ef6a0c382c106ddef6d4e09c71d8cd3ece4051b275570b98907df0510fcaa304d12c7f49d057599badd2604991493f36ff51830d605fab8349142beb5dab78ceec39385b47c8cf14babe748f33288ef634adcc1d10f054241424501019851d7877db9b76dd39cd3b5e79c5241f7131c6df568dfb2226d4a5410e79a2ad0c2828dcfedd948b594addcfed3097e4ab0567f331bc3e33eb3525d1414148a4f7056c81c90bc66fa43c0acfbafa3a8ea73cd7f3ed2d2a61335850bc77ed37e36e2e80160e85086da3e14ac862aa9ebcc4ce6079bbb8294994b707e9b07eb042b6ff83eb17d27ba15cd6ac7b9d3f88c5a5eb464d6a08392ec27f7f44217a193fc1c7267080642414245b501039e00000092bb221000000000be29095b7c4dc2fc95b217ffd93eef7afd9de3605429b6ba1e7dbd402e87373c0be23d53c00f992cf98f481355fac7188f199db384968903291a207d3465870f6b0fb85a486d30db2d20eaf8bb712b5f0c31dfdd5636e6da97f84bfce6d7670805424142450101ee00d7bef9bf883caefb7b01c8e1420a028adc24a99fdde6f117de0894c67779e4b1fa510c66df1d92b97aff272cfd3fc0c609409631b3a94d4b93d629e5e383c25109271c491110a697d3f201f051e562b86263cebb009d4d11ee5c8248e36e3ae2e8017d464a8aceac9fc36cac9ba8e854286fd2de51c6b6ff8b83d508f2d22a0efd18c330b7a32a87dcb9feef4b31ff49381b557ae3f175d6a805b24339fa536182ce080642414245b501018202000093bb22100000000082752b7b28e2f6779d1544d639ea5818cacb48b6e99b20f516f1eecd389cea58c16746d4550ce0f1c4f62ac29fca763cfe21104fa7b54de318b273c825731103430b419189181fc68411935aacc41a083e1124c3ba07c6f227476f47a1513d0e054241424501011c65df2879984796f5b29310ee70e5cb756b7803dd07137eb6ddb7146664807d87f212c73ae6d91bc155f2f9065b1f2530a860c1194a301351c46057ba4ab58b08a28a8fa3d8569d68e5a3fc3da3e1cbb2faabe332bfde88c6fe34dcf4d24eef3ee2e8011491645c6bbef42b9722ff32d5caefe83ca4981ad78cd5f6109fe538821b2a61f29bbb41d3bc4163c397dff547146e5f53c010e730e8b96e12e6fca71dc9b6c0080642414245b501031e01000094bb221000000000b8d24d76952e6ea30291125a944ce1a1e54b77c342cbc1efbea936239fb761562f6342baa1cda991814969315124755e8d395fb8b21559ed9374ce4643e22907ee2b18a29e5ccdff74bf767316212fcffc9554d2cc81b48003866d83d3170902054241424501010e39fbe90468a85231f1c3871345e71aceed93ad5bb5a506ecbff799512b7660e4c8c127c0db49e86d9c64b1bc5bf1b35b3db681f6ca9daecebfa79476964a8a91b70970a931ac3c80250a1e34670c355a630264c6f71f3f04e6332eaf9771e142e2e801521e0b54e9a5a4311ed1c8296e2ebc466278902dabcec653f3102c1e32d0abdf27843b1d2369752da1a36e1961b72041d545785c0655a6006a5e541b7f2fdb68080642414245b50101a601000095bb2210000000008ce69f2b84b13afc48cdba3352005b9750de9fe796086e66350d5dd80ff9cf0243cbfb245354a5d038a57e6e72c2511e7ca04863341d7e0c71862b47e814bd00cd099246ea4c8c1ffb03b01b61805c6db2eb9260c0ffe2644ee3525888f7f004054241424501016e6d10a1fd5a39e3e22039fb7b4858a68ed1cb3561b946d2fc1b57578257463e9d5886da6508d834c9571b5e0d377d4c98cfdf526bed6ad73ef1b27b8af50b8fb434d47428da214339ddbfb4c82152f16ac0bc8bca0f69ded2cfe4cf68aee0a146e2e801c2e2fbdef1198197dbf2527748f68873aabe9700aa5c8ea3d50fd788c28788192afbac91907e0e43743ca605564b91052cb97bde30f36da8604d3d3344082f31080642414245b501038d01000096bb2210000000000473526172f37b780f58564cd8630e2af9bada2ca911199d2502882f242b381de1dfdd41ad8d3da43504dd06d4e39a9acaac368f037ae559e6b1c6e9eaa31808495616c13e8e3c37da068709592a6fffc23ac7cdaf79a31bfa35f2706de39a000542414245010110be0863a95f698b91fdc71626d1433e8207e64f743210bb0aac6490f5c84275e09e6498bea2571b7245596c031f0c2727b65c7254c0b1143882d258f8a71180f591f9bf08f6115e1d942efc192c8d8a4c24fad98bdaaad7c469a1b4fd8f41394ae2e8010a5889d4b55bc26913a94249384d5ca40feb2449b28dce8eb4e493662c8699242876ae4e95d50ef2327068fab9157aba5b5f328ee3e8dc9538206331fa19a528080642414245b501035401000097bb2210000000003211db09a4a2e73b2a923aca7687d0ea3c5f4900c6e1abf6a033b5cf6f822a0297b3b11d4ddbaf63425e86f92d0b53458f30da82845d69a974c2ce1b27026e0893a522c3c5413ca1a3fcae70ec3381c7b8396fe2e122d6ae495aa6cacbce7d0705424142450101b22562c0f1df08acacee10e3dafe6a8f21ef4a69e1fac6e145906955b4efb702b46d0ae3bcaf10845cef8347ee97427d9eba5268fa8667575d4585aab2d3d0815e9e6d30c04366937f7091f46a44310d21cc836a330ba98ccc47c313f4ef885f4ee2e801a870447f208f5f724e11e3f67b3c19b6c52cd24689e3c257bce1750fa1bb8d660a513ad05ebc61ec5cc8dd2107217b3e1627de081aba49317e05eb764776d467080642414245b501039b00000098bb221000000000ecf1fd34a5122ab1788074d8049b3a45ca9093d582bb843c0fccccb77cb1c560e9fa962586ecebf64799b3567005d4f2595a760cec718f6727517c57439bc104896cf7c554f943175b139e6952e74ff466a48f14301da40935ddd747f7133503054241424501013889c41a64a705e69228c4c6e611ffcc94c237e13c10faa6396026a7ba3a49372182303c84385278906d7370a0e3236607f05befd5fed9370c749dfcdbea8b8869d2b5cb5f3d0df136fd22856f26def207ef3b34c06d97470d035627e384a2b252e2e801c37d748a09f8ee591f1bda18f82519d776a5906aa6084485db43dedada621532a02f24c7a215603287bfbe0cdd2a73b0a17f94f2f4310133a3411a1db188ff34080642414245b501033000000099bb221000000000364a25fa1d237e52856dbd065164f0ba49fdaa97f0c60d8f4fff9e2ae0629b3941fcd09b6f1817363725e6664ce5738acacacd191396b4314f38bcb5e4f76e047a7a22a8516dcbe8e3ce6b75b9f428397bd5d6af08aa3c0fe5b069855ecf650b054241424501016cbcb66277f2b3964a3eea9df97eb8eeca5f4764d34f93fa0f91162bc4f95f71522db41c2d4e812939a9aa345580bf879576a35dede540ff79cbf3889b98998882ba93e920f62c5a1fd7e5c178f94398cab9ce4ceb9a755cb9a4ec96723b6fd856e2e801712ad6de12ae243705206f35ae5aef287ae397bb458f11e1b9e35e44d6e54f777e230ade7b58871b31ae2b2cbd972491f9854ee8e5d43656361a789f08b0fae2080642414245b50103d30000009abb2210000000007c543847c8b70eb1847cb4d723735ad2ac0276e2d58e46504af3414d6bb9146d48d39f2da5c83978578ea60b8076c626d3c14633b5060baeafd5f123372e8d0c7e666317f0d1f2a80d6f876beb59e88e9e97dc9a708b92d698dd6c9cdc72af0e05424142450101c8cc069fdddfa485f8de746c1412ce0bd24a062aedc920df86907db43aa6422e3dbc93a8c5c9c36021a3cea6b24fd713f6e0da12362d66ccb737e408bfeb1a87817da3a424d4db351676d386c45b4b90eac7356991e0f3efa46acc70cbb55df75ae2e801d80937994aea50ea4fb9ecfa9dab75aad4b397b0d896ae0f44de2781c4e256687a22956e63a4bca1f640d3cc585e8f84d18c9132cb393dc8771f45bf1a251d95080642414245b50101a30100009bbb221000000000404fe07dfd87de62b66150632e162de3132b6ba6e1577d1ab97f408203bfce26e43cd5ef7670535be2bcce695dfb05b46f59112aaa49d94c47c874f2cf718c026fa487c8713c5ff3e46ce7ba73b9181a5eb2f84130a33195ec85b65823a9ef04054241424501014a267b3a31ec4d5a83d02db583d449d28d2ca0747cb2b8e07f68eb732dc82e6a88e81fe86c6311666fb7d90de8088417e7387dc3a6bf023ccece49151d1210851776aa294e1565124f47663191ae062d669dbb74f4aa8b2598da7106b04060805ee2e8011428595122e22a70d1a2fd4bbb70bbccf624516f08954c1f987973a7446851a23cbdeceb6d36c2a7a0282fd956b1d4743b4dbad0683ac8cc7f59ef7cf853f426080642414245b50101e90000009cbb221000000000ae032dd63506a71a718aa05d9674dc262324686ee171dfea97f29291545cc47029b6c042dbbeea2adb558ed6180882614630b76c5bfe0ac7652211a768c22b0a09f3829a3f96ee73ef90bec59b56fd045520de3619c3141f1313c52c0f92f60f05424142450101f2df686cf49e7ca06ede1c334caf9e21a7afbeea2cd399abd3de67a97a61677707d224bcba3b2def5fb09878d10c22130d64521bf17dc4b5b056f85bf6ccf889d9b276d9898b07b4e15e857108f717bcaf5d28cd828978d6efbd20fb9f17c88762e2e80183dce71d6df7c82aedab2a50ee5a97cf4baea212174e557be77da5cc927775fb02a2cfa7a929a2cac5382b7911ae549ddcc653e56f6adde1c12405677c228152080642414245b50101370100009dbb221000000000907df4184faa1a9ed88db3d1d5c51d72f4e3581bf74e4ae36ee130517cd1ae0743f993db87ff4f20991188babd9aa1777399b1954f79caa26c51d711c545d50b07daf233f7aacd7b9c06e8db0364c6d85cddbeb420401468438bf8bcde13270a054241424501016c8461bfae337ca4300c712a4a4cc498489239a3037eddee54367e82b89f024dc18544e379092c5d23270db7358cdb48829dd18d8afd4d70005dc3f78ff368833509e48180ebb1d4dd988e15cc5882c54ef75c5dfc790270cfea1256ceabfe4566e2e801a1474923e7246593c2c5ed2210734fc7e88c3873022f420c8bee4e86d4e1c6d949842bd0ea008368475f21fb9a781bc93ece377bfdb2603a003b78c29f9f000c080642414245b501031f0300009ebb221000000000c2e96c700c74704bd6fc03956dc6138584188abaa7d0c09ca66d838814dfa418ece33208017b752b5c5be86ddf9ebcbaca8214822f34cacc38b2a560ba922d071caf2144412ff60c075c2ef18875a4b82a6beea2e7a5a782ffe57a36ac98a80105424142450101407e90d596b7caa52c9a451c61a3e29cd42d2da11251e7b64b722c27a386115002316776fef9d1f1f5d522205fbd13f161e842bec506daab38bedf7257bb4a84a84771fd57c215a60aa088ef76d505bcb3cd6784200e2695d05dd7a59b3feb7e6ae2e801d00e722901a0945362f1c5e155146724a9124cc96dbcadc58d7e8a0dad652e335bd8343e12aca7acd287015026c23a20237939074def4ef715a67ae9d768e70d080642414245b50103580100009fbb2210000000008437295e475f42212421221463cd33fa97fff365307aed497a072c12d6116645c75445c8f27d4a5304dfc7be096e3fdafb9481c277ce7e71136404462001ee001de1cfc901dd8f1c3bebe8dacbb6a1381997d6cd13fa535aab80b0fc1169c00f05424142450101c0b2ff86b752298f570082e834959bcc3221e929e5e987ec19b77b4321bd6a52908311ce9c2efcce733438e782e27aa56e1c594a8af17388a81f10c41e9d18878a8932595fc42407ee218482e19ca7fc1a332a5e8572e42f42b58ee8283277ce6ee2e801ac9464711cd4d79031bbacbeba448c9bdc9293cf09071ecc91e209a01fa166d06e4014743f46a500cfbc12b06165d86acb3f07d280d9662ad7e34cb20ff347f9080642414245b5010302010000a0bb221000000000ce5be4113cf2d09ad96e47c5da25a25bf1e9599b38ac22e315fbed2d0ac08b46e2cb25a3c1b6d54cd8b43a548220acfeeb64133958ed3887eb4d09c87b55650a6a5890ef570f1de3a26ab713510f3c690fab37d9e66255244eea0387719c010a0542414245010146210fcc2c58bf401cd5a9f85cb611cb4a2c5cce974394f80f45c9dbb68dd17f318a1a87bf55f8974f11bd8a1feaf902c7b53b9ae11b236a766e54c50ba51380003c29748a5a2a635e54a83cc0e4c2be46122a0a204c7c0775b6c73f28381f0472e2e801303b035b7d0db0b9ac11ea5c254be0b97df553791d810e6dc40c49ebe192095acbc212607eb265082fb497ec8153076ff7fd31722317f9283f04560318fb727e080642414245b50103f4020000a1bb2210000000008cd3f090e054f4f8b275f4eac30135af0da335dc9b133b77d2df0d1120a388240eb27d6a63390cd7a9b42690b4c3f0be8cc38479ccc965255647a21fe2818609638145ea51d98ae9c308f17ba169917e3a06ffcdcbfbc335f64e34bd8673ee0005424142450101c285e2b9dad4725f2829dab15b8a1cbbc991cbe58bbfd8440cfa670df3ceca786e42a1364ea9258f04069e6215ca8df2dd1be5c68ff96bb2e475bba8ad5a5a80f696d4784f53a2f6cf062ec7036cf569d944eaff470fbec1eb662a54eea4f78b76e2e801917f29198949bacd08763d50484b4b1102e3b227fc1276bca344d8562d2c6066e3b1444cb1b5961d4192df9e4a46d4c93af98d0f310c853a11b8bc03aae60363080642414245b50103b6010000a2bb22100000000072d68cdf27d2f6b9c0da5e84e52566e46515b5363bb841689aa92683c167fb4d8c59c1fec494405b0dbdad84cc5f5b930f763a239fc12c253394222d8407560621b822a5ce8edd46eff709fd0c5d9858dec1c732667f79aae655098135b0ef0d05424142450101706200d6ee05762f0f2d92b9d529e0d692a889194f1378a32f0539efc1dc356c5365a7868f49047a6d70c64fd25afdc3c3528ee2a0af62a5ab6d03935e2cbe80de7999e4bf1e3d09ce82781cfbef44e3ea76dfa8f7b208bd77effc988cbf250f7ae2e801520343226dee6cec41a82b427f0f05e038426b1d5ed7d1a7da1952ce8c70eca4f6adcbcbefd26310079085c418adf57d8658ccdfbb077d16a72a3c42816c757d080642414245b501031f030000a3bb2210000000007eeffcf0ed51eb6d194be895bf91ca6e7cd6130dd76f65609166c6801210d518c277bd003fa55cc013e4bbfa50e2b75622829aeb6150072c01c82a7212c43d0b49f805f7118d912f4131ef16d1e60a5ff5ac169982f4ab9ea965c1d73454be06054241424501010480441ad20b007ef1eb293ea8901312aa4bfeb7eba00cff20a71f06a95f4f159cd263744404c4bddbf8291559647ba9ce5b581a727fb128ebbfa1d5ecc71a815796b0d65fae59dfc4a0156cdfe605f0baa1386a7f63d81a88233131e1f7f9597ee2e801bc7c2a3ea79ba8e76c8a7f023bf7a85cd5b7b47d674046b8867fa671eae21a2c4c9fe72988cb0401360d6999209d0f1970ae675aa0e8e78aa14b55ab4b708457080642414245b501035a030000a4bb2210000000004eeaa6415f207e07f635929226dccf9287aa12233c555a71dc86d735952b625713bf04140158d52a49dfb828d4dfbe9aa00578f066ee58b5bd259a6862a4460d7490d703b521a5819ab7521a4c0373be33b1c465c0fa732d821931c8eadce706054241424501016cc7eace783ffc29617dafd1416059c2a5f85e0c50947642de9f70e825300d7566e5bef1c79b673a139448818dd64ec433eac19eff01a713e7869d2f9c3df0851dc88ee8b48347436facfc94fbf2728fc78b19702eb5cae62b0d6a1abcce2cf982e2e80142d3926b2bccd64217469a786b2ac11990343e0fc6c0a65f51f25086f66ce8b4f2183b75b14d06c9fb1f594fd7b8393dcfcaddd357752020a5dde173df91a972080642414245b5010127020000a5bb221000000000b6d85016a2690a28bf2d11a333a4668846994f93e6f8c45be270568f5aa5d221b560c946c89856823d663e6e4ab87b54d88548d858c5cef71b767923789ac80f1dde4f8610ec00a08e3f2496feed7db02121ea5ae47c98b71ca5ef9f00d19a02054241424501010259b9428bdd05691bb48445acc377633475aca185333b150470b106ecb31c703ba1fc60301faa9888e1d7d49f3e2a4e18018c13fa78a5c4ffb864d70fea0b8a0b16df450bf980c821aaa0500650c7c08ed1d18b5d9bda358f16be7c4d7d5c7786e2e80162d1a476edf740f07b01dc93ed236bca171a1266c9972913f02dd64d2192d06366f217ab82eba7023b112f6dc653f7304bcdb4f8641eee8af3a1f0dd3a0731f4080642414245b50103ae010000a6bb221000000000ae276f40c338b8a4750d6d4db095bd7ca5c31eb6d71505b7fb4cbfdcda24353f0ccca9aa4cc5b2aef0ec5e53b8de61d09048f8a08bdd1294b096c638a384ed01f33e5da2aaa02077b8ba86744777d3c195030bbac7df917f376ca6847626550b0542414245010178295fc745a1fb533e223fb34dca1931b5c140e44c502bcefa98b0d84dc6e45e8c3956cd08438e1a45d1a62d66b8b647b11639c4eb6b0e54292746381a2df28f23f32a71f8ee27dd12c0c77a70ea4e2f585c93ce87ebb9ece0569025a519fb668ae2e801c4ca961eae0cf1d7aaaacb2203eea696e4a2b20e836c3f965901d5183afb10e5da899396c33d328e5a995f0792fc6a4ee45f3510c63f2b321b01e061dedba700080642414245b501016e030000a7bb221000000000b2dace59dedaf38b603f701343789026a00ce1f16640c0cbbfb95c0d2e96c9578eb2ad6a2dd46b3e3b8aba0754d2d5e289cf025c16dd8e8871a204343686850941770e1629feed989ad664371636780eb6ff614343b39dc3f88cc7faea72b00d054241424501016ec2fcf8a8cb4742270494e28e83108aaa420ddc6e195c9c856b83dd7c8a9660f650aa49b3a03879a8bd781506d6eb4dfdfb07006a333f31a888bbc465e6798f824ab23596c802a0873210939aa502077dfd6830957bf182e1d0686b4c50ab528ee2e8019e6d9fdcd583e84b0337e683de5ea34b39cee4294133a80bb359be67b0f09c1dc3108f14be0da981c90d05961dd10372b7efd087b4a1ef20080418cd301e0418080642414245b5010370010000a8bb221000000000bc3334e0d63b210f3af609f87a7512054a8033b37ba1ac7c1ebea93ea2bf5d26c93f873818b873077c2089d125f7dc08478cd3e7073c63dca46ceda1c254df0c15740e5f87ef25bf099fadf797b0ca202a1e9760f0a71809e9951323acbc240b0542414245010118cfd76c2fe91e7f2ed182171d60a72a6d72843ad10ed0a2f08b9bb006f9002e0610ab4d319f501e118d598f671af3b35fc7ab425fe2cfd4063e5eb98ab159814a22a8b7143d6b0ecfcf28dd384bcddcde5d2c15c254ef2b4665d93f26a53bba92e2e801a43f085bbb1957f4cfc921054e1f7f8652c23c7f2d05448710eec87e8c33cb83136f1764989d1dd1b36214e56dd60210c515c6fe8f65ed664b5206f19b05a377080642414245b50103ab010000a9bb221000000000ea2539bdccfbd2dcafe3dd1a69614b3590de5f96db0fb53363319c2706d5ce1775db94765cf06550b3cc1f05d29d5f1a515d5875664587f835d94520c62ca20335490ddc9768b098a8372e4a585bded4a38c3635b0d7ad8bc8563bfbdb53a107054241424501014a099339c4cbaf9d6940678a604190e283709ec53d76c1936d26a9249e067a5fa5053aea6ca8942fa1e5824ea72d4427165072d1222236f2434b6d9e901c2487efc1042ee6df16cb5540f320c33e9c6955d25beb2dd23a6528e4e9b91a65870796e2e801d269948ba3b229f4b8326f80887560687204a8f71526ca796da692f5c2246dc9cbf6ba71a5efadc93d05b0b218522518dc03bb715aff4c79f9b866d3984460be080642414245b50103f3020000aabb221000000000b4d0a6740ffd2cd43acb29cca95babae4db3a091c40f836ad32bed8e9343c348d9212c88fab3541b7a1111f376f9e6282f0684eb866a0f3a916316a32ae5140ecf039d2a279138d2981c31039d23808431bba891a3909bf22cc4c487f17d70030542414245010114cfe56b4fbdc7579e12327b3c9a76b3973c49ca0b76643a9e92b3dbfd090601b807bc2726f09c4c9af803abd701bd2819f20c60a2a14b1d298d6b925d4e2f8b1a79f85224ac642ac7c27c0999e2b36038397aabe6514201d2168423b7b7835f9ae2e8012a3dca3320820b57d60f56b55aec10682eb669dec19cb7702850716cc57fdfe6dfc3fb499a9439b1b61127d9f5f5c430580a3db8a4f5e7bbfd549271badf4c71080642414245b5010374020000abbb221000000000fe629c4d6715ffd38a7f102b360c4bf2a566d72181199347cd6d366368d6c10e8587bd258197b5ed590a695d9486648e7aeec8cf06b9b4fab7d2acc20ce2b700032010ca46610f52da2dbd6e782ac810222dacc2fd29801974f90d71e95aec0a054241424501018294ca118f1c50f500ebdbba76a70a17ce666ef3caabb204eb49a7178e0b66110377b03f6db70d32ebce29720c8ab424dd9a3c278cfb81a85af84fc100fa788b99ae783b50cd16b9d4952447551d9129c190d3b8656664ed31330a75f723108a9ee2e801c41bdc909c5e1da89f263d751ad56405974541d4bd2bf8fbc2815d59bd4e3a25a47885d63c79259cc9580e899bffbca5b1e681274426b4917bc9a4c006d1b299080642414245b5010135000000acbb2210000000009a62948a4d5b5e18bbe88f9e04f096ca016e14f5c6e1634abf318ed46e577c3ac42b3bc68377ea84afc93549281517208778d1022e383426ea8bab647fa1a203e960315fde42c8d93be0a3a435d0359d1b8f9e18957efa9019e77ce889c0030e05424142450101585aff606b0a5d51cc54a9a7c34d5cfd584365204144a26b9d3887bd95008b6ec70c6ce9378af76083e374c40aa55aaf3539070db23dc40f45f63bed6b30a68fe272fd325ee9d858db6ecc276a910bc6511963435a645ba6f87bd17f607a4919a2e2e801369731cb7d855f8a50a53e11533c2b5751984f8ec6de4b1e3ba087468a978953cc7c7b8ebcdd9a10fd495d698767f434509cb8e0356d0e1b6190e65c93bd7700080642414245b50103de000000adbb221000000000645325b1c445e634870965abb5afe2905dbdf9c16005aaec39688247913afe472b08a7944a333d8a0e1d12dcd93c1a3a69c53bf8e9156b851a8e7adc82c2b60fadd34f5e251f8622039f212ee3b8a09c035cef6a7275cc5b1d54cbccaa4a6904054241424501017846f953595ee424831145a33db83680aac766fe15a2db9521a7b4af9488815a2bcdad18970a249ee4a503b4c400f01daa95c397de4a6cf8af2715b4ef4a9a8ff7a0389480d292f08489d3e390199ff44d182a2eb477d0bf228c545e2473d769a6e2e8017e52b48854270404554865b71333b15c8c395f75769d1891d4d9b89ab4767a4d25deaf07b43407c0e09be0eec6819eb7202a987812b65b4638c810dfd6d85694080642414245b501037a030000aebb221000000000b044c0ed73a3531da781b0df0d14bbae34fe1b475e12023ea9b1934118334b5cf8f4dedfe9270d56b1c972d474051c08748c747575eecfaea756620a05d65307960523b641c4de5602a8a3f0245153382559395e53b200424a48dcd5b687c203054241424501017e40db70a333b320c5310143ea2964fe5d59d99c78e025d5e41f3d709174f9463fb333776cb2526a18169d27df65d9eaf446cb89ca2a1ad560fd0ea0ab58028fa0196dd2219c761a4536d6f508d52dd37f972892030d46c82c740829ba1f6456aae2e801c82bfba2494e340152f207ada86bfe7d1e6da486ce23b9da6b6e3cfe1b91b002278a8c5fe17677c7146423ffb928b48de6a02e7e6fd6b5fa92903d0b779254e1080642414245b501030e000000afbb22100000000036d4ed1e6348be8d63a7858edfb2ddc0c9f2b80129dce6a1ad8c09977b9bf62ff711e8ddfaebae9c0e437e64c950fcbdc332c8ffffdc782fed37892addc0fc06b44816abcd905077f20ff8eda6d9ba3841165adfb46af5741a5dbcd99bc3300705424142450101669048eac2fbee6de598758bccbebabe1d7ee0fcc4fdccac1bd5ef5e9e0f263728ee29d3a72da661fd65274435fcf2c70f60eb5f95cf6bb1e832fd246d90948a45b2f29d72283de874ce084adf5bc4601f43a4d10854dc6b4f74f0e1e6d2661daee2e801fd563c84da1dc0ed5c45ed27f46c33629404fe5370d2b76eec03a55355a9439ceafab212b17f847b3cf6dd9fe13be7e70cbe4636fe5e1e927bde84c3a91a7aba080642414245b5010364010000b0bb221000000000c256c24e7b798b4cdcfc865611d82726bc34fd12d6e927d433f0a5f4c617ed2fc2cdc2e045a314838338013e312e2f7c2a0870ed4868011117db4b75e5c1b4026115ef110b1699102fefee4401a69e41dd99168e66559e105421274e10d51105054241424501011237ee8d5c140e944df5ddd23778ecc5c0b2ea11cbe42c1b0998bb0118b0a360313e29ec8648563aa77442a738c446db9951f6f9d814f907a45cabc4f972298bbe441fb7aa0911acaf6a609d7302e607ef1902417e6e629b5c52509ad45714c8b2e2e801d61dfda474245d9b1a69b16e175080b5dbe000c3c3df59c98f0d62a72bd7786ac576497a8346242ae8c22f330f878a12f0e15d4614c99cfe3235d582a0a5f58e080642414245b50103c6000000b1bb2210000000001222bdd72e3ff397a3be136f839db753585c9e3d3eab2ef7843b7ee395e21410dbd9696cea8420718c0c18f35e9ebf25e3f1a25efcaf70d132ccb3b177449909885bc3864fb971c73c8afebd304e1f2b5ce24d3842a8341aa066def23b52e30405424142450101902b44b38a4310b4ce818b0d81dbdf92f7a459910faf6bfbfbd3e7fad636307fb5908d0fbdb0a93ab94eda2de4b54d3cf4365f00ca79c9eb6b469ba677eb8684b12adf0d3fc62ed4945d85d34899518dffb55bf0e077d99b759a99172efea77db6e2e801845e6114b93f24f15396b4f0e148b7c234af05d7188351ab010e2cf06028a8f3b9f3cf3852a11d9014d987d88da79cf24e38475d220aea72e55d3040007118c7080642414245b501030b020000b3bb221000000000a68fbd96bcb06921e55f1fb7a1e0a4bf8271c1a7eca36b337d9930e1f61be67136cdc1d3b38b6f8fa18150cd209f44c36af7992ed53116e67e6c385588edc703af868ea0ed0a827cc608eb117708961e627feea0cc4a8c9e041eb5465bafcb0f054241424501015a7c5b32706410932b3f164deea49cf53051146ae20ed4f79b728d4a797c1630375553ccd1f9740bb73fadbad0adf4c266f1ea022a5dfeead169e468909b6e8495b8c1c65e82ff80b8247e817c16cf4ed707284a5ff8b484f69d220cae954a71bae2e801d99e2d262d8dcf75d63b67372b9923f36cb74d3a9e206b40ebe128ca1f9611d4bca8cbaec5fd85f7f4f95abd5302aebc755e31321cebcb40b20b6f9de2de0966080642414245b501011b020000b5bb221000000000f25b192ae7134182bc20956c75665fcd893c5f5a8627f2439e58de747221ce71690877fc6af3f0d62ebb9e939bb351c60b411d0fe1fcf7599db1d77c7bd3e90f81212816bc1d7522dc0425574076d3f16ed09174a2863b1338ba8a912dfaf4050542414245010148259d222d4eac8a2e99672c7c1a5bcaf22498f5ee540e59b4afa1cb5843c5309c00fd19494e3dc39172b20990d053b2701cb163f5729349065fea50d1ad618c66f35bdb7edf08b0a0bce073d226147f59ba64439ee8e4ba1bedd69a2d4498efbee2e80108ba47349f3d21a08ca52fe85f07bab4facba5a1a494530b45bed8a770f29da59931ad9ef69b6fc9735383946922c08f35b96308558c04e75b23680268e89713080642414245b501036a020000b6bb221000000000887866b3cd7493658d43b0c4257d682e4db88189e2be0170c069b4e190471464889d5c654d26f521b3e41c165d123d2feb1c7b9b2205e93c505d8f20d7303f033af36a6b1aa8be572eaefb9af45a546e29c3a4c85a07e21c97935c461a48c20205424142450101a67519497c88eafde59da538a9181ffd8ab3c67f97b121b32e47a8b7cd8b15198a0cf9701922fae341167bc2e85986a6f27b0cb6d6d0253d5bc1dbd5f8aaf78ddaca321219935dcbf42c7be926e3eb7ebbc27034848cea754410591a300a3f1bc2e2e801bde2be248c85f12668e3fcd0f3eeccd6430f1e1c143b7cde1578fb6b9ec74ce6b4150da556eaa63fd8f4e1a759a2b5274a41ab6377691224bbd2f0ced59b6bd0080642414245b50103db010000b7bb22100000000046b1f8abb56c8bcd70504765ac6dd21c8b9a2f2fe784c8bcdb7bce45333fe24aa3e72863d16229409fda78e802bb4e114887ab2fc2f6ca30866c20907fa39e02d08c5543e34dbaa83ea12c438cadcfa46c870494ec3e494300becded6248140105424142450101361cb9a67c93e25880eb031e9b95c4fdf570173f8db799d05676d1d8dfe4024403e91b10ad11a7f95c8cc2c0733f8ae2c86bdefbf8f7d5c98de9a66e40e7b38cbefc5ebccad32cbfd524778f6f7c7f3486e08deaa7a191339f53fa8af526ebc1c6e2e80116f66c83ac448968ebd5fbf7f748e78b3292cb95301d7886da8875210925930d31ae50338e74f46ce7f21c931479fc391a789ef1f6934acc833c70d56152dc03080642414245b50103a8020000b8bb221000000000cab0e669c1b67e7afb63f7632973ff7fb375440bbf28dc38812ea983fd36003e69a3a7c199ef64eeebd01094d1774de83d0a8920be748615e083adf14ac2e309bcfbf01d2adc48122c2256b23b8ff528f5e5fe2c3d5d2cd16ccff35d0527890f0542414245010186d5a9f17a984a94e64c47526b3aecea6cad3a91870fc43d301dc4327881550f99e5e7a3310f76dea103fd46056358e6b3cc46f911fade1970b7f7f683ecf58dee1f01a01c87aa8ae94765547be94df4be682acdb9837c8d797bfd812c299e48cae2e8015cc40d2630c78ea8471dc2fd82040cbc195d8eb461225cb793839da6a22b0265a352e0c53c639625ffcd21f45f24101e0acd9647c7121d287ee0e8ac4b4db6f9080642414245b50103ae000000b9bb22100000000086549ff14463d70cd54bcdcdb44eb1967423dcda1a58656d4f65ea758b92c867f71b95965a3550808d647f5e284803fd82d1dbb4725cfee34edc1b6c74b39201dfa5ffe9ad0c83d04396b86564f3b122529318cd3092d79e8d9adeed6715140e05424142450101d050f50f3934d836143cbf1927557682b0cfaef1175b7fdeb4511b742959c54df91d979a526863869d65847518292d44aef92473313f8f8a84fb75061166d183bd9b10bf5c316ff0ec02b9b5aef64f57ea233e28b823a111e4a4de0722245db1cee2e801e86d42c1493ca4dd01ae06980de6968f12fbc01e12af124015a6438c5b886a5f1a05fc5b3caa148c227ce3a7152af3a6ec223d82a6bfcadbfb40acea5cd715b8080642414245b5010391000000babb22100000000054e7fc60ff8a381169f42767a89053756c4cfd5fdffa6acd01a1bfbcb164ec444478a815da7dfa92dc390b6a8f2b73bc7d6c98ddd60872e51e7a7a76513fd8039c3e2d5316bca465d9c6a6bb796e3ea19b5cd29d1b9ed33d61063d60b466b20705424142450101a4c0742721c96d0c53f3cd98916efb35a64fe202de170a07a79e3a00da1d897048f97b5dac7d7f714b80b4c173036044ad5c5da648e25ed4b946ecabb458478ddaed3fa8f39e6d9ceded42ef266d4327fd4b0cfd0396cba09fd7366171f70da0d2e2e801446bfe022103207fb0d192c93f9018aff91b4a839e2edf056c7a9cb7441e34895c2957d36d371bc991db8291bb3e4e214172779f2030125cce7081740a3cc52f080642414245b50101e6020000bbbb2210000000007439b13f7e0e12104b4f3fb0e8f20741d3d6b67ddeb2d0ba17227cbc623ed0296bebabacd4840e008955accecb6afab58da0fc338efbf9dde3e90186d4463f09f95b743eb6aa60badf5915357f08aa61f1acb4d08f189616f0a43df9ff4688060542414245010192b2012eaf1cb9b4bb1b6bb7c9c95a341b8c47e674707100bdb079a63849ea24287a0812237848e506dc785939ed92e0398db8ea680e2de2989825f263adee852e4aa4887f67760b36932855bd3a19c49970345afe3a3a7c324f0aba0bd51d6fd6e2e801fa2151e910503257075cf15078500b5adf9935dbd77f73f59a722b12d90dff7c82edc863dbedcdd56de94171043a9f354a0c6cca08b1d28353a268cc6721ebc5080642414245b50101a9010000bcbb221000000000b2d401e6f8ed93d9f0614e8413f10db0eef147d5d61edd115a981f0fa0c49968b792cab8ed4108946cb0cf01e6fdc8e0c5b223d1374bc34a9ed17281d94ac0097bf7187935cfaa4663f7c511f1266e2b107ca3c24a73babc799903d1ea2e7e0005424142450101765a5cc4354172b8d04885d326509af6e6238ac68f8dffef6b0e27f9eb113c03064ebb0b8482c656778058558bcdcc944735003d243da09ac608527027420b8ba1dccefb1054c47e292c14ab7bb09bd1a086c191db138be36723fa3bc68f6eeddae2e801939dc641571e034d65fe64aedce4b06238898985257993e8cab627ae31a05241ba405a93c8b919c9df9dd81236be12b35fbf0bdafa94438f769e113b4e8ff992080642414245b5010369000000bdbb221000000000d06208ed3fa0899da4bad0d831633a8303dab589bbc4117d0812b9e13c1a61093f75cd67ef0679e838b97d2ffa9e1d3c58c1102045d0ed69d861b9e71580f30aeb6d32de7eb19239a4c1566769fa1a44e4eea620caa7af5a91c7927b9bca7601054241424501018662cc629642b2633104b023e7f2e48493b8b6d848cd8bff6fa4730314bccc3ff63f64560150f6455e0d2430d49a29e891a9e6cf30ad9df14e209d32a51c99850d951b585e1abc51ef0e51b0b9e45e08d253a55aeacb53f862b3a528f32c7d2ddee2e80188286a5c69c6554484d7bd0d3e48ebd892582ba25b6b32796d30ad52bc439509fd1633e1576171118c27cc323e767d295f7cea641187c1be2c36926cb8946b7d080642414245b50103c0000000bebb22100000000090338a15f4ff02a9072b22502d7cbd4ecc77ded0635d7a45a061adf79e534266302316ab375d425ff88b165bbd7050205d0d2cf55ebb54e977302fb8e3f26c0aa14cb23fa8c35ef2d77b4acbcbbaf30fa5ae4f1eea6f037d1ffb0fe3100d2e0d05424142450101fc15ebd0f1d4d4b6bb3f2cdc9103a621acd31114e9fbde1278a331a9e8e5ad481018a3ba321fd956200f371b36d7149e242afe3420a5682aa406fa246bce8584cc11e4a4ba120dab9cea07f32b864060ef4ab7a7bd71d24b0295be835bc6e0fee2e2e801bad099950aa56019306278acba9045cc484ce86df608ed1d36be1f77447c513a5655c7203ece4a8f6809993dffc8d684adb20c770fb0285af457d5ea48705031080642414245b501031e000000bfbb2210000000009a2f0453a4198bde0cc9fca4b9b5b53f30eeea08a1279b52e5c7031ce2d3c6525e032161bb85c3b6984db8676b315e977512742995c5e9999fdaaddc7226ed059defbe9f76b58637168a27758eeff114c88491afaccf0df9e35e65a64e54fb0105424142450101f04ceafebe414a456223770fa678081d433f2d3c3f86e8351a1ee00ca200611efbea80a05389d3e2f62d3a7864d6593e7040944b59fb44bb66b3b443e6b7668fb3703d7cf334e44dec9c61447f74c3addbf603c40d2f7aa53ab09095ed17e7aae6e2e8010b7c1a8a9282525ee2cea8c3be5fafb747f9f8c0014a93d90d847539f5a672a1002569f087f607f7f9a3a77a29bca8fd2cea2fbd7bd20ea139b1a8352545536b080642414245b5010382010000c0bb2210000000006e66504d6f584ba77601af54863cb8bddc3e86a5f7a6ca22c8726bbcff8ee60061cde6d909857c5fe240c71b5fd974ca6e09fed27dcbec76520a1bc982dc5e0091af42951304c8a0ea04a0dd37ddd4e0d80d4e7fe3006d8dd9b99139fb65cc0b05424142450101f851c8d2659f71b5eced7854aa4d5853e48cad279a2acbd6669f191aea00c84db05c926d7016fcf08d3187e0caebda34786175d95a4f2ca1aff0d28526ff9d853f05ebad4776b91f265fe7da83d7e5674f26476f8d55c22c6ff10691cbeb72e8eae2e801b709a1c9c04c901f9e4817d1f0d7987e4275c81b10a3336fb38fccebdb3aefcb84b668893b1d098da4f41fd8033da064d5ada2383595fc2f70e69250ddfc3790080642414245b501031f010000c1bb2210000000001ab8db41a50bfe5d7af38cf8df3231a00bc0c8aa669ed4d69798700ccac2920cb6d5d647626c707e2cc4d903d8c23249768f5dce0664808378bcbc57d7e611075646192949d2e4015031430d96657096d3499e28c146666a3bdf338c261fb00c05424142450101fafbdd957b24b87e22541f8923bc9e68a8b658c51a7fd8e47e4024b38dad7156add17def499017f16b71f5bf1097d0057c3a5ad399f36ba42d3b9dea1057358a1313060bdde6794b69f8c049fc205dfb6e1bd8faf36a212c5beba624040e2a9deee2e801aae64574926fd88ced3601a8e19c154c1dec1919f1c4de0f5df273945bfe2e777aa6c7340212e5a8e2cf140ebe29e6c6e3ae162423ac234f99c887e71975242a080642414245b5010348000000c2bb221000000000941f9aa72e7d7e34c5cd83d82d489b9faf23f3c4d4c68897ed39bfff24582e77b7e24ec2c8b3c85e7c47d775066f4e144834eccfc3ef69746ffb16451e7574091587032f7768eded4634e93bef10308ff80ba163828046ce5c5d3a0bef4d7a0d05424142450101ee3426196828c7e8034e3dccf5524480c47c6c51e1fe7e049b8d8e9d07b93112f43c1792da813351ade99cf81454738de71f703ac840e1fab13981aa70ef9d87f041809921c027ebc025b63f791ffec8b2c62a79e8fcbd720d7d132f3a8915e5f2e2e8017de9f489e53ea591cf2dec380b958455ffff67959d08ca65a8b91585bacb393e2a3a0f179212d5babf24dd0961e2541697200a3278a4c1c286936812f2b0d911080642414245b5010392000000c3bb221000000000d6979f843958b4e0a4f4614d6eb6b5d10444d398e5969c568b93e9e1fbbf7e281ba7d41600819651bd450056cdeba01381c2c16731e5df46939355e8d8a71406d1a7dabd7f37b60174e2dea122bafb507f16a146034d9f7ccc50e5d394488f06054241424501013a24e533f6c19329f1789eea26d4ed432bbee59691084a56811c0be4ac9ed8589c1bcf9cf40d0cd3deff10460ab5530809c848343f199fa88e2c575645d7278fb11bb9731a0b3dd792aa01fd2c4d055dbe94c066a96af8cab0c9645e5a418479f6e2e80111494ae4d374d326ad8ed4282cb22cf6fad70a1033bff31abaed9da49abd33ac66f8b39806014f2f19cac62f802134a98f5f84e8dbe81ce243d2d1f96d472fe4080642414245b5010352010000c4bb22100000000034daaa55f405cfeeef5df9476f17459e5fdde610bc05e08be7088d5217596d5e6dbd46614bab554c5536695868cc61e3f2054f7380b7e31b25130bfd5cd15307c19a142dfe54d7daba55fad9418211bda090b1ee04c2f43a5904eb7b84e2cf0605424142450101d0b79abcc36026767116b944466cadd1a5eb94ce3a9f5efac10f6aa4c020c8722654e50d71a308f3f2acc5af001fe9fdff0754c9226336acd66c28dc3954e788f8979964d7c9c29dc99eb26a3cc1d67c7b3d39b7ef285246ec75bbdababda4d2fae2e8018e6c0dbc9b5a42930a1fca9e755e0a68065a8026cd7d7d83d204e27deb6543cf4062a350a73119de628e89cc7b279a041a1ec614715416ad5b62fdfb07d75414080642414245b5010103010000c5bb2210000000007ec24b3f091aedf3ec7f1fe192e25e9b6435f6b31c060d95f58dcd2a41d84c05e1052be46dd73132b4c3ad46e06d1150d3df199ef9b32bd9df58109cc2f62a0b279287628e969aed400b65a9e8c54574dd720f6a689727ee3232ec51ae0e3401054241424501010e16b89a0f931df56503d10c5321c06ba0f2dd1f2674d915069e8991de84322b9abd2bf0462a25e8d99aad7c36fcab4c275646e346bd87b716f2672c161df389dd91f87dd8e8d74916e5619100ad5c7f38f53638c01ddbd0236c77e3cd9f9eb1fee2e8015742f2d3b96c3993e84d84dca22909bf9241e14eb8de4c3c7dc08d6b6c579144f3db690a8a87e82bf3f8ee0bd84076946e37ab9f3cb371a393e62ab1bb3f57c3080642414245b5010350030000c6bb22100000000056cf0a95aa3dc3ef8199b6cc30b0ed1c4efffa4fa2d71eda9ede668d886ddf1c9c46fd33bd901646afff78542586f1c5c6aa90238dd605bc4cee44244c8c550c36c976dbdcb8dc14336716520dba583a98e7f45eaa05fce511eed41a99896600054241424501017e100bbf3870278c832e238dd4926b7eee9d9c89d56908259290d10e16a80e48ceb4ed4dee6c7e71b06d3ca775c5a8682c765cc44c62773099b838271552e183b252cae6918dc63c62b52246d481ba6d307aa90c4d6915e2bd08d8541302d1d702e3e80168e525e093c5b02c297e1c351f9af1d32081f8d8666f8308e967921fc36925ea3ad3dc92150024db9744e9ed6a54687560b724050e3107b6fb22819fb8117668080642414245b50101a5020000c7bb221000000000d2da855235255747a1a5b3715d6945ff449da7f0258de4220aea0861173d25197c3f836aa970470f530cd0baf6c6f965f87f0463f2f1cb68948fdfd0a4eec907d50586437deb927d952b93121fa976b283d0061a1e9b0845b53d7ec11c3b640a054241424501014a28033f1e660a5039fbb88559a1bfb562e9be8d9e92b0f5f7777ba78c1a9e5df1446c7c68553b6e4666703300f81df72ea0d8d066f673bcb29a7eea025b5883e6c1c65326dbeeda44914e609b8780ac9a4f0084addc33f8c4ff69f3bc11ab5906e3e801d0d42e49ca9f197b6dae6a8709434f51a8eadc5ea07ae86ae284024e134e32536081706624465bdc42ad83b85de0445b07e345048d5e3a29843d99e92f64c5a0080642414245b50103c3020000c8bb22100000000050d1a2042340d4056ef422dfdb564c5b151e9025432cc9229d42394f633b3b38c6db3ae9780db1febf16820c7cd820461a6fcd918d2d0f7c39eda4c798fb2a0928c543176842b23ce63e64b99386e9078dbe53e94b5dd2e60b31b79740b9290c0542414245010142ffa596bf6fd71b23fb0de037755cf279aa55464e808c15a82992cb069fc438d3fd3ca65e2d970abe14e1222a2663ddf853b2072a3d83c22716fbd672a0f282e02e11a7411d201393322008511dc5ac6aa14b1b8c24f0f5c236436943fbf5bd0ae3e801ff2de971f2511145e3fa4df7efaaca478140638ef722c4f270db50b44ebacb23dbb3b79f59d9961cd506dd7d04d8a0a85e18c51f21471bbc313419390e78c3b8080642414245b5010167000000c9bb221000000000a85302183710efc7caeabb476321148cea8a0a0b65fc0707576bde438993917839a8de1ac291ee04f4ea634fbe5252ef4b125659a888021110b33e37bc6c03063c898aa703682fb31a73b283ec8406b7a85d956d2d09817259c07abd9da6ab0b05424142450101849b62d0cd481f1f63038bbe136d7ce97a3d8413bbee6bbe33565f406e322841860a98f6a2b1087e29f6cf738b85c821c7758526283f882f4470df9e13cb0988d9fed9007d35861a8bee3d0822af260ed463e8f919a797b2a05e5b5fa4942adc0ee3e801a046e9980107db864798aa12051fa2c0cfa8165f2479c139ea08a56bc4d5a370d3820367ded273ac074956cdaeeb39d760873b96bd038de57848c582a8126527080642414245b5010331020000cabb221000000000da1165095726a00e05542ff4525714218f9b9064c417abcd41d4b242225e220ff4612e33ed274a6bbd257ecca2cf74408181a9c41f6608d7ef9989b4e99eda045d22e2dc482b264980e34c0bc60a1fee86c40c34b743f5a85141e313b103a30d0542414245010108017b10535266e640e7686e372b50bc75b791675b469ee6376720eabb250e4260b6d59a56f8ebf90b42e366764c326939b48b6dc113292fe841fe47b5837585aab3f8b69e68589e8a46143bd1369d3cedfe9f06c3155acfa99f5e073a9c955e12e3e8016aa76d6dd6fc15ed724f9322266d09dcd3dbe2ca3f5ee59fb59f02eb56e333cc3fe1de4bbab1378b69af00e8fea88746cd424a41a7406f6c50f2668a1629456e080642414245b501033d000000cbbb2210000000001e421c6f7d414b835e5a3513b59759714d34fafa59cff5c4ecfba1c022aa506895175983f6cc6fdc4370ae396ce5c51fa73ebb11d7eac331dd6f2e7ac3dc8f093c590d2a82d79bfc7d167ba536c8d3148dbc0115693ce893ef2f0758ac461e0e0542414245010160f6cfa55f983aa7f0dc68e338d32e5e0d3a9c696941df72fed9ddbbf9499b195c7bde9ac19fdb101deeb1cbcc088973c0c3bf40148c4c1c3ba99aeda4c91c88eac639f98bb1e207efb209053c195f58e507aa27808e7ae1859d55bb769b2d0e16e3e80116689bff9daeba4305aa86153fbebdfea62bad497ee36d39e6ecd8ad26268958932fe969101b9a9423325c8a8969504596eb03b5293a077e3e7193e9c0e6d498080642414245b5010316000000ccbb221000000000560cf7566daac52a84413fa5645b69cb42755f417e09a1f3be98d6a3cd55cf49af173b131d49b835f3aa5b4b8270a4f2c793c969b820510e99a26ab0c4802005f6987841887486785178edd4816ec61bcbf85648154d589616cd341ecd2d4c0505424142450101c26acaa38b661827ccafd7d574f059af076557c112c1dd1c82e852a204aae14b483c8ab892d5a8fe95a4299f5dc9c88cdc187d4f4c4e369fcb79efa4bdf019821975ce878fb2056bb61266f8ca77a0db73753f56275ba191ded36299565fe5141ae3e801fc4af811d45f33bf3b5b1581aff2c057c48ad6fe137aeec015c6ef8b7c000c31c109ff15a81b855e403adc88469906ae664f632dccad3272d854a65b1c84ee71080642414245b5010319030000cdbb221000000000b640748e846555cad614c33fe52911e31839e1fa70db69558c2a9e923a5a095ab58f3d11c4400024dc9cbc7bd3efeee674f31490c69562a067d92b1a1a13dc0edd3dd38076627db47337185201bf13045e9d0d7720aeb4d4dc5ab252e84e8b0405424142450101188d33d202269f58a96ab1f4cfc77d30f7544c9e1cd5dd2283516bc665f668685262519ae9cbd8f9d3365bd7431c13e0dd93c5f511836e8a211569047aac0f88beff900fcdec1cbaa065abd8760d474288538cbc7da69783c5c20db4cb3dc0611ee3e8010d4e7bd9e2f585142ba56f57106fd850ae3fb09d9d56a11d469b15d086104ad4dc54080e6d1c63369baa63e999c60986c337be3406115bb5f5b73c1131867be5080642414245b5010363030000cebb2210000000009c7028611b0950d66faf08ccc398feb7865edc727f36e744a05cd18ce45626197794c25eb213f385d16d3939ef4e6794887f54e8e310adb79e719ac09209f70241919ebe13a31f7a1435be9358e1a01f4f301f30e3b94e1f74f07c2b8a77320605424142450101debfb39c500bde9ba617b53ce40be048dfaafc0d69ad21c5b60408d8429158104d6c8be3e94173d175519ee01ec45900c8d9b2efbff4b072fda86b8de9b42a889f7745ccef548c16c1b2e96cd059a4ce5cfdb90873d755a5cb0a4e3ecaf09d9122e3e801d5b344ff8c5214e19a9affd9b2d9cf2472fdbcbc48c6780f0e4e7e3845791e3e1ae80b5b50b511a07c8342ba276764461783621101e587d2225bb06795539895080642414245b501014a000000cfbb2210000000001ad3d0364eb4d78f92b2c6ee438437e847fb13b303818515b89a130d07387c54c76de28a3fcadf756a8d94f67ff0012ed26c958a328ff29ab33419c603d4600c7580635b60c77472fb146fcd8fa56e1934077fe0fb704eecebf7e052563fd20305424142450101b05add9a4b7aa39f2cac640128eed305814e73403534eea591ab89939b7daa04bd3e7e66c68f3ab26022d4f3a7ce7448bc5b51b4d3f2d2f3f70298b293383a8b06f47be5b89b74fc117fdaac75b396e5b23d36a75cb82328fe85899775de9d8726e3e80194d6dbe9a30e98656e6e4d36ea45dff5b703ff278b42251876f73616ecf9063842a6257221709f6fe66aeae16cdc72650e138d037481304bbe34459a87113566080642414245b50103aa010000d0bb221000000000e695511f032ab45c94a431c761ce284af8494e00394c95108e76f90a01474d53573d63e6ce21631ec08947e4275b0cfdf8f977f00f14caf391a8b1fab4edac064b175db6d507f4083cc40c87c42288220ef4a783b6af32b1faacf7e6a8e83702054241424501016a3a753e49eb42bbdceac5ea8a044eb6f3daf2ce6b983d7afccba77f9dbbdf1391db95e82a25fc2ee15fe9c0197ef503702838b0de8d68cc6fa3465075663f8a94b1c8e9a25dd33a82b11c8aba678ceb4d4ee3fcde2076169b0f24921b048d582ae3e8014d879e984cff15817e1a86e1042a707a97297e5b4354feb035f98b466bbce0d3be41d63d4e8940672643c830ef7b10e6fbbf874ba9563eae16dc1ad5d65138b2080642414245b501034f020000d1bb2210000000000a3a059689707d4952dc77bcdb3bd503abfecc3df21af33b990bc29c408f422be2211b34eac56bd4b69feeef5c59a013a2b5a7287f05d339bc6a107d125a120a6456e9c667f23b2b110928841a63a117c0072a2c940a45f08868f9a746b3e60e05424142450101d2e5b9ebe813ecc37cfad26b918a75c41b80f4d1d35069f639f9c9e02128b652e8a44b7b991da1583e598a543255a255339ec116f1042813f89a0a04d6eeae8e9c614c8099c6dd2ac470296a19afadde9dc868c5eb7ee2f9ba4ca67cb86c78c62ee3e80183ecedbf684ca78c4c8c0677345ffa4593f0d5f53c6dffe9c08356003c81f75243b6a244b48a95f1958afd47ab755723eafee90936d50204faaf666c817266fb080642414245b501017e020000d2bb221000000000c629b3213b04915a0cd6907a7a69e458ed6b68ce83fb0502893443d121534026951d5542c6e72dc2602c7586e5c57a9ca468d2e7fea3b4a5d4aca4594d77ae006b8084c5fe2fa7922fd986096e77ec678b53c2bb64775f7fc8cde2636b22800905424142450101ca1c954effedd8e93e6d0abeb6c745f9196a42815a8d87fa293c997aa2a01256a4fcaefb54f99f95f63fd20251054be5b5c7ad19ed94f52b5cbfca2b1451e78474e51963791643a7d373009e18ab66bb25fded3a553ea6ed041303e75fe6c82632e3e801ace55fcbed307d8b5102f9516450d331c7550875bf3f382ad1479ee1b3ca691209c627c2d2ab568c7ffc41e5042a043a99b66b754fb64c1fbced67c6a3114cc4080642414245b501031d000000d3bb2210000000000efae897afc50122ab06c937ac3e3e76faa72c30a1862b8b6487ffe492735b5a65caec4c75f79d8afccd228766cb1713200130768f3729af0a0da68bb6aa4a0013a63232e08acb1950c7ed57d7606fd0341bbd1289be1455bb72cc0e01ce0608054241424501013c340617926c490be1daf3606cf386a34223bb93580d6f3c613340b34059654098355325cf3568804f6f305c7c313f4a7fe168c6f33d10d2cbbdb64c3af523865e81242909bd1ace3c458ae53641c24b5e519993bf939ed11344a736f61e9a7936e3e801af1b4fb9644660de7f4d74c557d953e710e7d8733235eebdd49479af325b3c756ec438f41527b60f1fa6426ed8886bde1b753b4b88a40e387d5e6c392181ed19080642414245b5010353010000d4bb22100000000054e5afe7388a25b00d6d241347df2337d6fab664ba676fed793af4a03766ec242171ac8a55175ed91656deae4e163e9eb0282ad2754a618bb1ef3774716ea20a811e0a0176a6451f77bcd9924efcaa3a3b5829dfb1215643cef5aede4b4a7002054241424501011abfdd667772ad78cf05f2785d1a27e3c7e15c477f1c93c326be4f53dfd67f2d6c21186ea6232c67de2e5020216b86ee6d238eaf0f3f00a691624b16d25c388f235820d237c89a49401cb507d1ad48ee18ba65da6a4c6c8d31262e82d466c5a13ae3e801f185d4ca140e26334b0f92b1ce1fc6f77126ece75cf43c57a87aee9e6c22eabf14402b9be5480a71cd9a4de3e96d64baf9a977215bfaea626d9a559c594b40a4080642414245b501038e000000d5bb2210000000004c8b7322cfde83177cd3267f8862300c5dbff9aa313ec5e8fbd9c3a9af6d272fc34172aa9b2b17afbb1928d916be0d225b333d435d058c222eb074fae2efc10a66d06f540698d34a6a6185f57b8cb12793011c611d3bc23b4f3230fc1d6b5a0805424142450101e073233224a116a6fef954e945209693ac92609efab02f91c1f56891d5201f4991e475a1e649aa2a689c1f229759990b8915e056c7995cc32ba73038c5968e8ab86d330bfac2421b104c1b0d794bfef9d3cfcef054f7520502c88f5f37f05ce13ee3e8011cfae21ad9cb6398ad1660eec8609e1629203953ba1d7e5ebcf19fa4731b93719135fc6f765d9bc34ee81ca6fe0a4f020fa496cc107202a5e3be9ee7e4664397080642414245b501013d000000d6bb22100000000004ab7872f95259499a459fda733e3cb79197e0f6285411912191cc5aca5c8d6c651fbf3d36858963fa29e8e53ace7b642ec7fa3d782bfb3a7a8ccc6581072b04804d8ffbe942020323b6ee7c745fdcae11cdabc36c3183ee14a893b45ea202010542414245010198b0d32893c8084f94dcda41770e9120bceb0c05796c0b495254b475b9f9d1034a44a28f96a9caf77e01a2a6d223af007fd380ee75f6cff35d0b604c1de54e84cee411e1da307ba343c754ee67e651dd75ba447056e0384948bba827cb46000e42e3e80183daeaffe3b9e7c2efb2dc929ed79f7ea5d48970c996bc7c053b79a2ec79854f8d6844201e24861b553d1afb482a8b35ac656bc259d6c9ee6307a9d4e562e12c080642414245b50103b0010000d7bb221000000000f27988fb796bd4a3a65d7cf32afc782b0d1ae5229b5aebc98473841a202c98687030fa6ace563f7b7aeb0a2568856fcf316cd6ffdc3ce3a49979246870aa7b020aeba64c198a0a697c822fc8415fce945669cdcc7bc60bf61546f0ce71d9a30705424142450101907774a3f9cce6876e4c4568f03b4c2226fc01c1aed3c99ebf11e2c74786db4e5d48593d53d97768bb7c2c1aaf172d7603b2eeffe267159147dbc61e1ee881843e080a7eb76d5f4c0d4d414688db0617a1d45f4aa99446c773bc3267a484dfc746e3e801a5f4bd20d7f2b5b39c8a2e203996a38d01eb7e20b6cda31b32418a4974053553b3dcf34fa905a609585977a4e4969e0dd9c7bad025f15331ddb43910dac40cdc080642414245b501011c010000d8bb2210000000000e0641bcccc97a95ec50d02a43df04c0576c18858e21bec9be147904c0f2351f501e90a129098cf0d44101b543c9e2daa8e7fdbb78a48afd535e49bc344d2d036f386d6d733e0f442670710b86522512e6477e82c51db3fea37a6085225f2f0805424142450101fe942a3f426f57d19e3cd5eeda93a9ee590670637bf873aaf19736cabf765b1834aa514a11df39abfb793a6dc3765f4ca65db3a3fd92626b766ba3f6793d808491ed9aafd19d44ab7eee5c433c69498d46f8f829d99d5c5c3dc7c4c21256b6864ae3e801b3848a5b2e6506c48ea124debd7966abae2e0a215158795dd8a14fcf09c20077465035f0cad032877f73649c3492b0892dfeb6d005118dd5140092e46ed04323080642414245b5010318020000d9bb22100000000076ffba1a5fc04812677b2fac6286c46dfeff4fa913d13284635496cf7ed1264d277481dba5b1c0cff35532728b209478926ff3552ce03f1554329cd7182d040b5ad37b5ae8b003a5c4e3110d83376aaff70e5e96e5976b4753a3c263fa6248020542414245010144918db03f4163785c9aa7ccf6850a210f81f6de58f4db519a07cbfee4a1910eeb3743066fce1f34be4cedf3af6e33dea7bb4661d33e9d77eabdb80ae0669e8c5a66789e011efb507a7efec0ee00f5e1db489141e6b5ae63e8cea07e93fa67c24ee3e801a27df6bf0511932ed06b598f9ea1858b6bd1cc235825ae09d4134b748acf57d60e2d6860275cebd90eb3af4ba62362f520c32210b2bd81b8def2a008705812d5080642414245b501014c010000dabb221000000000247692ad4cef489563e138d4ff7a5fb27dcaccb4f2d13d6ec7b783dadd86336beb6c5822f3e976ca093703b2899f03e048bb46f77c3be842da36c2589605c60ca12d66113f5ed11d2bcc12977c9132ea80c8a898a7c6c949b2579d8bf2323c0d054241424501012e11f26ec392fbd8e8ac1da05a3f29de40767ad9eda4fd8a4c98c6a0265dc477f62b055df348162ab43e608519ddca13659f38e8cd57bc2ca016d5494710e6850ede6c3a33e42d11dd65bba43503a13400f5639301d3aa0ca40414850181e3da52e3e8013fc004f89e59cd0b300fa37def40fe317f6a75257de999a9a45a7236160435b6451d55c1ac4b48fee833bea57d9df8996a6088bcf80df64667959f94e139bdb0080642414245b5010398020000dbbb2210000000003e85744e97a81d17b120776898540228190ea1d3b4f997614fc7c41228bfb022b2b3835733bb35245377e6d39ee20ae3904b96d89a3062ecf84b176b5df5e20216600eae608a21212b13c11b7a40d6daa17d759471548cec6109400941c3490b054241424501018e541f40dfeccd63c8eb572ef8ed1580fa635a30ede08d60fcb38cf4da176675c094e3c118e3ff119ff8a688d487b041f63ec964a55ae7703208123e3ab8be89e47d27dc6efeed85ec3276a11919e7418422355f9a4d89e86bd95df9ac1390aa56e3e8015cca187015640cd7f7093edb20414ab4358411c20d636df8b6e46b67c0de5bb02e4f194fdd05df9a4552e1978344ec6b6a011c4d6481ca44c503488aca3d9aa2080642414245b501013c010000dcbb22100000000058c9231317ed9b60411d9d50c18e38760fc9e9b45395c80c3734a48c58e7d1785d9548551c234e07d4820b2116707e82c703a6080eff71eeab4d8f5f3fad8f0489c7f71509459b51ffb10b8948031904eb5b24fa2e39379b17045378e7ea22090542414245010112b592facc327e544c1ef89baa0f0171f8e3caf118f3e63c08a3f82466399c7b393a7f6e1f47ea2002411b2e756594c57c2e436f25612a6cedcc85102b4c978e03078170e07643313c0cec3fab847d5aab5a4093b734ee90bc5936ff9d3672a95ae3e8015129b5e705d2be6aa2640754f289b3c11d36e69fb07a009c2cc352a6a7b5f5d7a450e21223c1dfd2f7383e6ad7fceaac98d0e8e9679db6edcbb1a43720f708b3080642414245b5010344030000ddbb221000000000dca690f4808eca8978b060dc8cc77cdc9e1f2c5a075fae8d1c62e0204d2691305757177b9f4e0da97e8fd4a837c296151c4148cc911947827edd22e89da27207dbb0292cc7b6614153449b3dc82dfe181f56209261743b7062dfb17b93a25a0105424142450101c2a58dd2fbbde07677e171d363ec3b99391e68ef69533b223af021ef6a77f250257a78d670e194119792035bfe5c689b64f464d0c57269494f248236a3f3438d16f40e4c4e6e28724f4ee3b7c9dab4d784ad475992322bf2b17b96109bda46d25ee3e801e9aa02120ab5c8ef163337b57ce74ac8c2a05ac87f993bab87fbf119748586bae4b4b80a272f4604227806b2593c8dd0b70585ad1dc4db0a8b4c2f7f0d748e28080642414245b5010307000000debb2210000000001a30df60c01ecfe072ee3605a003d4b315c6eb28a75dbcc20c8befdbe8c4b9036dafba899d4476fc2d3b22ad692d8d5b702af21778677558c0b40bf132a89c074cc9d6428252353c13bcd20edf9fcd868bd4459d449a929358d97122ffcd82000542414245010126df05618311a18663e63e2dbfca2350cc675a95e1003fefd4eaa89de3cbee0d18065b8ce2845190238804241d31eb2c302e3faaa758f40687c97624ad0e5b8636e4dd1c765862c7734f91be613cecaa5ff72639cd412c0046abdb83bd03e26262e3e801fceae042f1b3cc9d0c4de9f2cee56adeac69fff4b96152638016ba651a1742ec2449a3a1836d77d0b892d2c8f7483fb8de71e03bff5dcc9385b265c996810c23080642414245b50103e0010000dfbb221000000000c40ac06bbbb0e09345e7d09ee3b3d22241ef2b73ec570fb8e3a8e4531896a46d8757a8ed746f17b45d6e7f6c420f7e32f2e2c870ea65c4337715aca57361e40f9a1d3317838c8c1f29f469ecd32fccb3f061ec94f176ac5aa048886fa53fde0305424142450101c69cdf5d8a5f199d59387792688dade5d8ec91991f93c5ea16320f35276de07a361bc0f2ac9bee7b79f88e0bbe82af25408816e3b196ca9349c0dac19ad30a846f7526a5cdd751d32d3d1f9f0806dc6f5338fa5407efe03fdc001e627fccf10e66e3e801aa4fb82977df5efc8a6e76c4dbe37e149ff5e28f44a21445bb1e10e64e651d2a1c0848bd0fe5664282215454ee67e6195b19a6b9a9db2b32cd25ba9c8cb1680e080642414245b501036b030000e0bb221000000000045c46377b5cbd42e29a90f6c196d2ebf605a0bf9868393564a166912e701a79a1cd1b617db6aa1565ea4923d78d482939de9fd2a420a35f5eaf2b056e882705817604e2f5b0307c1683be924b919f83c0f66ed0343235d8cac4f66765afb306054241424501013078019bee75c45f24f73f8f1d350177ce679cede27174524d8eb140389aeb2e2a031d6ded8f210593b06dd8a7d2a38cf58030ddf6ff2df9d309fece297a4b8f140ae2911a82289bb225c00065ce37ca52f60fc1e3a77285df1ff0b588d83ef76ae3e8015e9e882e23a6e4cc1ae959bc907033a63608efbffd36b9652108211764ef76584d1910b90f94346c5a6dc64fc43703eda1151fd79e42aeaee086a1e2419ee0a8080642414245b5010344030000e1bb2210000000005ed02c9d62214121b8c1ad4a2602453fc22d6f041e5af572f6ff02383c726f1320b8615a5b3e2d3eb2df2e0c01e7dcc26bb4e9e8f39503ea95ebae022b0ef90a2269cf18737ded614ed671ebcb9834fa558227a0ecf716259547f6938931410e05424142450101d2d798e503b607d6663bf12ea16f596affe4b431ba6e3c816cff8749798c6138202d37721fb2ff3be940d343630e512d1863191f08d98b3d6bbc4afa96f0ba8488471f0c3ff23d0c3c3a616ec9f1869aefa46bf750503719b12673bb8a5c97256ee3e8013523ef78f74160ef585946b15d5e546417fd95709502619d63ac18dc7af10c98085d06d8f3455f1cf5fc36192fa79ce144885a38eb93dd6d0f2e13f1a32832dd080642414245b5010327030000e2bb221000000000d8bd2c181577b410114fffb27d9cc15b5a141b636f748071c99a6d8ceded314fa9bc395fccba9f8a2647e5768ed9218dd668d27f82cef27d0db4b461a7933d05854a90e6abdf39293b0c20648ee32191a50a1b3c6aed895990eaee899c19ac080542414245010142e8a4d403e467e69505fec7b8728d7c7dd76d7130530db30569bbb38ee8b27502cb57b6ba73a6fa3ca5a648c24a4cd5d8ba24dc853be1fc00ec55acf0363b8b5512f5de848026897420ac2c4b898d01b3cdd7e13c09b73f4c910e30bcdf4c1372e3e801d411cf545b1ee544f443f8c609cf5d9474bb5afa92fc699682018c3ec3e59f0bcf458cc18327ac4f62e63c58ded61d01eb9f5ff99437953abbde4ec262f453aa080642414245b5010175020000e3bb221000000000486b6d134b66bc2618f4889fbf212234094ec50431374b2aad2a53f8d3f305257e4e18270963d8c9a4077b99fd789e403a3ed9898e7ec3f01bdd6fc1c747370259fa6848d419f3bf42ba7d5ade5de4845ab6d9062a7b0e8935643d88f022550005424142450101a8850e192d82cb9aa8f3e8420c9c40effc55c1f50154caf9bf650269ff22dd12447674dc81320ef520868e3d4127f646de7c3f815866eb5efab8273951600a8f0be287b19e518da8d756fad2bcd5f0c4d97ab683d6f116016ec6658e29df7e2576e3e801ab1277cf5fa95ca2457234ce880bea6624e404eaafe972f372a2089dcac7f359f54f39dc96bfeecb7b902c7bbcd6ffceed2399acb6b10e38db23f869c5d9ae5d080642414245b50101c5020000e4bb221000000000f8a5b699245cb4b7ce6a4609b8d46d5606c0e543c6dea181a7ee7695de4249466ee4efa4fb80bb696b15286123ca3a2bee1a0d6ab451c468729e6a481d1cec02cdd8d2194e0de773e4a503f2a8d0b607a8726ea407eb816ac2ff7b016a5e0a0205424142450101c801ac1bb9fd0c8acef4ba44b410fcf19f024910a6ac5f7de41a7e8d7971ec693304bfd525ebcabb449a145aaa4d9a27abadfc8c7adfaabd65b712fec145788c4d9c8a2dd6432c4756bd04096bd1eeb830de29271889a180a95dde14728427527ae3e80167fcb1fcae156dd91fb8a291146c4fea4d9c00f158c4ce09519aa85dd0f79d5b5dce926dc6dc9c517c7f32519b597e8897ba3fbc22bde290dea60553135c3b8f080642414245b501034c000000e5bb2210000000003637d0d0e158190a14769c47b28043275a5ecc981ce0f6d6aa24713c31e06d327318d35729961511cf8713f924871e78f86059b60d62cc4b6790b0b34b85ea0b3079b1960620feaeb4ece94b411a0294032896d2921b5408ffda513d8cf5980f0542414245010188b2b66e9666bffd697cc9820430fc0edcdb90b09292d70defe28706ee81e87e4b7286a83d7db13edae6dd5ec8dfdfa95005d0865a62a734eaa5dfcd6988a8825add46e9c7a93248384e5431e75487fa00626068e32f5709a8c1d5b37cc215467ee3e801ce207e821d9657f2fb7db306d76ee07b07eb52e6d066df8158d2c60e0d0fd8bc82370579e9967ae5e1210038a4c1b68ef11b8e2a7077b45d29db0013a4b1ba28080642414245b5010131010000e6bb2210000000004cafd606d70886482cd6e6bbce706ae617a2f47d5c2467881460e0fca9480a7154fbd13fbbc23405edb116de168b53c6d8ed17cc45585677d806f763de3a840ad5b871e16d08756d0778539e47b6c4920435620251efaed5925a4d91e96c7b0905424142450101d8d1cf9ea1854e6718bfff6fb99d32da935200f5c8dd67ff39aec201fa57c262bef9d7927b2d76f6ec48c64be11fde6fcbce97f3567fcc7d01927c25df586084b0c16990be2fcd05d50c39f455af808c650dbd9d949e0aefaf73c8d41411a32082e3e8017adb899c935422591b864cb12c4bae56617f74edd36a4ddd6fd13e024468ebb921aa7ce3861bd798c527b876bdb39a6bcba662a8d84dda19777516663f4979ae080642414245b5010326030000e7bb221000000000464729bb84d85c182ffccb1d05c43e268a84bc9592ab3a893a7174d80550e209fd2c0639df4e03ce311a14a2d8d24e513be8d10532603a34d157461d138dfd03ef65e2edaf14961a1c73ee83c15b5b3eb71d65699005f1151e6e39caa3755408054241424501016697d82c13ed8fd440e5dbd3107e4c9eebc27e7ea79a7e3b47660222441b1d7e0a9afad374cbabb8766f3eacc8077b82be54ceeeb10961c972f2b3a2e4b26784c6a9350c315fd7e281d1673c2fd207984f60519410dbf5e82e041646bcb09cd286e3e8017858ae0bf8c53570c3961a9b5a8e800f4fe36822ba36afe1c778b719093d1bf47268d40054d87905f6be8a43fb1ed5931b72f6ff8a9908d75cfb26b5c3c59126080642414245b501037f020000e8bb221000000000a86a9916f103994fb04ec56872e0ed1d3f18e81e982dcc232ecbf7d0a401f572a961985eaa3db1bd4d491c0b3898122608f3e40989f0d1a3c24460190d3e6a0bdc15919589f481a4ce1d4b4aeb02c13da44d01de406dd1eb70285a24c80f0f050542414245010108b90f24cdf65d68719c31a32c014bb8efc5cbe69d601480afb566db0c9fee6c61b65725760866fe45193992cf39fb132be196f972bf773d64b4170cd9e1b28503097f655245098d1f08e47eac2775481e5b7ce701524e8e4f4007cb72cc40838ae3e8016fb05f6716eedcb68ec0bd8f7c03242b837b2af57ea303772c1aeb1d43171d8879be65930f0304eb2c5a33991e30681bb4b5de22e910e5e2940aafd17d15d360080642414245b50101ae010000e9bb221000000000a2166f1880033a5810ab3e37828afe163e5d91e2cddfd361419c39ee60a2043cea62be70b3511c6764302c2cd903fc2ecea25391e0f6142a37f2a356fc104d03aa4418773b1f8149c31b39217f2220e010338915a3012fd11f787d2e90896402054241424501017ecdf63aaef805abbfb08cfb5673b331089e8f00aee0551bfbc9b78e4af9390806be30d10b9f1f615fb0eb44c9c4fd0695eb5c721912eaca8dbd3a9a7596f18b9bf203e80a26c24fcafe4848076f4ae3f7f766f1f7a45b5f18112f632ed94c148ee3e801529d433cee8327f9780dc9b0ef8ee20e0dacf3c627cf5af31ee85a9ceb92456ed5fabe8fb9c3a05e588cf3b973f148036869086ab133ae3fb4087f79488ceb3f080642414245b501039a000000eabb221000000000f63a6118565ca8e1f0ba1289afb64a611091de2310057bda695fbaae0e5cd85b3d138c0349a9e4b1dddc2e8d1b5639bf375fe8061b349931cf6c689c3263810503d47351e133054dd80c2443e6f8880b8fbd9a5df06875c631b47342eea1e30e054241424501015cf9695b9257cfc6de6f88545ac163e750e55c4f6d0e3d188e098e4b3a541942d1e55545ae6f937924d691b1a4acedebb8ecba9a0bdf8d6044f159678c8e0188c68f1af7feddedf981e1204a4e63526eaac2685729baa9702d07a914ac2f0a7892e3e801ef9dc66cbdfa317786b3d67e31b9e7fbe8cf2d933997bea38587cb98a996a243990607f0f88e43caff6de4a5b72fa1a6af1d9a4fdf6c67a0a0b17963e825eda0080642414245b501036e030000ebbb221000000000e47a1a5e0d0e83329d55f398073b8d0b9caec46273dc3640d4f3e9af9a19cc58170647b74d687beea0c2c81065872ef4ae6dd4f74ac71fc0480bd98540d7c20419759b72301ee813c6cb5ca0dbe7c5845c9cf9b31d0efee617e41df76ab4950a054241424501010c89691347d8e6404fe13391cc261573777bc707bf30b9a78220ced317ab47357444d468b831f87aa12e73b41f17dbdc83fe844adf824a02819817959bab538ad8efde90ebbcc07af5cd123bb00deb818fc4a2a8684a04dc0a5d8546a498096a96e3e80143bc575994d0b44f8af9552d5c80c36c66e7769ac4dcddf3e67ee17d4140749ff2775ca7a038378d920be41abce677e2a062d638bb398c3ef0fe834376f564b1080642414245b50103ae020000ecbb22100000000036558dd22714b93d24b54b7489dc1e9df92fda5e6291317f41480af4b5e180462d6c5b038d849487a60308e228af26ac5ed5403430911b7c6e7a39aa6b762e0d6195a7c7e9721d4764083b1cb0e17202e7e08802ee8b6b5e01dc18574cc8ce0c0542414245010142e3ae727397c329dcc85668c1bf5d7e4df240aa373332b4dfc31086ba02fd750d409cd21d21f51395f30acc7bab9822fb807911dac489f6cab3b6cc989ef68178f992ee92d45a2af997f8f1d7a269376bd8be5eca57417436bfc0570893fb589ae3e801ba38c920a2727628568becc105216ec06ba9295f7dc686a1f50fcf04615d2c00f008c56ad8a43e0b4b929c7df621d38cd31dfbca9a65dd83085d28b6b34a870c080642414245b5010179010000edbb221000000000ccfa4993ca3ac9d3ff6b64b564e6a9685333e61b429d1fec400d54c2653ef176dcb1796fda726285f36757f769949c99afff5797dd2dd31f80d18d928c91fe0f2135cab8f7a6bffbbe082fdfbffe9ae5cce3220ee8571d77220066130efb4703054241424501017a4d82b6ec1db87ae2980343dcbf01ea7d69d168f7f9be284f3ccfead30d065a174b526305b8c9a5ff2db7656b35d192f5b5351ad54a305a667e95f426f2718d6e0776a6454c9e6398432102f8446386f3eeec77ec4d96cfc0897846b72340c29ee3e8015db5d8dd51c9fec76f1eb0a7b433dba0b5147e2c35302b69fe209a640f384cc67bc2c0c1da8fc22cfb83d474a40afd69698e3edae1fba7d8e28ed5f0dd265213080642414245b50103c5010000eebb22100000000096fec9d706a4a142fa34b43d1b74a0450c43222b0a7c3142e4d74e7d2c4792240d7a9de81f90b01c8e0a8c9622314f17578844ea0f1f1806037401cc38714d0efcba08153fbdb19213ac4da6d16d984c675cbd318f0e18e65fdd579b79d5ee09054241424501012cb6c12aded18774affd0e9f3a991fdebaddec17f525b4640a56b04574bf1c1991968d98a2a88f21f8fc93f4d977289f53b38ed6af1c854cfe6defba644fa5871c7d66a507607e3bcdf8b2ae6ccb490f129f4e5e7f6ab4a7b2f226a668593a4ca2e3e801da4a70d2c54282477ab73062edc9197b6cff854354faf13cb492d2ebbf0b594986c53661642bdbf04b566a1a8bbb6371cdb18a46d302d8f7f7799bd3bc0fade0080642414245b50103b3020000efbb221000000000e673ead4194a4c88b71b661b8a0233f679cf4db3045d836c76bccab46a59ed34ff00304ce379ff70b4710b8af30f1d6b72d632c87047fc9ca7a1d40e591e9f00329de49917e4493f8aef44ab03748a609d79b74c98e0257b14cf381363da180b0542414245010168a0495c14ac55172ad5ce2ba7cdb54415bb74f113a9ccc991ac3668c444341fc73c69fd6203303b4684dac3e3cd1815f59c5f1d034a4b771098984dbf8ae58ee7b36d06d5782c9e8bb5012efc2c052443239f3c7a03cdc89e91ffabc9761ac9a6e3e8014a4cfbfddec571e942e9e25790fe126707b770568d4241a808c07ea54dd84f6a5f1b43419eb6760ccd6907c3991248a5e883d4a33fd1f3a23f08d42a796040ef080642414245b50103b7010000f0bb22100000000050728dd48775bf2d387c56e70413aeb8540e24e4f9913ade5fd9601857de45412cc14078500920902b4af6dd0d6b3f95ffbe2754a7c04727da31d765b69b130c0162613e0ca00e48f13e87338ea65d21192586dfdfeb5689d033cc5a9d5d3e0c05424142450101b2b99cfd7a8aa8a74fe259fa42a61f8a143937c2f69cb5054b570595ede2983e7c937c7d537f7a25654842da4646dd84bc8ff35079017b0e3c8eb4af5423b087a18886a40087448164b4b59b99b42ee8d2f613d893d188cef088feae80424d3aaae3e80177d1f80288d7444e645ea3613ffde2ff8934dde74df516414f17ed7a3fde556c04c4c8f130478c7c27bd619b2b55063a2b5e259c90e06357f4803e43e76c33e4080642414245b501037e020000f1bb221000000000eab2e2c1a204c7b0daeb8a33e443bcdb57c47c1b328dce258122090cd05f976951ecd1c9b387f29fb449bdc4e9ae237740339ebab4706aee4b82588992ed810d21b67afe6064568096cefdd616f2b2baa2ba4b8e2903e50d707b1b7601568c020542414245010188c28e3e947737079ebcadd43460e32522734a8f186b665e297ae103250d5c65a14409cc987e6b1528aba925cc98f3ccd6733bfd935aa23843da2a93f610158033149db5b09ed5c38e39bcbe557c6cd1445314d1359a571631d0e475ac6dab4baee3e801f22064baedea595ab2359cd5d0d2e53376d3f4642673b4ca49b9428d61e15d0008fcf9a5b29cfec4d6af78dd1cee9d3ce05733dc4701993b0a2ce512cce81441080642414245b501038a000000f2bb2210000000004206257fc715637e3a14fbce7df98d166920323a36a1db5a83f0ed316403aa57bed3928fd7fe46e8a3014aece4da3ab86b7a0b57a8b4b8aad392f7d58a4aee0107e0a7d136c5234455153deae87beb7dc96611fac215abc5f472ed52a4e7080c05424142450101320e945e8c18a1040d88b061d1e5c3203e6c3522606465ebfaa9365f40e7944c8d996342a75f24cab9fc7a3416eee862def239c45a268fe78de825066271cd847b0e2fb248e63480c8e8882063f63dee7694c9519527d7a20c661deea5bbc3f2b2e3e801133bc78676379dde00f25ea3128a7570d982f5ff0d445ff118bc1431baa7719b9803fa4059db0b424f158aeeb196bd96039eef3749060968d472d924bc49f84e080642414245b50103d9010000f3bb221000000000f20f97e94fff542e250d5cc589590957fa54bb2b33f7564a8bda23f5c5c8783dfcf83e770b44455f55d5a79bbcf70bb6fd53b41076f75679f72aee4de2ef820629fd7eac02d9e307bfb682e7a634602aab2cf3bf0e8fc3c2e2bd432be374950305424142450101906a7bd0d1cfb80c8c11019e3eb252c44c1dcf5b66f947f32c1e34fdc3b7ab6003e343ce17c660f135359e4e9aec56a9ebfe9526f487ca918f12c539b8fdf88f39029b17cb96feaca04c2ad488333298227f491b6f0d636fa6eef2529d9885b0b6e3e801fbaaaf4449207b7a949fb62ee4bf22da2354ea056e6e589046f24484da06eb53ed69a92e2250c9441a27318533e362dd9d21c853558bb59d400df0a15eab91c5080642414245b501030f010000f4bb221000000000707c5646fd36a7bf939da47b35481fe64efc1efef9dfe299e71176740c992566e16e1d846c6df00b18116bc513819cad88ad7c2a19167c6d0fd398199ef00a0344b4c488770f544649ad6c262e584d11ae0815bc5ea730f97fb7aeaa057f150e05424142450101be51060170b9de74662a826e595e171dcd5adc02b8eca8a0e0be84c2fc629e41487605058aadc8935c42e00b6ced867198ec7529c76137f8900cb5ef08efba83f7fa277e40c966550adc98f91147d3bc6d1e3a31806514f72c117034b8522062bae3e80153c47cee6c1427115182ce351bbe3a83d2707ceb18def7498084a6f11b66ef8bf96369f3ea5ac5e76e7b5aadf3af88b5b58ada289a7a8d416eb3d6dd800c9d0b080642414245b5010300020000f5bb221000000000fadb00babf8d6fa20a64a52911cd03102b3c92d4e85783c44ea782064ac3de5fc7e724a577034708d31d605bdb2b270120e9cf3520686435a37ca1c727a0a10a804296a55824f3f650bb74ae4720d63c9c10d32a33bae3a72067b1d821ac020b05424142450101a875ddbe0b008719b706ebcc91a9ea857898037fc6a21482483e3e7add223e2633f4bbed308cf8e1cecb8c48d0b4c1d64dd0c2e6059fec2ddf0b1e3adb26f785392e30c0f9b5a8c7d1fe85c453c29654d62267874f02c812ec82f4ce9b78f360bee3e801b554e0da641ca7d3ef37d56b0e72a2657b38b780debc5b47ed37da6c17a1ce05b4edac2bd908b7f5c3ef44bc7528c1f9c9deb5efea8437bcce66b76f43621410080642414245b501036e000000f6bb22100000000056b6ea6a5f02c5a69fcc478f5c84874532e194d0695c55baa5aeca466dcf8802a8ab86598a61044522f9c92d4babfc33d7c11f3075807a609193140c7988ca072af27be9fd33a049538be60eebf95396ef8f4fe0e27b56b3b0cf487eb2b1c803054241424501015a89bb980a55f185fb0925d43da49d4c31cf20019e30f19ffade760faf90504035078d7c09101b022eaace9804a9b6f8e8046db926c685204d43e6e94bdc408fa3ccf99d7e2183ca33b1b4815c53c87ccdfa8d6ba0f403bda3822f3f5c038c40c2e3e80197d514da7c676bbb1055e5dcb69c072bb4bd3a336bc4b1049272446130cc52c32766d6e72eef149715526e2d3f3364ac01b77a828dd2187d55522fe761558a56080642414245b50103f6020000f7bb221000000000c6097bcaa8ec44bc21e13c2d11520d14070b2d30d523861f1faded1017562c4525128c449b1fba57043777cb824bf49d887001f5614659e4972ad08f284a4e08c0dd54d59b2ccb73aac8a5938f003fdd59fa0513e78d60a920235285fe72d105054241424501016656707268f7593391982ea50ee83ba2c2dcfd5edbb66ab5d2cd146564ed3954f1e484af4b8e0ca663f0aece688b062233fcbd1490b6d8a5578229d0ca78048e9d220a879e5bd2cf8bcbffb6bdd0e1101e18bf7c68e88f078949f3d3ac659ffdc6e3e801f07a5cf2bf5698ac84e67f3a5e413b2c62b92491e00eeef6c94d124d25859dbf32479f5a6bb196d2547caf19f4c346cc99c7c0328d232952b70f91453ace4e7b080642414245b501030a000000f8bb22100000000086a1f768706f4218b2c2e8e334294c1f85b3ed4cd1e25a59882a50c01747682e1ee9f390782daa4f948d44a517de63d5e25ea80cfe43fc573fe3a724c6e87f045ff2b2d039e029e03ddf5b535619f717dfb063d7dfc7b377a2714e22d782500905424142450101cee0d967098dbea2a803a0381c4684945b1a2c84e7f2212b63d3e3bd3654a41f9f3a0995b41f9f669b4b4b5e629c964408f91dc0fc03deb7e3b0943b8af1f88087ab4aaa61b74756c61f1b8aab506ac4450f126cd6235d382b1956faebc885cecae3e8010eddcffbd6a5ddc39cc611ab3057d931a1356b991540c522fca53fb464a2dcf6dbe14c28c120e46ad3b889375ca4344b3bb2247d6bfdb8f511bca2d5a4b5b1ed080642414245b50103ea010000f9bb22100000000060d31c1505cb74b7eba1d2761e87073df0aa0c5839d6aec347261d565bc175040f016b7c1ede10a75d20a0d82e86b80196c9f19d070faa0b95595b9a7244f00f0067a646d5ee1b7d7981900a1bb76b5f167069e8c389ce804df63c1f59f7e70d05424142450101be7574503ad9c2470d7a91d42e9bd557ebdaff8032d08ed4d55b4e6f6140b860aae96569a4e53bd4c13f417d188d44a07633a1323d0bf726844a5d63fd94d6887c0298fe04d97231545c74e5ab6eb1268a6df3e1c7c61f38d2612e188e0dffc1cee3e801196299c5945ba7c89e0a1e1bca4f2a955f5eb36644bfc4f07e7a6eebda41ca2af2f6475e4d2af97d221d74266f462ab888fd42050aaeb4ddd2888722ac23051c080642414245b50103b9000000fabb2210000000002cbc745c9170c670d378024c1c2c4b09561b341315eb37c1b96b7ad2bf43167582f6867af737bcca0719fc8566ad16356645c5a9b22ceb019e6dbd127ccac00372b3c89d024715be10afe30ba09c6262630beb85f13d8068dc9313cc5fc6c40f05424142450101c8cca842f834dcda3092d780ea7d87c62c9b68af516c69fba936a608ea0f5956569bf3e6467a5982e8366b1c0744c395a080833e29d7e6532215013dc6119c89453502706d781925fd7301cdb7e5996c5e8889e3298213b22567489552cf3e42d2e3e801ed9ae46509aa5156ffb4d0170d96718aec467ac10792dfe5422056a2d951af81603245860998ca07ede7410cdaf69a1ae09dcfc65872a2e3859134f52d31dc63080642414245b501039c010000fbbb221000000000ee4bcdff1faa55018d2835d3fc86173b31b4335f57fa746412e3938957651f504713adb334179d268f132987b19d927b5fcadd7259a0c35a6d18df69c441110a37402b65bb57637b6ccb3227e665c198dfbd9818358f8d1988c0fe2ba1ba92080542414245010190befa253875589151fbe4f758e132ebfc7d1f222cbabba3b579ee4cdfd9ae4a8a226df64096a13f4940a0ca3279a6537a245078f238240538ba21f5273ce58206b61facb489eb2c07f6c50d97ed9e4fa417c31a590d1d0b355b2d3a1f005f86d6e3e801c6ce9c723f3c23367bd752b12d5611f44a51abf31692be4656d89a28ba0bcd0cf83a7d40af880391b75dac482cd2c0ff20a3d442fa4ba0d45be46eb89e000b57080642414245b5010389010000fcbb22100000000046962f51f851cd678ded1df1cd8bc2d4237d23e2694f0a9aac6aba30e73aff2ddbc3bece2bb4a42803197ee79e339fa7d39dcfcdadfe63ebc0f1fdce7458150ca0887ca25887e149e26c21f000a0bfe147af6e3a0c1063592a8ee6e517e42c0405424142450101b4a1112ae4a07a90c56300ec5800356f30ae4b25af71c47c67d99d616874d54add0903e60bcb7f15ab4426bdf6241717055f6833f70f9cab3de7e5a44fe3478d3a9d4551cec0638ab4dcaab93e71450ce8efee5da434a1586d3e3c8644a0f1d8dae3e801f5107f581d03f134c0695ce24bb69eb33976d25133709d49265367d70ac3e40510947fb280c1e101714bbaf210c98659bd93469f809e49b95b8b56166f1db8c5080642414245b50103e3010000fdbb2210000000003e5e8bb045acc7e5d8a0759529ade86d256a5b883206779e392ab46de9e25336709aa28dfefb13c46ded39d825ac9e9db2378af4e6314b678abc9fe1447d220ed9d149906d2e36f9eeebdb728aba32bfec8be4eb2a61a83f8aeace45464efa0705424142450101187f09f17adeec7129505354a4b7e862302823de7a4c030c2e570b09750555553c4ed1050e39bf4d415c99c5dd071f62937291787841bd00f90808afd3fb0780016b5827749b7c99c71bddf9cd400dd1bae49ac797010a2a3436ed168940439edee3e8016be3e95ca587943c4624bde56e7ac1a9e52d2f9d5d0ae49b77139689093d1f742782fc158aec74e65ffcfc87a8f58f48c10a072250ba4f13f0139e825d1f3a82080642414245b5010337020000febb22100000000068af60a2a9118ad26d4d4b22f064ca78f887cc53e8d192c1946522ed8ceddc55364041e928afc8215bcd380a883d9d2fec7b2ca42848b91e8e931731843143050a4cab5bac2bc7fcee27f915ac681dd9c619716b7b1e0e99f25c08392412900c05424142450101460315f06da423c43e1c1af334923a706872282e72f891dcaf799ef492bfa5118ef52db67668a525896878cc83f02fa6b8f9afc19af8fcdd5d2f23419ab7a18531a5ae210aaa64215b199e64dcd8531b89939e332ff4636f4fc186f58cfcb142e2e3e80151adbde87159bb83a3b45ded72303c04b68d66a5e1fdaf7969d62425494cde521bf49bd8d603257df669ea5d108135431cd8d8520933801175f6d5f814c7324a080642414245b501012e010000ffbb221000000000d6b29fc2b134aead5be75233a7cf32bfe2298339e0b035bf7f1a1553fc20514b94964a36ba64034b528c44986656c5a6c60dd351ee540b645ce3c5c9a0a5df007126c1598b54d62af7effde8a15d61fc9c1e2c84de58e2c45c9caca65ab08906054241424501011a4c84de4ea8ed3a9c783536f130866597b70ca044a0f600eee6c96e689c3411b034b3145b77e7fa16b1021c654a171732e3dd42014e51b09320172dc850f88a2eb96b66474a929e1b936f9e49acca08ca4c98192a275d79aa46f9e52b1b0843e6e3e801b2d52ce254434ae7c35a3247ae3a1194f1c7bb48ee60b44254f39d855663c06118721115294db3abac223b2887ddc84adf2599e072a147d031a25dd3ff1d3fc6080642414245b501018303000000bc2210000000006069bc5f1d54c5ad1ad84257e9f878709a675b107b2abc180231fa228291b76027708145143c0b17f94bbbfcea31cb1ca85cdb6290feff3d33ae20a6d518240f6f9be16befd6f94bd082ccde0a9fd9502214a0db9d59f94f47bbd63eecbdad0b0542414245010132d0135228abf6152ecee8e2582cf61069cc49035c5365fe90eef46da5d460343d4391fbb6324e56f082fc410a5811e06d1bcbb362b814d2ea6702fdd452c88110bb967a1b935de9c385ed8561c67ad7e420c1576e3245802924d6a7330bce34eae3e80174e2fc0e24fca9001ad186f3de3f53aaa789bb0b0ad8281d7c8768d861cb4b1319af128716bda2b3ccd9968cc573bcaee093a5224ab0330c4fdc3b6a930c64e4080642414245b501030e01000001bc22100000000088b01daefe2eed603fe6e57d09eb3049ccb8ad19e1bcc06900126526c1727a1178c800940c3d09d9105538c13b285910a0753f216988c955f573ad644b0afb0c996a697f7cbaac6133cf529e8ca9ec9396f1c9d0b6b3718e8616c3ce0f45660905424142450101ee0febb55e38860405edcdaf312673883cedff37b03d61e4d47b67531080bb7eba8dd942f45ed67975f0393e6bff550a818eb8179c1d406408efa668ae5c8e8e26c2016f8e5002769a69f3ef5a4ccdfb4bb7d1bb4b6d2efd11fa69a11af4dbfceee3e80109801ccc9bb9dcc5fb3abe8a1960fb0f351edf7a7bc845b94f20c86ce4eceedadc0e8eaadf709779dba06d5d62d054d0c8e6c0cb26010ca9b44f12d28aa9fb96080642414245b501032700000002bc221000000000f2a6d79e79298e3cb3b9572cd4b48d0122f6643a3568de519f585340c4343a17b87f2d928a3b05afb02d686d38614a77db77bc9094f769b3f8acac7611988606b0a6c91e20345d5124020d2d3498632b57e671dc99f9fb31f741280c6294f80c05424142450101b25067b8b8fd089551ede28f9ad2e666f027d8c883f4e68a4fc9fd908f8a8802d6477c690afc8899c140160da6e6051a3e8f05d398da42dffe51742273a84a8bdd08f47336c8283efaec4acb479353d45ebdfeb40e503ca24ea996afd7c2ad0bf2e3e80129f79d915be7d6aaf638d225a09af3757fb1b76c04d940680d9c9e57840c6e294a46faf347b382dcd55a959a6b5895387fa84c0a9162d69181261f96347655e6080642414245b501033a02000003bc221000000000a49524ceb0e14a6bc9e6e63b3491bcf49b9784ecdb4e0ff17bb682ff28026666f0f6e5d99e9cdc53314b3045baa84544143ac87107ff4975e2d8bef40fbf340fd975c8d80a8ab392a51c13c4bc56950ed1aa6e5e0767ed4764a9343f1c05390005424142450101bed1e000b7f5c9229bf7590d1e4d3c848c88f3487fccb22eea30b475abc51068ded5edbafa2ce7d057bf1be907948217cd16a706404d5f076143fe0938782380bd873c64e3bd6b230fdd30954a88b8efad585238acd153c7d2e3a94201e7f92df6e3e801b4bc889cef541ee4bc19e3d35ea949df90d509fea235526e69666e5c5a7abf59acfe7f9757502e4e8f8d0a48ed9f1ab3c6e5018c04dd87d9ba6d90c7f19ca0bd080642414245b501032402000004bc221000000000b24530c5f11f326ffff7be6105f2859d4c9b9287d3939ee5964e7b132c6f0532d1e38a84014734b217ef81db4253b3cfb2f2512db14112f88c82eefad3ade80d744bcf143916ecb26b513b1f808b136e676bb4e9cca5218d06fecee2594adc0505424142450101b2efe3c2a6dbe444c702be007406965ce2cc670806d61224b4806a4d400b87376de210301e5d390fd2358c30fd196a1435fa900823e2ae000be4c338c13a138acabb6912a802c85a97b4a8dbc6adfab3268843ace1039fe09d1cb142ae0c9b02fae3e8013d7adfd78149d260760b4a098581485d88550a84f0c1534f5b12a141ffed7338d139309b4468d7602cc20ec82af5d441e8c9e0263d525c9c9ea8d35e4f38a5c3080642414245b50103d101000005bc221000000000ea7a8cde359818b481784c9230d0ffb3ee136824ba2708297ed747cc9da3e01d01ed7998f9654d6ffa7ed3d0e6c4d27b53bf37008c5a75010e008ab3f90ddd04277f5577128b1f27a4091328251d5ce1ec4a25bd7edbb6b4dbe14c8332c8f305054241424501017cd606ac72a028eaf3ddb32e0b4054f47c53a6a8d0ced0cc5eca02bb907dd12e856de656f40a6164ca32a711ffe1bcfb67d2f1e1c3874cd0e3a9d44214f3238c7513c63e401b90332ca1749ca7a44158c4b0ee482d0991205d0b675ebcf0dca8fee3e8013e49fc5eae10a16223523db6810121a001b114689e79a7b0169bfedfc2b8b5d8796b2e42152a680f7857549d300f2a24c0d6cf85a610ea45925f94df7cb9965e080642414245b501030302000006bc221000000000c2c16afd87c5be1a3ad9bddb5df2ad7171b02e77fc357962c2fe61ccc683e61e59b8c024737dc19e23211a5976942ca145cc5d962194cb501aa58ed1859b6401c09dc486439fb058c689bcc0eb856f9a2466a6cd6a024c7ce821a87191b2950b054241424501015225b8c7f503dc69524c909fcb9cc8e537c9c1f79c782a6c498717ec55559917c295ba9a7d070d7d3f03ccba95dc14c003ef0a3795833c451abd1c8d639b7d8afff5fe0e6cd3f75769c67237c7c06f6d277589a9d2dc21b069562769ba10781002e4e801c31b9e1fce382ca68658795ad4fb2e51f5c91325a4e6ca1c646499b85620482196be8deb3d703af0d63d683f6ced31bafddc55ec93c63cae615ed0808a5f823b080642414245b50101d502000007bc2210000000002a50e0be5c5fb2a3ec3879230d944804694876a9e9433b2882bc9bc64c4d4d724723077f305759ff903cad08bdce5f89a46b1285cc0b04b8d0f673dd111be90d6b6bb7fa9536818e30914b2b0a32f0a47c7ee26e3c71117db546a9ac3d007f090542414245010190b0326070f737ae5f083a6449fa4a147b943ad351d6e38d95c600c72aed5a7c78767f5ed78a9a8de550a187c14d1506497331d125aee3b6c9d63362e2039e8c78abbcf3a7285e10b7f77b55862954266d6b5c4bbadf564875afd33ae59424ca06e4e801024f4d8e8a90387d0b7758805d299de9fe3f484db36c9f6c13c3ab798020e88397eedc3128d529a4bd92b48e2e3120dc443317f94a6138295e3fab17f482257d080642414245b50103ea02000008bc221000000000229707c0a28c357e6e9b753363ea1b9d83e662bd1c26baee461f6a47bc6de81ff920bf5502698cc1821a8b72bcf2218950c75b94a732a4353e5eabe8d5159a052ccb6900e31aee543cf9a19ba4009bec7676a9887e69bc21acb150991a0c9f0505424142450101d859229defd021b7f730bff1ca22c137464b8e7df244e8eb947fd1698ff1da7f3a82470f62fec779d74371fa92b5c4c074e9a7485e2ca1e18374bfbb7397318aa488293641ce1377a991a7e78fb0ec5e7e83c9028ae850eeeb121bba902362250ae4e8016adb3c32c13687d6b5099bd86d2eab9c56c4f1f1e52653c84bb06046555d44bf94652ace05f88eb94c58cb4f1fc5a117b9e597df9d7da44fc343742bea9ba912080642414245b501034501000009bc221000000000a0f2e466dde4adf34559702db04a3b2f721060eaf3abaa96ef164451f8333903a72c6b960a8548ae5b016f058a830725ae9c962c7d54c481832ae73aaec1b50f658c8c4b2025f185319b1ece304cd4620c5a279bbaf1d0b6069d9d4059bbdd0a0542414245010168bca9a1f5d48fda637ffc88bb9cd9c8cb8ed58ca5455464a4ff9cb4d4709b05e721b17ce0a60c631607afb4e0dd4c5aa02739e572cd31811600478c2fae158266858a5aba71243257c1ad2c4d6778af1ed4c27367b8ab02a218866af74820060ee4e801b21bfbec560b47c1a80f8db961ff13791ea48f72082c1ad094802ea52d6ac9c3a96ad566904bac21b26e5e019261b1b833b3048067b19ef4f94ea5d60600632b080642414245b50103460100000abc221000000000eacee8753f4b659fc8a2287c5d0b0a8aa001fd22e651dbf42da57acc8be04e293de668f143caa7c4cbfc127556f8424d9a49cfcd72670604c38d9e874fd92a0d5d1c5ed675aa9f6d88c10bb98ac61c7d35f0a27d718fa4436cdd5b0a804bb1070542414245010124216bbafbbfcf226586aa6f3ce0cf27ba1f308984fea414c2ef919554b1b82ecbe48cace8aa7d8519137e29e7fb432282cfb7020f4057427e0c5b44b65388801eb42496f6cc9f90e8a0a6cef361182f7670b55f9341a936bb4abe9fa80f52be12e4e801f4c2b11d979f2f4fec2bfa5904ff29458656529b621acf80bf2e277e5f076b6646d91589be2dcae2c7bd31711032350c816d31716cff9572348feabe6e8e714b080642414245b50101990100000bbc221000000000e2a19023ac804420dfd5bfd4ba8f0db7b4a72f163626246cc0eb51359356e038ad044e38735cdf8edfb84bb5e1e78bc2b661eab0963adff24a15d21c86dc850b76f3ad5fefc6bee78e484ff889a9cb3c1147b8e679e89e1af71eb986d7a48405054241424501018c3a97eb4f0a9399c609c1edb8d0fe5703bc4a43cfe6bd375de6147908448f699a8efcb2e65e0a8f08d613f679e841e7d364dcf75a2692c0aec2d69dc7fbf8816c8e4d4404ee7e1d50266e10ad238058e46984f8432e0dd00ba1fbfc5373189416e4e801765ac8b79f778c5f84aa50909aa37d1a15448b5e489c32a23c861d318c5dd315484e2d73439dedc8e28988dcc35cd89e87195e88d134c5bdba4782b77c47d6cf080642414245b50101620300000cbc2210000000001843260fabe2135d252240705fecfbfcfa3b594fa769df0aee179754d22d08117898b1a7674848049242db3f675baf107f7f39bb73d25d46ff0cc03142b6f7049d815fd80dd08ba961484551d793d42e43725b988796acf68f3f637211469a0805424142450101145b1e40d7a93a62bedf572441b7534f195c66f702af275e68ec560b52c28c79a417bd934e105943fd5c94e5e91d941c93f335151d90fbfa9d850e7264ebc981815248bcf0ca7a4e0b1b09b10f88046f6673d8614a517f83026f0c851bc5e7d11ae4e801142954bed0ae164952a5349dc3e4568bb1d33cb0fcc44e94843fcbc343299a92528bdf496c903bceb8e0b6d36ca02ceac1a238b9d23b2b7fb14bacb37a810756080642414245b501030b0300000dbc221000000000a8b69b5c7cdd64774b4c62a6f945ef2fc203a1aa690ccb6024e0378dd3906f2399d1e443ff73cf783870b4ae7f3ecaa7d5d614087c9b9e49c8a2a6d631b93800cdd4f76b32a8ced1a996b7432d4ad23b02a2d4d5b225a9a0a5625e080496b8080542414245010166ccbfd185cb0428cf549b11e0ac241d2b662eccc6bfd1111e59b042fb55400d868f213c2b61f111ad260d0a1dd16a34f3f97f1c4ad3c1ad06adec69e40e538b7246ca599d02abea065c25a716f5798cb456faac1811f377ea110028893e46a21ee4e801015e573e9726938bf7f02a5aaf9b6be758789010fb08c2b9db11c3e1cd453d522d08ca462325c6fae9baf0e63045b1c5e357d36f69fe24738e813d70c83b3d7f080642414245b50101e60000000ebc2210000000000c329d76d5491e8a155c8ece5771535252fa795da9067869b42e9125d79fe36619b920b3be27bd464c74117c6ef7c6d161d651bf4fa0c5135d55b7139a57cb098c42480bd78a00a837d90e18ecb69e8683fa681fcfa34100332a31cf912ae90e05424142450101dc835160ffa7b1fb3afa5ee99bdd73366c22bcf8cf950ccee33f4f6cc5d4920b45fa86b757b88ac4a03fa31bf6dda23167391384866e43689dec02b687f678867fef48a30bfe98fd8e5cf561fb628c11821deea849c804eff056225b45397b1222e4e80152250311b2649cd6524aab691dd88c2fc9e6dc062e6ac37772bda60105ae6758324d1ae6fb458d45b3cfa136bdf7fb44ebe264264fadec8e578cc1c284671442080642414245b50103290300000fbc221000000000ea02416b044e9a9b70e4e41a16520ba14d3cb31310bc00f1253c4fa84a7c933e0afc9ceb6544f5550ff73dd4cc26f9202d4627feac9bb25cc2853a35d0dde607b04fd36e166791e21d21942350d284b4473e1b4ed7b50abb2028352ab380a80d054241424501013ccb8e64168f0d18637461377917a447fc3cacafe2d61fa2d3dcced242dcc43d587f985f7f752de51f862f66d54e18ab6cc8faf15e79d9e6e86c5bd79cecb8818bcef3128fa5662c3faee6aa6111cd24aab1eed84d4f2d96baba9be3e2dc692626e4e801a4b8518bfc5d9608e4169cdd62483a1f70ed6173335e57b0cff95fce1bee1c5f08948cd5e5d67256a250bf41e3227e5c9a70c8fc131a30bf3516749646ce2b81080642414245b50103a200000010bc2210000000004cc67c4611bd2fa067ce1afc69802f5d6a2a140e6cd7a46db50bb0eca82c773f57f28f9e45316700ffc09661a3e991391105f3b7e988cc284ba7d97860ea1801c6d2938bdbe95342863b20bf51d6d9db3266a327e0ac412d0059c9a94a1cc40405424142450101d864eaf31eae8fd2c215d118fba6375bea637556456d20785b5bad532f9ba05f33f2ef97dd14291186eef27b23cf0e5cc84e58e08c91f387c18f130418809084a0a7c211f288739883782d93ef24fe102b1e5b87c28f1ecc7c0fd8613893c8202ae4e801719e20d496ed167da7276537a10b85cdeda42cda068d05fd0e69a6d4d1b69e7ac65b9ef0b5d5957182d4273d741fd45e835fb59fef5af38b763fedfd73dcedce080642414245b501032e00000011bc221000000000d23fc3ba010ff3c62486641f8c8ca0a85285f5bb5f50f6025807ad2adc08190fa2b8c3ceb31878e0d14ba556cf9a16f3290b0e4fe7149457e82970577a3359062db2a193fad278a522fd0b4eb429968b1b4bd4f06dabfc8818d0ee584ce61109054241424501015ab15a0fea4afcdaa588c7a0a740fcf49ae86eb8fb2b701c3657d7272b98114484a5cbdc9abf0256cbfcd4c5cb91978c7d1f0a67e414a2b94397346f0594858abd37ad2ee7348baf79c3c9062e659c3a292d1cc251566ea209f529c538333caf2ee4e8012bc5df7e01aeab85eda5e7742b3dbf6705ab391e06ac522b2abb8e02aa50102b78c4c9fe63b9016e6b2a04248795fa06a0096fa994a007518b4d8c7131ca612e080642414245b501039701000012bc2210000000007e7d0715e7d208a36e1bb7ab10410a18b3a3638d2a3b8a6bcac8e5bfc32b542fde3e9399e9c090f43473c9e57616634c8e9824788e8abf430ee63fff2199490f324459366db6d439c27a4f04474c180c912a309ec125821eb6a6abf26f6cba08054241424501018c7ea81c91e8df3b470a71aa40002d389bbbbdd4ff404e8ea92d7d2769a9d820ab3cb0f4e5710783b805e5c6b43c2d462f0f5e8ef63b54dc53164daa042d178a3945851838a3a314f8876ff970ff84023a7cc442ef0d5856bdf0a0c411b1bf1f32e4e80133267c2c31e374d52bf4eedd8726281e0050dd23c967e8c26961476c3f5a1c92e7582a837dcda0658b408bee336dfdad636ef919ffe6f043a69e98ee0be75afa080642414245b501019a00000013bc2210000000007e9ea7de711e1ede3c5d3b07c72aee3402891dbd3a1af95eecea2f5e7b841256c9843d2561e8ec57e3108f3674d6fe0b844a195ff550b897e43c7f1d232e3c0a4ba7202265d7eedbcdb790d97e4382f0e53a6c8c19f209d30dcfb043d50e310005424142450101bc35555af4531205f7eee9d9f44cf40f6ae5430980a5b65d0ca51d4a3247f17ed1ec189caaefe2d4694110812bdd584916e617f57cfaea12902465bb683b08832a7a9640cee44c276bcf91f733b31df980094c9a2c2f8a4342add5a876ef827c36e4e8012f6c2f0cd0d3aa30c4b6a91941970062de13b1f1b542534c6708acdcf31c0f0c54a5c286c30ae18f8e29bb0d9c5ef6331dd75d6ff7977b4dfa9d7141e87ecc3d080642414245b501035e00000014bc221000000000e49729f02690e96487867ad189b7182e78e0333c9646f9722ea994d706e39941eacd686e191dfab752331bfe968da1996b2bd6de9a65288f2a05872ae2980c016a1b14b37a67bcf1fcdbd35f1b1cf546207012e139777590a7f9fca6b861c103054241424501017a97faaafe9ced7025137238888ff5271e83669e7a62dd82218e9d30f412bd16e87ff172c9b1cd17126c5585332e4cd71d97cee9af88ed40cc756b1568edaa86e88259ede23beda97019f34c3608d29db093eaaa0cb948679f987da66b516aa03ae4e801c3f5ead656c4de5d4788e95e81900d6a6ff16d2f2087360a6656c1aefe49f0eb8995857528649ef1b61eee3fa3f562176da927caa0185cfab349e978d6b451b7080642414245b501033302000015bc2210000000003a3c57aa9921b991fc2a73dd898ed4c3692e68f0dae34216b9a46a4aa861f11450ec4531377cd14798d2e0f31cb4b3db541099c04ea08b7e130c4b54edb4290b548ffed01ceac94c5e9c8d7b4103d12e3cc637821c3b0d29351befe4d678890e0542414245010192728f02d5c7da5abc72c8f2f320bd9e504c736bd82e134b6207520e13244860bf1f6ad24d4c44d28ca8fe9e65c4972bac2ac93cf7c7306ab069520ef7793a882409730478eca3f7d6ca72f9be8f1cf4bafcaed1719eee96052f000fdc898d073ee4e8014139b0c6364935e3363fe78ee31e2cc119b530b5250844ac3f4ddcb060958cde968e5409e67dbf6362524e3b850ede02cdf4b6851872527dd4037e1e63e6bc0f080642414245b50103ef00000016bc2210000000004e6d55c115e9f1585ced77e07164c521fa0057e4aa84462ced321acd518f4955fe7a7b585f8177ad8b3f8c633240279f248e25f6cd9fe11ed5c5e5261ebfae04189d094592bab7b078e279b34b1820d840e52e7a79c783f7af14285dbefaf40305424142450101d63cea9e8b575aa6717eba193e7bf4ba5e6bd0002b58150affcb2c1d1ede7f7bbf4ad6989d908e1595c4ca012470a6effb02c22567991af32508e69cb0a2d38bba0012c7e5daf47514c1dbbb9b79fa232490f658ca3debf4c237fe4b466b0dc742e4e8014f586ac8cab07f3b6c23f39ec260b8302b30f46feb6148073da6e4dc76dde1f1ad3223ce723236b86a65b44f0d92042e57685098777251af33dc613a44ee04df080642414245b501035703000017bc221000000000ba0ceaaad787a1a24bd899a76eeeb79e1ec6000e6b41fb67fbbe549402ed73558b10ffdeefed8e052d12d592d89b31c9413267ee5d29d515934784ec47288e08c5a72d0043ea375ec790f87ad272b3740125cdd5ae602589ab3176c306ea630e05424142450101ce0f3e54850453d1ffe5bd8d39920c83ea4d234271e99cee0893a46bdf35414d0b440ce9330ff406b930ca56dd6469c874a9c88e7a53514790d0abe95bc4188450da3b01af83a7fdd4098dc9b74cdfbc9c29b2832f5e9bb68a286659bac3fdc446e4e801fe5b64f55864d85cc06eef49c32c1e6311cec8b7c3940a7d01878279da8e7e74837eedf81704bcd55dcae7400e2eb712fde83d0e43022189468b23572eb3c8f3080642414245b50103eb02000018bc22100000000004ad509470fe0d26886ad52bc19333cbcd036dbc285fd00c7d30963e5f9e3317793f212a5b608b165e6260faa10d21e5195ce60bfb9fbe23aad3f780045ff90997f3853fd3c366557fe6d7d80da98b869cf3090ff47bc982fb735f1c6d37c10b0542414245010124b9baebe0126a009c8ab878155983b3c8c53d5fcdc3e2e32d971d3336d63400dc9914e0958311269a3db63c7841e2d377d41cd42056b29694b264ee4525ed8db7292df203c4384b807b25ed0ff6d06b258cc23b284b19c671cfa9b3e5f459784ae4e801252f587a12e496d0a013be88b7e3169dd64405b18e9aa60b883a7d238d6382ce8f75ac651db160b0fcc8f3bda48dd440ba23c2407a92bdbc034de2833aaba55b080642414245b501035400000019bc2210000000002aa07cafc0845e5999766e058cd2628614d7a39e04fb1eab64c3edb939d75f406afbf5e02f69a9983d7f492ae1029b368317862c73e9bc579efcf238aad73607c1e5d602e10aac345aef90e2cc322071532e52a2f306f3a35cd3657269dfa1020542414245010124feb70acfb8fbd3ba325da7a901579bfd85bebf02beaa1b91800ea17d37862b5dce5f3bb30b3e979e29e60ec613a5134e237d0fd63e906c6c8f97b56777d688c813b262e313b150ec46911dc0c84b4fe02ca5244177f6957d4043574411bc454ee4e801526c809aaa4788d45658758948e9393608e09e544ecce9c7c0964c83d85cf906156b4a64ca3909ade50ab07a076e07536ae0ece31d923b08f00419fa4ffb61a7080642414245b50101000000001abc2210000000008eb572e35f7da6ba7eb137782bb6242945f1205ce6a4f145e8e7d9a0d6f5094656388a025f49064dacfce3b86a9ad25e5cd0cd889de42a0519176760e21c060ba2837eda463df9c7fe0e93b1ed1d09d0fde8dafca5da8bad692cec9814321b0a05424142450101447ccca29278de910fe64b2b7e1647a9a42a7f130cc8a1c1677da04215a9d639e15e81e7bb66ec9045e9c5376dce4fe7e5dff5f5e69232d19a3c96d401d1a38ab4223e8b9511f82048fa1448eb0ac84249c2efcac02169e4e0fbbc498fab820e52e4e80175136a018e4280e6814a085f0f8b1a4a0a43f5364e12f8efe25664517669d23a925b1889e9ec66cebafe054d51fac694aa7e7c23bc47bbdae249ea56c1ad7613080642414245b50101c50100001bbc2210000000008c86a2673c1d87047754584aacd0765653ccb813f060b6932c48ad6ccf99364111574e1d4527b1b51fed1601a13864d218cce142c784684e8c46ecfd1a2a5306051ea500418ff56349c2417491195a19786b7f6e9a8b4f73ada8dd4f875c060b054241424501018873c68c3a038651cd1e63e7d541b61ec4a8b958ee790bed190d4c334353345df73de32767947ee039da7c7def43f0780b7d884f68fdaa211af8f20b12dc378269a5dca430b7e1cb988f8914d5f16d76a07927301b15d36b196284156b5e665f56e4e801433ed06ad899e10112e95493f8c3f5d6e5d684fbb820082908da4786782c3e1eec778e9a654390a40fb76fa4b12045d2e78aa7fbb4b86fcb94ee589e1a8d48c7080642414245b501036f0200001cbc221000000000e6c430719176d5b864687210009010192476b6566c00bac66d63b2ad8f93dc57b6f150498cf8e846b2a36f4646a8a3d30664c396dd0d0ab080a508d129f1c009c7daa60466962f25cffee5dccec76aab3a16e667bc2ac8883178a795b7222106054241424501018a66f855c1377c095ca93cac8886a2315aa9fde351a82c29fb4a7efe724ceb6e3cf69877cc52e2639fdf9a57336c64801beb6b8f243801edb433fbd4b5ac73875ca131c3c46feceb43a838128955e91785953ce0281ee4f92f8a184ea9c6fd395ae4e801f2074696cd812e266e84c7217cf0adc37c98eb21db60f329c04d06509bfcbea47105a996b969c262fe26a9530b069d05b57d5c00ec7a40fbbeb1c2a30abf3c1d080642414245b50103ca0100001dbc221000000000f02b0e0b58f0edac7404c4857b39ec7002ad3b00057e3d15cd542eda1a0d9722af6fd66c978f71a7cd6253d666a3e4dc60a6d98b1e2bccdb092d76b6af8ef209dfe8a0116b0676d6f7ae0484c9cfaa097005f1aaa7f70990e7a1a0a17b1c4d0c05424142450101ca4f2232f9dab525097769325af9ae65a76b7c089275034b9d4252335f5b30518b862d8e8adc5fd1d6ce515328f501aada04391609ff3e1ed83658dff97ed28dcee5b06a6974673353eee98be1c9899b625b711a0d3bd98d25933e5a08292c4f5ee4e80102bee8d19af0922cb8ab4626b8e548bdefd878f0c7e36fc78d578e106fe2be590b0d938c9900a1345febba48db9e5f41dcaa5916e15624be8dea166257b53fce080642414245b50103550000001ebc221000000000b622343e1bd0029dfcc1b62e98d874f4e1b95c390951dea9558de1fb79558515f0dc59ce39011c1686c5b770bee2bdb9c8e5038cea816fd61003121496637f0d013f3a78fe54b2c22584cd5d88fc1b1683032a3c4bcb89330916f26b42750f0b054241424501010e43343edca294092252d7ff4e6a0a9ade197dfe71f7172f3526b831c53fe3037ef943dd9581166d0b83489a16f9cff899adbd9383156cd249698c1c6a0acb8dca7ea70e80736526734390db2fae8d8962abd35dd98ef2250918de3d8829f59262e4e801a2caed452e29745a7831f1b62e7192e91f473569b033cf144991548fb2771aae588d9ccd7322bbb223d2f750a0f1b58d04522ca12065bf815a4c46e74265c4b0080642414245b50103650100001fbc221000000000b0241a2ff4e128c92e8a03b6b0605baf8d06a14674766f28b9b8ac1ee5a6f57a1e1a6a1107b89a129c85eb3912f1280732a38cc94b661df3ae4695ec2478870ffefea9b1cbcc2ea4915d0abaf74be1de3fc9959cc84c4cf0608b6059fe21f60405424142450101fad444acfa8d9cb36db1451ee50374a87f127fc2d63d44f8a1b099f04592841f4f74a3223505dc6d7309b0b6174be8d71af4cd543f32e7dbed45c586403c058387605cd02aebb0f9f191015f8f44354842c7f4bc6c64e427bfeb31a4b14943da66e4e801ee71970920011515928366997dbc103fc636b38161db60c324a1971c1c22958c31e0d69b95170a4383c2bd3bb3729a691035d37331dbe9e672ca7750eb04595b080642414245b50103da01000020bc22100000000070804fdbbb90fde744cbcb468424d286d16511d3521eef5918d808afdb343a1b6c822cd9674ef0452da29820dbc6772c04456049e2155621b14eed8ec10202044380162ac6883bcd885f4b1d5cc8f02897c63abc88a00f1dc33508bc94577606054241424501012680abdfb7cefe3cb55aff85990a6c3a4d2ddb3cccce4a105f840305fe4c5a3402af5ec3b221d3fff438b5e1dde2283e0592d714cb17dc8362f08c91cc929f8459064a3642e0eba74664bc339e785a9ad92156928f01edfa47bcd6776ffc16526ae4e80169c6056b6505c11ca6e6225a598f12c3197592d2a4aef8f046e4cb992c59ab6782c3a3b571fcdf1345a6bbac1e41bab0125e54ad24117f978b334be81610bba4080642414245b501037802000021bc22100000000048c6119166c3b8db387e1fb5e352282a0dad2633c540f3c20fb49cd1f0cca964c8ba9cc0698725b927b41c71b59b53bc09cd86a0296863be8c2018f58af99305817a68dfe1aa71642b2ca445d29b9e89e9db412e164365396b54bb5675db7f0d05424142450101ee4d9443aa130e68ea65c4feae1ab0ad5eef3a7c296ad253b42d33a80135c263d02927e6365cf3640ef0c36acf6601183864acbd010a0d80ae9c8574b93c4489761703cd2d3f241338a5fb34a0ff753256f5b2ada552c7b2f5a27354faa513066ee4e801c1a1fcdb9a247663f536b802a91fb676e40c7180f1edf377cbd29abcafc31b15f64411bd3945c186394b64291727a82bc28d0a6446a0b5480a1d2494e62cdb16080642414245b50101a001000022bc2210000000009afee610bbbe1198fab43e146eeac1e4192345aac81dabe17255267d8214ef23a0ac26189f2cdee27cf24118096a8680dba223a6a2bf040bb2f7c442e1bce6031c7d731c7f5e796644b32d8dabb5ac1fc3c60e53d041c3b433d6c85e5244e10e05424142450101b40a529ce93ded054d134ea986de0472521a8eda7c2e79b6ac199c0786d96a45b0efc91e3aa49bb93e234a7d965a7541d9da4178dbbcb95da3fa26f1f975658b0b27c81313919f7e3fe3d5ea35a326d255ed6deb978586e7de7a1edb53163ca672e4e80186f2bc62608bb2813ed34c5c65da26845eddeb0b72648952bab853005b760511bf1ac3232a4d622314689c14f8f187baa8450839e2fe1d1216474dbe09708740080642414245b501039c01000023bc2210000000007c183d6743d2144e6012009efa6051d602a0d38924745604af7e2da9246646365dad68f4263cac83c00903ed9aacab205bd029324d30c0f09b6d11641ccb550b17b4a2a17c388aff63a8ab5f6f9e6f0bfdd767149efe2079357d8f3fc450ca0705424142450101f27b08337d07ab0a0872e113f109cb5fa2d86c40bf905018dc787da7e63e0d6534c3d77660a1274d8ebe8d3b338c49a14eb90486c0ff6e275dc387ba491f298ff0e38fd6db3c012704431916269711dd91ec92f1c84a4a40adadc976e656a39b76e4e801af4fce31a7322d5883e81f5aa177e3dec975a3552d52b06561132440eb7707fa440a4a61152f7dbee1ddf71b218f627a7140bfeb8201e157e9ac40cdef9291c4080642414245b501031d01000024bc221000000000f22f79c24368c485b8741217daae6ca891382f346f50f1f5bedddd399309ad378f079e7999b93e0f17cc82d22581a9a05ca589216cb57b9574c61f6ec41aca0b91e56a838c012c90dc88309582478f2cda621ebff42ae1130342829c8e13a70d054241424501017eafe47aff75a83a7fce7dbbd37f834b64764517240b7f8ddcdac9e99c2df8689627716c1c94eee2b07162d20b16863d355f0d3b13163e7e5458d845ff6f3e8af15bae1915595c876f85a7a2d2a60c185aa52a2eca20728d30b24b46a1f3646f7ae4e801ec948efae12c17fc0f99a258c2e5ebcbdb191d212febb1921d79b8445b21f196205d81e66481dbecbc0109fabaa7ddec0c7662ef8ed9b47d8c14e099aff8cf65080642414245b501013801000025bc221000000000c2b1af630dca47f8e45b6cf8f51fba7b99162ffdbfd98ebbfa2fabfa04311504fff2d05aceca525afa40ba2e613c3b7aa581f04ed52636d4907d28038e595200e806f54b568203bac0229739f6ff28e23acfc6e0a56b91b6181b835af28f890f054241424501018e2fd526c774c8d52d946224360dd4a8bba5a9f0230a841dfd8a58f41f0dc94d9591f90a7817d73ac98ebb5d2493ef57ec5b96cb7d013c763056cc3e0f55ba8265794943ad850710940b9956478c6b3c382914835695967f9eb91296b9b246147ee4e80113b6ba312adfaf9829294ecf66ff98eff5877b50891f714fada97475b6a06828eb2267e20d619a76b22e4a0c4ef41897a37a788e5bed16cd39ffa5ce7b55e075080642414245b501033902000026bc221000000000d40fc3c26b4c7ff7914dd03781e658a50e4ac28595f5bb4077338e4de8ff6a4dde1f1800cee373632fb91871e10ecdf6e65a15df2d10d435889d9aba14b2ee0c0b46e2a31e72b7ab8b0bba3312522dd6428108c76a721bb83e4b2e9112ae5a070542414245010120491e13191b40678c6242bb2a41b69e9a72f8d95e4bcf2e3ed36615e975d2572bc6a923a3bf18588eb97734d172c337452275fc8d0a788635aa9174da3e638827c9abf680fb3e47f62127ee685ff8d92b0cc0b82be2dea203fff879ae62bea382e4e801e8742357a8344ce4f7585d50eb3579cd7b883e5dc28a09d1c66790aeed139435d64f2f8756817286275132e5f31ac9bae164dfff5ec9b41f1a81d23f3a667760080642414245b501031003000027bc22100000000042a052bb3ca730150464707c28a1a2e235fbfde3022fd53c8bbe9bd07c8f323d87f04997efd660d6deb3802dc95665d910bc593a4256f19a2e6226e083021f09582b66c7e67761e8b0ea265ea89152708a76686e224c59166f5fa0be6b5ff40c054241424501014e0d8c0bd9d08ee1651bee08079d750e6456c52ccaa40c15b81f386a44acdd0e9661fda4ab68aae407d2415319479f19b32334c6145739a097333671ba554286e78b0b330d211e0f06e9a5fbd59abaad76500beac2bf6e681e0595968699054a86e4e801079ab23d8f078301a075c50f68c342312765e8ecc6523dc9bdd383721b9d154f4326df07cf3fc65d9be22f42223efe8056899804532a96fae8877377f5c11866080642414245b501011c02000028bc221000000000c6d7dc4b51557b10dd2d68b844fb5e147e365f9304e50f60a51054ad5e390209cc43987ab26074e0c65150760e96720fa9ceb1469eb286a4b6b9a1f0b4ad570365c458827909021a75400e9b13fb551a3c01f0257aa83580153235b2d159f60c05424142450101988d4964df46e2f710ad92cab75204e996a2413e78ddeca3140d2ff7dd85e4436a27a87cf23e1a3f65b1a00814017a7f0263bec7fdd8ea82f4500b18e9d3848bca466a01351ed898aad06a1fa5b72dff455a8cd5d88bc1576f173ec1b32e268a8ae4e801cf9e091b9814ef1c40a777b51e88c91bae99bfc6b16f980afcb7ab986c8d9fcb39b39471f2888d764666d3381ed62528b4876fb5e2428f6d8ca60bd3bf281d6d080642414245b501030702000029bc221000000000946e92a0012a52154875f9ed443fd3616b17de1763298112d75ead32c4dbeb743a0c38eb99b5ad6492755c66b74a906e1737a0f8b2099c51c5dd492773937f08777e4109ab8113c76ae478ebdfe42440c528fbb37bfc43a660d7a01445969b0f05424142450101346ca51ef25bdaf5d71796b880a6730d4bc476371b1e1e0f1a48c8bfdc253063db49f7d603c85cff5232f943072880c08bc85b49ad3e2db11366f585f14364888da88fb8cdf5b0683f029c011843ea58fc9f9e9b92ad0dd21e246cdee8b6820e8ee4e8018dd35869071d486d224d54827fd54da2bb45fc5ba70cd6b121a7989d6ecf7dab34bf3a8c5097b405b9edee65e8bc325f5b8899e06164b582fa3742836ae888c1080642414245b50103740100002abc22100000000046288a5735ac9c05792e521dff03d4d584d687ca9d4baf8ce0cb7dce9c566a58953368408d0f6ef992153a6dd58c314a95061ab1fb129462ef1ce03d66b0b90c6d3af0a948d50ac45666ed8f5ffdb4f38c655eb1938d1f59afbe6c2c01956d0205424142450101369d3df3d25fc84843f25212d18480901ec62a20e956c7354a90e86a96e0af32c5715e17a2025ddba283710b9bfa7e0131f1e906c7ef8d2a1fff90e215c07b8370dad2ed3cb67c1e571a4a6548ca07c839fd6134de8e851a22ce8efde10e47a392e4e8019ba54a4f1a4ce7f85b8acd90360697e68e530b5676f56377b3612b77506a002287bade275a77d2bc5656cf1a9a227fdd4527a592a9a984b62d52b673d96fe1ea080642414245b50103e80100002bbc221000000000ec73eebc61fba8c6a47da3dce0649f6ed3f48367fc54138d91dda4629279dd78148562b04b1218521317957fc8565486292f07df3cb08ce28e71547ddd94b6085af0ea0b3e4fcf1449c3a4ab42087d1eb8b9304320f4a895cb407792682cbb0f05424142450101bed1ac61288d302683485b0944ca46eda9eca0ed0722259ff5f71a6850efd95d68d19c2b9d440f207c7f38c1b774170322196cff8d91fe3aa0da2daf1afb068a265a81bbe8bd38d391a36eda46ecdbeb1d11ba23b2e019b66f73c14efa75e8aa96e4e8011ce2f09c77289cf916a8a6f36dbad0d82db8f0e5d829d762e55ae0478c5f083e772877cdff417042c796aa9346005f46f6ae3be9a0b954179746b1ff9f5d392c080642414245b501032d0100002cbc22100000000096d76ab2aff85e7c471201a01f590e730c98f0a53e90a1ad80c093e880cfd133e064979452088cc2098dc582c465149ad10fb104b605d80700d4338abcb0aa0cf43e8b0ba67c80410e5aadb673d58dc0bbadc338a5ba170ea27539ae47711901054241424501011e781beb82c3e5f09f004e9b4ea3672fb58badb8bb6d3d4830f5f6986b7260143338b59e19a2feb23a28170c5907e438eb45995c825da43bec7f37649b3c068236c921d71803fdd067e99f8bb9c6b33e68b369b8f35afd9057753ac6b6ae930a9ae4e8012006d08e34f69e52d76ee8663f169cb36571e66460c43d090918fd3f9e2d891b648a6bd78c438340994fcd5d4a634a24bbb0dfaeac4e7ee618c920c5b223ad13080642414245b50103350100002dbc221000000000a0ce6f31d9d2873f079e89e3aad0a5e07eeb884ba0221a90725d9864ab8002434b3b908d8888cfde074e899d300d8e85845df445ce3df35250c4e6aa3aaced0bd73aaf9480f57a153dfc28fd90fa5c1100d319395f57e4113f564436da97340405424142450101ea71300efd2c33b259fe7e839f5baa91360fb022abe67214c5f6bfefb2a1e510989f2bab4b8b40f45241289a0b0f246455f9382981117e281c696e000ec79089981b24a031edc859152abd7d69e5dc5751a2594a577495ca836f08224e067ab09ee4e8018b4a0736abfc9334ee015d88f5ec8ba31e58cc6285c59b56e68df3d06d266c9355a1f33a43fa24776d666fb72fa0a29dc33724ca3e65c1354022d4a1c66c950b080642414245b50103c50200002ebc22100000000016b8ef570906e4b3813d12a7f881ec80f68b9e63e307065ff194f34a57f617409f353f79120cc29791b91a2c4828805f87fa0513a803c4de9adeca1a2738a50d20911ff73fa353551efdd73109874b8a221450fb488aec2f518925818006dc020542414245010196d0f9c44d91d3baffb019b9446dd2102039a6b46696e665401cf41f925ec2742bb12bf982d3e8ef43280a239166d5c7838cc5205cf8dc95fd8757b2e125588dc59b6ddcd436704c0704dce3804b9b7653ebf1a38de02c19920cff02bec18781a2e4e80107afb4ae57cf2f750c57cd0692e966575fd57933c9f644daf89bec6c92f6f9c950eb0c262b260706f8851ce554cad03949b7dfd75cec8ad312f742ca6a8ca854080642414245b50103170200002fbc22100000000016f3f86b932048ac1113bad84cf48b776cb18aa2ccffa437af624b92cfbff277d05f7b36bf6fd3c47674a06f134aa239ded358d3349c0e0c77524378e04f9701f79534ede80c2ef423c7dfabf55c3d4b0928b499ea0a3b15093e74f94ef4e00f05424142450101580b7ba39af0d7209034f83d67238f8f0a17424fb577b810f7551a25658d60060dd3b57f555ecfdbb9aec6c1b738bb39fb92bae44942c1470dbe3732d6c8f88877a9d3d803558a38751ce3e76da9ed8e5330dfd0484961cc0c5f09fd0a73bff4a6e4e801bbd18ea578aaedbf9804bc561858b8beeae3793465ecefe14f9b4588edcb77d3aa4f07a23a640e788dfac049a063623c43b2d143b1257a0201a5293db32db4e6080642414245b501038f01000030bc221000000000046d33ffc5a3a2cc3e60f61246b7e8bf5af0b6759020e7b2875c38b1fe779f3e0e5edee0b28b2a70235c03edbace9cec96008c80c0e29e191702466fe6452b019b161b4f73b45970caa25d6357c35b8bef9aedacbae4128b8ecac04017103d0805424142450101ac8edb35ba5a3a853cdb41a12efdd4d98efa05e6a7b987b9c412e8a69ba1d863203122215e6ee4bb7c99991cb49b11eb1d93a93081b95c517507e75550dc5980235ce7a167884cd48cb78ef34d2d7d84c58070fe1d37de2cc7164d1606959ca9aae4e801d5d8f63a1a733309ffbb7a1207412ccaeb1ac10c95cc037d4a414d7d520da00ad3939a1b3e57da6e9cd7bad15de571e24a380c49efaa3ca89e96465198bd0fbd080642414245b501033900000031bc221000000000e21281012b642aad78d798ecde45146ed0f08f21beea4b7bf2d30c5e6222cd68c45e24559d74e5e83a31fff1e02158e50a486220f46090d3f4e19c27138ec20200451e4f4971a649aa91ca341aea018c4a327c78d14acd29905379754ed84f0305424142450101e6c022dc23dc83e59896d90c2429f42655d2a75fd8424ecbb042068eb502c839eb3f9b245c9b83ff6fc7c1efab073e6cc511a72907ea43e11a1ad4b53dbefe85de3ae699b4c6e9a5f8507ddba100584e533494db27c16f8c5bf1e36a61e4a842aee4e801125dae8a0890409275fc85091872f8d75c08266755222314c49f8576e7b6df0713c3ae603ad58d1bde4a2678df29c145ae506a2f333408e4f6d60bedecfa8073080642414245b501038c00000032bc2210000000006cf6eea48acdf321297a4b62e9720b1989094ddf3e1febcb356169fd52038a703b6d957a0e8b194e2de0ad86e0274b4624694509720dbb464890d8b5ff18680678b24f9e13790d7270f57ce55fc237991020d19c2a36279d9ea6b62701cabb0d054241424501013e8d71d17b7ea4652b351d8d2b6cebaa77738f1eb9c300d1a4e31b99096c8b08003a9b48f55c7e4825978104593fbc77f3ba1a0b9d66d61cfba170b29f80fc8891c6c40b295fccfae8e7079a9d34da6bd135504b5148e24f62b8b3fcfc21f3d5b2e4e8011fbfd877438892faaac4f41a8ec9c8833cd253386a76a0958ba8f3012559fc0123a0a794896a96fac8ed82a995dafa0d9f521a075012ea60fc52fce9edddb8d2080642414245b50103c500000033bc221000000000a8c86cbe4810252224b6c0cbda8fffe327723a52f1155ed22109d626e28cdb59eb71a727c5b7875d1101d0de438943bf84a33197dffa8d49ad11c66e64140606def5c1c631ea61a1c07c67f6e6907810d521717f0fbbbcb13cdcacbb576d760805424142450101563e9d2d282451b5171689de3a2fba42ded841e41b9c0d3ecc49277655341362ed40c380088e9c2f83a2df5645a3a2042a3e0f2405a4f846f4becd8e6dbb2b8df9fad875727e2c50f757a2bed1bca85059a8c0e0d1df50e53d56c6524f7fed78b6e4e801e07cca2e025fc67aef7d45c90125cc9dc8017eafc08da5964c0c436855f6571a7bf27a0e5ab9b4324cfd002e90d246dbbd6fa4e705282f8bb1f18f8a18ac0115080642414245b50103b201000034bc221000000000069f89c988cc1641929720a324cee86a100e391c9c8fc483f8a17a80e4127d15ed4a52717880973ffda8e957c8ddd747c85c8ac5e0e15895f38be2ac7e311a0f361479626a6112c9a1df83bc963c528521d19ecd90fd659100d13166c4138b06054241424501016e439c653eb6b4add4ff31c9851553ca8c8fac5eb2dc7866b2cc963d12eea30a83d63db9c77206ea2bc15fdb18b57d0f70ac204be7a02d7c14debd1b51a01e8ef762bb3adcd28c69636fee2db6a941d727b0019004a388350ac3189f3c84943dbae4e801677f8433d643b781c4f9d953e2c3dd94a9ac278be451aa66e7289cfa806fa29c6a231dfad57bddc69ecd4f955222fa06bc89eee8d7b27bc394dffa48079d8e02080642414245b501039102000035bc221000000000a4ef96c6abf88dee1ed1f4091dda6416fa624e78a1225f61f84eee41b51b520c90fbe9d15d5de8aa6b3796420c55aa65804f600b418101ee6569e2ce537584077709523af23794094fca29d20ae4058e615764c8d129fe25a774f38c29e08c0e054241424501016025847dbf282675f656a9151329b163c86d1984fee08f487964b774f499db4bc8fab61d658ca2963a67fb4697d4b1d111514d2da86b01189fc62219f34e61889863270784a2c01492db8a7ebd3525080ef41ade46fd3ccd0e86e40ad5bd7d39bee4e8017f13c51ef59a0d630de84a93368678cb8bbce87eb7f6702565b8f89117f119f0f34dd81864598ea0e44c63a9dccfe3b931ad5815c551ac5006eea14365f8fc84080642414245b501031603000036bc221000000000b49cb1fb5a7d661bfc01dbe6a6fb63f8aa3b1b5f14e2d0ef54a505da6e076f6b51e7dd4a2e8315058250baa5d5235593f35f690aba7902635e08ec0faa17dc0e034c2b598251f605dba8192068832317a4067bf053a3b08d9650f3cefdd5d70a054241424501018cc64a64fdb0c26138aa6373b9d6d7485a1b30acfb686a1b5bfebcf98357300472190ad566e0367a09133615caa2271f4bd484de8a0a72eeee0b9dd0fb84e6849877c2da6539f474d9022e4de6740e28ee4935a1ba276a780b761d60f6abdd59c2e4e8012470d7b0158733dd13c92581eaca68fdf5988b2fff192e9b427a199b48a95f789bd3fa34fb590a72dec828e18e4c313f8f9d53b19ad14ac8a1fb10de5b880add080642414245b501038801000037bc221000000000629906f23ea0b0abd6778627f656e7c2f0d3c079c21d0bd28ec71b599afa5025ed9d8d420a5c78c630bd57efe044bf66b8193c483ca8d0c72d51f503f40e2c0b3d1d69de6f2fcc1772b03cca5839ee99c040da304fb8c9a198d2ff1f0940d60c05424142450101847ab8a3989062bd6e7094241ddae9f62da057429fa28e368bc6aaefb929967f3b06d31a82ef9f3bd9b4d6b8682c59ee2cb64183a157e6233d8c0c362e29518ff6d2bc1d4b6b964a6312a1449615de97ea6222536f96c6855baff1f28563ca26c6e4e801fdb42cfa9bc85e13cca854ace6f7ab2adff552d4ca45ded0ac28e208aac9047532dbff6eec2bf99ae0b40e13e1a90149ea5bb03a8bf603ec19adc1029818b4f5080642414245b501032f01000038bc221000000000e05e36ebd4b5b1ed18371aa99096349b092280d964cab02d5da7b4f9377dee2bc1ee29a83d9de2fe4474c16b4e6d05afb70d38ae1af181f77b3b39f4e2189d039d25e46da42ce2a0017d966f40a3e75ec6cc9f4e2596b726d144e9c4e578530905424142450101f8cb9a1cc262f17a638478d1bee7fd46c0aafcc2ae76766c3dedc7f4dddb5452ef132341c03112537526b8bfcb56252afc3bfa9879956332489797192276848b629cb6947470fea37cb62309001fa489f5dcfd6e0de858b5379afb0ca6575273cae4e80123d2c4e81aa93ca2fae669ce5a4d0c18c18d9edb0d4655902df68b6304bdaaff47515c9ccf428b0233297e79ed5319fc8f8a8d64a1cd052cf90601fd257abf3b080642414245b501037a03000039bc2210000000005e6919319cb46284ce4dc68ce7d861fc72e7a2ef597ec37a0a1f1b27b1bedb5c2b29888b27ac88d050bc0844c4fa989c7c0d3ee6bb59fc2e1fef1e17e8d8a90470cd5012aea4aa1eb95eff7bb6cdeb92f3b502a2ffe90f566a200860cedeba02054241424501019acac752eb08f857c401ae928a2cf5da5f3db0d2befa7d39dc229df4a57ea64e419b0dc522716b5fe5c38a02fbcaa5ed6243e3bfe5e12a40a058b1c495d78d876c4537f4a773d94a1c644d3b9bb0688d73ab4d72b0e5aa6e4274e66d6cf2cf08cee4e8019a7ce7ce7771fffaf3aa18a6f86e35d1cc30c4530f6cef198646c06cc927d304b187c21a5274d3864ab838b13db8229f87241c7d89d9da324ed49c69bfe0fa1c080642414245b501013a0100003abc221000000000bce7bcda7577377213b61a5213713c5b6ff1c08f9676f79d2e6c22823340e635e55bc25414840fb9d6a48d3bc7e7f003cd41babac929de4ad5d6716ec0bdf80934d75192fab3df1be6be232645a7e933a1e704e3649fcf0fe10c74b684c9230d054241424501012ac36ccccfd10bfd0cf4d0b96e8e37bea16791012eef0ac4b345a6e90c82b01da4dbaea86aa9cdc54863d74ddfd5bd87a5a5f52786b0c4663e31f0f13f400986d77dd26d504d457998e162a8fcf652150aede9fe04af59b68505aaf2e3ab7c1ad2e4e801ef07d521cf0fec8c39c9df92ac149f93029d0925606b11cb9ff903ad0461ff70591b22fa697480ee536b9b3fd4289364def9f1d31402a13bb20daa1a41425657080642414245b501018f0100003bbc221000000000d8793108efa7de9ca17b2ffb5e09e3578288684b2f2ef8c3c48b6f178b5ae3208b6e1d29576e2e003e63d597b7f910057c49aaced52977aba7816c50568b0105c1f880129405c55656f328125568324705db6749d5bdc825b1263899ac181c0f05424142450101d0f67a2ec2f437b72c974a1153fd50ba5d4c281972bad9f1d41255b8ae32f85619c02fbc03436be0df0bcdc9afa564e97635b5724e08746aa1f296d572a98d8509ed39542bc65138e7f8eb5d163432b5f3c0327469d0ede5ea8bd2188976c014d6e4e80102d535929aae6ef3561073f77f5b7a80e5a42ae7ffb162754537c0425ae55235b7d033dafdbca732f37dd102f885fddf4a02e7eb4d33f7457fb199b3d91c138c080642414245b50103520000003cbc2210000000009057681a75f49bf5347a8f3dd5e113b6317b993f8930665f631e58b7e15f95527266df8d96332ebef2bdb850083d35908461cf6a793d244e37b1608a3b5929092b5c7c187a431622116d601172078c5019a1026bd851f5abef9a98097a484a0e05424142450101ee22bbccf59b97a9970707269cbe9683a74780ed43a90e59f31f39a7e641583a1eeb9234eb3b3083273d885dc4a65c1ce587c1007839b88ab5690550f0bfe08c78b719c94916ad5817a6f2338bea6091b946ed5f6849c8eb96169944ad3d4d59dae4e80120b87ec61dd93c1b6d2ab6cbd111cc4ed964809203a817a59a1791a0cb3af2f25bfcd4c6954a0e7ed7c076431a0432c10d9f083f40050176b8056fb84c23fd85080642414245b501035c0200003dbc22100000000060100482ff1f6d28a629b846300d9bd682a7ac12503bf61ae5bf6d7cca25193a6e50ab398fd47f144087e0a71460787992d59974c8b0819702c27c1fa61ac502db4735579bf2714fc3e11398cc53f7eb802b73174c4e95c1c6d963200107c50e054241424501019885126e4c1dc538041ec813ab575e630fd6ffb71876edeeb0461fc888938b3c8d6e5b50e44cfbd88c1eb57d3b99e181f6077cab610bae8ade820c99f7f9e5801c9f564c0640ecf53b9ed7cb2b2a14696ddf49c244deef9b704fd03e93b4f721dee4e801e6a2ef0e4d221f2b35a7e925651279165c486c4fefef70c740735ff98ca25d685763b843cc6a79ac2712227d468c42b36818952e9ebd1c9a5cb4a14eb3a1932f080642414245b50103390200003ebc221000000000acef6aaba4af2eaf5dad144b8ee92878f50994d94c3375a5c21c3468abeb421579cb0f773d4ec55c5fd04c7f3bc1b86b444bb100d73e086e50f9801ec81cf10d43570eac69ea94e5b680d33b248fca108c5ac928a06d9b9937f6e59707866b0e05424142450101847f74b914ed1f9ea71e71ea457664503de6adc1f7b855e51a6375035fdcef57882c3809387bc793b2181835e0582ef4123ed25f0040279066447fb0f054e580939541828b526d162dcd387e9ade7258ef16c97f18c4fb1f61ab29ddf4903081e2e4e80139f2b7092514d2f820145f5fbcb29c12ca768e10c027598a9f77023eb054bd126982d8d188c563009ec8aa781b5d324c5b36e9e5305770360e348e593b78eafa080642414245b501012a0000003fbc221000000000ca53be23c85b135dcf3a8aaae27398818c0eeac3f7b5cccc4075663c32b7fe1e8082e2cb55dac949a604ddd290289ecb91e48d03cafb2206763a2f6697685e0cfb1623f0864e578c6d63bf211188c8565aefe7498e1f0e1f31d5ba61acc8fe0e054241424501014aa27352223211948d59eae03894d8a77176b0ff4417b4cdd3a418e4b5fe6a23f9c317f052bed752a7b5623164a30f834380be46c4b41234ff95621b3f5cbb84382259c6189889e0aefc52c092a1fed72c362bbf4726c72ea3686e3bfb9da3f3e6e4e801ff52bad1540f53d454e878efc0189f3b214c9ee4d59b1e28738026e7e2cb62313960be06820c41f0dd3bbe567b22b4b54fc26d234327cea5a82db5a2dbb20d58080642414245b501012c01000040bc2210000000009c193bccc33a6fee2f1fe6ac819f7b6eec11bf0a07bbbf4c8d0307fd2591c552df98dd394e2b1ba36f116d48c91924e56e191bd057d2c5dc4cd7275d7354b208b7c962c96c2dc6252ae7841782a6e911b82bf000b5f3e44779701d1e0b7792000542414245010158ed50998f89bf7ee254117e56746915ae229cddea1a55cb153ee53705b7220873d04ee14931a5b707b1ac0551b868e121c895b7c974ea3eac327d0cd1c7598ddbc476b41384bbecc2c15e4d495f3a746348b94adef9b041b1eb278c73f60415eae4e8015c4d5aaa58084916b97fd8a79ce5ac1f66a629ed3d1e63a119baa682bd3b7c5dea110339a247f8e7ebfd86f8c42f2b23247777076246d96c103e4610551bb2a0080642414245b50103f301000041bc2210000000006edbe9388dc45fff3d5b63922d1835b860c0197239976129a03962caa69ac031597b68f77d15029be3d868e3d63ac2b7b24e0d24266f55ab262d43fdfbe5ac02fc72d18c0dc148e40684542a15602b1b29098030f7310b240b49cb67a3d8910f05424142450101da8c2748749d4df9fb40e4b039950f50f85b91cd61eb504e71cb55d0a1633443c859bc674989b30ce8558857024a69f63c2b4c2d03a6c0aa5766707c1c842e873e577b6c374db47b48575a1177d6577bd92f2a81ce80ff315bb824d29d08fe96eee4e801de91a0480629bee4e12001e43a6b54b5d7b91fd4b24f7d668c8b1339032d74e6f12d57dbcf7439c3d060571341355d353cf595987b4ad5c314d5aebf8650220a080642414245b501035101000042bc221000000000801a9158ceb1ab22c37a952a499b71949721b76062bbeeef590d2fd89349e52bf6b9c3a898c2fc97d83daea2eaaa6fe37a04a5f3b24fd5dbc3cb33747dccbc062ac099468e2f75021954d2cbf1922b5d28ee80de8b9bf93864152bb6f089f70305424142450101ac17b1585989118a8997b4774bd832ecedd4105d5d11c64baa08f1ea89080952a8d4c230e514aa95f3bd17f5eef0ef2410a328f95e9a069e775499345e37928b77dc88742dadf22c25e1aad2072dc595ab812fa3948322c2d75322176afe878ff2e4e801b55bb61b0cece56d2525bee03d8c0ece079a6865f60938eb478aa8e388e9f58e39d371dcd57680b10e98d835ff6e8a313eeb36254a360729b991a45108edd269080642414245b50103b402000043bc22100000000068e082c1dec6e1ea67002c051023dfaa3a7f50ccd9bf0aabf20a7c6e0856ed2d5580a8ed6ac2ec27fb7efd3379593632e032ec57a2cb36c5fdbcf9f1524f7209a2cc5764d2937c1e2438047dd810eef7ad64d68243370b69b97d0084a26e2b0c05424142450101fcaa1611fb407f1cf60694c314c974e72442cc1700383aba8edf82bcb1244c494266b669dd1017d053b4bd271125b14e3809e49f9244f7419e77d563a60ca78557544c965254baf76ac2f01ecedcdeaa98522d1389368625d7ecc69bf8988db1f6e4e80186c397512c213dea4bd6f7d740d1a936471889dd8f6847873969456f8a82ef72491cc14d79104c3992c6604617001c85fdb0476b4fbf75b34e26635a4e44b49f080642414245b501036702000044bc2210000000008ce4fa0cb11a3e51001337265dbd28f6e3a553a6a111c0d0d2e9c4ad2d55730c7164b2bfe050eb8558b8f875cc355b461e5b371d941de89dfce9637f8b55970b745d8e8186c2bf2ba9851680f207205fa60c5c13a407ee0eef7fcbef85eace0d05424142450101b091e9a048f371c832273d63be68617b0cc0fea7c41128b4dc993ff9b0c44b7ca8acf2e40e168bb4d9f88603f28fd2d829ccf135d741b4a2e322369cc7fa418a53750ac15b6e47a0ae1f809087d9741289df7756bcf253d6985ceaf67af934f6fae4e8017487d8b4ff114d04d8a4b703fef7374cb8b13d91eee976deb904f208932044523aabb996e77b26f52847f7218009e18f3901f2cca8ccb93f028dc06221e5e50c080642414245b501010901000045bc221000000000d89b296cabe22b6bbb5037d98a3c17e2285fea1826ed2f680ecf559f3b806b63076720d27f3781dfa79451dadb7f7083e0f983cf5c69e4a4cf84fa6abd085d0bdf6eacd3fc0c0622ead2da74d6763e46d94618780c2e07d3e43f960cc9756f0505424142450101503a96f4df477a287da2f8bb571da2b536650569fa51fbd06df1fd19cd1caa7bcf56cd704a99de48d313826ce722b952dbb3c2c20145a88a85cd6859e7068f802747dfc9688cb5b35e7a89e5fe3a68db17ac292e2b867682427eda1c040d0a74fee4e80183e0299fc43987520ba029db6b85b7a188b00522697a0cde20cdb3866bd6e9f4fc2304e952bbc6cd7016dd262451533a83fd616a256cb8a2524a9d3590568b6c080642414245b50101e802000046bc22100000000000845e56f2e7f78c4384d0269fc2b70bdb08652f3b198beaa843f25b5db92c546eacfb671f7be7a1ea76a5c6f6d0f03ebe12fd0c3f350615f5c2cbdbbd8eea0c6769cd50dcdd682ed57d6127e4020ef5aa5fbac2795b410997fa344e03b7f102054241424501010055abd7a417e48fe6b65b5e828fb8c6f2c1dd3b7e418b7387956a5625a4d9745c819f975801e2e718f3f8348fb3f84fb7551c674f5f59b50c4a28b8b28a4282a19132bf539374a096dbbbd4da59264862e4b70ca03f828fb24e04e7b6fc631f02e5e801d7ee45290e09f17944998c11cdcf959aab00c804cfe3ec3989d1782257ebb05c290bfd52e27933a506810492e97a21894691b32d968443f1753ff0af6572a14b0c0642414245b50103a801000047bc2210000000003eef3e993af3d5f58a4ae69acb0ec509ba49711cc68de3ec7e41cb104a1683171a3d02eb55143b839f9fbb0719b74e1104d1510e27a531528c9f05a3881f91023db20bb56a27bb2d1856703c353dfcea4bdaaa7f7a9432b2bcca4350fd20920f04424142450e33020001110ec434cc142de3e83f6c5d32526fdb84efeb826c7441298ae84621479a717ddd2601000000000000009a4419c9fa76e2a85014f2604f3acbe29d052d7f8ebe31373f213cfeb3633f3901000000000000005004ada038dbeeb636a58c61857a2b2f64a467b929128d118889a390a81739400100000000000000b681933791fbbedbb24bbcb2eaa48411c7c27711875b942537fa2d247c51122e0100000000000000240a8579b9c6cfce8e9ffa06e49a0df2767744abed4a3b3458b01fe92d44b4540100000000000000ae8ef962e1fb879eeeae9f78e8d57fbe6c0fc2a5b404c93a65f5dd8c982b51230100000000000000863a39f3310812dcfd9725d1ab3cece6dc763e7a43eb5ca7c54f7ed5ba1ec3190100000000000000f44df689a3d5ace5b6a109e35ac63d1e62b52e3a1a582ce0fa0e220fc8b671570100000000000000124f6cb9120882340f4289c1e4ee09fcad3ed8930ec634feda536628bcb4fd55010000000000000080e41ac5dc4ac06088d9c560d193b6f7b7ac4d86e0e9bb517c21df9453efcf4f0100000000000000487888f977bf72f61ddfb9ca54897c22fdd73acea696eb009b5401dd7101130f01000000000000002ccd992d7a9f1650e3fcd531b307d9651c104df6d5facd2cbab688d3fc334c440100000000000000d043cceb4e7a4daaff5be2f8fa11cd425af051654571a146f254afc04ffde42a0100000000000000ca204674c4aa968ae4cbe115db05e478b6ee03533038eaa9cfb95ba1b265653f0100000000000000361fab81129e1e3ac7e7c9b4aea10a218991b824c6dddc55414a8770e84f7e6101000000000000003652dc7e781bf25e8bb4b1d25de1bd9e9a7fd67a8ec75b479ae370ed9bfb2c5a01000000000000001873d87e3d21ad568f5cdb255f9870015002606921c6a3a905ce146abd77a2340100000000000000fe585a6b3636068a9009b18083cd6242c78da6f5f57dc0a7571b65a36d354c2901000000000000004ca1067f6f4f8fc7c9432818d0225242e98796be516fcb04ee272799a66dd23a0100000000000000c441ccc085cca761a998a310a1f074495af3e839f8c71de12615f22a7ea1073501000000000000008e747ea3ea05bfccecc9595e9cefe7fdeab4a39fc7e847f56b04135fc4a012710100000000000000fa5557d061b27db6b1bce99a95c830029fc60ac3430d55af30e94edb3a77a25101000000000000001ad6ddc62c61923290e127bfc7708c944c8d6a012805b14c70156109d83986400100000000000000285c8ef3c7445f4ff0ce912cffb2e521044aebb62620963df624201c5e4dc7700100000000000000947ff774965431ec727966c6dd539cd70d15e9181b192ad47b74b1c85745b8480100000000000000e479230ab2921c9ede58fe6211d8fc8e8a3ed3ac8065a7dc6ac50ed63763607a01000000000000000ed017fc562bf08519533de0279f2f7588ec4ea382d48a63edf1df2c807fbd1f0100000000000000d8d8296101fc3a99c56bd85c4078c2e389c071f731e18cf6be7c00aa9f4e2e6b0100000000000000845f63a15c8afdf9737eae1b91666268894eaa6996079ac7297490544137e80101000000000000007c3c62b725f20653adaa44243e4b3d8485c66c310e57178a3d3940f8f780043a010000000000000070e65e0e42d42db5509f35fd338047df8c7a6879ddaf62465c3947b929eda6210100000000000000e415160504f666dadd30265cfcf02b9d10136ddcd69958d9174da69329f5f8490100000000000000da8f660f8f4b90fff45ff6fbaa4df167a0f94baa1daac048346c50698fd285690100000000000000de6317d82c9feea35a44cc73086d6e73d0c7287728bd16c63933f913b7f27a11010000000000000028db15599a7fec3c93c26f62014fd30c214358c4443dc6bd515ef09a113070760100000000000000141f7c5f1baab0cdc3e2b10fa7380084c25f20252d96578d81700414f9eb7b7f010000000000000020b32e50928e8148a1b3d137e6cacc80f2a1ee5a3730e8880bdb0cd5d6b07f630100000000000000a406fa3390513c131099c9ea276472163db611174ca44e3b5c3e52aa3b47f94e010000000000000094c8b55eb88a7e3764116702ef768950c3e59336623aca8260ff44291ff9f66101000000000000003e7c3c81fac56951ff05ff47ffb49285750f6f55619ef8d0e11a388d92020f550100000000000000f840d40d3f38ea72cc1598f7e2012808584c0b90ce7546cdb82250f6b049972a0100000000000000962c3b7413c205f80b072ba3d8b9a828b10e42be559fbc07e9c597ea72cbe56d0100000000000000f2a70fe0286602a06633c7aed149fa7a7bd542c97bd6ae90cb1a941e684518760100000000000000b6cc496a16c32cca3993d385b995f38a6ba1df00baa71632dbe8f750ecb9b2300100000000000000d625a53dae5c0f3fa7af221c68c96fdaa1a498f941e10a1751a93378c4f4d04a0100000000000000d236b78d518725dc6332642273c5f6375be4494ae1e8e6e8b42bd1a19af14c4501000000000000006464efe71ac4e34c699a088b19ea08801d9e394ec50d56c7cddcc3dcec103d4101000000000000009c6f977636e4989f241a12728b5d2635effc4fcacddb1d2cbb45a739504c827601000000000000000652cc6a94406c8d2bd9c9f9f09768125cad75244c0f495373af649b0c82295301000000000000009455588e090f0f98a3a77dbe74d336e08abe7a1b6e30c1ea6ba7d73687ce721201000000000000004ad8792f5556e979d62743cb45b3098c23c003000131eff8b41de0dceb8e053a0100000000000000e0888da4a5a14218fc2c38b8d18d21a4acbd0a912f39cbe8276be83305f1797a01000000000000000678705af1f738381114b82ee5b8a14757515185f69afa15b02f40ba9adfe4530100000000000000ee4343f61d67fe213365a0495087acbea7b000429f2fbb80af14d32d3244bd4201000000000000006844faca3ef4bb5a7ba212d2a2914ab99eba84f93bcf37abf62db3a9f19fe0050100000000000000145ec81be8208098bcefaafbd43e887c772685225a803c19730d25580814bf640100000000000000dcd6c04b237634b509ba798f7a5da05f9dfcb67ab5e920c7c2c9b9eeff83ec3b0100000000000000d0146ca7b1e2653d0127dbc6b46398f6421cedbfe73fcc796af22317c6aca71d0100000000000000aabd30681e440dd836c894b44d5af8e890cd99599899bc0195125e742fbe184b0100000000000000ee768be474ba6200704b6bb75d6a0526263047da6fbdcb9344ccb35268361603010000000000000082f72bb1cc00b5f96c95de38d2698ba9c64a7ccb4d58a077538ce1ccb55efb570100000000000000a0674ddb6f130eae680532c6ff0b05a317cb3840c8906c3a326f26ce8544f82e0100000000000000ac2bf96630b117699e7fd6c6c717890510e00fb6db3f8a0a5c461a9309ccb26801000000000000005afb98d27d68e67f6e8eb81c6d3375425a5dc323bfac122963eeb580cbde5f430100000000000000f614bac5b188d32a7041ae91c9decb77f68d21c8ca76e06ad9205eb838056714010000000000000068bf63365fd8ca42dde5e1adbfb98817f88f205e17c1a38e07f8620fa19eb909010000000000000098dda42a2ae6079331599ec904ddc18cf94fee85ff96e1fd3e7b6953db8fc70901000000000000005869c7315c16568345efb6241e1c0e9ca9d279c8c7b398d32ae7c7323a93092a01000000000000007a043f552a7cdc9de8641b15b0369b677badda49d8082fb71c7eea3344159e390100000000000000a2b759bd4c7dea8909c86f2f570eff3a3b56e71d50ec8893eb66abc945a7212b01000000000000004e9fb9675233d8de1b78adc3f27ec87e2cb1bb838354172d1fd9c93b318e582301000000000000004a7a434d9bc1105ae7df912b7d64c32ff70ed5e531f464447b7d3188c06736700100000000000000b04c669e6deec9b1f47db449905e1bfdfea3bbbff5d4b4fb9c85dbfcc5a0546d0100000000000000baa5986e9b5b50de5febb4ac70500028e71046fd84c8083c0f62ff6fe31d1e0e01000000000000009ccba303b66ee95ad28150a987a672273809107589fe2f7d29665fc943ca62660100000000000000c28524b5af98c5c4f13ea78f14ea81e91df1afdd4500333f9423a80ff331de0a0100000000000000e83c8269c658801c3ac9014fc5a3c8309d1769a8dfdfa272ebedecb1a2a1e4290100000000000000ae05ab8b2f6db5296f2766087549efc6169b3499dde3d6374f7c772085f65d2e01000000000000002083ee72d69233ce625e5f1c3629c331bd6c83385c73722e846752018c64e4580100000000000000586418c4ad73c32f19381e4c32d8f2d082671af513b0a2b528dcb24a4230281d0100000000000000863f88d41e8128af4882262a1d55cfac41539436f280b567385da2cfdda040260100000000000000e077c9be8e48279abeb236ba07e011b082f2bd09528aae59180c7eab0619e60e01000000000000002ad69fcc5342b7b7e11595eba5a00d20ba35d6f081cfe395bfa1dcce4a290a0a0100000000000000900733f798d51d2dc54005ba53eeb9c3d63909d4f7e7ed32dce66b7259552f6e01000000000000002c605db889f693e3f6f832512bf4d6ddd8570ad3c71db7a9b57bd5689b44a73601000000000000009aa7c8266d57e70714e5757cd2e187e287c63afe88ca3a4dd42b3f403737f6760100000000000000705af57ca737e78669fc625f5fc98b16d120ba871b488ec79a9d12a1ed72f2390100000000000000183be67e3f80e946e21d9a53db35eb2d9d53b227de1d371e61ecf0f7b5e6b87201000000000000000e2162fc65a7a09a88b48781078143933638f7f40c98048b0a9b87694e34d7130100000000000000ac72ebbb90dcc166bfba1257e42b8b58574704ceae439f2c19ac085b61d40a6201000000000000003a04567e101ebb28329de2600817729f6f3c17c5e5789f036a52ad49b99ddb23010000000000000088dc2749fd6af775f8d9f3a1dcfe548d60037cdb8ec1db422165cacf9c96970501000000000000004af471b98c45055a8487a042c44567060000e421e7908d4ee86169d4f36d4e0601000000000000004221e0d01a623d31f3f7afb312e0ae6158db5edf68319a8b6ff98d623b0b1b03010000000000000068bc9e681587d349c5ac904a296ab9a4481c7de5d6b585621c5ed2670bfebb5f010000000000000016d1f436ee6064b05ea10ff906a7b6baf9cae49431489a8afbefae088bdfca1d01000000000000009231b7f5d1f17d7dd8791204288f265060d3469292edee45533267a7a3976b780100000000000000aabeff206862500ce4d4d54491712448e3823ab909919cd759937759a086a10801000000000000007e4ea4d47bf58ce719af34f30ac79ad8515ff9cfd2574abe5309a21ef0b17d5c01000000000000004c230bfd3b60ba5597597b154fd95829385acf1bd740607b9ee07262c994106a0100000000000000f6b74d3139ee7543b6821c342b061af75b609c2728c9eef724066fd35abdfe5f010000000000000018aa6738aae287470270a2213f8644d31150682adff4cf3b7b2da19c473ac8390100000000000000fc81c16f4d0dc4b27d1e0dc381ef15781f8e0ee41fbe0586bd9afe15ed13173c0100000000000000249f2c086a0c22a7cd3189282875e2398b6dc28a2b9a8a0ac9eb3a53de73b43b0100000000000000b266ee40cfa61107e9ec12f162cd72c6518af1df74002c2004c5a98b476947130100000000000000286caf403379f1cb584e8875fa8487e3ec404c540b21bfd7ce4e85c3ba31a31c0100000000000000aa42949770f022aa18616f18f02917b25ea0182b6aa062ab117f9268183d9d3d0100000000000000886edfab02c7ab5f4f62f26edbf5dda9868c8a413a5c379b1b63d72a2121cd36010000000000000076cc698d58e5e4938fde51916c8c320e0c3f71822b4664254fbfce6cd4dfc03c010000000000000006d80e54ace9eadf3a6200256aa73882a94de54135dee1ee917f79774e5132670100000000000000ee4b50b0921a9e1f4e5f22cefe6bb00d6a4357c8570636a39b26e7344b3f66240100000000000000e844261de8b691e753a7ac7132c821469d83f25ea8e323ec1cec4dd478a7e6530100000000000000925864a0fb6058ba04b0d1425df158e708ba37cfd877f126796e2bd7970c1d4f0100000000000000ceb4d80222ff1340bbf953176e38bf2a8d40a4c6bca020beacbcac1f1ebe7702010000000000000060606066af08f9fb9f9afa87fb07ad0baab01233516511c5e6947ab8f3524c7a0100000000000000661e5553fb5d358cbddb07dbe15fcf7826a27e2c30847b9548d0d8f8f8048d7d0100000000000000248769c8acc11f3a3dede5b15035433729ded2ad8af740f2e7d3340d9d40ef0d01000000000000001e97263763dc6b55363fa51306709f7e0243af1910630253381a8592de97f00401000000000000003a1a122ffc962253073ab844867308704f6d92ab8f6e318e734f3b18b7e3ba3301000000000000006040a8414acddd0b30dcb1fbe083cd72889055353bb9f64c6a4875b861a4295e0100000000000000e06380c0e44c490844857db7ce6269e52272daf3db8c9c98f38b24c79c1eaf560100000000000000667ae5fddf7c7a58681fd75da15d319dda30921d6c659db6d0bf982830cfa06c010000000000000036b1387f3674856f0fb22a876ef12bdf2ec21fdcc849f200acc609e5f33279470100000000000000ec2f04aa8bd2ce4717e5747c506be32db5db1cda05e3875fba658505ad2e373501000000000000008e9fbb58a96118053914ff5c36348f8bc82d7e0051e8183315a4746d77d5af000100000000000000f419d56e18e14258c7eb351d1951a0c091194aae5c55ab1ac4cbaf795ecff17f010000000000000092dcfaa7fc0a16bb4a2e197db3179d207663971a4ec18c5a78594d579d02fd7301000000000000002ab8ea26eb951dfa831de87f69aa6608b4de76df2914cbcddb163beb5ce21b7b01000000000000002c2a0dd67d2550d4231d79efee8891dcc1cc103c007935aa49e6a2157c11fd2a0100000000000000f8106e64f3900bdcefc9dee82dcb471571bc421ca0b0b60ae2d9fc0f482328630100000000000000d88f551dbc4416e3ee46cd89c683eb51054fc9242037ff19a52af87b30402d2c010000000000000056091588f3ee5b9fe15d20fc336d68541a08e823e20d691a41613ce2c154196201000000000000006e2fc806b2626507a907566aebc2a6fb1039a533df8e7a8c9d9a287f3e7bfc3d0100000000000000e45c8c0d6a5aded1d938d8efdc41e5ab6b289c605cc6c19c3d7a12b384ffc81a0100000000000000a0fe04881e8cb6865fda23d69a35ce7f28574bdad4fe3b85a9925f7aebc6620e01000000000000002a11c0724e4fb460dc5513aa71488c58d8cbe2b5af25a62a4363e51d23fbb60701000000000000004e263edd08343be113e4234765496cf31e3bb0068d66e4782209171df67b2c3f0100000000000000265680af1a1392be7d96ba0fd2e32b5fcc11f2c394bae83dd49a29e4bba585000100000000000000c284219802cb0e56397b42e6434eae1cc49b1f67b0d05b2f13ef175c8e0aee5a0100000000000000cec30d319a93c00b7044b2f11165f3a5679d82ff4db6cb2783fefa42b236b8220100000000000000a88f2dfe51d3fa0370ca543711ee20e76ac172768abc40759ee95d0f99bf364c0100000000000000a88a466b1f2b4b7b6c497f85d1d1c3e8c13517217f2f2cd00c8ce300406d311a010000000000000064326ea393fc2fb9fd9102b08971b19ddbc878d77d0f53978be7f3ab87ed826c0100000000000000b4e7de600ce05dff305c9e1ead59c7dcd2b27f05b71c71387583e886504fc453010000000000000086899c0ce665034c2d701b5429944f115e3c74ae858ff9e7355dbddf27a4e70e01000000000000000e71bed03fe04edd6d1e28e7bcf8cbbff881734408b49f7c3e87e0aec6489f4401000000000000009a64638ddd657bedde38872091a93d700c6a29facdabec86f916c51266375f570100000000000000187d6e92ae950ca3e83ea4b5def1553313c9f732e865113c24a5dd1075ce2f7e01000000000000006432baca82539fea5029b23f67c912ccd9a652fe58c95bde1646d21e2b02fc620100000000000000a4cf6a4ed9ce95da9df37128939a4bd909771b726e5901be83669fd30ceb9f1c0100000000000000c6003d828b9b0c61a99e86053843c22cbd9fdd734219f449e6200f3602c04a7701000000000000009ee91399c765031a73adf234280853022bdafa455fc539ca3920838855b11659010000000000000070f27022b5b79b964b7084d664d1fb56899b3c00295962bd8e7f330b55ecbd3901000000000000000062420d348b1b6663b61c0078995d5c0ce88659271b202b9d15c6597cbf760d0100000000000000f61ff7a1acb727894f81bb4e328a7f64ddbca4fe5477b60a72fe491536f97b550100000000000000341699a2305af27ee4e032aae91b4779218a2be3173500502d6644dddd1e653801000000000000001c0927ab80ed3e549a848e77c86fd30b448d4d188e0de7bd66370c05569d19790100000000000000444488d1b3e45c7aa8e35cf71dc80b41639412dcaed88f71743811c40abadd61010000000000000040639e72e9c4806d47f4bb7519ee7ff613ac108b17d3be775d72a173f0fc691601000000000000004c8ae5be1fefb1169156bdad8365d33f6bbd91b77a3e3897cbe0d5c4d7f7e3620100000000000000284b3a6ef1ead33f5fac01a077dddc179190daec59fe99586b99c0e35340ca72010000000000000092ef51762ec663f4b099fe6a72a74107d3844913df78f0b80d626fc4e20b1e0c010000000000000008ff6a7d89b6e1936b595c141ffbda304e6d40e36ac264d411615faf8049ee110100000000000000ecab81c08817d3365ffb7e7f3a7d847a701947a1db288bd2177868251da5a56b010000000000000038ddd57c995be34d71d938d2d1cb188a0e8b827da6d23ccd08ecd936850e9e520100000000000000149dd20740e21748698d0d48fcfe3e97e3ec1de3fef8730d5ce5f2fc110590220100000000000000542b657db2973a25f1f076f7b15ad6cdccff6e9687e7a4b5c0129bc77c847c26010000000000000046659edf4ece89d3ba4e4539a2a4466334f4107f099c1a9fbd8bfba22de09d7a0100000000000000ec9afce67bf970d2eec61a3f309f52c860732b69b156205775af8eef041872720100000000000000045fe14e3b9bfef6b174bb0624f9ca0f897e4cf6168ea5952124c15b5b2597500100000000000000a8c43807924e0b4efdf713a557ac34056545de254fa3745fa6cab97df913c46e01000000000000001afa773e53d051ca10aaa2e7d76b01beaa90c8c2d6d4a9c36d6dace0fcbb88230100000000000000f40475fa28c0b67b56058df22178c5baab25a28d153dc3277e0dbac6c094b25401000000000000002ef10a06d765808d2d44b2034edee5654fb27218398f0e79731b318678ac07110100000000000000500b779c0cf43579b8927d8ab50ea09f3d540ef36911b7a7ae99e2fbf4e137570100000000000000ca1f7eb42dabd6595813c3b7404cab648a2467fa9e31752949e07090b286a36b01000000000000007e80e157841d2bd8452224800a02e3c4b561a27f1731f0125f1291c359b4d4130100000000000000b0d74c7b304195803ba0b25627cba2c79293acff064d41b010a32b6d3ce1060501000000000000008cce5eb3755f181c945b8de1c6a3bcb7454557ab4f2d8d3cb9f7d137c2e50b570100000000000000fc320d8d5e575097cfffdab4641c3feba3bc195d7ec01121e2729ade54fbdd0801000000000000002a8cb7211931f3477b5731b23c2d973280b64b509ff16096da574f0648c9275201000000000000001a5d6fa531d4845ce8e8b33fe5e37f7f6c25593f92edba3ed34689d15bd8db0d0100000000000000ec4b66140f558efc29047fa4cadc629c72756a4d70727bc00b7461b74baad96d0100000000000000c4a4b433dd005332b2ff4af2534cae3ebc0a696340d8add1a7399e95bb0ffd7301000000000000009c1233cf95166c9255e9a6c5e0e927b8663b1b8a5a179b6bca90aaa1608705520100000000000000100f8e7d113d41416cd80214599a75f85b3b19e96cd2da7039c471de2006a64101000000000000009c1b9343afd827544ee40e6f45af9f75d1d4488dd9f479510ee8cf47c188f92e01000000000000006cdaa7dad71fe0ac92388b99fe1dbdc5425b39eeaf06a022ea8d8976720d914c0100000000000000c870afa85f2324fb5d56a923fb5a481e606bf27a3660a674ecac23d81be6cd2a0100000000000000e293330fcbd554eee59e0561b517231bde9f1bd1c64cf8530525f997eae2690901000000000000008a40b5186b72111670500b8cc2794555e4c67408a703f89984e957e9dd9d532b0100000000000000e2898e69209f2ba56c07e0c3f037fb20a97e5980ab3bf5f19c840b8f5d14641c0100000000000000a44074d35bfb2169d9f8bdae3caf80bcbe66f8bbb20dac30c29393f2eaf4967d0100000000000000062b28feb8046eff2268a58ade1d5c30db94ac566c33ff5a99a60541703431570100000000000000a62c0a8c58b06086dbd00a76e81ce32688c54f510a13dc5a287bff4303f41b4001000000000000002aefd1ba05d4f7c13ead2b20b4d868e8d787b8fda35b182040a2aa9a8a163c2a0100000000000000beaacb072ff2347efd4169fc7993ae758ceb56ea6f6897cf9a25ff71cd41cb02010000000000000012541487fb284062dc65881d5a326f3c963924b68ff36efb87efa1520c816d060100000000000000a82f7ab0942f27c701a45054359b3658662c80c0c8b1ccafe8271c5d3489ee0d010000000000000034a21abcc93598500f0d1304dbb4df783cd4faa9ab063e4caae4dfdc09899c570100000000000000a47830f3a4a794fba6e4ab624dada5b5630a82f1d61eb6dc406e01319c1a2c2501000000000000001840550329e05c67e05813a375fbba9a2ec82d1934388ad0292bfac2b45bc1030100000000000000ae2f6e59604ff1c1492face76604bddc0c48209485eca50f8d82cf67c727e37201000000000000005af885b0d311bd032ff14efbbf42adfae2a8ff2088f1e2259394bb03409dc4330100000000000000d6d45addae162f1bb235d16821fcf1136568b644721367bd3dad6a9ba9dba6660100000000000000c248107abc3ab176f7952a645a89b3cbe1303b3af53b184a32360cdaca6c400701000000000000009e51e504c5eb7a27f36c064c1eb1373d66a4e11397ed215dc8909db7fe523d5e0100000000000000da5f327512e71306ffe1905a70e12a259d0f0ef611dc7f56be8f0ca30940033e01000000000000000e6bdcaea0c05306fe727b5593d281bfa2eb7ff3aeb4bbd965af01396807d6100100000000000000f69d3157e535952e2155d65b90f2059bcadeb8a7b898e0443dc2f719e5f0841a0100000000000000d678254fe840948c178fb1f90f58fe093d1aaeb45b4919089f52c7faf222f5000100000000000000eede1bef0c43d1266c31905705140c0aaf136887ae4446bbc94cd5440d1970660100000000000000868dcd842b9e0304ac20a855f4374fedddad8781be75eabdf4f1f3d638593d1b010000000000000066c9290ae33d9153a0635c8b00685f11f9a89dbb3e9e907f8bf56c7b1d797e670100000000000000bc7567b86703c8aaf553fbb6add4de6c65ace7cf1816e31db07c6b4056ee7a580100000000000000e0c2d0e86ba6e4484856ec5a68dcce77175b15ed87d436bfc5abb5a01f38c01801000000000000008e7d9341619a28dd6a2733adef62b13f8cee0733de46494549fc38ae52f02d6d0100000000000000c43d0c516a2377bff6712585e03422e226f99014c9565da7d61bfddcc60ba2230100000000000000c6b1190ae760998a1286b3e6b9dda502a335c56f868f442b971fb891f361f75501000000000000003c611bcf0d259f8b57865f3250a1a09ddf562089bc322eee54be703ad422fb340100000000000000dc0e6a184863bce89bfee06c3c046a7cccf040d0d235865c6f0c56a75790244e0100000000000000500f6ef080819e095e751b2d13e5980446df95cc3997438f3b5ffdfa6ea0ec5a0100000000000000ba94bfa261dfe0b32d2697c88f97b9b1a3e603d69f916023dd3afa9d8f3715260100000000000000d4d43a66ffc7bb3c0a543a75fa1ba8c1b25eb338c2165bd0ad9aab3ef367fc170100000000000000be495e4b7ce4540071ad9d1c723a5a12a7f90801b10f20b426bacfb2df0a7b2b010000000000000060a92cd57e6ccdb5667307b0a0c9daa782758ece051899c00a7cf0578c1f131d0100000000000000d064fcab2c3f2b380af1b6ad48f31c17df5ef9ff46ffb7bdbc1908c7af5ad26a01000000000000000244515309e58b54210661c190fc86d5b4568eb4e85c578318729b50cbdbbe190100000000000000c4d7221426175dcdca951d8e377fe9c314972a9667550d2980d184dc7713244601000000000000002e2473679e557a5ab4b5f53f3775458dca1ecb9780a69a12d491e06e7b9c4d7d0100000000000000666758dc7096f360265acd0446e0e68d4d2c6787ac5d2a5d5ca76ded97805f2f0100000000000000ea4a297a0d3ee9491ac96d774dff2da3c675b3d7e4dbb9fa4e76c047b093880101000000000000005aecc7d9d96f48094caee4d395c8891fdc90529b74d835dae0d52123e5d2386b01000000000000005e092b5458ab949391f3323be3b09b90f02c9f2cf57c99e61bf5111a293c245f0100000000000000285f4d091e0564aded8101aa98e417e47008261ff729ef856003c798fe62725201000000000000000abdcf937b0f368b81ed8e41a7060a9d7917e6339c48b6abc6d8517b00620f7e0100000000000000f25c49e92258e740a9a79f938d3ba8249438b07884ee48a4973bd15e6fc52e1901000000000000006233f94f49a8fde5ca1b30460fabf5f1537bf4eb800caef619fe1c5a0572353401000000000000001845051fe44100fab50a4621b65b7c189083c3701b8366e0d853e139f60a45470100000000000000a41bbd69c636b6c12fc0512d2b016808851770a22c97337acc5fc8240bb82c0b01000000000000006ac55a703d9f7b20d49bd2387ee9fd6f1c6447a3401f793ac7be2064f3b6f464010000000000000092061d3be62cbed8f429d95dc1eac9d9bea8e23c12499426704ca64ecc28675201000000000000002c44ca2a77214e408a8ebbc6ea0e31cc4c01f1e9477afc11d9903a47777b345801000000000000000c34bc418f0ce9002c060ddf2f2b4b6942b1163f4dacd5608c0bb6d2159af135010000000000000092a908b3b5f108df9401ff0c6213ebdf5eac412295be6fbcae76a9320f12231401000000000000001cbf8a564c0beda112e06ce28226c1775b350a506fcc9133f1c87594a655e02e010000000000000030ad59b9fc56fe3d392dfcab57fc9b874707c1fb3d59ee78463f8d74042ac3040100000000000000361601767f088b9ffd46588d7049d46d33f1acaa4ea4561219c4709510c1cf10010000000000000074111e0c15602d7fe0c6eeb12065b07c83d890b94029c8815db2443a01b0eb2701000000000000005ceb546da5983b4c431d196e5bd32aa4803dfb513b792066f83546f63dc7534d01000000000000004a16b874d8591d53c896cff1d280d5c4e5197ebd894804efd727e4bb37bde7060100000000000000141bd9a074f86b6b6851e89bab648df0e45972c23acc7ccd9d5bec4adbea3a190100000000000000365850afeaa8058c89017ceea5cc3dd218f4955d2a4567f8a964ef389078c54d0100000000000000608e3ec03a52c67c8b0f637cfac7345fa1c2b4b61d09e50bc7b79a2644ad053001000000000000005a7d2d2e52a9a7f01b1ef3b252202b4c96af120e867141675c0820fbb67738700100000000000000baaf82c13c70e24dd09199d2fabe38d3712470ad7c8235bb9c47df20870d301c01000000000000002c64e7353c81c2b4ee434edc4cb8ee6d7a484371540875c546f6c9b42f6dc2300100000000000000c2a1109372991310a96d78b8da314163f9385399aeea368ed7e7c37f201893340100000000000000a4db5602ea76d64a281129f46538f5c0384c1bb9e3a7c5f8b96c4d893f963a6e0100000000000000040b8138245dea143b0e2cb70875ea60ee74ad3df50b916db931db5e583d5b0f0100000000000000f6d9021798d518b38d24a74035499871973f7e0e67a3bc5ba37c5d4893e1d9480100000000000000ce87603414deb87155f1df0a5bce17ab2fcf11302407b96b7b73ea33169a69670100000000000000f045461d722b94b6c0928af47a0d8ed3b78d646b2f4f754b1c90196465d7f92b01000000000000005ea61dfeccf577b54216c24d8580984b5a948fc2e0cb15cf75d3ec89ef00470f01000000000000004eefad32d3c3dd9b713031d2d3412b110a5997283e12e1573f9523bc479e77220100000000000000949ab1dab46139ac9df94d74a3a91b263a98da335b9127a2fc7369476bca3e41010000000000000080cf2f4b8c6f71647d087c97a83df3f3eb230bbaa5bf86299a2d3c8ff2f2664701000000000000004a85979f5a5af886d6808c63ca7f266f7a55bbf652e039b72f1fdaa9c90deb4501000000000000005878e7553def1229480d89260e485b7f3a5280d16b9c1f1163151dfe472e247c0100000000000000d876abe03feb4a8bcfdefda30228ed6820b1a1fcc5954148b0ab36c88fd1ef2f0100000000000000167fcf03f83b30dbddcdfb8e4854b03645d77a3be1268d674e65ff2085615e0701000000000000004e3ab53a4117874a7fc193a4e51a4b50efb808cc0f26a3c3616cecf4974079660100000000000000502537a2d009d0bc18752420e46bcf3ca17091bc04458ab50188240361ef633a01000000000000004e3ab9c976dd36a1fde5ba6c85e91f24b0e0f1a4101b2abd92f8eb6953593243010000000000000072febf0d8454db0a0c9287d7b22f739ba0154b6319111cb5104130557b6fc3670100000000000000e036c3ff765103769a69a0015caee3b83fdccadce2a269a3df376d0dd4001d4e0100000000000000e64945c884e8e1f126c36899bd7540a6866ddc58aae678cd0415e240dd3610220100000000000000744488e84dbbc73174838a2fe49f231ee82448487e5e52c5268f9e659be8e95f0100000000000000e6dfae0c3ed3754930c79a74e58d09583dae0bec35363be7c1bab1c3f25bcd480100000000000000408fc6ba61cad770bfd6555973b079db6ad49880fe3335c4561eba5ee649372a01000000000000001031987c29702b10623bcc65912a080416d2101ac6e204a2075d9a66dea5752601000000000000001c85862b279bcf4a725b66e8fdbf4150e253a52affacd77f2b52400a2209fb430100000000000000c648a1f7aef735b686df2634b86d91c94428cab340393aea3a5494d02cb57a4f0100000000000000181ad1bad2d424abe7f915a584f39a6953a87fafc1ac0b9942ea2c64406d4d510100000000000000a0668c4c43e71d99ad863e6f10e887b2a1f2bf32778d1d4d6eab57f28ba5425d01000000000000006a3f450101fe066e048ef87e288d88e0635b3677ea222f17ebbe87d4fb92575301000000000000003847ea56b040d92b53331bd3db1cd353b1001bb6678bf29cb12414eb5ed03178010000000000000092049218a50b3a59059599fa127bc3719546531c80ecb68d8ca8f5a0671c915a01000000000000002e1c9f1141ac38310b771a3b1477ec4456a02000abf09c4d0fe505cbd8cc0e2f0100000000000000acb79942c9051fe2047ba6a2f0c73ac71bfa033ff1b76d84332361573dbe8a1a0100000000000000848667087ecc48b5e6ce7a424eb24090c6b597cf121591e3d648bbde53393e79010000000000000048f1908f2c135d1c47c7dc79f63d6c4154a56ebbb46b039db014d8b444cfc1380100000000000000f01fba66537030d59b1e78a3246fed5a25c5322734ff422a9609e78eb0b6bb230100000000000000000ea8eddba18d4da471cc88fa5ebf59aa7e0cdbbcc7ab13643e2f9d2a1e120701000000000000004ce0e117bccbd3903add7cfecf3546010867b1a5fe6ba3ebd189e4cf9586a224010000000000000042f67b46c75b06dbebb389f8cf054f7c82187a85ed234f3920b919a161046e2401000000000000000a5aa5bb5d80bbf7ffad004fe82a73d66db8edad93ff6ce0d8df78d3e0e6fe1c0100000000000000acef4f49f13788a7356755512b74656cdf11fa80d7fe7223e250a7478d2a8d3a0100000000000000b4acebdb8857e0261b2229a1c6848cbf75001ab181e0504826d6ef04ca04e92101000000000000008cf03fcba2760c34855ce798becfa293b37a78b79820144da5ecb526f633cb60010000000000000082c55569a2c6861ffa4f116b34d3c6e12b5fb075f048e7d6ce35ceacfa40a93c0100000000000000da124054c707782430358583f3fd98aa1d4dd90968e8b4f3d8683ea3f27aeb1f0100000000000000f2bafbef8d77c9facf5a04d9c079d847bf793bbb1b659b4ea4c7679557007d7d010000000000000092ba571f52063897a993dc9818bf50cf039213409b550794ec32151fa089d31b0100000000000000da088ee3658a5e9587f0e2e7327bce5f20d5cc98cd986a5bb817d9b9a587567f0100000000000000dea6ca7823dcb0837f8533d8da27fd1a1411b1b48df4a8706b2f1201baf23b330100000000000000366e377084ded70d1065bcfa6e0629d9df8b01def4ff4620432dbd1ce70a6a55010000000000000098755d5f5fe724fa2f0116ddb92c0d9cfac4da3eeeb51e3ecf0356cb2966a71401000000000000007642f0c1c4d20c54338f0e3fce9b66576b392cf5ad165368c9924e1773c8f0280100000000000000ac198b700e0e627e2642654d791a9102e19bded7085332a1b76f543f0d94a1700100000000000000380ecd43a50457d037678d57a799cd411c1f56265ef38e0f777790e7ade53e7301000000000000000a42164a9749a911c34ff4a8ebdfc5d004de3fd7f06891fe5c882d2b56c2e6340100000000000000c2ebf281e96ead69e96276de04407f5d89a6626b5cf25bc97eab4d35b1b50a580100000000000000b64acae010e2e85df0d36ce79d919a14c43f0dbaba232ee96ed1e5708ceda94b01000000000000005ab857faaed9e6c7aed2d200aa81404e546160140fd0e5f314df885f6cb2c31c01000000000000000c7f753c4216d22f5e9a56f145f2dce74230a875c77c15ebb57bbc672a84bc5a010000000000000076f35f08d6050caca1bc42f1bd55d6d300580caf671f4a3b3bfa14b23b8019400100000000000000e8a3d82b64794602d009b8aa44a922a6c4aeb9c19c36720026b9b010e4880a420100000000000000442b8aea3303213b7f5e5b592ee0ad9a2043c34b62b8bbeb61958eddfb70912e0100000000000000c6daea6e872ae2760484c874d425703ce31f238748821e5fa7689e529d8931380100000000000000a2d5d4f254b43ef338ae46aeb6c96293c9e80b6508a4f9e3534ebc3786c3137b0100000000000000d00d343b5b85e47a6494a75fe67e3524841029026739ff98f7614b269c7e10440100000000000000e434239ced7867561ec1c186d12742bcacc9e69a19f6677540ab902f0e450a66010000000000000050a9d209313d30fc0decd461fffd3f46248364ab3a7e34932ec3b1e4ed720334010000000000000058ef7a28606a9d21571e962f3ef8b8adbf9d3acc41517bc9313985759ff1933b010000000000000016e5bb9cd97e2054f662f70325ed8440637c299e77736d3c31795e1225746c5e0100000000000000e03b6d54dd7017cd4ebcc6642ab9c23c62e2d846e610c7ee2e2d3cd71337b55e0100000000000000e6801825bed5f4cce30bde6abb75623a845bb633ff2e14a6d696a85e92690e490100000000000000f6c65ba1b18a790cc704f8e3a1bf3177ff51e7eb0081df2cb69173bf44a4f71c0100000000000000fe84d61179f6f25c0f3b84b4867b3dd0989aa22a2d9bb709ef67a8095008414201000000000000003231e6a8c5c5e47509eeb593347ec53cad6f05a0b30938af3861075488f2b03f01000000000000000e78888c098cf42dfe016c9ee1cfeab499eb765ad64cac48b28892a0a41ac20f0100000000000000d896c042b016100c440efe8a5ada9d0fe30f4eec18ba67992bbd1d5e513d147f01000000000000005a2e0cace9c852e709ab431b7a2ce9bfab90fddb0676af571452b8373756f63e0100000000000000de32e1c268482c89301b141116a85c438b6ce208513c71a12fd81777f144033701000000000000006211919c6d1b11584fcd6f3f3fef54d0d72f23ea821685ba235675bce10504290100000000000000e21d26c4a79725351bdc30e3825afaf4ec037fc82890c9eff8eab6c5dbe42f0b01000000000000009e25cf7dc9db334d01aa6215a0a9e27468a5bd09ad742bfe68b302c3975c31510100000000000000eeae3f5142c5fb5b16632f97c3885fce76a7969df551a3daa26c0690c610716701000000000000005adb6c8cced26392d5bf6151b5118a54c8c83d4a6b3bc26c2a623eeb3b2ad3190100000000000000ac91d2d8a78bb7d759742bd920741ed92c02115c7218d39642dea17086de3d3a0100000000000000d263e15b1310b82d6ab02f9f77b307fa234ea4a139b1135739602ec3c97e3a6f0100000000000000c20ac319e9cc300bed0ac95df0fd200751ce194400a66775ba8b68523b3f895e01000000000000008890f167e5966fb28d63bf02377a8b064b2466930fd947e54adaf5bbe6019c540100000000000000f6e92ca7e76a3ead20727d91199658a84ac784f496f078a71da0ab09e16a6c1a0100000000000000f65d2e46fa483be5f4d052b2f03563fdf7621ddcdedec753a4ef9508782e106f01000000000000004ca7ddc4814c4452f6d1c98371c6d2ba26ccf94841249edb7fd120fed311262b0100000000000000380a3f04ad89f508b389352994ac011d1845187c479281320ff77f4352ec0f200100000000000000a2c51949aef85bd6d86dc1d699c453fd89f19e171c580a43244b37ac32db775801000000000000008cfe7abfb01580f1d3a9633b11b9229b37f979e440073d885ecccdb32eb64e4d01000000000000001e2d439fee3ab0966bcfe0569144e399d8c1e431528b04a9a103a0d5d76bcd5b01000000000000008cef7f6ca0d96d48c03771e33900c28541bc7d5ee15689e697cd160aff14797001000000000000008c63ab0ca01ed8b358bb5eaa2582a0866fbbb68d170a03f2ec98e9eeffccf85a0100000000000000dc8fc9b4d3b7bb540e9f12c606e4b0d784dbf8542db625f56e686a2d2fcfcc25010000000000000040fbfdbe5c13e3a46a731b8191f92cf01734f42cf7293fd56d664a732c14ec5601000000000000000ef24b61e18983aa285f70ff8e361397b82a15a41c6742541ce7b92da9f8931101000000000000007854ee17d89a8b19b9756fbb60653d788f6d261fad13a56933e0640c674a3b3e0100000000000000e4a3a09d9e9b63295de7b3cb2284d87af269ee20b02de0b66fcddaddededd0070100000000000000e2b114eb3572e899237c85d50df9e64d4f29901220e9de1d7a8e04d96d2ef24b0100000000000000a42e70c4530076b877035b43c2e41672dbb02b95bf1755e1f808e3cbe23369630100000000000000d8df9dac3ccd81b28470ca51de56a4ce7c637a0ffe4b73a826cf7fd33b7820040100000000000000fcff2cb776b86e8a3aeea98f908c3195e4ce5db676d36bc3a7af3608698ef0690100000000000000b684476641cb59f5820a10f788f088080a953b907f990df22cc7943dc3a46d3f0100000000000000c0a1d3aa25d5150e5c38220dde8f6049268270e41242d623989357654078362c01000000000000001431ee16d3a18ea78d6d22801c6a25fe304172dc5febbe550026a1c0929864500100000000000000fafbb4dff5111e01f4ec36533c57a92f691edc68cf3e86a2a51ff16113728b290100000000000000a01d73a031bb8f4aba71d271be16a28896c85cfb9e2dda4fa3b44936207a2b290100000000000000eab3d83726de7218ba112c82a45c033830114b31870c16d0a49d9070621e012a01000000000000006c1eed8b9700d423e00bf57ddb3af32d21d8f0a104dc650e871ff044fd754708010000000000000004ad9d2a4b586292d19f935a31d89b90df9c99c74592adf2d29e64a5e79ab0120100000000000000e6dd828a602eb81c7522b98e53ceee9e22d0cb5a6f7cb0e3b00a7006f0e0770201000000000000006c7642774bd973c6114e6f0c3558c8f166cc9c8e980ec1d7ec3cc0276426c77b01000000000000001c3d79dc60c89a9873f2c5379ee71f4f298165e07109d4d47bef6d993a084e1a01000000000000001287be7a45554529c73b9243a51a201100a37a2c2509a9fa3acaa4dc04b5093c0100000000000000325f3840c5a9642addc7b6648fdcc21495999e5c00b368420b23e8173f5b9d570100000000000000820409942da35bb5ab23f20f62db3f47c5f87492b13f030734b7e7fbcb05312f010000000000000088ae601d5eca42f99686b6d19b3889fa79e32551d065f1cd650badb2fdc9450a01000000000000002eafead8f602b2ebe286db17142e60a41962e071346b4f1064fcc70f2e19552e0100000000000000de53a86b00a4690bb292c096b97dd222fa82e28a833bb5969aa9ad4a273d31630100000000000000f0efe1c1c20dcc2667571aa3375be67362e7f28a49bbd9195f4b5d71e99fc1650100000000000000545b0afc9dd9b56d8da46d35f40723548fc4c13033912da4fa1da2e41265047c0100000000000000904d79ac4481c639afe07fa078bacba2d3a890b3f8e133cbbf744e7fc5297558010000000000000036e5fcdf6e878fee49aedc20f21dd56bb41ffc148d32c34ade94e6fe40a947250100000000000000bc615a1317f7bcb567f078dcf0fb173bc02efaaaab4751c1d50c45b9d1db891f01000000000000001c808827ef3bf3e9de1e02128c8dfad7c13bca0a657b416dfb2352b48b02db3b0100000000000000ccdf3a11292657bec36bca11a4d5be787cefd82cac4440d0682b5cbe52af6c5e0100000000000000904c675598403fcaa16f8f3da9ffd6fa995046782c87590a7c715f2cd590543101000000000000008cb90a65a771000542944e389890ad0abbd2e2abae26dff9c389dbdb20f525440100000000000000b6ebe42548778f3fbb2fb24dab7aef418391da6fb9a07dbf8b889e338ca825010100000000000000fab692e1a2e6397aaaff6fc06302935eb2dedc68917fab400c049df1ce790e2d010000000000000022aff5aa2b5c2f1b1ae4ae3855f3ead541c2f0c9093a946178e127dbec92a04801000000000000008056a44b805cb78adca43e77724ea222b5cf6c25d6b314d38ba7c169576b0e1b010000000000000096169e83fd02bc9ed28c6c832a3240e0e01bffd7f8e5db4f2b85521077212a620100000000000000c848b06bb3577ba4fbf042fde0779bb9a7eeb2978fcde090dc5733ed36043e2c0100000000000000faa30132a968c6ad5db9938a74cfc8f2a878349fdce5776577bdeb3db7a96a13010000000000000026b3a3fb30b16dd2a07b9b6887998c789f2e0b2b50a4e26b792d16d9410215790100000000000000e68f1824cfe5dcc175d3796ab3ab85442bdb76f9210ecc09e01db47291f7e5110100000000000000b84d61ad8579f5930af27feb2c48e63831bd2929ef86c3145e1bb065da6b1c4f0100000000000000f4ca3b31c69d7a198e30c876e1bbb904ca8c5b038e5a811667d13e391e13207f0100000000000000fe922a6bcbb95646bd14a97d96c31cf7a4bc599b1ca7a53f39eb603db40d5e6901000000000000008ec82c3594fa26941692a378722e80fe77a45b83bd1c6adaf06ad48bff228423010000000000000032143326782c6df4957093bbd84260679d1373bb0a62bf12db89e2a7c6969d710100000000000000a0cd6c7a7119abdf54904eb287334be70f7d9ed0b14eb82e4b453efdb7899519010000000000000092ea0b49900eb565e46e7fe6c88d93858a5908a48e13c5e073183df24054bf44010000000000000044479354ecf5fd25d70f527143ac61b7824743dd51426d1eb89964e05489af02010000000000000016a58215c2c1c4a19db6a79ed12df45d1bd036ea02ca7fb81798fc28e80d054501000000000000007a9b8439f7407ffcb77f97f7bd878edefcef5c6c780bf4a49a5b4ecd9fe850190100000000000000e238e399e8aa6247525fe6e10f77ffe34e93465f6e2febe695ea74f90e3ec32d0100000000000000b45bbe08cc25b9b0e98a490833440b7932e4086201c82cc950376eb77a6ad94e010000000000000050b8e2a374c29f6a14cd27f72c3467663915b006599f220c40672bd080a0217401000000000000008ab52131785bc4db73448c72fd6bb6f55f6ef370e9293489e124d0eaa3215230010000000000000092aa82fc527b300d0365a62edab188ed0816aec9a68071f88d7d9fd78efbfd52010000000000000028bcc25f41f0efe2db27838a212f5f7abad9542345b3502ceb91226ef95d0e080100000000000000b2370f9da2faebcebb91db0f4acf2171e4667e1e88f6a011c1248df5e19d85310100000000000000e25536d123bb812892da8dca4cbe099a8b1c86ba9aa702cb5b95743935ab9d1601000000000000005467c16e538a310001c285235bf0ac5fe3a9caf9d82738451a9dc188c892cd5d0100000000000000f6b6e74ef97f7d14f4493ff14c9f0f6a76cec7a4f713d48fec5d008aaa830d4c010000000000000004e09dba366325cea9b0b8df63e0a00a917b53ce34aff64a8d1c3e76a15fe34b01000000000000005ec8bc4e1b32dd9e65c029e27494ea67fde94ff891b068f6f01a0ae33b17312c0100000000000000569605f7c34797a11605cec3b4950f8f5c690db51b24bbe366a1259105fbd1120100000000000000e80c0983f6e5a56461e1312af164e23f0bce9da93e1339df16b99e45a335434a01000000000000002afa459c5fb5a2e43b8c6162674b968cfede7f551acbffd9de8ce753643290400100000000000000c6ba3592ef9d1e79b36a3cb707372fd1e0c0994887600205f2265d370341c71b0100000000000000501a9f8a14e0f544bfa2dfa5793cd7f1549d45d213ac99a09f1dafd821cee6330100000000000000a4af5fed768cd4433ee90c534f86cc4a458795fa7bcc54b0f3ed8cab36cda46f0100000000000000048f25ba12ff8795ec8dc6a7ebda399cc18453accfdba1e58b111bd3c3db322701000000000000001628bdd8cc64a774215982d34698cc24cbf79a71c526e1253259c75c3ab8273e0100000000000000f86a955e179e71bf054104adf25dcfe05d6b4844c14a70b71818c6e0b1dfd81c010000000000000012a54ebefa1cbff0e621b59449bc24d94881d988c2439178c91ce7ffa374a2430100000000000000b2d01a3feedc72f78e575c8e77306f2f337f33a7d8a5a7d28d27dc58849a631e0100000000000000466e7c8220cbdbfbd1acf470b839b2c41f428ae619698a8bb640f10f0d9ebb6f010000000000000008427554054a0e5a7ce541a69a3b63c4734661eb39e303e3fed502435e5a73730100000000000000fc6000725023c95d259e5e405737c39a2965d6289843d3448fbe06a5165ac7390100000000000000789c23ffdcaa9fead87226e570e0ccacd11ef3659a0c7b5d960517d86797bf0b01000000000000005cdc38b8b8fcecdd85ed9c6ae897c0c6212d8fc55fc06ed61f503c157688831601000000000000000a790d1f76d6fafe4cf76f5086e16b4c0102e1f05bd92844034b342a69c2303801000000000000005404349feb5559d19fab0a2f924c4cc70dfb9cb28fc954322f184bee5026e10e010000000000000064ef00a9076e78d72baaa85d81238c5c02efafbfd43a0c07d8e57c19ef10b34401000000000000003e39bd391328e498ddc44d51f5ec01017790c2a0334210fce8239ee47487dc1a01000000000000004cc73eb33d62c0ec3e7f886195bcded7857aedb9ac161226bef22b0317582e63010000000000000004b263ecddcc9db4186f90e8b65bdceedcc5f786fb72698779f4d523a7d28b430100000000000000fe8fe762d7b321d00ca277b8c4aa433bbc8001c7d1d7a2846929c42e0dd6483b01000000000000003e6f47144b40eed6ebcca435d33d00d4609bdf3e7af96270c65e858094b309150100000000000000a0fdd556e5d6c1f8ced14627bdf01fdb4234118e85703d24274a265c45f92b4b0100000000000000522263467c287a18428c4a5a92f37d273699c9ebb0bdc21f5e9a49eee434dd360100000000000000c8073eef80dc2bbcea972a83302442249e5d2a5213e65297c60d56f4e9be86330100000000000000fc04c8e12ae94b0e3889ca36e2b4a09c32f9c6b7bfac999f56e5f2dc72badd1d010000000000000096c09ca3191f61bad04d1bfe86d042857d3df82ad457ea844caec645758c4d220100000000000000a04f6b14597d61c7600128326a1d75205fd239ade0c3b25cf359ee731c80c85201000000000000004e0a418c4c371c6e99b76781644eb38a63789b5d7c5263f949751e9714fa7c350100000000000000f4071d8ef295841a0bc13deb8799e23dea815e376eaa23c86f65a5083d9adb650100000000000000fa4086b525cf61d35736e736110859250992c443420778a90cadd8e8c88d8205010000000000000016dd18d2e8f3a507321ce87ca52145f9526c0fe35f7c868d95fba2b986f84169010000000000000040eec19a099c9ee6ff24e3eabd0a7bcf36e0e0130b4718d50106344044c0d6270100000000000000de220179c5aae1e95b3ccf3a989ca36ac76061bde0cf710670a6910d26564902010000000000000090c3500722dd6123a02fc74476a10b866118e9930e9c73d6e1d48cdb6d57e43201000000000000004246cd1eddf8b9255734a8503025d3d0a30995d71fb5645b1b16be777c78e901010000000000000070c83248401cd7b8780895793b6a0e568802f297a3bce0af7d51eb653d23304f01000000000000000854a35b9b536c8f18af98c90d6e8e639f855eb29bd76cf5105414c21a1ceb6f0100000000000000ae7c51f4c49c174b2400d057d61a1f43a8e13a6850abcfb079a909d136fe374d0100000000000000ca5b9515daf1925b3e200d73957dbc4aafe71160d33b5635ea57641eef8bfd7901000000000000001a43d2bff1c184ae4e83348c8b373df9dba72d90741f3e3c664ba6a2cc96e9460100000000000000225ddc65b656709973da9546280bb4b9921bda577344d3078f29a2a8d463131101000000000000001645c1277cd42f9d98b76f5b54b9c8e33550d31727f24e5c5c50acb1089f68600100000000000000b6e834b1c53a32fb3f8713bbc96bf812c6e208232c2d6f094805e8fdeb0c876d0100000000000000b60af9ed41b27a8e5667ed9c45cbe12bdccece1dbdcc096c0763c0f5046b7e1a0100000000000000f2118bc805ab00459fbbbf94668a8793c8fc142956a91a538b55797633728b4e0100000000000000e2d0e3cf521829f443aae687d4533647fbace9e641f5fe677a2c4bc3d7d3b142010000000000000034dcf88929109b300a7d0cb74fcf0ab261b1a273e09f3d9cde025e8a2b10d62001000000000000005eae7ec92e82f6fd8bf2b1f720a696d0256b35724406cc1c16fe4c071448311701000000000000000e28b34ec63d7e1e4dcf7a5136b63bced930bc9e02d416fd5aee66d248eaa2020100000000000000ce49383c47b78ad330a3e5899850092ed6c99012b59d57307de5168d0d75b423010000000000000098280d11d9c4bcdf35884c994da018af07701df1a3ac3da0fb8bfcc95b7aa05701000000000000005eab887073ebf35904cfc25021f4f15442d6f334488dee6a02b9117698f0d538010000000000000078dcc07cc7a2f8cce0384b4e14f9518d3f771960abea998292bb6a52f6bb88560100000000000000c8387a2ccdb4ec7443fd3cee75e8e559990acf2db6c54a88cd99f45fac1a66660100000000000000327e66f794b3379ce2ac64024813e987661fd4567c9f067d32438be273911f620100000000000000d25555083cf20fedd9b5aca0f26447b57bbc2788ccdcbf0af2efb0b010f0ef26010000000000000082693a174b02d3e8b8d6cd1574b5c782dc98edd92134854adc083ecb36c829250100000000000000fc18fad14af9aa7bdbc62e1fff9ed7078d63d1137a3c1e670bd3a3cf5a93d3720100000000000000b4765627822d1702513ff5648aa320cda597f54505757d630e96d66aa1f3c73401000000000000008a2c2b3a835311965eae34810e418fbd1bb858fb8840a850bf036fb7eeafdf7b01000000000000007abd57972d56585451d75240342fe7195758d6022e0120fb2cdbc9792bd2ec270100000000000000a2d8db3a4f3fb0687052b930bcefffc672c01a65538ee7b33ce9dab822bca1780100000000000000ba73d9161f8469269ece11dcfb9081eedb02df28643417622e258bf2a424a6460100000000000000084b56e7c7fbdd6b45d2db9e3e922a969b28318b35ff26692d88beca3a749b260100000000000000ecbd30a0be3dc9d3f8f4b29af83968bd76d0c62bea36505a114cff593e0b8b4d0100000000000000bec516da47d1cb28351ecaf868365c664aab9076816f1c536a24be5892426a150100000000000000688a95e2be5dd66a92c6b401a64dead0b35f90f517fab9673dcec192e1fa207001000000000000007cb3bd528db15070308159636ad9ca12c1378fa7f8adb5206ebee98c0986065b0100000000000000d8da588746cdbf28af1a52046bad4d5374c656a7111c4969094929548945df71010000000000000074c38c9e1867a292166ced2f98282cde342789a3b5d13c5f76f15998eba3e8510100000000000000485f591ac71f970ef4610ab2fd5f034fcddd9584d0213e46f3161611d141512f01000000000000006a33e9371b308fdcce6c5a7c6bcaa7f5678ae01db799290539167d61b2d1102d01000000000000003020c45aa46e4f75fd5fc42d966f1b99bdf1104a667722d91568f063bb2fd511010000000000000028c912c4d4ec34a97f6416c64424bf80494332248e6a7bb65784a6221f3fdc2b01000000000000000a3c03895ec10aa735b1e2eccb283242afe8ea6f17a675815112ce842ebfba0c0100000000000000e45e549d0f5e96b9c5b320e37828c80629fe7130e5ce695dccd3438dea5ae650010000000000000072f45f59c55d6206a31a1bc2361e096731b34a8745d0882ca2e77694b650134a01000000000000005405d5e7f3457fff778bad5558ace408e563e388edd901f3af12990afa7e8a0c01000000000000004e717a4d2c84dd7b159a91bfa677ff9dc57bc7fd3cd661523c8140a7680a27290100000000000000b280b7f37e7cc9a50d4a52e0b7fedcbcd3cae22ec16ed4b4b75ad460efc227160100000000000000282509d0ebf9a77532c60bbf28ba1509de4a0ccee5ee2f58ff267963b4e11c600100000000000000b85015643739ee03dba7b523f32db23cd192333ca1c2dc134be5b67c306f104d01000000000000004e8cf6fd4c64d23457b08b38b3a72a24279593b2aec4aec3294c67c92f4d801a01000000000000005c3a3673e42f0560adb44ee5b7be7948f8964eae967f80ec506c0147cc11ab020100000000000000d6451fc5d7809e480c3dd53853789b86678b890f4bea62024194b14a745370090100000000000000c0a1f13309be3124c2357c00f6a5545b843c70749fa8ece832dc77bbb7fb630601000000000000004c3b4d7a82e669e759b0cd7a6d6dfef7b94a00283be725f25b263d1b4d9a7f5301000000000000000c714d39ea91dc8f07a7b925a03895a148022b8f117d19fa2f29479189fe423a0100000000000000f697d527f96e9f3839e3502704ed69b500b765f6a64ce6dde88af24db12f14120100000000000000de63bd0d9626943917c561a484e93462de4c05d233bc4c1fd7c8b6833d29353a01000000000000001a4e01ffeb9f145ee8a17dcff9bef42b02b5c9b047e05742ac85de33639cb06c01000000000000002ad227e1e2b705e448702ad1d8744ba85d7d5756952b19976d3abe60d60fc70e0100000000000000907b44da2b726af53076263cde755c2723774fdc25ea52f46769d8f357ca9f01010000000000000078d57cef73882a2d361a28b7fd2ca671b3f726ddb22627d4b915217834b7f2390100000000000000082f296935a2a606bec1f195e6b8456f07cbf7b27e1e557b3887e1c46e4bc37d0100000000000000f8549151ec9c6dabfb6d844ba74728b04a8ff079d00e55f5ee55bc3b70604e0401000000000000001a12a4f27227ba6a04b5804f339b0ac61510b6c15abc85e3bd479d6536fffa7c0100000000000000baede105d1ba83fd42159a80f1c0969bd4cd6ee77700ebfa1aa3283a9d274a47010000000000000032e2a6ae50a3524184557db89a681b1de81e490b4066b4fd67364984da49931f01000000000000008ea9246c435f67a71e9708e1481478b762011e0965cb3ecc405155b4d2543078010000000000000048baca83f5efb426424d78dde70340fd86117f01108c84f4595deaddb22593420100000000000000d0d353277195e01a4236de8fdfe59b442f17af9028dd3f44967dcb17c2e01c5d010000000000000006978ce2bdeb4b02ac7d76a3086ef1bd09e3b9857e950083b50389a48871f3690100000000000000a6d6c761b15a3aa636f5254f6a838ab668421ba56e31430282b33a118ac9ff1e0100000000000000745f2d27e596483e2ebe6ee5750b0479f3c0d355af3498c570549a29b8ac730601000000000000007260ab80632d40fc1d7eeafeeeb99dd747a0f4800de34c2e82578786008c5248010000000000000064ba39c577fc90f2d2976ae979a7205a4918f4c8db532081f06bda711ffc3721010000000000000080ae71b506da12f8a73992372864dc19b64df51f1437fcb247c215a47a759220010000000000000032df009616a2b4d4f6bc9111f2ac419abf4890c99d6def4ce575f82fe7dd163501000000000000005a378e104d9df02d1dce1f9a344f3d4b6f9f088bd8904d0eefe08734a98b42710100000000000000b64c6d920385362820b9f7f9abe817a4bf64224c857fb65825376471d8c95e62010000000000000082b8b2c123aaccd04d02db43724f51bdb437265e59832714a2c2748ac83f2049010000000000000084ec16701285c3c5edc5ee9ae8172215873ec71af3abd53ed9d407c2bc029b3f01000000000000009c2328407d05c9c294c6f207f7c8829f1de10c13a77ae0707dba0dd9f40f8f6001000000000000005c450084e0a0ae90f805bb2e856bb5b46a43cbeda05a7c1e2cc09265e1791a500100000000000000e21ecb8f6b42f003d0d9d3496bb5dc1d57dc74e496acf2c4a3735940cd54231b01000000000000006aa3cfb637cdd3b960d2578823da2b6dac1083495e421c6c32c9e17ec0c905250100000000000000a25f7e57318e03aa832486e574815234404b22d1548f35cf77ffe26573da1f0d0100000000000000227bd022f3a23819559cefc5a323fe8347a3b75f74de5c7930db82cb7fecc8400100000000000000b4af11fd00750c08b33c22eb7445aa0d0fdd877bf701f0755c436d7907c1c27401000000000000002aee8f940e7da1699a6deb601858ec4aca5d597c50ecad1c3402dc97da339a0e0100000000000000d4738db14d68af6357fe1d33fe05635287c20b35a55a6df8018141ae26af9308010000000000000026ab3e54f192d9f8ddf020571780d10d92a0460284fd7b9ae94e74c926c2112b01000000000000008eb2e37eee463c14fe7855c6133aec6244ea3457ffb9b8e0f3a6ef5784548036010000000000000094d6f27dde80e42a7f95295a0f62492eec7be0b70478fe5f30f4c29e280cf92d0100000000000000d27d11dd2c3ab8a3e15a00507904b9fa81c0182176208f622543309788c68f050100000000000000f64491339e6b78127580800529b6ce624217adc82ce10f56d5c6911962e3135f01000000000000002808e5a217f5aa78b344d154002fefdfaf686ebfdee6b7deddfd1411b6a06d3d0100000000000000602f60398fba25dc09db2b4d8843ef9d96e0c60a01ab42ad976fceaca42195700100000000000000a0fc4378807ecdc482fe8d93dceebf78cc5d627c03f9412ce1c1d87d7b44090b01000000000000007008909e788793c04e57158251d1485884f3bb98741d889ebffca8ed9644141b010000000000000072dbf42dd0d9ec7a221912ba34b585d7fc6a0fa240d719332bc372180b3b636d01000000000000008a5716f1c004e49a930e1f0de54439f0a32145cf2565727dfafc11a024503659010000000000000030139a5482c8907486506234bbe2eed7359b6b7c0630468d46dbf2118fc5bd340100000000000000165d26b408a69bf67c63a945a49058f53250882485046f71375f9e3128aa9e52010000000000000060cf7d800df60f707be3b735805e13cd4a7eede0ac2ecbd7033a872b46403f2c01000000000000001872a4fa55daefa13b0b4e4f218cad38d08d7d39d42e47f7df5eaaaae02f0a370100000000000000bc800344f0bf1a71721fca1cbd7ccc47dc009987bd3524a883ebc1e925071f610100000000000000741b61fb1574f9989b1fcbfe4bdf72134ed5699983228a2cf27ca11d76c990160100000000000000ca2e3f3b6f3cf94b81b3bd4c55c730e41d8df97e97bd8084cf1cd24e68719401010000000000000060b1145a4451816b6d3ccc173fbc25b6c7b287fd270758152b92b30ae62eb7520100000000000000483e62e86514eee828c7144e945b7364598a01df7867f7f637cca09e702eb02201000000000000006aee8ec61df9edad0c5903feaf88f6297ea64efd82a52c692c5e937e744faa010100000000000000040d3137e8d771c19de798bd681da04262e8185db315bfe393cdb5cfaa79fe170100000000000000864b7adae776289119fd4da00567efe67846ede7f576955aa4ae889981276a680100000000000000ec52884a19e99c0df416f81fb211ef85aff4b0cbc220d9ff6cd34afe35bfc8790100000000000000b42d7f267a4e469af84c38da01808f135bd6853a7bd92f6cb44e7fcb952cba3d0100000000000000ac220a7798ad88df760c16cf8d18a42683c98528e854daefc7ee32bae85d646101000000000000003404a58d5e89fc3bbe715dd5921f5f0180059e74d9ae6196fabead7746cbc55a0100000000000000380702aac808412a0b886294725efbf4fc033b7bbb85d3b24c2876cc9e05d212010000000000000082af68c2e6079cea3474b2e9d76796b70b2acb3a0ce9fcc98503080f25e629230100000000000000345d63333cd7a9a17653f94374476eecf5eba6b1e4cc1c6e9ed215ba55e5cd000100000000000000960789a11e8dc46636aa94789c0985f9b8fe7fcea34abd0e56f92180c0ec773e0100000000000000a65039da8396aa3e559367c50a309f12e8747e4d16245ec6000d19a0af26f9470100000000000000680e8993ada64e3423fe61be3e70aa728ee73725fae78fa5bf8c57e8e1c54e44010000000000000052019b0e030fac4f642c31526c6308bf9501a297c9b6193a82d6915fb708ff7c010000000000000036b72e06ad058a7708948b66346a0350f8e61fa8c58b0e925600aa9bf1476025010000000000000068b06ac165f434922b8bcf48deb490e43b0d5497a2caa64654a1c673da36b83f01000000000000002654b04f0eeeb6c03a28fb4647305162a4e1112f631b66293ba16b563b971d42010000000000000052283740793ef867c77327902030e4f6c0deec494b5f4db2d680452880ba11040100000000000000785545f9357cc0a5e100f2ee283dbf8f5f9d4967b07aad4bca6ee866312430390100000000000000d60eeac98c4a07a3f08e3ed451c61174b367a787f720a6b7478178209e28334e0100000000000000843d6ad78e2665ef2b86f659fc0f12af29b11bc7f138e45f013e9afdc728624d01000000000000002e9d04dc6cc5425a6ba5e0f6d8b88623849a669542d42435c7c843aae0bdb7250100000000000000e6a422c3c7eb6cd9165fee529afd2209fab7f39b1ae491dc3d5c1320ccae1c680100000000000000c00470ddb02e84a7bc7500d90418dd5d133f4f8184ede2e15cfb43b881cb0434010000000000000092ef07386f9bdee89cc4cf8dcd1aa942134b21ace1bc2f121c261fb57388d278010000000000000026126b727ca3d359ad9aa85e71508ca3fb6a6fbe1416cec946bac3819fba9a590100000000000000d61549c459c94ab938a68b3d96ce644c8672effe9916ad3435324727eb18967c0100000000000000d6df798a261d38a33360d44a4d53e7a686562dec34fa12b7e5db309b736ca642010000000000000076f3274425ab752e21ddc0714e28e9651d4206841b736aac6db37aa0d0d4c937010000000000000098ffae7440754e3de87fca995a95250a59ea23cf595dfa1e53b1367ce20b455401000000000000001a089db458c31ac827e683803163d520ee1b08ed721dcd45b02c3d85855c8c2c0100000000000000267812fa98f9e8e92421e2cf5fc5de6a9423cb81f3b9242d57eac782b3cd09700100000000000000e4fae18a7a4c4f5196539556b85a450a0c1ecae4ef28bc52d1ff9cf3374c946f01000000000000001283692388ad47b5a1ed8b0d791c4f86a36e4422ef3857c912d68f5d425f34730100000000000000eae8a9a4316fe7ce6cb8f624a6615ea281f02082a5be9d40593db1048b234a1601000000000000008eaf59711472c3ef85e130855a4e34e0e3f2b8852af88ab561e9a49f206156280100000000000000dc6956dd35ff772cec5f85ce4aee9e6e6a4b9242495077d77df3e3c48bb26e50010000000000000080d7f21b01bbe9c1c6d89a7275a660af2c01570e22e9ca538b212a9577421d11010000000000000062c124e506bb86a27ceec1d2dc59e15802f14312b81e84f8998735ca13ee9a730100000000000000667ab5e18370d52b66a8c57cf9104d5732b22de108fd38eb14f8b7da2a29ec0c010000000000000032483a2e28748ea5b9d6b87256ae783ecbbdc3bab669542fc642d2c8a6e15d380100000000000000727f2cc64c33ac6bf5b433538f35548a996e80ca8fb8b1de84626882c7b6e1660100000000000000be210d84cead5ae1c0ebbefbfc0b35f33310d6aeecf4308529b86c2cf82c0f5f010000000000000058070bcbd7f8f06c03f9b7aaadc8678f182aa4b3672f3eab877525324f35774401000000000000008ced7755d2a8b17b2d9d157c1b9ab5b0ed63b25b9377d21230d346c7066c39520100000000000000caa319312d929a03892d06e2a524621cd9fbaef1f86d325501b4f1108e65a96e01000000000000004cc8f3fdf153ac8200029d319b8fea21e9b483bea1ada7cb9bd9265da69cb9350100000000000000349352bae005c140e9dfc1bb094e3c75fcd206a832d8bbd16cec98cf9f53423f0100000000000000346e685f148b3adfaed81980e47a61844871dfd86ce1d4f84a7b36998c66712b0100000000000000c497e8cc7d05d62aa5f59e9699962c395433b9ad38bbb84748f90c7d9f1f373301000000000000000ae2ca012bc830761e5afc8e1d81d62c6d160962524fdc3cff021ec49f34e123010000000000000018f128a7e5736dea14b3cde003c82a6ac8c922d2ef21b7e02e1aedc841033e3f0100000000000000a875111a42b538e568016c84c89f4107e37e5643d9a307aad904a7bad7fee76e010000000000000012082ef825e367087ea396f92c97868c24aea7ac2435c8acace02be51068b9760100000000000000a62c3f742cf861feabffd3f949f64703932d3f49aff5214e7ce5f7bb6966374501000000000000006a0e1ac35bef24f8e229f76ecf852ce3fcc87a884e8770c38e2677447e3c8773010000000000000004b06530357d34a7c37df9bee937d879dfe32d5506d1eac3fec50de7bb44dc5a01000000000000007afb0c23bd2d29c347a9b435a6db5b56e035a3e021f3fc9dcfcc47e52f3f0e1701000000000000009eac82acfe866c0d456b51e909c6b83dda9ea53d89b71945c3af1539f7b367740100000000000000dc514d2b1231ab63823ae49ff21fe0c250eed9b4d2e3e83d9cd5072b5f994719010000000000000078ab99c3d9ea7f380ebfe7db64dbc72ec4ec0516b143c2f66f8b06943cb49175010000000000000060bf5700590c273827ccd0dd56915f377fa2ff366ba72bd81942908b7f2a742d0100000000000000a013268290f12c0417675510df18855b1f1a923f0b513f7e7ddc1e763e27cf0a0100000000000000fe0446048559fda5d5d172dbe2ec70e54a5c3080394d055b0b15badf80b6af1501000000000000006ce1cc45c84f9394951284b950417355ff4a093946649a1ed9935cc650d3457e0100000000000000a253dd11fc5c488265b2a938adeff374961efab3789e0d9e637b566a317d71390100000000000000b0d22d2a52036e2e23ccf11b48ee7636021e88a63160754c8e161276e2bd9d520100000000000000744a5456dc956cb654fa881f1ae233b3fa4b245d357826acb8874669726a9a250100000000000000d8971835a1c90022cd05c0f259fc6407970d8fb59fc3ef0c515e07be6d4892390100000000000000aa22f189a8994b03920c2bf1d15d0699bb548c81c52809751cee4ccc794f5e0a010000000000000098f80bca2432755fc52885f1d8023db386ac20b33fe47dd516df4d9318f8ba6f0100000000000000a434f5d00ed6fdcebe637e8fdeefb417f9fa17c37970d1cb13e0ea4ac82e2d4d01000000000000009ee276b61fbded6deab6125d7b348e03c4b3aaffa7840190ed1cbcc4a07ee93101000000000000006ec37117872c92130592d0d0a6929d21e3411790f02e20cd313f09ff2cc5866b01000000000000004e4fe940a0fade721c4c3841d8d88788b100bc0964b65c53f1fc9de3f55d416801000000000000007e0ce7ca3586ea27861b81e7334e5dde29293682fa860c367b5a47e538e3760f0100000000000000549467ec60e011b6bc97752245e56aa4abb389ad87f13b914746bf88c3c93f0f010000000000000084a19d04d5544e82fb8f9f928af37029d73c82da314f437ca62339890b878e4a01000000000000002eff374cd044c1a795a453673fd050ac67510592d7e051ac432f9f1dc1080f6c0100000000000000d05a7baf5e5fe42f96c92c6243a6fe99cf7d5a2f314ea6c4672e5f28e23cd3220100000000000000b857af823b2fd4d88db51d82bbaf4d69b624e1edd3f4fc87afc632c34353971901000000000000001013f3ffe121ff83fd6c2c1f10175b3ad011d9e57c41e85926b1033277ecad5801000000000000003c68c21b23ad1e95d92b93146ddfae139e2ee2bc2c7a8b437c41e20b8a11d9280100000000000000ea8e718c93e3fcb04bbe362c8ed92207599394e6e06aac3145a3dbd6e068f04c0100000000000000e094c83c28dfb858b2cfda0bf6ac10b079e0c35b875365158201a60800d5ae6101000000000000009a14a73f6d4b1c9896ad4a10169452a5dfff45aebb946ea701a5ed94d23c470e01000000000000009e69adce8d1e72ba462dd7e226f1b34b91b038d63573cbfd2963948673045b7e0100000000000000045cebbcdc5e50d0a344d6e01b0a7d180c7fc14d8d007de35db199b31c026f2301000000000000006c1895ede65311cf2f00b5c57378fcc48a95a614d4aee03ea5fe0abf27d8d5580100000000000000003c355f55fe52ddd78f410db36294273478aa852a75c8eebffd3efa0ab2ca5001000000000000003ef792db76744a93838feb7da8b7f665689e6db4dc8a510578804a49320bfd6c01000000000000008a24331a5b0a512762ead3fdba639afeaf649b9d7ba5a939b502bae8d709640b0100000000000000b0951f5af1811973fa55ff157a38b0995e5700eea61ccff01c33096543b1b053010000000000000042436e76e3ac0e2b82971e4e7a063b51405500ed19c6a0c041ece7f9dbd92860010000000000000086e37259e697af7b4028a1cb533d906758ccbe3397c121010e5a094cbd10231201000000000000000ce0da1b140a5d9983ea7245ddc70a90a004d4aef4c5c056cd5fe3f271099a7501000000000000004017c942a56376173f9ab39d8ea985e21b703c3350280cdfb9e495795bbb995b0100000000000000c4fcf817d2c8c0c0c7adadfadb4ea60b6da2913587f69641ca43325516d864520100000000000000183a6a2a9ea985d9096c322b2ff3db841cbb158e4737965f5a98d3a53d0c551e01000000000000009e5d3be1837102ef421cf29a2c0ddbae40b42ad3e090307443f8d4af4da603750100000000000000eea459214eef62024f3fd784c5bdeb42e844e39bd272a92917d12fa66b62394801000000000000009a8f1dc9de51f2a09dada8a66c489135472c9d514cc9c9d17e8dbcd89a307c3a01000000000000000e4c162c806a25c08db000ed62e044cc8ca078f61352f0cc5c83f70cb514c15e0100000000000000100d6b2fc74287c9d030678b8ef8576c642d8ce76b9b6ce366c7ea5ad1bc4d7501000000000000006cc18e610d5c76a068185aa7f300215e2fa46ff5f1bb58307dc8f0df0b49112901000000000000003c26e68e3c41050e6603b62482ac1e6852f1076795ab0a9ec9d87bb4e76f1e210100000000000000568b52d575a8fec7299159b9d534b3268e50a59d3052fcbea17956d8c3e53f2d010000000000000008fac1a954a0e7a7702a7c90de2b1089ff57865e3181fb6e9ff7a1c34fd0f2650100000000000000b63e767df6a931fa235e41cc7bc47f939fb11dfa4e25698e443023bd150a437b0100000000000000d85c008dba8dcf0f67cae0e58ccd7e09aeda4d16e78ebfc94c0de3364bcb9e4201000000000000004e7f5c7b96db92ac862298a99bd047b0ca3f8356459c7e42bda56a2d80146f0b0100000000000000a6cfb4d5fb89fc4d3cf2ff522b7fd4267ccbe8ab89546bae641d1a583923b05e01000000000000000ed511c927dfe3a5d2ea0d858f16a008db97532b7a311fa82885fbcdd96e51160100000000000000ca1fd3a7d39bdfe8d47a70d6b1c82d19ccf792482181aade0e8d2903ee83a5130100000000000000cea92756fea9002431fe31980ac9fdd87f4139f22bf7436b182a0dd03e762c4401000000000000003c1a2bf3bc6b6c09a130633141343b91e3d84a9ee3572a00f5ce2a1bad23af7201000000000000003e7f4634849980d7f3ac6804336df6758ad393816afafa7c4932371c1d8797260100000000000000225b2615b66b0581bd26b9d2aa025900e83dde43819b4d94050de7c90d5aeb710100000000000000ee80790df6b6c2d1c29a053a364a5bab2536c77ec7c07080d5d6d1755a16e171010000000000000014fbfe3cfef628559b53f5cef7a3439af56553151254d5d9e8d07e85f10bef42010000000000000020a5bb4207395b9f205e128f899c3b37d265ff90531eedbf42170121029c392e01000000000000006e7b420de20353a979ff79c79672b543e70d70081eac397b039dc39fddb81e500100000000000000262510163ecdc49a4c74ec24e9a330d36f6ce31747090a05c4a264fe72ac3b220100000000000000bc45808b65d5f14bed3ffc08df2c647507893528e24e9470fac50962b60a3e5301000000000000006cc53152825109cab7a5e657c955cadfc846a338285027affe175e6e89d5c7560100000000000000e2ea16b3bfb7647e0bb6f5a8c01c0462662485630e4ceb1193ba0e17c52d5f2f010000000000000058af2d747b43f08004665cee97001fbdac2d7f8f3a90ba67d57de144d1be3621010000000000000050e17a1e724d93c69103ab8656f7f38022a83394a1b817100992205e0534927301000000000000003ab369c42e4d12dcc2b478b838aed3c279bc7c0043f6398c315c9f2d3c47055e0100000000000000a204e47fab483977b269636c625a6d568e6066d6a1dfcf06e8b20864e342987f0100000000000000a2fde5e21a95cf7fb0a6d25e2d1909492aebea34a623572b46ca9262d44aac2101000000000000009acd340a31e012f94cc096eb81b69913480341aabb48cb7a2451ca9e5bce3c51010000000000000026e8b6bb0f086aaf10d543e45ecc77511b08c7a5f61ac760b6321bc961cbb86f01000000000000005c17a2edbe07f27251213b7f4db4f4e4ccceb01b5f957a296ea89b83cd6b6d460100000000000000f2441030ec7780e75e43490ffe6a2918beb750128114c2a54f7af73eb015852f010000000000000024b9dcf73084abbacbffd70baf520f1edfe5e08877dc6dec17670be166d2820d01000000000000000c2ca9ea7ad31c8c17d4ad472d7520dc29b38fec6806b95d6f13c5f431a2fd0e010000000000000000b9289b629edbcb7691131095127580893d36bf86391a3497fa0642c58eff2e0100000000000000dabe3557e532052159d0eb6aed80464eff5ea60a7d4d11ea6f3bbd93a406fd7401000000000000005ce86e8ab8446889e7794b716d39f72772671300c4f286247c9b94b3971f3a6e01000000000000004cb6d487b23b0ff1c1dfe94c87adaf45ef8cc0b60b3506bf72284b8504d7ad7f010000000000000008e78bcca3d3e2ca7f2813f465a76cf048f628556415860d98c8997d10c4ab5801000000000000008658f3d380c23e829c50082e5393448d014928bb3c7432b1dcae95566f32fa0b01000000000000008ea53537ec9986bf2df5bcbb81af619d3301489bb12953949cc4d62d44b77a080100000000000000fe20cbd8720f46d2c16756fb7cf5942981e933366885167c3905d3213dbcdf6f01000000000000006c15f28efaac1e24c96bbbbece09d237532c1436ed316744249aa55700d5e4370100000000000000242be63827b1ed32640e6465378afbc28a6c83b54013b0b79cf671c7830b36510100000000000000e0dd1b9898a07d1e916784188bdf246ca60638be485f3d1a8407ae39355d644a0100000000000000642bac715fd971189538c56c5b2d81af2a5b86ca9512aae04490ef9a80c0736001000000000000009aec4750f312da2c844b78f64e6da58d6a682d4b7d3ab8ed0bdf3dec3efef06b0100000000000000765548435b4a773a2aad522351915ee05798f3a5873a52931df7caf8b615ea1c01000000000000001cdd4e72f7ce330b5f458d1988a481a6a518f81f98c6e2ee6e3cd14e247d19520100000000000000586b3b1fa70eb13fcaba8b11f82835084416ba431613f235af469cf144abcd1a0100000000000000503ec36c254ec34d47788344256e9feadf0336386b742992f38b21ed62a0677301000000000000002c1ee1c5edd89c3699d7e81bd67caab2351702c68a313ba1a1b1530025e14400010000000000000018bce81817ec3f23e2d5f45944dbf6c9e72569390fa8b002e560291bba469f7b0100000000000000e2e55d6ad3fe7564b653e1687684978568da1df5c07f89d3ef6fef6bacc3b41c010000000000000024be8c31c6082f993e6091302892a1d5e678ae890daa1eeefb002737799d512a0100000000000000147e76962a08ed7feda65a5559464c39cd32f64a7f5d2879b22d06120e01634b0100000000000000526322af40924e6aa848968ffd4cd1f64b6932a6af827e09d6087ea2a23b8e4401000000000000004e5103b8b919c61b36c3162a9c8d47c9a78a2f1bb849a9eb518bc2338a3e0f51010000000000000020494dfaa45734759c4aa32982246b29ea6e49f22e0ab07e8760372dec30fe7b01000000000000007a2d4c9c30b5ebcbe2ef461947979e4a70a2d94c3988e16670b1b51598af7f190100000000000000d8aacc82937ac337a930403e3dc610bd4ff7d241a0a4140044812e2c0b9ba16c010000000000000006924daf9bf6f8ba2317e0fd39d4cfc568409c67b2f0b584db8ce27e8e0bfa340100000000000000c23b317e2ac29311d399211cdbbbf116012f9577212cf68ead5301176574f467010000000000000060728068ea6f3d0f3622d47ca6ba9e54e49742d83c728c6c1f3c3db925c12d350100000000000000b0a42ec890cd378d66733e45c739319aa6817fb717f9c882f6646116c67c113e010000000000000066e62a41d32cf015685e4316f2c5d22026c358abbd815b590af151fc8f65256a01000000000000003cb5fcccbe5ac7fb6d86626c29fc5960a6ce0476d8cfb51e8338cc2b619c497d0100000000000000a098655547e92c7ac123facdd4b3cae305e330f764903eac5abe9af962c0ca050100000000000000a84e5174d0f7fa67088df644df104c96f66812907348626646e1d5de9ff8976d0100000000000000927bddfdb9c270ed770b442243ba58804de2a383b51198ac17afb48d4fca602e01000000000000005003dcea465f6654fc042d1d2e44e9b3491df630b39769e33df13977f081e175010000000000000062da836ff1c0f853f1e31c2cc30bcde8b0ea955eadfefad24fa6d2b4421447520100000000000000f0ed29463a12cca8b1bf44755b7fc4b271611be8322d125f6d6657acbf9866010100000000000000f672856259fab6a08e26157d63e00a8af36793f579358d2549fff83e845c607e0100000000000000ca6ce2b47137af4e77b2c050dd0fd5009dcd0da8ba2de762c20826d0a939307c01000000000000005861cdec6edbde92a2035ffa83f87b17acf09262ea38cf4729ba5a8071ebd24b0100000000000000fe9a0a40f1de6f902e0dc05b033a066eb3e82dbcd8b3542336e3119facf3332501000000000000000e43c4763bbd1595aa61e839d16f29b82bf65e942750eae7dda38e9546ff201e0100000000000000be223791af645d5d3ca38db90e6b59bf0632bbed9e3a3940a54bf80b112c4e1e01000000000000003458516691992e93fc2e1af1f445bb9f5a20883f2780ef854e77d2c1a94e57070100000000000000da6c5070748ea0655df06b4cd7b3670b182514cb2d1d0551225c3a7d5c8d62290100000000000000e2f27f21c4d6c72e3542c85e6acb1250107af219dcc1ec272f74e378ea5b6f280100000000000000fa11dea7f82cf0f0f478a16571cfff58242a1a766f510240a67dedd35a8ee82b010000000000000024e2216cc3180349a56347d56e164d38959b7e35cffd69eced79fc9b8caaf06e01000000000000005664b72ea779c079691d0e6135b1f2022e8c634c014b5b937b90f60e4e08042d01000000000000001e48cb51cbf5a72eb204d54a4689e849243adbff41d54d5f2dbe5d93a0e1cc350100000000000000ca6b5b26ee50944997b2061b81120b0ae368c5e32bc65cf8efdb47fb85bba3700100000000000000c01e3dcf36740aa070991e472d0d82d7f1904e76a049b957a6caf1564e46a32e01000000000000002e8e24430b51f6d5f8dac6024f87e451813ed4db1b3cb6b3e612ab4bdab8f267010000000000000086b1555953c5f9f44fcb3a2f0dfc8ca8331e02dee7116484884e0ac7ef2230770100000000000000009d14aa7f46f47424f9925f605599767e56f962960aa896008c9683cbc3401e010000000000000040506a20667b254d811877385dfe5663399f75dbe3e5a57f289b344b6fb3ef170100000000000000b02c947b1a2bc4d773ab0e28044e8293a875d7b8870e4d607500fcbfe649cf2d01000000000000002c52d329744a9d2458e129fc50094550facd4aeef04a717cd5c452774128cb2901000000000000008c35da616e607d118115aef27079b5702f9f496ca21f96d682d614928446b8440100000000000000daa83cfbc4d25a2336289f3261e6a4901030ed6b8dd063b824fa787a3f68126b0100000000000000627037405f779821f74ed2b514b22bb866d471052ad295340fdc38835abb520c01000000000000006e97825a19bcf21e0e299dad9a0594fac9d40bbc95c375eac3cd3824b3e7c62301000000000000001ebe00d3e01f16514c89161a99881d53753813cf680ddb3e3c6bc577829064110100000000000000d0c63d9905fefedf8cded1dc7c758793d257c74656fac1c2ea134d2c8306cb6f0100000000000000b0e486d3f6c244145d23032e222f807db5f3c553dba509dac3ab9ff4f37543760100000000000000e0dde9387f1c3fcae0bb2529e0cf3eeaeaa05e244be364a3bc316c9580d6e93f0100000000000000583e56856e4256b5b3cf4a254312d6dd09279a529d238ac9235d603ebcf0bf38010000000000000018ea5e9458304122e280ac00a656c32f68fe24f92fb51da1183949cc5045241c0100000000000000789b7f450bcbeaf7b6353015413c24a5feac5bfb72372b7085d2232146c9311301000000000000000c03563780cfbde0d9184f984939a14daa87754e9e5f74a23da0d015172ea35701000000000000003aebb093fe4e3d0bd38e9b040a6be249a10f680d4afee82111da0cce9596de05010000000000000062f15b56b585aca4194e0646c4f1003f1ec573c9563dd8409ee3b9e06de18a5201000000000000001284399e30f2a03778b05ab4c104570c30d929344d9785cbb412ea09668d7e4f0100000000000000eed0a44cce8ad788c707e7bc6454365652e7733133cf814e5d51b2ff95d553630100000000000000ea538b55ebf258d78fd7988d5a1c843c9f52ec8a519a977a9c835fd872346510010000000000000092b1ef5e4cf3ec9e8b011e3295349b53577943ffab41f1222b2091758b9cc15801000000000000008853f09259ccdb3d1210fceea0d14bcde576201f38f0fb7ae94930d4a65baa040100000000000000ca8079469e665cca6ac1dd47cdb0bdb56bacbd031813f710ea82e6e14a88b94801000000000000000061bd8f3b3ef9e9f391b0462ca53eb93fff4f3d58fff22e9f84061a53aa236d0100000000000000a086680178e3769f1d50fe93858b70fecf29743f7bbc87b11501ed64bb087b37010000000000000004f431a123a6d3061a18f9553340169faddbe7c5040bb490b4a064b1e0fa927401000000000000005a3d41d3610332f2f571541f984fe6fc38da4251f65a3d81276ed2452e4b1a100100000000000000b0f8da2a4b24053cd5999ad51d0240688269f87aa7ccba7714f46a027977f7080100000000000000bcb2b4974bcee94202bfa32a193ab42facfaf3d3e615c7d901e66d1db11ce6720100000000000000c66d20874fb3e00d049eb98daaf2aa549adea852f3adb625f905518c09c2ea1b0100000000000000705a9cb74d5adcac0f4fdd165d627e10d3aa2ff85fcafc33f2c5315454bd61090100000000000000ae3b8fe38c68695e5442fe037c7d32aa8466b10d500a5194a5708084647bf7380100000000000000de73d7b248f71d461a03af4cad901c83d5959f3e0b039dc987798cce072956590100000000000000a0b18475b63b6ac2080302eaeaf6284c78f69079a4246b9f15ad41691310d71401000000000000008621a861e5500d932d88ac38bbc44b3e5f674b0f97ff0691a9afa25a6863a32a0100000000000000c4089e51c06f4653ba21e1d7ef818b038c009df54cf37e0992d6584fa65fa92401000000000000006e94e70c8e78b8589c175e400a77f92544ded52fae33252482c902cdd20c9e0e0100000000000000e642c2865d5e506e56af1962d29ac53960c84d3bb39ea88a5c3664594313ea780100000000000000f2f4985003d63b08f627b236beb8e744d584ef3dbf5c82804b2473258bcf522701000000000000008a5d4c0de40939c7d94f99684174fab5e0a2ab8de50ff648a7a0b7b98749391e01000000000000005e480e3ab14cab90ac2d02aa586ebf827f06a4751c38b87e3e3860c06a16b80a0100000000000000a611a7e5eddc98ae160057f937968c8ac1f79a83aef44f680ef264c4f88c931601000000000000003a38fb488088adb8b7ea111068484073ce8a181cc6b76737080734cda341842d0100000000000000dac0f653da931dce514482b31b67bd3a7678aa3f8a5a173a87355aaacc4977700100000000000000fe3855e100f68bd31587a9565119777daa2b19e411fa16d9f41c86c55a947f7c0100000000000000e82aff6645589ca5801dcc40ef37a7ece665950cfd48cbd26723d7a2625ce67a0100000000000000801eeae66544febd9632341c6ef3aabed279740b6b28eeb1d1303db8ad0f566f0100000000000000f8f230459692d95fad1c21557d279d5d0bad1a3d43744f8312fd51b339f81b03010000000000000040e2c0bda3d579af7aa0c75a37cda7f8e6d580fc57df98c2e46c682b16ddb92a0100000000000000266916c0513f46eddd8b74fce853a00389345bd7f8e97783d83cbcc9d23fb266010000000000000060821cc864972a7f113473f9e0071b64c69584e77cc078cba367aba13efdfc580100000000000000f460f9d60c5456f75825a685b229aa6e887b5f19df3a7bb24073e09180b8b66f010000000000000044a2fd326ef1e0ca462550f91c247c613baa409868fc6592aa39ad478c85317f010000000000000036d0f298717ce466ec596541acbf95e460fb8c1b40d5e50980e047df4191786b01000000000000001c796f4302a0d1f0a32d1fb9162b6627ca7097d7a3a1b30adbf4ef21579ad36d0100000000000000b893761395022e6f6b8fdfa59f894bf51dac1cac6c4f52c6b8338a1dc19689300100000000000000a4f4f89481b6ee8d5ce2f50f9af9c49d1626966683a01f3e73c358576127381401000000000000005ae9062275202e049d0d4b8d7701ab0b3b70799c3f89d79fb25e79d10c33f92c0100000000000000dca27aec167e0fbb3b984e209d6a253fd1d0a9e4c19092bebed233c7452d530d0100000000000000eee9adef21f4cf375f086327411732b51607179bef18acabaf37d4f92f3dc00d0100000000000000a066fc4292f0ac7db890846fbc944d62f13b9edbc0e6c1eb92e8ee916b94f61b0100000000000000641147da6ce96c1cfb49abc2f3445ba4dd8e00e90721e19ee6ba99c1dfb1836b0100000000000000ca4a99507a5f4391876a1a0be7c26716a17e9f65a96679f2c8b2228bdc29661601000000000000003c09c5079e597b9b4abe38fc0f67677609cc936eb0dbfbf03061da4bac95b53901000000000000003cc3e4c20c232b2ab4f442d8bfae67a34730b69f01b0bd2033d20b5b7e670d6f0100000000000000d4db4b7b7ab59840347e5fcea8f1cf0524089fdd64a349fe5b8f5aff6ad99d7e01000000000000006c2b5d66b96f0c473fa442e5626842993528592791e62875260cfa928c96fd410100000000000000bef4f75529d960a44d2878fae1824d0903d5052fa53f75358d03b0cd2f550d60010000000000000092b4b6988ed74ccf189bd2e16868543b853d38e4ca82c63afcd3f3ad656d0e1301000000000000009a75a1a445f93eab93aa0f3f328ddd64d5c25c32788289c42301fdf1ff8aff4c010000000000000042be82ddfb8da6ee77760664bc9c0dfe6a5b7bfa58dbe148b965284514d0715601000000000000000e8d5114e90194b8e11db6414e52c5f9c3c99a886c478263ab17e040e1689f6b01000000000000006864c42f3cbb918799d7626c63725fd96cda1134944ffdf2925dd1c73aaece1b01000000000000008cdd8b5db78d01220b9b747813806f33d6b1d8c19d9fa3fb82098a3f8d53c80f0100000000000000765aa204bc165c6f9595b2cf359a1794f42ee6bd273118a38dd7a45dff33406d010000000000000050478b2d5c6272ba095f2d3316f19aaa06ff22f948d727999bb32feaa873017601000000000000007476496c52a5cc5cb0d1475412135699072772af64302a5a4cbc384ff2b2ad2201000000000000006c6a5941354b9419623fd51e0747f8f467f31b6f4b1e38b465107f02cecbbe3b0100000000000000de09c9e8d3e9c785c9f51c407a17e14fbd6c3c3187dbd85ae23c23e88dee413f0100000000000000d6501868e1cf90df4f8bd798e6940f4121f0e986c899f5539138faf8ff50fe0d0100000000000000e28b8124bfb689408bc656ed979b101069c42adb4329a14f972c39a2a3e9784201000000000000009459d084cafd40dea98075177523fd137a8e6f35ab1068dbb420e616ac176a2701000000000000001262742c54cd31a33891c1650618ea0163c1dee7fc38b716d4ab05f487369d78010000000000000074cfdb0c8499cfe04a51d0cd85ac6ba602436e8cb00b77fb2c1ca01efdee565e0100000000000000e88db1672f41855dc8fb8089b8506989ab40a1f8566f4d564e9f82138cd5b56001000000000000005c223c2fe106334557847ca756505e801f3f798f3537a116f2890577be420d4d010000000000000048839721a668d391498431b5747d7935d55104a2eba9489077abd8777763dd46010000000000000086cb3131c5406660f6adbb0125dfed1efcf8b348126898732fe9e89df5aaa90901000000000000009256c366f4a4070d2905aace2d57295c36d1082a179b17c483f42e944851cb60010000000000000024e8ec732707e61646a06a013d1657f6a35819bada07714f7b57462a03246b070100000000000000323bb7505e17594b4bdbbc7df6a205843f1f7145aa1c40810b2bdae58a95da0f0100000000000000cc179fd48870861bf5cac6ede454991d4f354f986bcf36a3aafcf9041d6b22660100000000000000268a98a0b0182f23a1f51e5adf1e56274d10a977744e8d0fd0b6e10910ce1c48010000000000000040a09307c5938cbd5f5838199f5da3d56052449b745ea13986b5292e290da33201000000000000008eeb878fe73763486242835e2d2b3c65a1a78bbccaee2ce658fed20b793c02350100000000000000344ef75e232ec97983125b4cfd8dfc4ddb568ed357332adf06f209d0183c86790100000000000000ac1fbbd6bffd92d23bc663ffc22f2d056f04534f6fbffe6fdcc2418d88c17800010000000000000054e139e51d28832bb556dac1f9cc6bf8cbb0c729ffc0a2d1d1724c72edd6e62201000000000000001edf51c6e6bc501a13351031b28451af2a98085f614ade4f2b27113aa4473c3b0100000000000000023518b184a5c21ab39941bbe6019b68ab6f2cccf27a4caf0099b8d4cdbb920e0100000000000000e2c23fa5e675899b0de4ca207be07b8d6a436101c5cf4c023db0624d56a46c7b0100000000000000d03a71fb9750afc4e9d30f612b70cbe2e617816dbfef42e5c1ccb946099be154010000000000000082a7bebeda9f9133fbd5c9af9b470e1362eed16a603c28a209df52743262015601000000000000007e3d9d762911807fe95f9a79e45faccb182e612c98cae93e0d419f8af768982a01000000000000005cc4dde005b2344a81d2b52f427c60a1b447fe5fb50f0cdf59e4aba4f281911e0100000000000000e420d5946c2d0d4d99cac1e224b2cea6295490ee3b6a9803cd28cb03b9aff57c0100000000000000b8af11e910e884a06701a7f157684ee2d78dc205bf3d330cbe361586db733346010000000000000036c4bf964f566aa45d6a4581e2b56c64923944dceb6f9ca332a5b9faa374a1790100000000000000205e0711fb2e06eb53f561854a722e0aff2a8e3c82616ecd2c6b78cc56327e580100000000000000daa0ca1a484715341fedc1f74ce6f2d62764eef5a95a5fbf3225db0ba79b076601000000000000007a8c92d925792e50eba4c4d4ac50b8302ceb21967cd44e94a3b0a8f1f6e837710100000000000000a2751b4ac6489a8985a70ed6b3437e0649c909f3d6effce47f0a20ecc33daa2601000000000000004c0395b7d2f32dd6162ca1e2b117e8833b26cb181cfc4b6c997a8b73455f1135010000000000000032e994c3f4e677a6d4df3d65ca980595264dac5140f52b1968748216c92c75550100000000000000c6e3275fb29aa69fdbd35aa9b3c3161875f3946016e575aa2a95c9cc6ad575710100000000000000aef5d56e0a88f892ebb22a48771a10c72eaa3886e4609310cf613c08d8b74c20010000000000000018e2ffa37c2b51670e849d1ba370badc5d5abe3678c59d46e0bffdaa4ad49c28010000000000000072cf291be9f6b9330459b406d50a21ef4d13e68a645f93b80422be6b9d875a080100000000000000c2ffc531b154837795bdd573a00b6b27e177a872f311353de1640060d6c3c8570100000000000000422517e03ab4255a0870a7fc588ab1655061c7322065a1fca053d82be3d1cd660100000000000000eaabe802ed0951368cb8379e36daa67e6571aa3c365c850c124fd9567c4c9106010000000000000002cfcc84716cbd61fe5b3b4011701326a7bb865f73c99c2736fbe41a264d30130100000000000000aea9dc5a53103f390c8584cf892bb854f64ad845b382abb3220c2be0a70e492401000000000000002c2a070ca5392bec7c3f56b2ad808d2a4fc3fd8e857f0ee2d6496aabcc970a5e0100000000000000f4d8f4014d1bd8343aff7f5f1d4366ae6985885032ec8d6cb198a054d085360001000000000000007acfbfe9d17dcc8bb5cc4ce3b62c4149d51ba5ff74293ad82ead4e3d5c278f21010000000000000092d47173f0a1a3d8db3ac255c2dcdb9df605501a7fe33d78d504c4cece2ad12f010000000000000036487c3f8eead755b47b123b845418d12e16b590aa24338a885185809b85a02001000000000000005458c0189a88a6f5807d6aabfe020ee80115394a1b67c33bad109aa647fe3a3a0100000000000000946b15252e2d51056c52ac8024b47513bb9d08e406f86a7b7d4778028c4fbf770100000000000000aa1f85bf11a5565f36a54f17dffbc62fddfea81f4209796c120aa5bcbbfee25a0100000000000000464e26e781ecaf929baa74714f82d03a533bbfc07c95037032a46459f07d5a2901000000000000003867a37432ab13fcd7f9c7bc872c23e0e0e9cc20bc01b6783e3202a396cab370010000000000000092cca5d3fdc514fcff6906eee877af979b006474a4eda8cbffdd861dbbd33b02010000000000000020aab3fd9115b4f4385883164cd41e282f9c9d547a46f9610aea615d99de2656010000000000000098378caf3d7a44614084d31da9ce1d96f2f7aaef3989ce583dddee4d04b8252d0100000000000000980b477be76f9319f3ef5bb6f0de390d75f362890dd6124a0685ab06f4158b14010000000000000098347031396336c66a994bcb21b8be5906714403cfde0dd94615c082cf42ec6d0100000000000000700fb979b0f33bbc8a9a4ed3fb3474ed2b89cfbb7fa7d1c6bacf6d2f1b76025901000000000000007e33ec70a5a0f0f5b9b1a0587deb97d390b5d6bd8315ec4c69ff8599d8821c3501000000000000004af566dc40a9f1b9698d4d28977db99590560e378444a9592c519c3c7e0f00350100000000000000e6d63bfbb03afa14d919f48fe3a3ee4255e8c30f32b6ea6281ce7152ad694c430100000000000000c416de97cab24ddf86cfe8289e6a18ccd18abb607fdb5b5b276b7efeb86a29f80542414245010188b5877d5fc3f15b6343e9d3e37432184377f9b6e5379278f87c80039784186a24d241269eec81fba4a6308b2a0bf3c7c56140c8d2ea69ed3978bc974aea40897185b7d6b04eb4faf8315746a78bd067632d96176ee856f594b30c673f20632906e5e801eb9ccc0b7a4c08be7d679da652411e6e51b7e5be6b08db441933e310eb8c6fbf0ab2e76f17decd1094ecbf96905671d6b75917aef15ea7cd11b3cf0dfda132e7080642414245b501036a01000048bc2210000000006292ad67cd17d804ab28cddb28ec455353d12b684491712bb86ea34d07e0e12d927663541e0ae08c6c92ecbaa66f4611b4c7d0c58eaecfa58d1b402e5969770e9dc7a811f7b6b91596528930c2d9908100de91f764fea11885ed72a51af7c4050542414245010122972a2d2cc2befb84b60642e0d775f0c5d32c2d39812a5f32e28b39c5a8c17d0ee8fb44808551a05ccd4e62c2742564e68f6c3f6753148e63dd70e8c4b81288541eb2e690abb96bd18b961ee079af20c8407d8eb744f0f39c26e5ea950b37250ae5e8012d26c938a93d2e11197d541a1fd08aab53b16b2512bdce462101aa20194516048a1a8f4d5bb662083558c78274b2f0bbec0981dc7f9e10596271aa827314bd23080642414245b501034c00000049bc22100000000082a49fb2c39558bb03024ef5c0c128218500a74a2ee7126264bf7d7acec083679cf3fe221d36f0d30a274437541a6e7b3e4dc7f3b8dbdf96aa2f239672f3ef07338cb2ef21dd3b7d3ffc14aef2c12c9139c92d5c958080613fd5d598d17cd20805424142450101262154822b7887d2e42954160c8a9be5dc05b68c88e8647d290578b9435ce134bc74906b7c29e70fd6f79ee41e318535ed05acbd5ee34b7ba1772f4a2f6d5686fea23223911c9a4b61f999d6d6aa5874a784a292fc712c5190db3d3f1578b38d0ee5e8010bae01c40e3bf55bd25cea4c14c7bc9c4bc3ac3a7b1c590a204a1a7fd0fee675fa42d97b7d258d8ac97b73dc8677ecb21461ab30d77e3d74aef0a38dff6c87ed080642414245b50103720300004abc22100000000062dd1e8de4065675b93a51fcec19bfd15fa770a2f23663b003448e11562b3c3cf7cbdcd272928019682c66bc2e78bad497cf8fd3d6a3e8627365556eb192e30bf0e457ca9e75a23ba982f970d78785085a7a251b90ba888559ba855bbd21cf01054241424501017abf46caa839a98e3ee928cfb96b8c5e64e848e83f7002c425cb74eac6c4b413a1a6e8d1e747fbacd06793a06457a12045e98e0bcbf3eeadc979f99b439f82899156d4f0784bddcbdc9e4b1ef4daa30eb63cee8be715d43d860af196ae339b7712e5e801b8e87feef61a35bd06022b7ce6e879a3e1cd64363104615f45c95df22b3ff0b380e30c1e8d93d61b5a780ade8175f0564c688f0943ef84191be9f5eeefb02dbc080642414245b501012e0000004bbc22100000000022be4f3a675aa81a08642b434f3a0eac129649008c78534a318f15bf18a9446717466a26def6905af52aeb5556eef128bba2a6f61801fd6ff1d1bb11d45f6b03b05704730c02d66f34662aaa9624533253c8ca8ffca718d349ea59ac5d9f590f054241424501016e0b3578b1adf4c3db7efad324c649f2210fd555fe7a607608f2ac8eae983b6795e1ff8d901b633a44136d128f560e9bec0fb9653a578985ea57415bde321b89d100d5ad24a5033d2c713e8f0e3fb9318393e2496b28e87a7b221c62dfc5d0a016e5e8016a050217b9c236d76188f0fa44af32578c4a53b60d10be875dcfebafddb8089a4ed502032b4f8f8707ca8c6ca61ad13946b0894b70d4a78f1303a0a231d0ce1b080642414245b50103240000004cbc221000000000e4170097ed98e7cb28ea3347d38903fcd20d517cb92941ea7a9b0f78080084575bc0c7bdc7ced24d6472ecba9ff71a1fdfd2a2c51a53e7384a4cac0a87473508675bdd89f70e534022fc0331c27944c21a5232a8315d297c13014ac1082bc00f05424142450101949f7a33628cb016d8b3a0e46b590a569f2b5e1d5b4e6b6e1ac88d8a8b38bd56bfaeda86c830ffe331a4e40e8f3888129188942657d5a374d85d6024cddea1830925652a71ecefcca7cfd7d8809b2bb5570558f7468418b4db7735cf6d2d55591ae5e80117c3e077756488ea411f43e3fde40528c10a012767c99ab72758743023328d688abaaf6e7231a12e7ce55ddeb79c9adc62d14b98c61726814c78d72a39e7b089080642414245b50103600200004dbc221000000000425eb603a42778ae21ccc877451db5000a018062b7c107d60fa5e5d80bac0a4884f17dd935f030117289f1c21afaf39b2d0faf77661a5c6e06d50769829e6d0cc921b6d58ce50d33b3cdbc149368833a998f89f400894364147baed738b64d0a054241424501017e0613b93b1fec1ec7c06754eb356deabed02067ddf2d077942024552e80db547c6aca2472091bedecd3409fd56b06f5c786808d13fadccf939e6e74ac775d8fdb003fffb8d4d4f160cda79f4e077a67583e4286c96adeb1774a5d777535414e1ee5e80158c6ad8127a8b517d45d1e15ca37874908a46667ec70b163c2cb439d9db3f131a4f8ec816561d51357fdfaaa52e81573c6959c273d40a12794da415da49d177f080642414245b50103a10000004ebc221000000000a28a0cde977a10a55401de5294be00a4ad330c3bf1f773e622f0dcfbf4ae38323f585a06b48d1ebe3b560c49c87f1fca4add7474af3df0efa31840c51d16f20f2788ad688cbbb4a25ed6156e33bc4cd1d1eadfd70ff4b9b79a7914144030f3070542414245010124483a0f8856e62c8680e41b6a1fb12454ebe3d4c009f33c67f2b130007c703bdf8c13d03b03e95c218eaee0e02107b44b8148cc013dbac32e1b32f712454b85319dd16adb14a4d044bce6b33900f059412369879bab14ff0988b2e9e82fca1e22e5e80184809ffcb00078998f9ed1743ac98e7f643879a6501dfade3fa9ae49cb8fe1f81368334693458c5be805d80ea639ffcac13161516f701fda883741aa7906b820080642414245b50103c70000004fbc2210000000007804fce692905f165146b7e6bc56b1ea94fd0e33eb4fab24781bd442e59405370cbf07909e6605e05aaa3ded889a0e2d030b5006968349126e4837716b09ad08b6a19d656e64df9304e3f4c4fba405dafb837bc36d1d5eacf2d5a3a3f05f6e05054241424501011cb0332f6a7932801e57bb9f1c875c2eb27214abdd62149a7f110f340e302e57d06cb7b59c0a4fc9a2f8d91640ec4027f2f5c0c4abefbdbe3450367c4a0ff68471282f4c550e69014a93f584ae225a3bef90f3abf2beaa90e883e8bc8378455f26e5e80193e9d1a987dce489afbb3d4ea0dd64c8edd1d1bd82e9ec3d34f55d3ddb1eb6065f65975f7ac38704539a21d60a877746c4a89e20f8ee41a64ae5220490c956c4080642414245b501019400000050bc221000000000c47b91ca32f0661797c206a5f8fb4da694f7ee864b23b5177b39e8133c40ea2cee5a3c704b19a1f7c9a0266562edb67d88f7db7a22781ac60a8751b82340a700a28788a5188d43fa2369b0b34b104b8f1c8b71bfaf6eb3ed40721b2659bdfd00054241424501017ad302c003edaacfce666024574eb34fd0983281747d59ed5d7946ba4e390147250be80f02691a684fe6df2e991f18ebe6390cf1dd47b456cb9e1e708e8d3e8bd516b3b7f4a7fe647abb4bc9d54da6fce7f040b9480e59139b78c02a4c0a7ab92ae5e8015c8e16cafdab2cc5b52b6dbaddfdd1c8d3ccfe264450c8f8737051fa4ef6adb51d452e67f912b0791e0840de82cfc766cc10cb62b7303baf97743a4c80621a58080642414245b501017f02000051bc2210000000006e70dde7f9fb77341dd595dd54bfcadc2a6a6fbda113110e615601ad6737fe5071edd9dcbf363aa27c7fe48db7042cbc15483ed1ac39a6787eab94c732b9d609e1549e2d6be14db72fa52eeedf99d9592983f0885369eecd0679330d5760ab060542414245010188b9809b114d9e6428bcb6debcc72018b38a405544f39c8c48588b69be6fec1038e71fbf9a571526ac5a286c7fee9bd9c6c40583878514823c890cfaa33b8c88376c1871eef72b233b17fd15bcea1890e81247301278054a55fac067a352cf822ee5e8015aa1bc67e3e7de17e821cd03c9d127cbaf4a67e24b9f097148470666050dc91c2116b12caea106a8affa19dc1b368e6f6fb47f5e1a8b0ce7d7a5e8acca7700d7080642414245b501017203000052bc221000000000b465c34594ebb24ef88f4edcb02abd6534f7b7da842eba34d33c8348f6306815ab429978d55d67882517443d40293262082a1ff40939d156dad8ff05b834180c496282e1f4c598e5e6189b8d054ba5df0f6150c9146a598743a82bd8eb51040d05424142450101ea367e5cf91fe56e2a3231002e144e28dc341bc5fe3267aea88438ecbfb8ee0003cfb3776bf3aae22df68e47cf100a83f74cec8310f798db00f7610ed3124f8147cf69dfc7a9bbfa86b56c16850b0bf188aee6515ef9bff23fe7a763ec4d3d1e32e5e801e1bc535ab002c36da213c08f18934245d0fbbf38e3fd7578610722d21f3bdaa0c60e129266349b9b22541ebf62b047676befa9bb0eb745d12c275346ac4a49bd080642414245b50103bd01000053bc2210000000007ca480a77bd5370a2131c4eb1b0ec8593cc4ed90185b476d4c4a2f72665e432251bd06d9e62179a8602779d092d8ec6956781cd52c50ebb4c42ea8bd549bc50c9826e645c3ea5b75da25c9b5147c25f21af36d1a246eaf234a36732e43f3d30405424142450101e2c4563faf7606a0b9ae6aa0b4ccca78c53839377ed7ffb5420ea0fdd00a9f346898260f7b9aaf14864d6736a68f8bbc0ce54bec60376ea34f345a3042894c8e385a22530e3678e048628ed715d55d65f737353aa86483a7c5a50e88441ef97f36e5e80153923ac2e546234c6f06bdc4f0cb2729a1ca02ecc6c82f0f2a6f5372e3d550cbf3cd10f49edf3d9ae3b83d7e09e457bdf1d82e778e0fe5c662b4bf88a3f7275f080642414245b501011701000054bc221000000000c05d857ff4b358c00b29f923950ba441ab0ec19cb256eb43875c3719b8b90907aefe7c80dfeedb7fcb1733bba307b066724794b79333fa61ea9daac6f169220879078c65a2b41d25978937c566b52f558683bb317a1fc163ea9b3ef29aee590505424142450101f2a0b3c1f8750cf4ec1be26c3ddc968b29044be1711d7896cd56630bf2d4631b77dcdbdcdaa32e2a5ff32d7a0118c8f98336f14b27e651368f4a13389f89b380740c16d96af4f785890da07fc6f0f66e798334e0743dafc70ea0ab236cdf5c4c3ae5e801a3ea1a86d45f580f6018fc3cfa1b8c6c662f76e164ea364020e17777733f2ec90803afe527023ec5c68d659b70bc39d13c2612e8cf3bf11931442bec2ae3f77f080642414245b501035d01000055bc2210000000001ca01620571f0ed52946d09fbb55f38c0f614f50aad705d23e0873b4746aa5793e61cfaeaeca1063109a045d510f199ee3e8ab12bec1a3ccf1364c57ccf4f40ce18749d7a7c087e3085f60bdee9f1999e1653eff5a856128d2d1d1d5d6dddc0005424142450101a2c73b1cad9dc3c33edd1f26472a062034af1e5cd0fa1caa42953c263fc433557c5b6dcf11074fb1aff311b2249471b62958d64f45fbcba8097f9ceab2accd8b82a9ea324e825bf921e22157f21d1c40384b29afaf64cc395d8cef67b8becbfc3ee5e8014e1eada87c0beecf6c3957569819ffdea6c3a6b18c97dd89787c4b778403934a8d4251d9506160f7e79456e93dbed95f39f45cc89dfe81827262403a8474c408080642414245b501039402000056bc22100000000034cabd0a30284c3ab6a16109ef38311b2ac12f2a3161ccabddb45fe6d73d5f25b3051002cbf645441e92c6c4fef4e0445d9c2acc419d87f53487789d316c4b0de31e10073cca96e7cfa4d8b78ce5e66a36947b57b8b23f4ba94f025cfe83030205424142450101fe17caa72e6b41b37a22eef47eb6d2ca892fcf8d3f1507b344e8ee90ed823d3c85df3dd7714c1f1c815c89da23028360746567bcae725df24275c1be463b9f846c6d313610b5dd45a9065ec4e405ef222e9b1386368d7408357db182c67d47e842e5e801c9de6a47da34aba94afaac9a2b709b794654d017ad6d032a1aeb0583abd01afef91236b9d181baff8a469698ce6ffc241af5c18e397420d80caa0beff9deb6d5080642414245b501031d02000057bc221000000000ba26549ac68593358c0eb4a281c0b73e838e1dd22c8e742d72e5ef7c4a49751910b6d513bb218c2c60ab062b3fd38a639eb218eebeada07f99cee602cac7b00903a721b5aafc56f0a14def2d3a6a9e9243cc774e79d03c6d35f3e6a1e67dd30d05424142450101c4ecd077546d4226bbe7f36930a4e4f754f3a51d0a5f2ffbb9fa3b8d7a1c1415f9ac1b7dfbc9a40233b117a5f506b597fd87951fdc72b35090b9dbd447b3d881f428d2241e83065cdd2aee653e04de960a5073eba4bcc8cb7ce058a5802aec9746e5e8019558f5f4eb8b04c37398f517ba853d4524b35be7bdd2ab02ebda43e1fde98b9e862fdcb1c2efe79e0092f7c916e23dc8141e4f9e188e5bbc40dd616f7ab08d28080642414245b50101eb01000058bc221000000000d2abc719b727147884dc74abb77690aca6dfcc2465ca255690d85758d63be74dc1917541caa6ad9c3bb3e10c71449e73237afc4a9b7a214050d9ba2dba695601281e7a27ac84d3dac65eff4073226e02e8de116f0d9baf819dafd8ba2fc89b0d05424142450101eaddf85828bf8db68a364c1fbe662cbba2784b3ce1f542caaaa0e3eb3cefa85f2e17b02f7edc6ac1c9e5302daeca031e223bb17ae84df4859a8883052dc7ed8330bd8e265f2aa8059ef4eef3f22aa124fd156369080a852b22a2d372ffeacd164ae5e801b0793826b734539bad2d6d285f880fd27cc6c62fa35d9a9b8f5606e26598835a681e104500d2656e1b949f1acf1df4f430b6b4211b0f16d64945c86a23234f16080642414245b501031f03000059bc221000000000824722f098737aacf87433156f94776cd181f8f1795a4fb0a10a5a98bbf1aa646d95626829dd3ed2f713f0dd0e602c335a272574495043190499dafda7b24f08568f21d1bd20733b5d05d3170822517a6a76a50896307baaaef4f3c802c0bc0d05424142450101acf291f45969745a9b6bc8839644f13a26f62187666120373716b07931ad76274972d14ef77ed94619158da212ca19b0330779077a0135c31acbec0187c2bf862fe27718793dc89050f03a7a70aab566bda51b8ebc63cdc93d08672e35682ece4ee5e8016f8b01cb0e2c5d4333fbc1810277b1179ce31b13163e7bb6432fb3f5550db0c42eebf83dd61c1ca1c20a4b972afd8edfc8ef1eb39b0554f764169cc2c9e302c2080642414245b501039a0000005abc221000000000e880fa954f3cced5663e287f4a3bda5849f329c4cefb1d98422621630f052957465bbb10fe0512f65a896a8b5f9ec25d04ea7335c16e970122e51260799d2f05de2e7f4fa85a9369e8528885103d2bbcb35f13b6f7752441221a594417ce2a01054241424501019441897b7560a89b5fc4a08ce1e1d85a1fb1ed6047fc047b3d4bdcbbdee0411d4976fa8546a774e018138aebd31e8a85985d6277fe8e03e9fd5cc4ca5dcdf685f6a16dd04538358873a6295a2cc42fcbffbcfd9e6d1041b445badf04211513eb52e5e80133aab29c3671fb54d9de1844929f4191814512d130e9a752bd8986f4040091687f144e56769883643c3683f9e22ef77ef0892e2d535c2143fc37ab2d498c5320080642414245b50101860100005bbc2210000000005628bf914f6c46b23df8fcd7e981691e502620cb3a517a08aba16b26c0f07d3841a142c09f13477c8e93a4947b039ca1779f21a5dbe0a6972bfbb1db882eaa048e1f585f58279842805dc4de45ccf417085b0815b58f2da1316de4d99ab3350a05424142450101a0c200a9a4a038846d054d225a9b2cba8abc2c719ec687451a22f085a534f806b16fa76aae4d7118d85945b23d10a0751ecaa17814e1e81f16f15a8aa597d48fbead0e8aa042a3d0801aa3ddaa9c9a8c9421db789d6e547a320a27ff83e6cdd856e5e8014f02bbc13b24ea437dabe5885dc8cfc465c7caf62de2ae5ca7b66ab731f5550712854a894bb8254d1cc41d12f22485e40c0e3692df931b463ed29cba9d14cf64080642414245b50103020000005cbc22100000000070242a6adb9fae46438cd6d8b1c4f05eac517a0408bcbc7f6b73c9ea3efe5f3e51000d06ad13415841acf2cd63a968d6d18ab86c1822349dd90fd4f6f0aeed025d862cf9dfbc7f910fe4dcdd458b410912d8a763e9e0be9f5b3f8f2a662fc40905424142450101a4efc24623d417916d6142a65e4c512e21ee5410454977c9c38e77a562cc517372594694541d9b2d4ed0c36c119dbfa11042b1dd03aeaa9f55c15c5b1013838e54f926f93eff013fa63d89bf5abd7464b64afbf41aaf2733a4de751e9051247b5ae5e8011a63c7be3d9675fb3527cac79edaba0284d9ab5c9d3d0e3be3dc3f8f750cae0a89d98b010c093b21b6625f02885e0336fbcfd819e07006c2482fa56dabe0e3b5080642414245b50103a50000005dbc221000000000467fc577ff7790a36494b7a15403da3e21a1e471fb5da3a4c4e739606f1b5635fdc562c1ecb3b7ef8735686865fa40bd9edd25509a303f1d520c72207637f40b030ed289abf78f0e773a07aaf1944adf5edf1a812328bcc5eb1bf28bf657210a0542414245010156b7b1fdd034494145ff84456732b0a1a1fd592d45c0dfd2a21cd579e499b06c429cfd4fbe9d35283eff3bff4e4b25aad458cc8d0718708916a83f7de87c9787d14a5d16bc90d8f8015a73e298300f071a9979ee513edeaca2869b24725210005ee5e80120dff40d6287ca54864e40232628afa2f66f8efcf35d1b9d577e2d3faa3f008a8caf3c0bf6051dd4a8e4fd0b648c8cf2359103e04d9aa3720fc3e407948524d1080642414245b501031f0300005ebc221000000000dc48b172878c89557038490cb219fcf2219395ea9a15ff23ff490e3dfffafb4764f13e6889ee88e0daa5336622fb0e93f6ef12e3a2380acea4ada074bdde1a0dde98a1818951c9b070b7db89372ea5eba42eafa49fcb6ca633782e5817ad6e0e05424142450101b0e58864abdb7dedf0ba55e46eb12ed8527cf733418d64372767fa1996e5ff3bcd99e5ac8aea1b65d0bc5986e81b6072a9a59e5ee82edec2e12166b54836e287cd170b2d075a33cede5cecc172721d903b8f92dfb73525fe4376df5628a2db6762e5e80166e2deeaec25a305f82a0c2a5ed2eea3d6232a555d6be9233f7360b7c45eb66ce547ed7871fc8a1334370b2a577b32881807c1ec93383235bb42e4adb396a6a8080642414245b501014c0000005fbc2210000000006e9c626d18295a87a86c7d5bcfcf4d38a8ef825cb5bf27680caf42bfdd3150167a8b378327609ab9d6ca3dbec01fe999e4c9a12996c33c5757dda12bff41410e942d157ca94d62a392fe58cb7e9395114adb319e44546ef1bca53ee58dc30e0c05424142450101fce73d73252c27c7472e4e7ee8210963a392c79a55ef5b9f42b8d54480f8c94eebe96e1c49359c6c24d332c4d80eac26a07e883e3f6be8422b81723df56a19881e1ce8227386071c1f879e95f5eab838a408c7f8e473176073101d655f8cfc9166e5e8017ea032707ec6d3bb4dd060fb64570d024ecf32b50d97656bdd3383d6e009a6b2eda51e72d2d6272ce40ee7a1c3f15492df7c15e878310f4a788eb3c686ff7f17080642414245b50103ef02000060bc22100000000018a2d16967e01bf6a8fef779ccfaba674d7d490bcc9968b14fef67066aac1d6674b6d2f674024fa8440a8aa309143b203915f3200bfd8e84d18bf919f4db500fd2870f8818b0345631b10e02d6e4306124faf9fda398661235f9349c203e560d05424142450101f8b22984f99b47e8f4d8628d871f66a94b1f92943cff0e1632cb1bc176a0a450175cf034f067dde42faa5dcaeac0ca41a3acaef44d122cb30d15a023bf123986252993e2bfe2fe5995baaec602fb4ff958c6cfd33e8736b61488f30bf27ad5256ae5e8019e9a9647d1e7954d63a061d3ce86ad3b845ca5f4d1ebc799ec0781ada9f339139ff19ab4a6a97410cfea312d2ceb847d9dde6056464b399e4b55d81710c35183080642414245b501011b03000061bc221000000000a0aecd3de918ace19ab28ff0f88a5a5897cff01fdaa30930bf7e3f907916124ad679100401886becfce9ef869d179402fb25c6bc5a38c7848f27791bd8d3f6040c290fa3f7d56651b992bee7e09aae6f5e605e5437431387a6e762739fee520305424142450101b8f475fefbeaf82608e277404822eb34bc6e0153d79bd53eb9ff4fb24ba56122b50eb481ada32388a3f016a9fb5c13726bb69dc071ddde1333feaf102c12e58edb4234f12930d880b11a11ed6659f0d71aa1bbb4cde6e9f7071df030dbe344516ee5e801d41119d112e605249ce90380dd0b78ea51e53f0fca3076c201cd6865cc49a260e71515262edde7441027ae11b0dcdc8f5a1506375ba0da6910293dd23790ba62080642414245b501031a03000062bc2210000000003647f13d8557a5ff6dffb236fe4c74963d2a2f91a9e6962735ce8c79f34f52667c9376c83bf2c3a11e5205b5cf187a5e65be369d14eb6f440eb61ac228e2b9025524a8b6d0e61ab2675eadf8fe8156ebc2a50ed0e862838afecf0e79cf0027080542414245010132525f7da29629a3e5168b1e8fc4fbec05f2e8f24054f3995de0e79b2762521a5f9fbb1883face4a6df7c526dfb53c7bfccba36e205dcdb87d0f0b22a53955816f8a8087a6c835c7b4240ac78d16e7d231ea07f2940b664812b09a292857a2c572e5e8014f11b0fac7aae6aeb2b905f496ec874f0568cb4213c989cbd4b0f30c43e19600108483b0f2d0249c7337a4f470246ee7990b0843629b329dfb9cc797ab205edf080642414245b501038400000063bc22100000000010d9fa152262bf7b1435e9b504e8ec5a139b159917256a19787dea938a4fc52aeef2c56c782ddf098c795ec5f1704fbb2e116b439e5790e9d4aabb709337b802a9699bb6e0d3ee9205279da9c1fb592e72e8e4a6ed37128d567ee91e3aa6c904054241424501011c94e1b2804b94a1bc5e5079e614177d936884bbd7b9414730cf5ddb2f71646a67b61b73bdbdf9d77a1c16d623857cff90243d1a3f8845671f017e628522068c66abf1c572e3e3e0457b694a2f6724c6ec88c62dfbcf9a532ac9a0c6bbd536df76e5e8012477754339c2115319d9db1f5ed38e3b656a648d74ad0fd040c6861115561491a4968abe983245a3e3dbf56c1f02c82f3e030a7e358f79f312ac53789810c925080642414245b501030202000064bc221000000000b09337b0d14709413a0cbdba5328595aa0fa5d665db66b305104f12b3e558a4c8799ef8337a07c25d21794ea9fe53ddb71c099735acc85500d842df7f2049307420c4b80db1401b25d2307a4c8a5247c01ee2bd46acea4e3989bf25ae3ff710705424142450101e653427d6a6dcfeac818da03f536feec3e09c21750b448aa40f6a362d4d313724188d71258cb9e710e87aded8e071a4ea25ba4272539610ca92dea92943a3d8c7a5ca7fe6a872c85284eeadb9fb7b7d1c4fa4d1f8b70eee0aa35e627871696d67ae5e801efdffabc5a699da7e61af3fc7a15b2819cded941a3012eba98356e47052258f440d71ee07528f3343c44a2dd955571d4eb43e8c29ef184351020b4c6f3d102cd080642414245b50103f701000065bc221000000000482525b37934af02d32f62e4a6133036db60fe65cea947913d8189845a97a50b0d3a8e70b8a4c831145eb644106e3b97bf611ccc405c14d180e8a3372eaf65057a95882b0f2e54d756898d7a7f2665d9a16cdcfae46fab4bfc68ccd8ee2a1f0b054241424501017c1e7d668b1fcdd4c79dca7cfefa72bf3860ef5d56f7ce70666bc3e8c20ad660f6570bd2c3a3219415b631fefdbaf4cca14dcc53a10a7a0e226b5e61bade4f838cbfc60fa1f332389eb562a179cecb9870a2189d2fdc584b60313845c7c394237ee5e8018300a720f8c1bde9dbe5d22061fb49df4a8b6e79d608e7f6d228b8ba9f32f0e51689218e74df0e1e7ca33015131646ad039381f9ff8d45cd67bb3a8b4b655f2d080642414245b501014f03000066bc2210000000005c3dd0bf799a08d57d8bffa36d3db2e363d87f75b9a44e7f41455ffabca638716d49527f83e63ebdcd21196846dd525e85d844a0ef2018e24bf8cba1cc09c709fbc41bb01bfc084a43ea6804042fcd1872bf76400bf4899022c08f4faea0180b054241424501018a93a2463895bb0446907e01177f0aa8835f0331cc0ff02016214e670da0db29059fb2df0ac7a0ed321cb38ffec6059a7685ec6f82afd3d17915140c757186877732a48e6c1ff282908eb00cde1d59721661c15a02754036ea492568b7fef92082e5e80160df0149a649f36421eafaae470b2b70db9fc38ac67f4b1689deb3a190d3193c94d6244128e1cbc6e26b122d7b7cd0bbc71fa098e2cc75d313e033fe12ecfd93080642414245b50103da02000067bc2210000000001405d4d557395a244f7d6d09c90ac95ce3557eada8e6f576908c1da1db766e1384d97672c1fb08e463ecf91338ad6ef1c7ad36c0cee4dba87e1728dea9e178084f004af4dd16724196a5cef3b50bdb3ce35f3b11da7f62f681fce50ebc9f630f05424142450101f0aa075e884019b7539efa252d6d61117b24ae0a571bb7991c9416a49c87567071e07d37670482f4ac273c96329f26c2ca0895ab1f390b485a48ac2d998cf587b171f0a496110d278a73095ddf501f648b14ab1769f9abdf48f30650253db9f186e5e8017aa143167d484a620785a0281325769802c8e65076863b5629b9ad379502c32af5c7e3904ac0da9d6b084106409d1871e2ab68b4d2a1ffb35984fc5ffea45097080642414245b501010d02000068bc221000000000d2fcdc4edc75d1b1b11fe12386c4a81298a2655a5c65fce7caa677c93078ab0bab5c4fa98a18651e421892065efe6ee51ac4446eef01b7f49fb043d46232ad0f96e0541ede2558ec637e864fcb6c93bb7427a6fc03776da63c01b33c6e5d500e054241424501016c49b7a2e8b565491fb6fe4cb742838f7fae94461db6577861e314296168e518c15372f1e1c05cbf2a3795f8e768ee77331799ba0a35b0956e8ea1b5fa4a8a8b5f2b678e2399e565f556c5d7367096155985d62178e50e4a82e775be9f8b15e58ae5e801d852ce4ee06c83139e1d75219e173acfc3d314e39eb3ea6cfeceeab2480c81a23ca9cd01cabc2054d3c2ade298b2122e40f2596f44b7b0ad003886f0a08cd5f5080642414245b501034b00000069bc2210000000005a58de4ec5fc5f84ffae233402bc7499ce2c2386bfdad58ab92010a21d878c20bdca6f0068c35a93ed8d1845bb4234ed445e5cbb4bf5c3642b5f02fc623e410769898a25c94040ceb38d69dbcc485dcf7f6ace3207121294ea5c8cf3f12e9a0905424142450101c85499544307fbb4e4ecacc65491fc3018f3c9e6d143c1b494fba807f93aa450e813978df357010f384face47f21fd8c357d4c3e31188ad6dc81229c7172398da0fbf8ef2a22b074f9e2b8fc12c3bddd89f5a35e968113b3ccbe65daab9ae6bd8ee5e8012f41f33e323d27a16c66ea13236bc8864834c5e8974e76f72c325794273239f95244c464cd34df5ad58db7449434c41b403d58aba640f65773fc2d4beb9eacfe080642414245b50103880100006abc221000000000a6c7c913825792cb0ed3e0b12ce7ae5caca1c2f3f4edda27251ad2b8a77cb5476b6b639a2edbb339fcb52535d098c21b1aea58cdba39930c966354ab65fdba0726c74e59b4b91bd58a770f9e7a0e21ccd3b2f93b37584ebccd5a541807f6a00605424142450101f0ea1f18d9d84abe9425c1ae5b0898b9ed31559d6ae6ded9416973437c67af4e4f267e3d0f60b5501636cec8fcb19dc2a8b5b6ace428a66f94c50d9fe4cdbc80a21be24582ace6b4e104e9e062bfbd60d70c3eb4c709fd08b521b7afe15de3ed92e5e8017f13c25a6779277de3ab15396b45eca1223a7ea000921b5e49a8dc3ef622f7ae013200b1df47f5c58cdc87f5d584ca25a47353bddeccb406b4573712524e0d98080642414245b50103d40000006bbc221000000000a4bd620757c465a3a59d9f4a65b3285e5a2f056ad302df1a2dd8598170abfd56bce61265649ae2daf1d7b85052afe0d86a923210dd99a7f2002873ac31b4a60f870de682398aed8b3fe149939f727ebfa8900b81e40015b55192f7e64b2e740405424142450101aa3bd13c40b06b8417161d909c59fabfff26f3df2eef82b1fa10f26d17326e3ec5d11e7551d56a31f8d35b27be621007cbc7b13bd5989cbf60ec3eb90a95bf824d19e5a4e5b35b09186f7380edd169876661dd6282f8af007b71c95fa65483c496e5e80105f34878740c737ab9ad584851cfaccbe75ec6316b9cbaf217184f95832e916c144229ee14bd22c384406bc593536a79648803c20d7e21df6041527b5a75bdc9080642414245b501030c0300006cbc221000000000ea72842619f1383565fa871af0b70e0ad1ac9d5b169ec68b3e1319de153d3f1ba2e440bf4d4157f31da005086af2bab9b8e154100e83234a56a9658646740307f664bc97ecb0b935e420220a7b247106780e48d632e53abf8ff8371c846fec09054241424501014ee0f872ef731899040c6091d6f543d5ef2438ffee3ea35558ce28ce781e56443eb32155d1032b7043e700e01efb442ccc912307c0d00c40ae04719f877ec38f3cda69993ced0c4dc79512bf8ebbce7e66031b43120ea3e4c7d79664563ad4289ae5e801fa4b59126d354fca1814f6185bea4e997aaf56790e7c56b7466b6edc6bde654f89d3a094417a0eeb1b541a04817aa80261108da91c569591ec83c02284b7cde7080642414245b501031e0200006dbc221000000000d87aaf5c68ee26a56fe184c3ae0b0e3d249f77e47dc36eb9d04d1dea598e3b7f292b6d1781752ae26f096a037682b2813e3b778baea15764197bb25b6a0995096a4d421ee582131dd6bcfa05a4d0247dae3ae82d59671e07dd6b4157644c0b0b054241424501017099192c8ba1de0e21a0a8fb30aa287eacd58c79014de989ef414a057bb9e35a2472e2b5f0912aad2fd309d7ee9a2c7207122a03187c65b795bfc9a3cabc88897e8edab0ffcbc0911e97eb4b65dccdefa92d8a3b0239422967f98219ad38baa19ee5e8018414f1d7f8dab09d02f55eab69f53e13d27c48c615634644206b2790b9ec95e1fb543db05d98011f3fb2b770353cec649dcc1b39eb3e0b1f0912d389261b8fa7080642414245b501031b0100006ebc221000000000188d474d7c8a47924d1eeaf5afcdda953f3c2cc81a4ec568f101491e1f2fd715aff8ce92dc78102a33b9dfc5ccd920e926bbaf06b3c3d136bfe7b25667b88b0608d8a8fac8db00f5461d0b574b77667ca8342c4a22afef2a4cb9b028c16c0301054241424501014ee23596d3ca0b18720855abbea016746d83be690163bcb70cb4844029cec60d686ae789f0d063e28573c2cbe1f8f75fec2421c3ab19f3d133857b3ae2c0d6866b1e60d99c5418ff2cb95bdbb8e11232fe72cc61353106f4da02d0b6bb4f6578a2e5e80199753a8609345d5c20c17aac148cd889ccd4cc051d5f57c83274b22987823241a01a62cacd00b91285f2298eafccfa809f15ed0bc492ee65dd95305836037f5d080642414245b50103c30200006fbc22100000000096040088af5df33d8cda8ca69af52c80c239e81e9fedaabd8bd92e824bb3700c241281f8b67c5833a2a69b5d3d78648bdbed11ab0a89163386ef731a0b630608805ca904e290907620a824284a1aa88e3b93cf3aa4d211728089da92d353eb0e054241424501012485106da7daaa607680e78cda5f0fbd9fbf16b291d984f2002733aec5c3b677e3d773e3bcc4abd6cf2258ebd87cb63625c079c0e7e4bd2c52060a52179af0870aa537c5d49d0d9083560c5f4f31a0152a8fa21f63265b66bb73f9e3e79e3eeca6e5e801cf5321b8ae7823ba24c11198d1e26f769b59d1dc76f1a8b7db87af3875554ecf808f8805ab1424b5d5c847cbcf646507c47c7b758f016d3d22a89771d2fbfb6e080642414245b50101d700000070bc221000000000149ba82bc2f6aa7600ce727383572983bcb93da6317b15feb7735ae554fcb5268a02e042c24108abbdac14454ccd9ec4d810b172a3166a7932627716796d4c00359f33a31de68a65e9d46641745a2350efd67665e0369f7a3c323c386706cb02054241424501018cea8a8e01a755e04af421db74b88828df05b11237601aa53d3ab08a5bf55d6d00d8f52f28eacf1ebc64af12900ee3d7caf729118ca3061105e90c450aee90885ae519c27bf4b477e8fd08ac754ac134a6453206e0449460c387c9eee4510034aae5e8014eec8ba6f593e2830859e7b0865d4133d87fcdf5c3a0ddad2267fb83cee781bca3438217fddfac70af24f1ac54299c34b9421a526a48f03dd9571ebb18a271f8080642414245b501031000000071bc221000000000304e8ddd78d8e8615857f844235ec819775695c2cf16cb2a4d33bd8c219fe638277bebbd6dc863887cd605da8d40f2850b0573e726feb965e315df4c0559ab06568c6d5b55c3682dad724da9eca1f7c63a1b7df9c804197dcee53acc9a6f880505424142450101c83161dece4f107e78ff3a100d725e0eb0c325c094358b9ff347f9272bd2b933c4f14e84e7a48523084150a84a0260288a48f4d06a21b7e60c0d18f26382538f1748b1d03cd3d15b5a633d52e1fa8f9fdabbedac60c84b9706cc710c6ecb17c9aee5e801dacdd61788c654eef0a413f70ca2c89b05778e5a26771e29fceb694bae9bedeccc2a529e9cdc0b27223c06e04a82a9bdf42b1c21fbbec072ccee7e894e717dd5080642414245b50103d800000072bc22100000000048e413ce447565d3ecaba25e57adbb1005c5bd21471fc5cef4710f141e17974c5be4c9e2a7c92ae56db235d1f5110d481697895156f058ec619ad60789fb3704008d38aa5f59edaafb4de17ac92610d209278f39a2c173f7a8229048573a010f05424142450101c62ab3fcb113ba312e4a3854ea6ec780b1bba39f30c8e898bb7c724ec9b75a6b6e2698498cd63dfd6d666039a09c11b0b9d4f4ea3fb6a43213e96c026ac47e8b004e2c0eb8f655a04f892f51c3f2e4c4b6b414e5b1745cc0b5058ccd4d1e5bdcb2e5e801730fc8b0727472663646f703e89691c14f774b1106e86d27f97e29ff9a01afb1fd602893757ea9758ba079e82fd00e67b091f8d41ad680a823a4ec7defd708b8080642414245b501014b00000073bc221000000000629795a401db4b628c19400d3904c3dad386add8120948057da72cd71d181157f856b3b30a8d148622284552b453f3c835c9455c8d1aec6bed6c00ae94b6c3058043f9360c9f3cc6679e00f52a190f5851c62270cb4cfd8eaae2fc2af819ef08054241424501018c37272fc40008139499df167aa0d2ed42283660e1cb1c95493b0c90f41fd15e1e0d945d6c96e8c17dd0cf63c590bafbc4b16adb8ae4f1ada13d0e2698e77e834aedc5c593d1bd07084913cca309f0bfece1ba3f434984e6e13718e0eba0b0a6b6e5e801b5af4b325813300576708a16c9728094c14428bc4bbeeb5e7a8b1fc7a5df372a046e2ed428d620a07176efcd8e09033ddced66f5e84f5de5fe2b42c06bfe618c080642414245b50103ad00000074bc2210000000005abf05f41d7f29c3ee90f2408e1e919bec7308e88884b17a0b5be2f6fad6d26ee72cbb41482e67a121f3cbb6b53079743277d04fd81da61bd20f0aaa822ea50b8c02e651d76ac39f54f367ee96d625af42ed47d8dcc3db0b199b116aef8a2d0c05424142450101c8594d4d0299fd0dcc2e58dd474f54acc78385d680dbcef95682e59a0d12b163a816ff35d3c74ffa3f4bf679b06202a2f136f5dd1793d786fe7dd87f14fa038c95432c4d47ee342136041202f7f621f93384961d4e75f396d9d8f916cabf0029bae5e801da9e81be2f537ec7381e9a9b0510c11706d3908e0c1183acd47e4eaaaa1ae7f36725963e86c5bc7aaaa219931306a21b3bdfb84782b68f25256edb3489a6e9d8080642414245b50101c100000075bc221000000000480468d8f81bd9ee1b1f8f3127a02c17a0a6faaa1df0517ab42ea6945fe2721679f00d8fe8e3aad1de3fa80605b08b5b564d7b0ef06385306a9d96b44124de00e947f0059e48d446502b414a7a619dd2385fff6122a4a3c3ffc6a2abba1db60d0542414245010146fe81694f12d96762b2f968c9ecb130d0219791abe0a76343726c44e0a41264c6396268fd4a0bd14247bbce151137bd50f5ac6547167b321830c2762944108b56cfcb8959226803ff24a9e02b6d036e1dc599ec672b635b2d29379553b71057bee5e80160765da10715abd93f458c17bea2591cd6f3b7ecd0620a6e36bfecf00ff7cc02665b93ffa5ff88ae45481b20bac92b3ebf45466550cd2d1cbf1fc4640c37c921080642414245b50101bc02000076bc2210000000005ac4c9737fae2940a730f540a970909ed3526b33277a229d67a2790e35439b52c9cb8ab646811b12c3f6d485cdd0d2403ff1743585462e483f8fa86da0549607e939c7d4e8e04be6dc51bb8d683af323df42f754e73819cfdf5e9c5e91578d0305424142450101c4395b27bc6e6e826fbcb22783d8bb3822581d55c4888d61503c8d72d8d05421d5eb4e4d2dc3db13a7ac7c550a8542fa9b8b4d77878e3e8b3f71d8d9ad1c9289bad0b10b8bdbee727cf6a3afa856639c0c28f52a90857900b32d079c301b62cdc2e5e801d82b1a5589c33f4ea3603826aa061578c7efa9ef72e41e940972d11ae007ddcd9f8a4eb131b937d6cebf58a1ae76ad32a4f796a1b207e60e9a6f8dda7ad0e6ef080642414245b501034102000077bc22100000000044e01ac465bcb0bb1479103e7bcf4bedf54043a09fb437c456f66edb0f798577d97488236f83336bc1b67ff080fa64cc1e4998b196380757aca46344cf99de013cc64b9bef2e43bef3c17590da93f6d5524afea7b8f5985cb3863c38111c880d054241424501017eebf27ea9d302fcde2bf4281c8c9c43bb0f828e9fe0ca7a07234ef2032fc47b85dc2747b37ad8a289ac420db8d5041f8050b796c76b9252cf1f1b49a294d683da9536097d21981e973e645b50e38303a3d4d5709e0b3db8c0de29f8d2655087c6e5e8010ab09466f9131a7a394d0d971eeb4ed6ac5b31460e7febb6bc9890fb8f20e266c855331f2ca35865e03d1784ed212f4958f2c7f11ea6b442447678e4e4f849be080642414245b50101b601000078bc221000000000ca60a83cca8c5e008c809e645baf9ac49cd681cf85aa891de47dcd8d9342072c294e7441df8513dc016eb60a2207a3c8ae48a4b6fd73f99258034dfee0b98d0f44d4499cc17a72f111636440978e9fe414917389898402372365358996235b00054241424501018a9d71772c615f65ea1143406d5b04064f1fa0a5f1d3a3f4b9b7a3c92400773bfad53c33c8ba1281685aa3dc84177d8c9e4d45c39ebcf102435965c19e3bc28e3fb9a885ca42880847c7c16e97e94f175478905246e1a539882f732b75f7c6cbcae5e801f7c4aa648722608cba15027a07974b1d5e88186e18dd41bac1577e0f31ccc7aa0845f5fc6cad98882b3e94030de79b6f675659ded68da6ed28378a26854e20a7080642414245b50103a102000079bc2210000000004e501cf595d9dd74fd3479e7efd818c99800501298a83ea10d531afa06ba6a0a314c4f2b85738622459b8876867a26f7c45ab6fc52d1a0302bc6579954f5b40df0ac5b6606268bb192cbd8a57364779d9e9a9f856e6e7917094786da372d9b0e05424142450101ce1e3f3673188d69e6c53383cbaaf1738d9505547b87e0a0db09bf97096e6a51096a0d29da03e9cfef79763b51e567736b300288a79b9c683e771cedef675a895553fa8e82d0fdf5495319be4ec49520754182c1852429e9764bcdb7fcdef944cee5e801ef63586b70d2119c1afdac58a173b4cb2c508d3a3ef0729a021e8ad15925cf39c5df06b9c0ce50aa220bba38933f61a9ce2796f36cfd3c14d5b7efdb8cb484ea080642414245b50103460200007abc221000000000504a4d31c67d22384299408e14b026d71e6b9f6b5de3c2a35c15986f2156731a43f20ede81c081be478a09b00914ece394c091786d1d0f35762ece9e8ff4c0035f45fad17b1090840a302dad9dce82591d2c5a34b1ca6a82b6753c4b01ee5e0705424142450101a866c6964e78dfb5544327d86e8863026fbeb576643aff9a836b517c096f33676db4d0d61df75d7811c9271235287e88687c27da02b4321e76c9584cfa8c2d8de0c19296560d9f5ae9fffb3068f02f88ae8367614f14d4e8733e19be4e0ffdb0d2e5e801ee2d445932f8573c49cc83f22c4ea93aabd9085c9efcfa22fd062876798e90913aa2b4e78131c781ae1d980ac4f9b4071a05c9d35702c8fb6d812323cac70566080642414245b50103fb0200007bbc2210000000001cdc7a1ff0dba7011ff1b88a6d142ea58986e611307e6498c70654cfc517c06332b02307d4dcc9228907ca359ae74578044d7936ddeca28772f7e53997f81c0cd07d1894f276b520350e1146bc94e1ff3ea9cbbca0548ef776544cd7939ab30d054241424501010e43c7fbf836097fa4d75e72e2af3d747a3b3bdb73124e6c2464ac497bbc4731aa336f74685d5b813812bda59cb2c40682711c60866eff8a8cc882eb155e198f96b88e04c8ac3975588d073c4699f18ead8b0c44f23753c470bd668fea2a72d2d6e5e801f0ddcfefb57825605d0ca6958db01b937a6d87bc8430e29c34cb6b9bc4a73ee47e71ff90be2493102be3c291074bf2863cac41effa9a7c4284893c3f5fe18133080642414245b50103020100007cbc22100000000048d5c62a71d5d786641ee3472f7888bfe6b432ddcf51a8a27f580226a2684154cb985a71d52084cd3e98db6ca37454d2f9c365cdad98148f5a5ccf089e99cf08e408f0f10984c26d06e0c828c595d670294c641d7a4e457849e27be6a03de90105424142450101dc2a57512d368067d5abb1bb4745483892984624db88d2d9b6386a123e5396042814d3c6c6a9a7f861e241bde9491a98979e78eca0ce7188c25085b9ffc5f389f30eba1494ab0f59066e9241d450119272561ed4f14d5faacbb1ac9e408be8fedae5e8019e81e7896d544d6a0510581c22f0781ef76bbcccc60bce19194cd731f024c461137519ec0446140b6fe6c209d34977aa024f540b0250c332abe74b106a3e42c4080642414245b50103180000007dbc221000000000681135199a47c41b4193937db1e6e90cfc6f42c43f9839b82c46f76cf29e5b75e8430efb3a02b3e3be3420c997cabb510d84fe0ef02d93e38024be980493ac08fcc1e5b1667d3b62c951f3188765b4d624026b6668f4423ec6d59c9144cc1d0005424142450101e222ebc6b429fd958b0532b88d517a7489f1b6a2b5369d99725c2acc62742439ce1314065f18636badc497439e002a18c731788ee0e87126a8dc5355cc45f882ff48856592329382b54c0f3fecd42279231bd3f0ffda8f7c9684d764f3aa3415dee5e8015bcc608a00a294717d40e0be752283950f561e3e7c1e0c26d792faae17916a293eab6bcdc137364892c447579a4696a6c102b32b39b51953c624e10bf8c05954080642414245b50103790200007ebc22100000000014cbb9b686e2b476d083023c27c25581cbddafaac5a9797e95bab40fba7cc951c579c086724d719067b1c3ee2ca1fa07f1aa670016f6962db59607cb54baf00c58fbc0f6d7cac2a6a6ed21d2765d488ce2ae57453fdcb736603df747888a2902054241424501012e72663fb5df6c557fd36b2536dc9c2a9969d31b8fd2fa722a17b50ce4a3fb49e51ac5da402ae552b1b9cdfb8cc6ac20f3d62d0768a03f8b716f8c6f9bb6638fae6e2dd7f098d9ef8dc05261ef033cdd035f17a6a454e81970464b5e4a2f227fe2e5e801215d7ed3358964b8734b8477638c40f0e58602d4979d157f91f711f7a9126a8e0648d4dbffe100b1c45427162f05aa52dac18a86533774b4a0b08d92a025a6f1080642414245b50103890100007fbc2210000000007cc7446e45f0bde0481206d5e82ca92394da6daab2e085da1777e3f324d9d659e1dfabaa1e5527fcf92446656614d1e9f0510ee23907735252c99be27ec71e011117ddfe2154b7b675d0195f85be0c183d780abd4e5b53b2dbc6cc4556537e0605424142450101f498d9106bc4dd2f3aaa22fc46b729b0ff2adff67327e6041adca8febc4b2579c610fce4f2432b1320c03a5fe72555b9ea913a6eb90c9293f6f53a48224a298eee69ecc538f9b9429211bcc4f6618b3693f7c4db2950eee895c6d0c826f0c3c1e6e5e80143ee8bd562a76c41062d49f9ca8b5caa6f3adbf867623b824f49f09261608cacce397e1302655866c846e2691cee4eb379fb83a12fbaa1cb36ad6bda82c4eeed080642414245b501033f03000080bc221000000000e4d331d4dc27f63c7babe9571addd45b639d064ee2affda64e32e33cb3d3cc5269a61a8d41d7e4b8180f537cac0cdc6f36ceaac830a3d4dfbba9a5fdf932bd062d3b85529af1a03784e6e623fe64134741c855e0e130ac6374ff89ee4e25e30a054241424501014a74eb6f42542df27a449a925108e03ae02383ee3de22f4a69e7aa31ec8164122164a80d4ffa9cf21d4085e92a1fec6fa649a0f32c3c2e79722c57fdeab4358d4d8b2698c49cf59ee4724ce444710c4b096adcbf3471b825dd88cd530a82fcdeeae5e8015a95c2e28b11b3af6a30aa54bbdc43d69f850ae532d46d49a953adb7677f340c0906bdc221d73d4f7ec6c14e056de5b19fbf7c12d9aa3c0591362bbc4c9d0615080642414245b50103e002000081bc221000000000642625825854083dec5faf4d8e33c33f48e9f62801364d2220380504b92cad36dab18f278399649423270f49ccad6aa5857120b8eb9f030a9d867a34c2133e0e3eba3055d53c1e019a0ebef1f89460551bf4620c9e680907c39e3559fb8f4f0e05424142450101e217960ab23b821170d56c68f36b1731f1dcd01ac529950a0f7f3b16b8d33420cb6dd348b1c4d75e0a8309e8959482761dfdfb000ba8e20ffbc4f13a426f988c7a098207cb0692a83320269f3b9d29c691a6a47826cfc928ce74a68355d89abbeee5e801c5bbcb961fa8826b88933f0478415bdf612ec2238a3c609f865745e66898efe2ddcb823578725d898793856f587ca82a55ed63b61c86cb7b1d535ddde422a643080642414245b501038902000082bc2210000000003e43c06f59b22cd1b1ce344dde3999c2302de70b5b44bc7b904bd9552b6a781bc538c1fd4c70f98a1264bb535d81aac4f91a0e13cfeffa58a375d93e87ecae0251e24a48731098540291fc260d9f219db90d19fa94c5861951499da83b30a70e05424142450101c45e04a2c02f7d7d1f9cff64d342033fb9fe2b3df391c9d26cd19fdff47f5a4d79f1b1f03f892ab739a0268be202ef1f30128d9853a0a3de30d9c7000d184d8bd247e246aced0ec42dab43c6185b2640d904b257b2ab7150ab435434e5e54344f2e5e80146219f0ccce3dfc793c0cff235282b2ce08c377b384faf4a3cb64a4cfd8ddaba8942b6f760a8d9a0c009a5b1c53f49ec35a905523d88e6463c94af98d11134bb080642414245b501031403000083bc22100000000040ea599369c17fbffe4dd125fab8d2c512a7bb307e0fd328969a82b5e46d6855065b7358abc3ac7fa31d1c090c52040d5f1e864fd7d18ed094ec58ff15325f0e929852d8ac1c61c2ed650514b1716c89b7c34347a6bed1ef70504ec2e318c8080542414245010168c88640aae6353a5f433e32dca6495a9f29a8a00097c4327751e3756c01a27b5a73c1614732cf74420cc11cad49ea3452bfa8f917909a0d84bf7441cf9403886ff686eba6e9c180814dc62f3301feaf9ce9a7f6cab4216c0fedb1189b0a1873f6e5e801dafd78dfa9c15e9211f691d1bf0fe7538cec2d3a48a341f13f99a012a3c695bd3176cc1b55139ba3bfd341664b9e113071cc29327d9e87841bd6037bdd9e8774080642414245b501037802000084bc2210000000007cc2f237dbfaaa782a7bb3077b972e3bc5e197e188fb011af954b9cc33d73d047b0244c6dc94d5ac6191be33875ff0a70cf26818762910b3ebf2e96eadb0ad0c66a201ceb6778ac1c91ee1af08c7c458be81aaa420b3095e527171fae57042030542414245010154c18045a5ff1a6f3236241efb131615c7dc06a74f87783338fbeca92c84ae55f97ea1ebe906045fc64d3565f5b445a7ba121d34600a22a89fac033a09b0e18f0ee19527f0f987b7014882d84acfc611f4ce182c0af112eb8c039ae89e1959e2fae5e80172d46425f08c8815f43be3116b779598d7fd4ae9c17e4623a48552e115406acaf7bbc9e4f5addbbe121f1060202d1436c0fcf527621d78b608ba7837aa4c1aea080642414245b501033202000085bc2210000000004a6d2d9fc4d65771dc5094cdcf27b54835a7ec6c302dd427909dec711ebd8730ecef5c33fcb17b7ad7d344ed9bc059f24a016eba03c3fee2e9f34e19b5d6910689d97c0956325e699f056db332e35e0ab54e0b47d5a0e6de861dcd0b1aaa3c04054241424501015490d3e6b1e1fcb62a18a4d0b52cc8be064233bed9c895c346849b998fba4803c57e6586ce39029eef5efdb23cb3384f25c546c6b215c1e4d980fb8a5f0f2d8f5668a7d8ce355bcc5e28edb577b337d485f39f4a7d2c9892732481033666a3eefee5e801c7810374615db4f537e71cfe11fa13a851a5c437ce1f43fd301632f2dfff9b107c3b2d7cf3f4bd69149d331b950a41d9414664d823d67bb0321fdbea8741e68c080642414245b501017702000086bc2210000000008e65cb73bb1eeff530a94076f5cdf14d8c8b4b371316e4ca7023ec9ad62c5202006ca79c0414fe1635260b4f69de0d0158be6a03f861c0a022cf96baba3df4072fa319748ccd12b134441e909fb635de6e7f7ee25b830cc7eed0081a1450790b054241424501011466773e3cc6e30ce10394f734f91358a5c330c268470cad729d27b8360013689947644de6659fda5416d80784d4cea38ba9a12d155422586930a68c64c7a689922deeb306524eec0f8d50085a63c51dae6008f873b6d24ac1fe2effd88e9c9c02e6e80114d5fcf41012335ca01c50ef7fa93097b3cadc9fd16848ed5b1ae34f0b56f37f509b00f4f83f108a30eed47f99d162ed519cc11a97a9eaec93f3e5efe00bae3a080642414245b501032c03000087bc22100000000018296efbe631bd701f41051f9a874ca055e744da021a5b73bed0d4bfa333f57368cf26e5f4446db69f831681432bf12a418f4011beef0a14f829e6730fc447065137acb98843fa901003bdb8d6392f399cec9cafbe9ae0132c40dbbd56fe2509054241424501019e93fef91a298da45124c2bbd2e5983dc01321004e522aeb676a319e5de9f97404adc98158e8e80055e3b55bb08a18708768105968efe9b3d4739db9e57b358cdbd6f84adfa84690e2b8773bb5e5686f95dd43cef0f9d9af2355803a68c6265806e6e801c111797b2aa4ba3795b4ad8a007139753dc84b7226ee02e61cba11b42003a56c7c6fb663f6ef2da36e3af3555e9f0ac3ccf933a219aba59ea8c1150cf94b9591080642414245b501016a00000088bc221000000000704a7a09a85949051187f8b596e87e71fd135b066f17a95350574d1d32c7a2204e84f3b5250d284b83de97356e3a83119bfb184c2086f34e04e5d05126fc19089d1dd6c44eecf91b0e9d132244b32e3967e99b4926e1f8f82badc9681606ee0b0542414245010168ef1a0ef9eeaf5a2a5c28d901b2a2c577446b7bb924cdcb3223ae4aed8ffe7507f10ee12bec2efc035d5ec1857ba0765c145ca5f7616fe8734a404c0695c48cce12152277e3d4b73653cf3f5e8f3edbd146680c8d5a691f3a4ebbce03dde3a50ae6e80112698792d0b0cd6ff8fe7bb2ae2fe34c0fe9baa2bf16a4deea3fe57229136f4905f8e0b816a731fd1fb2ee021b9c810f121558925c77622737f18f28f113184a080642414245b501018b00000089bc22100000000094b60083e9722ab3a652d3ea5bfdb9fc036fa3d5b44205743c428b01df1d223dd10e0f854a50ba93d6e656bb6e5eb3beb31577b952c94d2696ba4908fabc93068794e09f39484dd0a6ec8f596b14bf7f6ec4d9bc6a57d91f7976da6928d3f60e054241424501013c81b0172af95a5a7f3b5623f5a374e883279b39233868320a5d86077fc0295a3c57b21523ba1f4fdeedfd64f8a64f2632014471ea8d7abf9cc808aae1c4bc8f7860936ecaab7610cde16b6265231cd32e70798f9ac89824c8611ff9e431c2330ee6e8012d87b95f832a6a8cb2c717f56e7faab21eec8272cef155a5a657ac903c29828768360d5438fa339e4b619be00fec4462b1b44da69a1295bb4879fcd8f6641058080642414245b50103700200008abc221000000000fee7849261b1567acb48de40a9195472426f52b7e491b701c94564a2cbdbc0685e44756b296d0655c4f2a16c5d3bb2c13b7522183bb6caccafea3bae3080920ef096cd23fe5b7d107d26673c8bc492f55e595d9eebb7a9b51c25ade544fd0d0505424142450101aef6535443e66b72b6e2235faedcb21303d5733d8cedc8eb543cbc6824aaf63368bd7a9ea1252ba18bce84a241c85544a9cd519fd4eb25a0aaacd345f2997483d3c0712b4eba4cd660552d3d628d1e9727923141b9c1d8583f528de5e752261012e6e801f2223f91bf9673450ba1f77ffbef15a600528268eca1863761635b458b0af43b14d80ad5e6028b08338c290354a2d9fabd26bf60f0184ea10f4977b9dcd94b36080642414245b50101970200008bbc221000000000721f1cba86e23c53fb123772d2a0678479e617612dcd4d83a86d6375c054421d6cd53d156bd95b86ab3df6ea64e67f584af83e4e8d2df286b6a92a02e8677d09154660ad5bb40ae5f9b602f4eaa5d416cd157ef62b77c3636ba323e88f5259010542414245010190238e4606bb0f6e20bc484f53580376afd4df7acab2a501335a653e2f751242d73312a4f3baff9f58dff6bce199aa914b9e98eaed644e78b07fec86b44c5b866ff4919c6e5a1aa62f3b3bad591999c12407aabf39d8839ef201c3df3cb9040516e6e8018c0ebf52d1a38bf617f2513dc85ae5be824da566957cba12f2a0505c678475a91aff4ff81d4315624aeca183a2f772d26ab2c0891fd6f885b30b7c5979a3819a080642414245b50103130100008cbc221000000000567f01058622f2c7e8333b86ca1b26dc0930e79e15eda4bdf7416ebfbcb53d25652aac01ab65d79953b990abd4a3b3afdfffdd1b3d390729c8ba4d923479040f43daa806ee466141f01fc95e46c7e224213babec669f5e32609c39c6f809b60a0542414245010102777e9e14efe1f2cc380aadaeeeb62a91adc2602b05a9d5a4883af6bb39ca5cbfacb7a7b110adfd18b4ac8ba2f6b3313ef01ba4c61b89d818d33b592af28b89e588e124771695eb1c82388c717ba7fbee458fb606ba0ff6fc63f10a2500bf671ae6e8017e87f829e9f33801166a2125434e3c27aca829b285e7ae46837922c09a08867e61ba69b4d06f06ead6f850e7c54ecdcc2bc5c8de79553bcc647bbb568088c339080642414245b50103600200008dbc221000000000a674c97611cfccca56159eba786a2933aab3d922cbd83a86c817c00134e2aa527f8efd17d9f701022b6ddf645d053dd0e2ab01b5471902b6ec3ec67a410d07094581d1e9ef4cc94cbba862f80876baefbd8cda4c258e512845a9a6c4f1c0500505424142450101a88cbaed2f3e3077fee4435fc38d0127605a24b79fad6782e8030881214d8c164363bcd52e5be3551f513809f5d3432b2f5709753dc5bd9c096a33699021d48066766afa4538eb57bf29a8225b4e9d92a457079e70ed9ea49d67170946354aca1ee6e8019af72aea9c80c8659e93f28c84a0ab9a570e81c09d62636f6fd22f2d43ca0a8d75c94899b8b1f9893d8f3e6a712e7189af76a15a9cb36373863fef028876ec50080642414245b50101380200008ebc221000000000d859c3cd3654b3da5095e80bd0fe2ebfcb7f0d8e2ddcc52ae6594ff2482e8003f39d4bb8162b9cbedf26193e86bc7098942ba158af9b99f97a282f0d9c48730f6e9f107ed70beffb524cf27ce459b4927489455829967e2b2050b4d71c46f70a054241424501019636390338640eebc937430e60080c6aa9875972120c9c7ff7b188ec4e0c5131732c0a3e52b6bb388555ba2fbb8f53e94008d5a324b73d0108f60a9aa45aaf8dcd265f1d55657cff56d65cac3e00cb5fc1da05675df43934a4fc09df4a5309b422e6e8016be31a1cc7bd52f9b66eb9e311dd5eb660762b7fecddb46969af9f1839702e3e536f42ff7b3109197daed4958b526be2e225a2197f939c74672f9af8dc342cf4080642414245b501030f0200008fbc2210000000000afb22c7d307829dbc3aa0a4b3d8d334c5891fd454d10911450aa93cb6b56a1a438859171e6e75b177585175595aa2c0b7dcdce585e3c1f8e04cd18f102e0d0ce3c3ed63716d5638f0ba24be4116e43c65af43e06157fb0edf43ae4a18716704054241424501014c16a18e70326e7292563874bfc19b1c74c224f547df7b50762daa05f8371a562b4bd6387f69858affba2856df13fc2aeec6edadb37a5a2a662bcc082fcd73837c2fc9ae00a433b39cc12b6e45ab802537d77299572d191a8d380aad9657e08726e6e80162bf72c9c4b17fd7d1e4e7c03298bb6b75e1a434c4b1a203fd787cb2f9cf072187544c3275b243d70c2dcc0cc3b81aa6114bf12443839b65e5e56e6a79cd14c1080642414245b501037e01000090bc221000000000c8d78cf5b55e7f5f79f704aa31c78e4e6abbdb65dd793d3c18b8fa373c782d42a03946855724a6a65949301b2fc81ec5e6bc1876b73b6d27a36fd8d3db4618070bccc9616d3ce6ab5607ed4f597c52f056333d15819379132ae16f7b5e15100105424142450101bcbe76d42008aeef6bc11e8121c55d0e2aa6ca0bf3f2028a849936c72ba6c401ebf15b2ccbcc66eb9a91db848479d55f0c85da07a94b6b8bf3412f7302ae7e8bdfc450eba524df2cf7948329af0c52a880b4e40de3239160531a7116edfb8c222ae6e801651bc31e831de56c20350a8082d5959d9545bc51a73462cf5852a5d301fc15436d3834bdfad20657f737cc70c3d1ded69b091d859fac51e69381693f4b9ad2bc080642414245b501011d01000091bc221000000000f23292d36b80d6a075e7ac3ef7ccad65e0eb31638826adce0e133bed3e18af338f352fb0125c399f32a6faa179b107d976f2104f2d346ea6272fd7ac403b810bfcad35ebc180d2281417b397ce91c9fd30458a70023e85028df25ae0e55a55050542414245010112ad4b3d0be3a7afcd39c546e4534b3c7225db865be4a46d7532e14b6d9a1831c8cf54ac49949b987cf600f3043df993bf50942a3ef38d87df1620b6ede5b184fdedf975a642aaad12967a44a59efb96d487a20d278373b0525dbe64a02a1b0c2ee6e801b0a9044e2350c224b89f71da5297a766177557a9e514dbf5a472057ada9dda410385751103610faa740c0c90cbe9ccb183d7e8477cb8a35bb9e947c1fb8b1474080642414245b50103b600000092bc221000000000329193cc52e21a41002d5b769ec34261a25f56b893b378a373186ec715985611a4b1eb4bec16d89abddb09c05ec00d9fa5ede6d6a6471dee842bb700529b33057fe12af679e4a6af90f15366f1fe3164863dc53d15ecf6f30a8ba34553f376090542414245010150c03395383875efe7cd3e7990beafd9322eb062dcb3749686d262b2d4f62a5437cf59dd3a6e029a7158d836b7ee3cc61adea7afd0873afa7634636337c8fc8bc598223a199d653847c09ed7d6f198db37c2fec74aee451b623ac693e84ef7ae32e6e801fd791cc7f7faadb77077effcf7a04e023803febf6640c6e58cf68c498dcf6c48d3cf6f7a420c38b82e2339f32d35c874eac9d12c05aa57813d21b75def780a60080642414245b501037e00000093bc221000000000940e9ca7e8ec6bfc1200e1939bf43194f3f26824ae46da188868ebf9aa91f6530175b790f7c69465c97a10dbbe134a9d245c32193a0a915141733a90ca5d2607fb6f01189bb44047f90d7dc65e438e9f3ff2639b8f04574c4d86d3278c3f2808054241424501018024bf94078ec679946b5f8ad2c9fbd87534d84eb527cfb01d30475f7615bc2a3b29193cbfc35ceb592a6b14e4152318f2d4dcedd22e5f6ee25d01ff3c9e2283d027ed2a9ccb46266316c6a6f2bc5ad1a53eb15aa3ec34dd6f0742674a0974e336e6e8018a6f5175202730d865ec53358a10036c97abb7582ce875c6cbc477bd739a72cadb6167c0eb2d55984af1ccfc3b95e3ae38bfd87b0e2e13becad32e2576d3ae94080642414245b501032903000094bc2210000000006a3c13d49f2e2bccb674e25e855176fee47c0f449a9f80869d4aa4aa2d5bee2fa2b875fcbeaca8a54287526346ba0a16bbe0aa4b0cfa13556a41270caf4a830ceff546c41cb9f83a43e45cc7e33c49c5c1980ae60cd94030d2b8b0835ee4650605424142450101a471ee9651b83ec3ef8424c6f6260a27ef2b7a7cafe3a35d6435775d57c2bf0716b0f863ae6048a25a6ec6a6885a5dec5ad38104d478f2aee229d318461c818d398091d7390116bdb30de36f3bb4b2d3c9a52c80143adbdbd875a04a9265b2fd3ae6e801e2d81ead1822ed63513824b9fc063aab074793cfafea2356cff607eaea123ff0fab0256b54777808a82ee5aa50840c5d4d57db3dab84dd8e5701dfb0c6222490080642414245b50103e000000095bc2210000000002863026a62f6b72500caabf650ccf18c23c590d0f7c82cef5e23c7064c72407b68a31716ace178c57f9740a41d916464df4d60ab74bc60d3518cc62ce41b28019088e6f1efc62ac510d9f0ddc2e3defb44580b91ba4d80a9724f1726533e780805424142450101e2e0034f8c9ce5418e916ea3f4f1f628231d9b18335dddb249ddff7d6b5e991e28ecce223d3638fbcebb655ccdc580b7228b9a936063fe8c7722a3cbd9bfb783916bf50142a4704ef1e1a97c3656adad10a3283e69e5594378b323613d06b8353ee6e80151c9aafcda8371f807ccab955cc7d009abfb32e78b085d35334f73926f7bfa02ca83a014cce1f6e43eeec4d9c58a70f1b2578283d5bcc3c62c248e1453937480080642414245b501019c02000096bc22100000000052a98872c9217d8e958be154d7c399cf4a801286a6e61125e82f03964bf49f3af019acdf73fb0caab369402ef8e1a17e5d0c39cfb0841518f1511e71ba517f013f097ce731fdec1228d83fcc6a4156014c249465d818065b517be2c14240cd0105424142450101e629cd8492a249b02c28d9d217636ef0c48cd15cc07af1b5b05afda8dba6a6467040dd01aa076cb5af933c97549a96e9cfa4229d0ec833f8356ef14fc04d29869dc15b3fb2403833d384fc0ff1f99d88a42a339f3a749cf696450b834027d99742e6e80105c6bb7b8f0fcc84eca94f2e4a580c560ede3cf08da4d421ec73b67cef0bbef554794cef6497c1610bc606db08d28cd98214f46a2442ea035e409ae6fef7028e080642414245b501034b00000097bc22100000000062d67d19947d985956b8383c08aa6fd601fe6b713b10e97f9646771f5aa09e6ab42eaaf99c666f030bb0e5edb2078ab6085e48f8cde2a0e819757c96ff40170e38ea4fef6691e633ce2460e01f3e064a7662902f26d1e261fce1b887e537d909054241424501011efd8df61e68f07a669c6a5592c444106c48b3f7f94422b9722239733e9912526deb30f71a1e012e723e1d97657374983afbc56a7b5e85640e25a6d7dbf6938dd0ec71a30a7a7cfea792609856f785f246055e36e4cd179de59c4553d9c471f346e6e801f2c06424d485c9e96ff0fabef88b2eb48cf88f616754994efc566d226ee63c6544fd66427068273fd84fc18ac0086f259f4884b32207f1471beb563430f595f8080642414245b501036203000098bc221000000000628fbb7e3c5c05bad0d23f5201b039dbe72d11200610cb02766c755e63e7e14485c1a94479e34e4d2ec0ba16513652829fa9605ba5cf8ee1325b6f664004ed0f8972d8891940b62f014e96823b0d49b82419f5024e126becae9f0e2096d49700054241424501019a5f255658e851a8872d799f35bc8c78754a29576a076a04d0681bd0df26d76a68e2c556a9b204b7eb0d0f606ccbcaabc4ad396851fcae966775f1de1536f0865451f683a6167c992ec8ad660ea28704489d8c54a920c6691943e083dcf1b66e4ae6e8019648446ed39b0e2335f0aa0f6c8f06f60b4bd9e7fc998ba697fc26027fe3fd83052478dec9bd22cc0075c4ceff9bd74e66cd43baf1eb1ae2a38863581ace26b8080642414245b50103b201000099bc221000000000c0a9e462292f846bf5b665d331c29fe104912f8bd1f2faed90113c7365f49b39ab8ea6ba5c49a4417d93ea7a471eb2a54c53a0f3a34454fc4c1b671ffff7ac026f531963bc90d56e1e3badd83f3f83d37c6dd01e00cac55bb18ef06746a3f60f05424142450101dc0b60007a73cb2d27320805355f687453e639af7ca2b383acbd96dee36a433cf99d62157c77bfbc153ec876b4613f9d3aed7854f163e4645891506d4818b686752b451d4a29610885ca24be792c8a3ae8abc3dab7cb5494ce67e85ebec2d6f44ee6e801acadeb9857e056fac3963f829b0cdd7c75aed2e2f9f093facc7760393a9b5483f7a3e9f488aab59668736868576bd21d346e6ad13336477aea5506c3001fd88a080642414245b501039d0000009abc22100000000026bf42cf52bed70ca10fa5222245f92233e6b787d3d6782cd9eee4973b257b5dc24e6555d160e8fba18a4afbcafe503ac20082183b9881bf490026b15dd21c007d56781e46dbe86268b59c0df851f9740c992faf31c7475eab24a5b1e826ab02054241424501013e52cb3aa149bda09de40758b1db9454cc4b3dfa203b82978b38f35a8908f912837e45b26f0e67d17c4cbfc2a3abda96ed1726aa97a923b7d29d6592d57fc7895dd1d365dfc7e3a1fc3d93685fbb773022d0e20e150878cd693bf7df31ff3eff52e6e801a3351c0bd0ccc7e396d60a1256f2854b646870e039ce561a706dea7e09094d27f67c36cd53ba3c2eed006fa7f46bc4535c2d0b0826e0cf0f1ace2f3bcb74d4f7080642414245b50103bb0000009bbc221000000000de4a857b586b51f4139fb5387fc8a04e83b7681c7533d8cec9dfba1531910b3651a93de8a1c92a4bf7779314cd401946af8e0f0f608bf2e1dfe387e2dea7c608de16a579cb05fcdd6fd913d64e9516f7c209aafac75d89556737bd01eef99609054241424501017eef4f40c53c1a73f65adcc2606134ff59d467ca366fc8136e216d6f929f0d0fe8e21f2efefce2f34ec36ad93fea26e2aff9e2efd3982f28c7cfeb401d4bcf8239a3d3c7d6bb080ffa640d149695c5ee9acbe294788c67053d3c3b0badafb95256e6e80153dd01401df88403ca923616c50faa1d53b7fe315fad91f03f53986afd9b3836d6107558533850c95928d70d40d684cf8d57525edefc7199f1ce7665d7cc45ff080642414245b501039d0100009cbc22100000000068b06e345fbbf13687c188a4b7bdd65c618ece62b0784b463710be371f3d3a2484c8b8ec51280071b8165ca7f181bbff2c3de0f1548dd91e40e1e74a6479ce06537f7ce36af3142a97f5fbca26c9e2d0052273b61156d63ee1586684edb3a70605424142450101fad705a9a162043c2e3cf14379596412017d87336a9732ba609cab5716ec0b58bba24fdeea8cd9fd7d0e23336c2a8ed821ad8e55b7d6b06e0ecd21db3b8ffa829f36c362c27cd79e4d1bf0e5cee6279362b0d64014f67197ccbc2265a27807285ae6e8011a4c4e838149c2988eb80178c60993dc4f4f7e7467c0ca62a14bd15be4a0b7b2180fd55ad4568466828c567dce29c074c259dd72a51a15001ab0202ff364963d080642414245b50103230300009dbc2210000000007a02a48d0be5ce6dec81c8082b7d3a59526cf960af3ca8f252b10b013cc5755de2d8ffa3f519656848d83a0b8f3612f7f7e0a04bd49a3e3cb765d52b65e0a60f6fbbba056324890f5ce0971ff10c231639bfac60f46830ff06f434cf2954ad09054241424501019eee4e2d2d3f5517c399b6e8819747880d26b36363bb59d23b1ec841efd0aa5d49ec227781e1ae919586b6fd0864cb1ff1f130128d3f34c9b9dc9a3abee61682b9b0a0d8a40541f96a77066335157f417c86bf8f8c5395767c7cc6cede25dcc35ee6e801102a7190ecc9ec2958e28e715e3a49258aebe19560ac2bff2f6a67143e4afbcf14d29a45c249d18a1df127c6dee308d5bdf5c9d13ebba3c00e2c8dfb6e7deda1080642414245b50101be0200009ebc2210000000009439fe386ffd7c8cca0ac07a52741fe084c9b6fc2cb0f1a0c1903d0c02ceb51f4fe869a7410ca994e46115a14918dfd8404ec8ae3bedf70a18efcf9c6303db07c3791cc3bc9fb71c74e9508db9cd92730fd7af6b3216e21ea15cd26e086bc306054241424501018492942ce3742dc7711a18a46025c6a679ebb699d55f41842ac7a901eedc9314aaccd3a21fe3a1d7aad55c86a90b2e366a20afb498939b3ec1f68676b518d0809ded08074d0e55ada3f6c9792526baf1fdea1689e055f2dd3dd6164018ca65e762e6e801dde64a3288039ce8ccf22a92da94b443d7111cfa7ba9b9ab365125c86170f5dc83e674c36e204dbe165ad63035faa70f6b5fc85234c9028ec72e4376e9e1f50e080642414245b50103160100009fbc221000000000d22a1ca373e3d0b0664f42fecf2bf5463c91f3e33d48052980d5b0d9ee655f65972878ee264bb82aa58e17c0a4866689fb161331ef2513ae5652a8f5e276c90dea3f9981b8cf5f9fb6a88ee2e695b9f176bbb91ab0fb931674dbf2710dbf8808054241424501019c0fc33f1e9da6336dcdf68685ddf04ec3a949813402ea8219a63f7877ef305a752ed80404c8dfaf3a1029b34e8299256d0fd34b732e6ca7f16be6fa8a9d748984a586be5bdaccc951a1ee67972b98b8b4d4c50b37e822370423812db9a2051566e6e801e4a5d8da0013e2af4bee0e78f7b4016b55ce3202098306cbc7fb8185dab2b54610b256f763b7eb44e844a86953878f74ac13b7f9f4edc57ffbf44ab124e8dd15080642414245b501036d030000a0bc221000000000161285a1052ddba5075d1f751476907b843e299227f90c3ec82017ed79e11f38bfd9969f263c7428078efab5fb114d737a5f245375fc6488c4b81750b6b0380d7fdf710106b5528bdb30f217db9ab5fa099b77130dc675cf944e6a00fec56b0a054241424501016eb71d6284a4445a1048004b3ef5ef6fe07835cc97d9f47c2f3f277bf02da959e15eb49d94cc9056f8008713fa1a10b3894edeb5e26c963ec02c0f653fee05886e473f73b98e8babdb5dc7ba930abe9542a4511caf949d706fff79972ed6e0a36ae6e80167d3649856d506b0c4794ccf783869b7f9b2d6d72f512667eaf2f7857c26452857577208b4304e8702a1f2b56bf82807e771034e9017b41f94944a43dfac2c87080642414245b5010108020000a1bc221000000000d497e03367040767dfb578e0f1835f6cd359d53d55bc4282042aed3409ec2f74c83ea8a0f5c15e1f74ed12c0c4ebb83cc418a49f0b45f627301a4aae383dee00888ad988c9f95dccec84e7e3c48e49d2124e82d9acae6d767d005744433e86010542414245010100b1841fa6334148c4513b2721a54093d007450e0729c75da13ab3edbfc47c09c6e76e00d776c97a638c67d59d420467eefacae830012dc948e25e09e3a7118256c9c1efb6d36549ba31426e9ece65507638af0417db3fa6d9f28b353d6ebff46ee6e801040b4cc6b7f9a0940c19e52f8fd433f45ac47176536b84a3bef60fbd11d6898595e09dff4c785996ab89f4978011be6ae34a47e4845816b75070038ae8ee2ad5080642414245b50103f5010000a2bc2210000000006ea2846cb1bf369a69c203c646e03ee576b5342b84d47871cc51a4ff4fb0e61101fd6b95ed9a833b49c7322e4b2d9ff0c00076d775a7fcd0610c04a4f4a6c20c0e6cf337da59269a4c3d8d3ef97e7c12c755ea8fa16049c45399a765d404ce060542414245010148dc05800498db47fc7fbcbadeeac4e943508491672102d1eb067065e22c6f70a4e14e10ff8713e21cd0e958306018ac51c9f24501b297243b09093271767188778916979a3974ff167fb1a585649fc16fb0fe635a3d4d3accf577310bd0724c72e6e8016d21b818b03ee1084cd717a7288b8ddaa49dbf23aff0bb37c101ff518543b0cb3e09cb170f9a232e76d056d28014d4b8703775b63953226392b810170b5e5849080642414245b5010397000000a3bc22100000000006611291b8d1c1ef22aa09be267c71d91fe829331b369d61b645366ea8245c4e18422e143fcf7a690306a1c74d900525dec3c43e6b5059ea1323d4f975423500c8e2286096d66c14ab0fc5bc0d74b995172e956de1f215710d617cf09e6bd107054241424501010ea115bb7f6f4cafcadd748dc19f790d9af5a87c06811305c1d89bfc6815b62d5ef577c7b8a5e421135cb4616f07007aec30df51bb14fc1a8e9403eb38075081c18e900050d7efabebfcb02c7ddcf6d0c2c2b4f2960ed148b69bacfb91860e1476e6e801993907beb4899b9217aebd6531df4f469751025107815891c13c44298c4220e53739e2eecc029e9faa7c8d40e41c52e04e7daff317ebacd65eb9612d837f1234080642414245b5010359030000a4bc22100000000014fc7a90b8e7942a0801d8b2d15ab0691101f5492b07d74c6ff31d1e21ec47574d529ad5c096f7f68d36d49d929e608f3834175f3b893b4c82a776430c55070eb5918d9ff6cfa6dbc08deea4b846cdf7f473ebaed1fd06e7c5a41152d4fc3702054241424501018892c9b36d286ba06e8b645c7f5e361328df53f2686167527a534423094b6837f0100faf68138c8527751431cbb45e11214213d70c7aecbebf83aecd14ea21899897453f61ab9d8d1e44fe8ac4fafd673b9ee4b9cf97ed3aaecf5e3303ec27ea7ae6e801322b84776db6d263171c4061ee70f58249ade34a427be3a737541c9598a21c318c647a141ebfc51ebf65e3fe8f98c72ab1a88e6c37c8d608355ef30318d3b957080642414245b50103fe010000a5bc2210000000003496b3530eaece51c3098f6f77bda6e82066117fe783407f8e6c885563ed2909b6002d9a4f81752fbbcd4809dd929290198bc802b3a8b895326b814e3e96d30ef951aa0483a5e0d3c2abfcdc4e6502cf0aed192e915a2a107e390aa4b4e7430005424142450101408124d5f356c4d76f786e4f688868217b5a3ca6945ad1d9dcc6bea0b1cb6f28433d08c0de88101e24bb8605c8b8e6b1239c1f23114fdc7b7488e501a66c00847a76bdd3de2fb21012072aabef1ded1b97da449a8f2aae9d9da9e487686447d87ee6e801b34daee34f4a5e411875baa71652961b7f8a159efe72efce4382a4e86e185a5048a46657fee9da59cbe64aec6765d5cc718484eea2157f749dd0b7ef1dd5a5c9080642414245b5010173020000a6bc22100000000086bc8dde7dd2be420facc429211c3966f68a6783acf67d673bb2cf581077067b812a5bc04cf5de2ad94526171ce9b6d9b7eb9c899b85bc5045741ea9fa1aec09b5c7077d49ff2aded5b8f86e528c8a7a63f26c8a033a6cb2b7d4a85ea41a0d0a05424142450101f6e621992ae2f228585608ea826c58b9ad7e279767477351f2d3ce4dc064cd47a3627a94d3531aad6d3b509343a7a32e9289ac20439f1d9eae2234248644628931d10df25b70b7331e1acb0460b0036a82e8b607a2e440279dce9684b347e01682e6e801605547e1dfc91349885d6b6a65d9483b2057325d6e8e2832ab06b26769a910fca4dba451c211abc5251bfecf4d3551c044bb29940ee5660f4fc3c635bb5e4643080642414245b501034e030000a7bc221000000000d410d798d74654ef3168c59ebdcbd06076c61f1c1e40cf81de8d3baa7394b04e21fbf48a4509567031a6edfce696d6ac3721410a87e2c0210bc80e2c60667702feb162d1af19d1f7c1b1a588ef0876ae2160b2a01c986d83c560c25b3395c208054241424501019858cf47c6c4c8c93c43c29dcb308071106a75d5bff61abc5c46e1e6d221a271537937c994dbede92fcc992db71834cd878117e29607b2e7b1bae6e6efbef88f11a42eb4ec3eb6a46839dde5794736c9f874f79cb9780677571d06947dc997e986e6e80164ff652089a31f32ea33398c143041199c5e34fdbf48ae64fed54b1c25c5c4575d81ddc1d726545b49e18f61ec862a05c38f8ed7709c21cb58c52020240a79ea080642414245b501032f000000a8bc221000000000ba3260207bcdde010ae82c938fc8e47c240a6950e2aea4ea5eb39f7b4957a106c48a00f23e84bc4b0d251b434ca315b2cc6a998f03fc00ec32c39b1f51a3e4030abf66c2f4151da309c659188312c5cdf4829741ce40b373192cc852d041370c05424142450101929b2cf1441dc804267b0446f3a894f22ec2e9973024ab4cc50f75bca31b7f306f59d757cab1c7cbcc964b5cac87db0c6ee36c8d729acc909b1f5c07241ef48de6157a65375c99b391082f01d2fceca47060b6906eed1c631113715f39b77a188ae6e8012e7e8b83a4e2b4391823c327c47c4884f912c05e248dd792f79834d98517193981b6a8507529ab035fb9bb6446e2cc86032b27300ca5170b8e7979f1ab28c00b080642414245b501032f000000a9bc221000000000e0910cfaaf36d39ee47f4e8ec5f0a57e379154e1c9935d11a4e86b221bf6da4074acb1d9256dc8a608474468ebfeef7b13c04995933869ca8c6cf1b0cf96c00a17a2d5f3649b84b4ff533f52a16ce5129611ca035e8ae9db0a3ea023430e9103054241424501013cc3c1aee71c80e3d50e10317afc3039946bbed75537ad35577d823cf9ee804b1940e2a57366e0c62f5b9e4f4047aed5c1737e84f035b66d03ec8c0d519fad897a1803114543bdfbf7c047dd5e10e6bed2492ac335062236b48ffb2f66d070d98ee6e8019871a895e2f2ea330a7799492bfa224dbff01293da0f3ca99bb717496540d0e96e3be90fb153d6346b8a690d1a4aa145b8122e7fbd4a855bf56c318240b09a51080642414245b50103ed020000aabc22100000000012dbe37f5dcd34129ba9eb65a4a5e9e3e1b44066da99cee4c17d96a61323d5550076ff1739352e16cd971e004000579734dd0aec36a987c899ad3b24463652055b3fd62690a446f3873964bfe110a00acc405a13073def7ea2672b87aad1e80e05424142450101b2bc7c3b4fe7ff5c52f3a65cc777c21c8e3faad565046b94e3f747228543d969a8110c144396d08d994b1f2f54302e7dd3e3a16baa70169bed2338b90ea8da837d8c3b424d99b47790cf0c1f52a2b9b3a42a57b70d1fe3465e60b3b2f6cc67ad92e6e801187aa5c4359401e68b85754086d4332f7b393b99433294eaf1be3d2c9613fdb98356dabf1f7ed2f459ae7ce6f7128f64edf5113ca7575b69e83df9f30fe47df4080642414245b50103fa000000abbc221000000000168d89612c8d7db0dea6bfb965c920a1af4cf169eee51e260d02d71cb7ae8116d24e8d42173d32b84fdd5c74343b1997423ecd19e09c3718e2a58339acf1fd0c0efad763493ccedbd131edfbf2dea2349f793f727977c72a3247a1a58849950405424142450101e4a643dbb96f2ec3cd22a2406b62faedd937886a456c3b6837855e9ed5cd1b009bf655aca1c55a706e0fc4f7770c1d0524d14780b65b95d98e142349150f438f260b6057b34e67ab19230a655eddf77cc9f43675eb10d3ff6775b0360bf5e3cd96e6e801fe9bc2bf8beeeda50ac476531327499c151fbdb0f370ffd4e9f30f1c498b9ef70d6c1c549bf17fce1e088946722faa7c333994e28a601ada08b007d0766bc3cf080642414245b5010319030000acbc221000000000f27d1bd23e2b9578061687770bff6e193f19ea876c3e4622f546528004c05a543ea7d9a31bc20b24a5baa304c882afbc817cb9a1fce409c5b4e66a2d244a14084570aaca775614e3d4a98408a8cfe28fa3ec7276c16a1bf59f830607ff71cc03054241424501018c5080fd4adfa001b61d7f56e30be90ef3153d4bde1aff759f5db2ecb6b65d041cc7f1cc7da50685b949bf4e11a59f7ff7cc31c269d8369f0bfd8f037ba435830cf023015bf62f2be85656aa683c85c32629795ee508f8c2b87fcad1f57062829ae6e8010e43707b7128caaad76c19747f44080d910db99f7b02dce41fb2998dfaea8c79726977388385eec314a09c9a5fdc6ea0fdefa57a8040b0efc3920df5bf5f041c080642414245b50103c7000000adbc221000000000f4fab6203cefd6965e4d0630a7dc3010fe098d28d73e1403af08060c7777f3755671da47fbbc1d1913a17ee0b694152f3037b306b6c9b42840f37fdb3a7c4203bb0cad33d7374fd14f30524fbd2b12454343a79aaf2fb8d84e30c969c651b00a0542414245010138c074a7ad7582cd406e1405cc07de665740a4d316bfd1d3252c6dda3182f024243766c0934c1ebf31c19d7148278dad45c04acf5727f09258755e5cd04d7b80d090303ae6547ebbd1cd6fcc7a2e75a49d8e1eadc74a7603676ea44f62f122879ee6e80106f8cc23b8c9f1efe770c1de3f5a8e59bb592c9b6f5866c34a538c51e9d000e2de8258f5c50ffed53d99a98839d81dc49db22840e1408d25441cc32a76198742080642414245b50103c3010000aebc221000000000268ff7d61feeca17c59ba13e602958c14e6ca92cf51c85b603a7eade193c4973d2b2473c06c26f540a0326a224fb9333b954822dea21f69f0b96b6bab082fe0ee1fc68b8379c42842cedeed03b9640caa98065a6129423876d4050b00701560f054241424501011098f4a5714330c7b42f05c0ba781350499690364165ba1280285cb43ca15300d3596e2bdd49009ed76bd37dfc66f1219b0f56f0862bdb359eb26d0a77ac3e8e0106767b84073327a10956e3ae237fd9a11c1a97e74479175ef6f4b34f1d4517a2e6e80135f06f2b579ddeef3bb3f53cd62866c895db15cefa95bc6dd56d6711c3a78b6e2dff25b4f32a580b4a8685340368f9cd2a49ee5172584b38114a74b5bc43b787080642414245b50103ce000000afbc221000000000745db3148f2b550fd6553cfaa3927ae8277f85c219583c5675e875af9c84707984a3045fa2570ee145a9cdd0361509d9507556f19db47719b49e4a9bb7da8f08a53801de956326a889c7228b24949c36c8923c74f10414a585adf92f0b623102054241424501015e6912a4958a353d7617c8885fb959a703b724bf0132b919f6f80fd4d0b2ef22ccbc110ed9cf264883a46a94ff19616a70adf280ab7f03c1ba18ec9431f8578fc29f857d82b23690f7e1400d0f8940c4eab93d3fda81c9b9ad444befdb474eaba6e6e801804b9b2c9ce5b8193c7e51e91abcd80c173723a2401b3bb0eb083201e34093692a8bb06671939cb78086135ce33305499a37f58e0452209b0b7d365c64e33ea6080642414245b50103de020000b0bc2210000000005ca836d49edc86595f7df84947e279115740c3c7275cf3b90ca74bdf35399a04761ff0240cdb8cb4b611066aa67c3099f13034d2108ddba3004f71e877d9e0053d12db48de81ef0c1a24a9d193f5ba3e9c70de518c73fa0e6b422e3b508dc502054241424501013cecbee3c536e3e6b5ed17b4210a627e47d75f6f29d35cc70b4abb850264b511acf2822adc2384193f52a6cf6c08e5b816c2ed5a5fb3343a36997dfc3a5c918ece26d27ac9681215e4cfd837a2fd8d5f9392d911e21bacb88c2552fefd59e5baaae6e80197659a5ed123fb1e8ef2e8c5bb117cbd75a4e3a12a73e9dfcae9d7616262c091f171b762f724df5f7858676c4e1e991852d288f1a7cd4aaddfdc24a7297b5582080642414245b50103e4000000b1bc2210000000005ab563ed26cd2e46a97b54169f7e4601573dd265a9bb8d91160d168a1f05ab3e0004dc34e14bee9639572a0ff75b4b3917fbae018cc79fa4957d3ffb270c6f01c8557bf8483291266cce5968daa56d1411f9ef58f98bb7f73694a10a6a418f01054241424501018889fc1f9172c27ac966c1a33a3bdb987b1e7b62338e671f3317a69d8b6e34577cc1c0ac977c7fba25f086eec8b15174f28f1bb8d125a75bd73c74e2fe196d88838b3093f9e7c430dca1725d558011eb5243fdae2596eafb173f6ae4f9850395aee6e801593d97a7c074c44a0ea530a788bb5e8a76f819519b3db7e10302d511d9e73534cee373759f2fe3beacb8ebdf03fea54612d0708d923c436ab8c79370e7b767ac080642414245b50101ed020000b2bc2210000000004649ac13756e6c3fd40a1d39fbe082a3183e35bbe763e1524e0f55699940e57d1f7ebfeccbb30da5c5ea01c26fd95ebc66a2e62e411d8996d60b6a050489fb0f157956166d3bd1fc6f9d76c4a4a6933eef712e38305262ec84299a4d68614005054241424501013aad9d4e76f191745e4c268274c3258c00bd5cf1a40dcf27738596127cf02540707c881702e642989e24505b12ffdf913a59115a731efe7be605c6e71f9d81818002ccb1da922be9e277a83ca3b425eeba5c1cd3c516b3e33acd5da8fb4aa9e6b2e6e80132ab5f7f6e95c2e53952b283c45c067828bef0bb7766f84d87dd3b1a9879e21e4595720344ceb3cf428ee3532a08cb6de9fd0335c36268e5e8be4b1d0b838645080642414245b5010398010000b3bc2210000000009efc3c93f6ad52334d6cbd9f8d436374a3563caa8e94c990ae9f36cfbf4ee9131dd673574c8924aaf1983832ca57490c3f6b694b5c6bb3d10581458d608b5d08433ae01b798afbc6d7268b9b3de5588c19ed030bd28c82781c288ddc66615f0b054241424501019c2fd1b2ae003951ea61d807ac12b6b2793c04bf7b2ac32ada9acfbd50d24b23f6be13e32b82076ef6f910bfc50b8c88ae28730f56b0fb8ecd5c70beee756d8e98adaddf4f3fcb60a08f77e804bf34dafb454c471af9195240991e86eaa125adb6e6e80180d174b3df0e0f9ca204bb84794871314dff9323eb8c467e627f8d80963f42de4b7f62f07352e5e7a04ae301a9a471724bc0754dd0af6b30747c99905e4b0ddc080642414245b501015d010000b4bc22100000000086f6e5c56454f89411c68080138ab152a84934c377f26e778cee8f3d60974558ee1b4bc840090a8efebbe3650fd96be6fe5921d08f3fce8561ecee47de09ff05fcdb17da106ecf78c17e76722695b522d891060411d2a80f163506b7717ff80505424142450101ee5b33dd5f119d7c2eaff176f63ea4d233e4440c361226207602fe82a0144811d221b9b757ee4257033faaefb646c9010acbc57368f1b4be0d9731eadfc0658ee8009a7a25a0a80519075a3cc64a5eb72a197bc268e74d4cf6f2ae90e4eb778cbae6e8019f36cedc91e4e5dc2a3c23086b9ab3e91d4657eacad6f6c9e239a2a59b1d8e1edb3b4a31632c60a9708949ee6fd9f000ff8ca651a56af0e55f63f7616ac3da8f080642414245b50103f1020000b5bc2210000000002a49cd58efa0505b75bd9ebf8a4d9908ab9e9521cb59e63f9beab9a799b3032a59f2feaccae733f0cb0c5921098ffd184fde64419921e4bec3ba0cce6e603d0759d0a445a0ee5d7a947044faea6e505d1e2b7627f1051c6759fce84ecaa1e000054241424501017c6be2050d8eaf6b0d155dbcd59ef358bddf4a3709223040b5579e66b0f168669eb06bd6874508681c814d9105d5fe781e4aef9000f4785fe15dd80a740c978a6853f39d8c8dbfec772ffa5a7a44e88f32633cfbc4f0651766ca3117f5ad23e5bee6e801055163050a06211be5b4d5fa61caa3f67d82957b0fa751966c7dc8bbd40d8b24ac454ee6b50182955e617c975eca4109c9646026175d6d2791ebe4357506e2f1080642414245b501034a030000b6bc221000000000d6f381b428907767e94e2f591dd6fad72c0272c89b3528c03d50abc5a34c4e765ca35806cba07aa3ea95b28af8acd4f03bb1c075d363c7eb2ac8a634634ff30ba2c0f2819cc5dc5ad69cfa3a4c4a35957f1f93a95f9566e9c9e999a32a9b9c0e05424142450101a40b86d8a3dd09036b9d65aadc819177189e75bbd486651896bd7337d7a7eb19c002f10f1f0f9a58f5c761e66c5106b9a4325a973d899652f2bcf3f879d2b78bbd66d235eec834ea9cc17c57a43ebd8b2cb2171994df2d9811db84fee3cc125ec2e6e801ca07458aa083958f63e1fe8d3308d4b32a1f032a7e33093a76bedd98f01fb9dc623bb01221a28da707f8c267d340036938c3944435c8532c4c674b393fa6903f080642414245b5010302010000b7bc22100000000048dd92e15d642333ceb90ae2b64ff6e8500e9c470c445f1ea35713da5d6b06715f3f25b0f03c62f4597780de7c418f96f74a1026b40d4666db202f5b6454240d17be5ab67a9155b2a5c0b16a395aa1ab5f8b2401816254628b9a7bfb2258d10d0542414245010128e944bb2ac336b997967a3618ac161fdb705781846f3c5da34d5d769984d865ad8f172868fb52fff270ab394beb364d1b1811084633046763ec271c989c1a88bc352e3be083692dab04a949fcb33fa53042d68737fa2d9db448bdd5e560c55ac6e6e80112c7f05bb36bf45f8204898846fc305dbbc963344f4997b816b9bbee8af639add39a3a92ee7dda66248e5970a576e68aa36d91c0cd96b03c35a07ffe6e8ee0a4080642414245b5010399010000b8bc221000000000d8da218225de51708acca66a93413893563b97802c3ad1d15639fa425c0d655d95ff0139806cf9c24f42933925fa6f5cd30fea2d9b85bfa96ffbab383aba0c0caab9d1c1d5fcccd85371ec21fc5205bfb024986b8b974c4348a06fb3f11cea03054241424501018cf87e283774bc73c2b4a462e95ae816b96478d26748695074f94688cab11578f753dbdec8c7e5bcf02dcea90899f7d37da07808c7cc2244d57675441084758ad6544ac36f1e34eceb65feab0f6777ce854d87926f0022a6c4cec32776b80963cae6e801f013012a5ba23b2b1aca7c35a735c09a9e0bf6257ae524a72044388fabd21e72cffd03eff42374eaf63d77e5451f9324f37c9104c2a3ad5de419effe285dbe67080642414245b501014a020000b9bc2210000000008c67a87c0b2c08995d912b8c2a4de397cf92f5dde3c8115b928c5eea2b00de0737af85f466edd1d6e418b0a4566105e7719a30197f8ae4e2612b6c50b9710d050238a1a7ce9186d442bfc89dced4d8b69985ab02e1b5dd390d231e88a7289b00054241424501010a080c9338dc3017c834f5a717e7829a341914755378069bad0a8f544f5ade5cb7256ebd18e8091e76aa19ebb848a77d9a7412bb3467c630a1e878487d5ab4831a0c28380629644c4d9036ed544d670065d23ac3c2d54edfadcf17350b64e371cee6e801dcb33fc0e6f489d5dbeb071a6af5e610e308233724b47d5ba78e2646ab6e92e6c8593478589e8abe7e60c5e60f1fb9507ede58dc745f77f42aa41adeae214fe2080642414245b50101f1020000babc221000000000a8a80b94bf85259fc737155dae8342d9667eabc0d40e87a2a0889ae7f71d065cd870c559a0c7bc43731e549a757e5c9214269442818d8efae15557b82dcd320599e1e19cb860b40c378c72c31722ba2d76b6fe7cc94614c3743440b1f38e050a05424142450101fe2651873b6aaaa55af8f38fdd464013b2603f948bb9cd8d7c41b8e2f059083345c4e43143c0ecff0859e96bc613cf3b34c3f7fa9985e7a424ae736bc37d9e8cc10f781a06f6a7cbe423b7b7ac2ba116b3dbd16a2427b3daf9b141272ff1dc12d2e6e801dc1576bfe62e70f13cdfdc521bbe16441603a4c823a7feb091eba4acb1344d4f45d8a68777c51544bb22ca1ac1e3ccf2a8039dd3998a892886e5b562524d1e80080642414245b5010167020000bbbc221000000000060a558cf5a5f87b0709dc4e94d2a422d274ed9ae8bb3bde7da0f6463863984098b29b31447e342d5dcda360f98dfd86c0bd5296fdd886ff7f6a327e1d3f210269dd46ce53bf0c9e0cd55933cc2f3e55713f442c4ceca785f29a9331b645880b054241424501010c0f1e2544f5a7ed6b5c69e7b0a7edc7e12d932917a271b49b2a66398f7acf21ef2b22445c5adb489c9ab4fba117c089b9285d4008a5034839fd4185d32a078cc271cc985e9d20bd09a50b30b4a2fd1121e1e2bd38cfc63247be7c22f51477fcd6e6e8019df62a2c39e11ebb6d82c7b150bb5a7b6d7f9fcdbc10de72f46cab7e9cfdc1eebe750dfe712d88dc7fd2237b1091c0cb27b5a4aa3a915e4799911788ec4005a3080642414245b5010341020000bcbc2210000000007ec358370c207351a1fba972aa39006c5e9c166e234f45626835a08f73544b5d29c49a3447294e7e7b886e17985205dfdc835c024f51bcc727cba50be3d5590c71650095145b763150c06ad6bbf8fc8dd083a2baf4d604021ec1deabb2bbe30c05424142450101f22a9df01a9e26a8d7dcad7c53ab8d5ad7cf4d0a4bade87163ba663f53e2ca52ceb4cae627b1b9081483e02308668ea95da52738c3e19699ea40f264718908865c721fca375b053cd77ad2dc7fd8c187e0c9255bd401b75c90cb77edcdbcf6dfdae6e8013877b8691ad6bd85018a25eed0a641047c8589d7e25b34d6b49d996f4d0222d852f4a7aa4f395dfea370f4c0df8b3d55e59825e97db62e6f0511921d7da2fcec080642414245b50103fe010000bdbc22100000000010eb8dda3f56bc5c0eb14918f4547a2e47e407e3ebdbd3a4d32c76be06ff6336125bb6e43d328da1acdf7395b4465394daa7d58c719e961f3690d98769e24b05355077e63be54d628e03e06f06848693921d5fde556b65bb45abb116f462ac02054241424501015cd1c01e98be0a38950bcc467817c89682a43e8537ef18d57e63c13351f1802f6e01349c42462f21fd23418f89796b930561ab22505617fd5eef678308c3938a4388f4bdeb04a719120f267628c345f4c3a865f9ac5c1505e9c0e18a8f6247addee6e801aa5a59d315a71decce64e222e659e71d502e413c903ef0ef5fc92f9353a675f3c389faa91a7e1628413805f39a41a7d2065ce0ff70e828ea21cf0ab618965a44080642414245b5010345000000bebc221000000000c668dbcc6cf16512c54e4161259299cf82d4fd4500e2cec383a1020e3acd3429aee1f7005b4085054182d0dddcf235240242a7068b95b4560bf943c550a7c207f463eebcb3c69461e0e1176a808212d4980642d5722475f0d34384930e848d02054241424501015e8e42c9656991c55c0e1d4bbfcc0540ee464c82372eae7dca20305155a899522c301b26bfcd74a62e1e6591d3fdf1e7e6cf605d01ae5a6e779a125ab246f781616c6accca4a1c53f3d0fdad1c94de5d2a72b1af405259f3aa39e427b6a4c7a4e2e6e801de189f369541ea2ae60694ac72a588e4112b32ef1321276d16f106d416719547a35bfb9335fb3b75228ae69514b27a38831c99b8a03f31838b0d7f1355919aca080642414245b50103e8020000bfbc221000000000ca88eefaf032e3f1b27a01433a8efd34b7b3cfc9be12d2780375b40382ce516c773c27d12b7f4bf6450ce13d37e6e154257a1408126d48c9dc759c604a43700850185447faf9a7871b05d44fdf399e50e33f24f0e29e447a9ee626791d37870105424142450101dacaa07c0057f1c10acf2c3b229deede4ba84bada8aee5c644976f4b3de9bf734316e4cd8fd719275d01ead7b5a11a94b160b36cc83ff0b74598f89d3e476c81de0a18e4e074df4f3372a3c437458e4a47f60d835b4f914e70d7ab2274110782e6e6e8014239285f815db60ff803a5dc8d253d670d1bb820d01225bb7e819b48cce3708b27da5ada1c13b3428c9df1648057244cd56cecb8773e713a2f295e532b3145c1080642414245b50101f4010000c0bc22100000000032f6334c3c25588baa2c4a1eb747800454bae9a11ab437866b4035ff9d9a230c8794f20117edcd405ab567110708e08cc3f4ceb0e3334c316a03853f66bda40a74f87f0f21c9d97d1523d8aa7ec7d648db2fe25ab4ba42f27db3b0847286570805424142450101720be8e048b559f2d3e34b1199f871fb1e670e75ba3fd174110299006c647f1cbb1e96154fb4d37a56a9b79b190078351a4027c6e079c70e82e7a5b5e25f2b80206a537eb931fafb452604715be619cd1ddbee10eee743d5b104f489d588256feae6e801fa04742dbfa707b4977af6d0245ac22e638c93284058805d81e5534566fc0d661fcc80c9c927bcf5492bcfb9725ad30c8141b2eb7693ff0568be473bc3ea00d7080642414245b50103b5010000c1bc221000000000bcf8fa55e0d542a3b4e44a1456dd1fdfa08e57d5cb3eba9f4f174637b71ffd240971507a2c9897f63213207a36354c7f816f51b64b9f0bc0ab04f81065f9bd0254cf568a996335a549897cd859eef0d23def962c21eedaa89cba3c9c1c509d0b05424142450101202e19a620f2ab434d0c7377b8e47734e4c1c189d86f27a96bbd57864fb0a47d1a182b3d01615070ec60c6c407469eb05a570f6cbe58afcefadd83b77371468ca20561aeed1e215ed142caa7e010dd5cfa1f5e119bfa819d74010969eb520c37eee6e801b832c0ab27a66cce1d082f349a428063bf050b5d6982311fa7691e44c9729d6639a5a6a41c223524402120d2bf03d3cbda994fd82c6b32812c559376e61ce90b080642414245b5010190000000c2bc221000000000ce77832807859b28d8a71a27a4b820f23c13fe7d7fd1203fc47cc5e38bb6f039f4ccad0bff4fbfa9fbc6f1887e8af519a781c0b577e3a503e5480d93e22f2801848b3ab61dcfeeb0cb36147728396b9692475e3e305d6278b12c01bfc142e30d054241424501016ed3d5d40229d00c76cf14bdaa516079894355b7e16ec578e71c9216fdad83267a7d63392550a7ac428b15a0046045cb132b47261526e070e2e1d33fa80d74801aa47188fbfff9f7c360c4b9c5d845aea74441ad68d7eaf9696c49e6f511a1dff2e6e801a108942a67607a2b8ce2cd9e2bf5f38b93f03897f3aa35d6378d7d5ae5dcb9b914bf501b55d9ebd2deb685db8e1bf24ee5691be456d028e63c24882eb402ee20080642414245b5010377010000c3bc22100000000078cbae353b6379d66d9ad6a2932169382688b5b875093f945379492196472c3c85ff1a8f33e2ed4f277615709f8a5be6799fd835a9033f396220bb0ca82f2905c449f5ee3281eee15f7a432853b220a1598601bd90f9d4840e6c11060c25690805424142450101fac9f2788e441165a4a989f04b202d224b21416905873bb45538dc0e903cae7bcc227316e330dd8454dad6ff664ce03ce99ef8620dfab0b5f21376f7d0252d848d27f4ebb9fa364fe586cbca8a0c91059526b8970c45901127967c8322a91392f6e6e80159b21da9ac339c89a25dac74710facf27c211781acd762e252b4fb1d5203c1cea987a8be85b59e2614084f53e0d188e1cf32d867aea320bf69a4d1835b887aba080642414245b5010155000000c4bc221000000000cec4b238959e223daca2310d9ddff47f20578299c950090961cb4ee369356c27a0bf9173456ddd8122c7448f7f65592eedfc50473cd812cebbb20c8a561b870ef1139f5e6436343be75936d5db75a01ef60666539c005fab6504280abc81730c0542414245010184f2445ba4e73fa3c607d15caa0d1810cc45afb913c03cc310941784af26d605ef64d50e0699aa067cebc2f8edbb96a0bef0b5719efe4fc236dd818a96dfe2870cca1046a44e1aa8e172b48a2661909bbb60aec1f52b941fab93f66d4524ee03fae6e801ca84115617186244359cfdc8668548919d09e3aa55ccbff5f2915cbe860075521e39050bc4a491aa3b471a4101bc07a5fc5f4c58deaae4bc925100aba8a00a47080642414245b5010365030000c5bc221000000000044db6141b413d9013ac70cc5d03ca71821db25db658ca7c99bff33ed1639c46a2ca6257c2fc8ddf2ceac78f2675656d36d1954e71ccfdb9674b7d61459b8902b80246585bde0709f01544137db4a24dd9678488e0c0be77e49b0e1a6b5be60b05424142450101bea5504dd0ccd7284a1f86dfdcaa089ae01257d71352961731f5599695621c2f8c80d41481b440d25ecf1efd0aff36a9469986ed69bbb592c60ec47e1ec7e8818e77cc502559631bcd96fb8d5c3dc16ef468b6c5ac01857aa72f69b580d3658dfee6e80158a80619d21bda9cddc3d811be18b9095ed8a30f3cb75e4f9253f1cbbe080e13507ab91211617b03f7c25c444257f1e03c9aa0fce95b68731bcb685edbd0545f080642414245b5010137000000c6bc221000000000ca0c2e7648ab38d3196fbed412fb5cbef635871dadca3c61d0edab605a0f9b0a55a2cc2e64a2035186c6267cd390cde98e3fca220db20c895f42b0188096850ec35a18b408e2b70ff3bf36b4103471dad3ed4d13a8c096a88bc691f095c5670005424142450101de42f0efbf0e1291aaf24dfe824ab360b4c40e3d4e8eb1bcf8c2ca104fa85f100b6fd070b8429c0e65eea7a31c9ca46c9dc16e9d1524cff86c16cdcfedfb50841e95ed2f9e14913303d04e62d4e585d552f3517d679da2d8b088f66e563417fc02e7e801c0c4959c944fdbdc458ea5d43ef63d01eb99c3696f5b26851e415884546f26e0a9fe2a9c2fb0e6914be3fb9dd53ae62de8f9875907becd70032bcf501f047867080642414245b5010355030000c7bc2210000000000efc7d548211555b4148d1fdaf4c4a440ab79e9f6f3e1200c9825fc9d881464b56f52244b23bc3e607ae1a096112ce78bd05a4fd7b6218449f8633aaf0e6fe0864bcf426a571323630205d63cb902f756b86e63648911ed8590b8090f9203e0a054241424501013604b26ed744f1c6c166bc1cf5f5887131276539cfacbd9cb8b35cb5342b0d6b195e481e88b2bd960d1c30c67e7f3059dddd5afb363d60ee2840bf105292de81ac9693e7b79dbf718d756af121a0dd95665638bea1069e3547ec5047fffb191006e7e8014049de7d7a88b3b8d5ce585e1e68c4534ff1b08dd803f15ebb90afff6c628653885645b780058344eee633ebe4995abea6b688dfa3984089364f354700306ef0080642414245b50103ef020000c8bc2210000000004e9ed8328b1ec3dd3398862abeef44e54f637f22b52d3c7a3b45fa1d5bd7ee0f9acceaee009a38d18e6d8711aeab8087b5a4e2daf3b9aa21465fc0fb355fcd068365534730ca96b40de3bb9f0197834bd3d3d3644a8f9332acdfbc894be3b402054241424501017a0f0da04224b8645c8b3c7570a76e7ace48f085ce16582bcfee4f5446134649553aab3869f229b2e7fca822b7856fa3d6d1f2e708177340cdc99993e62a1986ec1994badc4a70ac15925d2c707282869d369d28fb168ca43ec75434d94d727a0ae7e8010baefbb6ff79044865c5e6210f6e9d581cfc20a43e4d03084de3266fb3c263cf2476b6a31714bea22571d298b35dee8660995b28965eb74bff16a1977952e33e080642414245b50103d9020000c9bc2210000000002c8a4cb21aabfbb3c0981cbe317a0971f8b4910e3f3f20fdbf65a900c6f5c35a17196084b4b018391e15c0c54208574382c054a5b59326cc56cdb120ec0d3b06f5fd9150eb3a8f3a883c6017b39c24b002a8fda4defe78ee98cd56a160c8e601054241424501010cf42cb9801c20ac5ec91512c753131d8384a8f0b10d30fd0407c913429fe8698923dd9c92942bd20323b5a10f168e45940662f6783bdc1462404206050b218cf057d7f6a78740d7250788f4c1ab796e54dce5df57042d3d824844a8c78795df0ee7e801ca4795d5d1a0fa81c18039f109465e76d241161aee20c14d2efd3dbcacdf09b26fae8cef2f5fda9dca4864027feb1732acff28bdbf5eba95def41c7663be6e59080642414245b50103b7010000cabc2210000000005edcccab4291a3620c1489b8709806cff409f25f95bf69da99ba6a8053b1214d0bf023a21be2addad51ffbb8e931933852fbbb69783c77a7fc5832a80710a901c381da70c341d0b3d06c381e07204fa3e06e34f37bc5bd8971d96464da6e580c0542414245010128e5e3d1f11229fb7ca6be9a6bd3e43a1af700002e2fb72686afd27d5e39f67da9b63f92b93036e203eabf00cb6cc8863af8790bdf5deff0bd1959681fad6481206f5a584d2cd5bdeeaa36f15395ad8922df5a5344708c6ca4d73b1fb0e1231712e7e801b4dcbc37e803c23dbe5544e626c418441cbfc938b178b2272e20ecc9c586508c7133d73bf9d729df8b7ca5113301608c61ece9e865ecdedc431184fa97b3747f080642414245b50101d1000000cbbc221000000000b870d379a8fb18770feb7b4fcd75fce5024fce5cac4d3ee2b5cc809a264b2741b6ebcb219883e00cb3ff06cef092125c130ef42b0e1f4e3b2079ed491d137f05e116825fa81cc913a342ad417e254874ba012975c7b8b34b9c035513beb67a000542414245010122135e1fd91239dfa994f73d4660ef7679f8629a640545d8624571487af9ea6ed3d1dfff4192f1aa4c771ff2f510e29b99e5db5c48c6fc777b2a1f288e06d78de2ba88133a09c1afe75b85c66a704ac98a5338859ff68447b8c2d9062f7c0a9c16e7e8017163dab8ad36695cd8f062e830a3d9b280962afb7945ff324d77aea7fef06d46c581481a81f417da59fd7f69372fb455c356ee866a0d8fbfefe5ba6fa0a917c3080642414245b501033e020000ccbc22100000000072988a08bea73a643a53a84b02c4f03d090d1f68b54cd235b4daa22bf669bb425a758c04140f061d4ce18e770d17ed1e845dc2b591ad7fcf40d406390060eb059a665b733e3625d8ec13c196cacb4f8e6be51dd488cda8dd1308a764125c580305424142450101921391abaf4d8db5821f36596d541cc86fee2559291b433ec358efb409254614659ea0769618a7682290d4b98c211cc754687f051941e9be3326fce03c7ca086612f3171fd04cb58191c0b56a8e55836def98addf7cf58c264ed5a8e7d50ccce1ae7e801df19e74fc9929f2c6129f0b1b1226235e9f3ae47bf6262e8d59d5d1bec8fe2af8acdad8126344a26cc28369675c907f47906a6d3c309134dc4e931c3aa7e364c080642414245b5010354000000cdbc221000000000b840034f09606fa6f1ef2538dc782fedf41addcc6f72ac0933fc99e02197565305812299fd5a6514988c8f16a3ae73054cc5d7bd414b04d60ffbf8db2803b408f23ab350cad444b9e8f594c65df4fe4b71e4df8822e6ca8a9e4c95c28ebe9c0b05424142450101a061f26cc0e3402e78d837dc1f57d13ac6128d8cfdadc33d8eb71aa13811262c25012118c63bec93c61b401ec8a3f64316c4b2e7bbda29c1d2c8c694113448865a78602c6323eda75762a3b37e87a64b1fdfae975e15e853c10f1b6fb18c19cf1ee7e80119223f392b4ec8907f22a81b775c09496675f5407edf5435c7c963aadba2f340b268dd6f745a1ccad99c68a91c35caa6b20dd084db668db29c03e3bc01f202a6080642414245b5010337000000cebc2210000000004899d3d4031c2d015115971d89432a356bc880e85a3bf249fb8bde4239d062064d3de7640f2c9b53a13d52336fdcfe5168c2b97bde6dae992a51fcba91e2250bae1bbaa07a9d9c5d3ec142da844374c92f98d2ed940b6f0fd1eed724ae831b0c054241424501016881b8b3094e1b3649ce6f02cfe2f6e9d79ac94d4f63549c7ac2ce481a25b77a957d1a42dafb8a6c834c18c34d9a2f91a0e94b3509ba05cf4bccb1f55ff82a8f1bf509e2eebfcf3f9d90c7e1ed273349731c6be411612a0c3886d5be830c565922e7e801a1ec137fd5aa3895937443625d67249d59dead7574d7526021733304c64556940318c5670a01597488694afaebcf4d1925762e03632b99b030ecb35a69b85663080642414245b501034b020000cfbc221000000000bcf1594f4cb145cec0edd7e1b1dbf7af0cf0563badc48469c7bc4b755786ee023d5d87178c628700f77d5fea66a73b7b633d035e8e393566a065a2e05256ae0daffdfe3db39ac39c7295496f39e4be71bc1919b4e3816ee06954ccb67c67900505424142450101fe671b72d9553503b34c67f434c7c7203e63cb551046bb8a62fdfd9cb85529504c555286071a75db6b121e1f59297e55230d8e3b6487e007de2d12615bee1e8a9c72d8fd1085880f29d94b90d8cfb46484e7ec91f2930f5a94f58cb161542aea26e7e801869d13e32449be0e45c2b6ef5830a723daeb679971866c7c8d9979b9b57eb3cbd6441e5552ee7355f628b74d0509b5b8f85433683ffa50f1a82f7275a2b9efd5080642414245b501034a010000d0bc221000000000b8cb88ccc01a1f2b2e8b3871a3cb9bbf0ea29f1d73149d0ef504eadf5580f525ccdb860dc920f715eae3dd5f9cecf849e928c2455d761e66cfa563729d4357047bd232785c15150b90048197f23b7c8d79cc2b2d293105e6beae044ee992cd0705424142450101d666be2f8355d0f6e1e4e2b3b97d99f7a62c9db5ba2c51fab74b0746c347fb25fbaadc4822ac91f3a4914f4c9a079a1ee90ae17ed4b98bac39879bac9cf30c8592e1837aa6ab66c2518d6ab730bc39e17b61404bfac2fd1d743a0aab91b308342ae7e8015a293085ff125ffd26338410dc276bcac2d6ba796a7bc7f39d175184bb856014de7fbd37f42aaea8e1f0f26548a5600953ebe873c3924cbd514ef6b4b3dbacb4080642414245b501035f030000d1bc221000000000044a962de14c7ab07eb25b73508402096cd5a6e6110252585d5b654b56361e2d44e96542ad2394c65d0a733eefcd61ba77b6d001e2c3fc8b26899a54258f2607f140d265f8f246f5b78102ce634beba97a251123f0f1cdb3a4ff421066679d0705424142450101683062a433cc5fcbd67096fce4551c69eaed62c0027369ad21d0f8a79d67497f0fb8cb392075edb874f8f13994bb8e1a60e62f066c2eba0f30672d28efab288b7f4ad6677f2588d096b499a8b2252f9062fb99f5e3f881a51af6e9a92eda3efc2ee7e801e3a6f567b94dd4da3e2aaee2445855f45fd9cff04173a1db82fc0b149679e7cf61def9f184e2a7a760c41f794f1ee9928412e43e0fa2a8194a1ec82949df9d2e080642414245b50103dc000000d2bc22100000000068b56e386fbd9dc652302cba89692995c4e451e029b571053838c0badfcecf6f60aaae8e4833a13f101390b9dd587986c7f3d70ea4c64a04c3bba73b646e130b3911cdf05219875f622cf31b26227eb73c53748090a9cefb54cc694ce881dd01054241424501011c9ae0afec4b106a165420010dd90f6911c48f5f5ad2d422541ebc3f8268527f4acdfe4ef8c1c7ce02e44d346d67d967b4fbbb5a73fb71d0dcdd986f7bdc6189898cd617f2f832d6ff51000bbd473d466f28d67e0f959ed762cfbe2adc21476132e7e80197562cfb458bec5eebb3d99dfd385ae8cfea2d86267d2a608970db000c9d4dcef2c1c393d6ee3335679bf04646699f93dfa75ab3f9598b79e76def0d213f202c080642414245b5010300020000d3bc221000000000c2d621748e348ff36e5ab381ca75bd68370be2f1465d22916e1a0aa49be5f07f460c1205057daa406b9d56231e879b967b500f602d1e58809f10095a7c0f8f0e95c48dd4d2baa0daf4d233b22fb25ccf203236b3e5e3081ff5de53c3787f500905424142450101de6dad5153944828c8d5d7f12559a8fe7fa13e5d41e64d45619b91a083a3903be9d309ee09a633fc5bafacc7b9bc9e63a0521c8992f17288643fe66e32fc9783798ccd7378d03ab12ae00a22924e9c4e199656a755d7c892a51c84cf5adc61cc36e7e8011a4d8cec0a0018589f7903948159e7dbc325335d48f86f1eabd7c3b9235979b02214c6ee086c8365a17e5f68fb1e35c18d4bd0822de7f806debee9a7ad3cb2c5080642414245b5010322030000d4bc2210000000003c8d89ff6ed9dfe4d1e8c2af7d167c96d749a2798cb073bc736acde7117b76513a413ccd6dda2310224e9b5313329fac965257b9a2ff019a505be8e3a361e706091b7eb081c9fde9246e99b4cd376faae962d41b075a3e997455d09e44c93c0e05424142450101e407b92f6f7a25cbbf8a2ec6212cf182b43331af0edbf41fefcb9d78d98df044eff7f05b42de8e26d6ca4fc4530b8a125d639cb1d93e5b365ad59a330b37e18178219a05b88fab857aa3082ce241d2731e0e72335b5b9d7e4cd4b3ab991bffa43ae7e801451e1a8dc82f3c60b73d653a97542b846f1c6111019e891f05b55e63be3ac32a0c5f7d6368685eea57c19cb2084f8fd50f373eca74ead79739c33ee8c65166bc080642414245b5010357000000d5bc2210000000004ec6fbd402a4234149783732e2267af21a407d1af2ed50b6426be4d776d99823b3633e42005a540b4ef3f1fab0f979b8d639a48a17d4a3ed3827439648a93607fcd971a7611e9ff0486a674abefc7a923a05173f6b2762aaf86a89f984d0560105424142450101bc638eb80b826a660ea6c5b5fc0bd7007e30a2867f4f2cceb29c8648ece4d51f603f6b60b88f5690c3cab845b1b6c721c411a522e65952191c10d3388577108c7a7582dcb7f5c835b62cb3a3675a85fbab2932bf41166855ed594a10e6d871bf3ee7e801cf1597a0520fd41a835bc7f889cab25c33196dc1246bbb554e811a2646e9e9f5ef9be0cb7982d5b60c28c0e5c2d7f8e0eedf40fedf3c44c0c731f4f3747a2199080642414245b5010354000000d6bc221000000000a056e1cdcb4ce91c97fa304b16f88092cbddfd8d6d4a054384e78cff7de36b0d06c4c5cd5af40e92e6c213f386ac4edcf2b1f92c1f2649465552cf780baf0e002caba96ab091c08816ce320b74edf5ed0806c5b3271032a6e57949a8f8acdb00054241424501012803e9ef01a739b25a90ae1d22b650158e808334d6ec29cf3229cb18641d7658791f712b23842a013562a2987603d1eb207a0846c8b2afda3e45373cd0afb58e452e2cd8725cc9fc09e2d18f21dff82163af3df5996849f10ef4ccc80855427142e7e801d00ff7dbd39364878ea01c8026446b1aafbf9c0c0175d9958bb7e38a2523f2c1d3e955779c88975c29190adc342ca336c20241a91673e872f20949a973d12d3b080642414245b501010c000000d7bc2210000000003682733830640ace27dfed97c2cd9a307d9f24e162cb93def9e653bf3c65d65355ca1c650e7c28dd9f01f981e5fe4755d0b384765a0e302712aacf875cdea80405c32ebb961c7b6c8ddd16ae87cb15fa00788e4cb231516729b33c3207758b040542414245010174588635ec789f57cddecb75b43530655e9f4653188b7dc71dd84ee5164b097260acfb16c3fb77d3b5901d37605db45b5941d61a1d657b66e3582e9d30720085b842eef2e5ee8b01de6f5383ba63b07d780ff46b339e679c7ce07373b7076c6946e7e801ed319bb99e1ae4f14bcca42bec4a67164f6e8fa8fbe1c1a7298d3749ddd14b679a13954ac3de83fee85c18856c0367eaec295f4a9ac616e474c2f1205ec11427080642414245b50103aa000000d8bc2210000000003e29670bd6e95be2d9f3dbf3a785d6baa03a578278c4935c88024831facded74e19d216ccf6e585558b97b71858a029cf9e083c7a6dfc4d5287b14fe3e8057068e166918f0f9704c67fed61f27df7f8687b9a3df48a1de5d91e5de53ec5bc108054241424501010eb62abf8dc8315e3bb576c9b9409bad1f1b13a390237b3b4eef02a1f02bb17c72a2b4e6181c4fcfa910ded70c82b6b6d52b68090cffb9e8517ec960bec1d08df5f70d37f252a051a098e49a0c8a7134a7fdd00ddeb76185347ad89164bd75744ae7e8015887d73fd5a704be2c3f70acf0e820a96517f83370c19eda7b48a88bd1dd274e986d1540cc1574063c7aa74c692fdaacae67c73ba75fa714ac8bff1bf565e275080642414245b501031a000000d9bc221000000000fc9f0a9a5caa0adf0923f409d8aad952c610023bf72020c83e219c2a1a621a4d9795808f1350f1c6c3808ac060fbf66114f9139c4279001098934614abbb390144e977125d477ec18edc4457a8848ce6d077d97aa63769fd8ba31f1ecf918b060542414245010120cad234975d9cc792b4b40128d799fa889738b7f552f2399579f72da5caac37e63254a7e9ade241390b925b2acd797088dfcbe03b570b957014aa9f30dd4d8ab4ccad4de851605c55a868873797d25bab519efe79c0a438c5165908d66356d94ee7e801945b23fa4848df8ef39b7cb03954e25da31cfda6297129f2eb14a5ba52d3cb78c8773e38b170cb1064dd424752db5e265ea1931b76410f7aba38e5288e84f0c9080642414245b5010315030000dabc221000000000d8fdfdd4726da03ac9dda8b34e0358b568258554a0e9595786bd233451033c69a34a1013abe088396690aa624cddc893385b3935e018cef28be83db8de154606c43cf0bf7b17f39f86e782c9244d71ff81eadd0e80f155fd687866f48d66ca08054241424501016cd2f526cd1e4e6cb7d9fae7805d050e80ce1d287390f98e15bc86aa82e4701c32ebd41a53367f5ac2f870a70cdeb81a66f0e11ef37414691dd5cc29f0cf9284ee95a8f9f34a13d17d7457e7a9738942335eb8a0091f6917c86ffdfc9ebca8be52e7e801d80fb6c0d0e1688a677a96196fa6eea19e4aefb527569f236b1eccb54f4dd2fecc43720970dd0738378c93d3dd28354fdc1d873789456b9a254041c1f36a5e03080642414245b50103d1010000dbbc221000000000e6648c3ad1516d95f662e86c59741f6cb322be8f753d248ccf942f725358376802a51bb6abd3cfbff1d815b312f50bede9d3f2d2e37704d7c7302cc16c07400b249b2763856e0eadadf9904684baaf586b505392166e75f26366ffb8efb34f000542414245010142a9b1f777d27305e20df2d1879a416eb7f27f02fdb11e686ce879085b9f9d4e555fc7b407df6766b5ab33ce951d7959db8ba09195a36bd6049a78a3e9628386b08ccf7fe90f92a5678d119012faacc533b8f1d737095885302ef138b368099b56e7e801fad8eb24ac481f8e0e0dd76150d384c8b43e959ded60dee4e84f31cc376d464788b8a324429f6a4ef9c1995c129beb0c44276ae35ded0022efd57cb642cf9554080642414245b501017b000000dcbc221000000000d43aa3b1abf23e86d62dd09273b58d877cf48e3435966eabcc79fcad09a1a253f09a711ba5d484186d2d5a70bf8057a445beffc6500a936ed2b1fddf7ae7320cb757614003ead6706e19befe81dd791a75e5cbf45c3b270539a3641b7ab9910a054241424501019cfdc28dd86ae50581b222dbfa915c54db89ad69da21b880689b38a8c3435b570dc62bcf9c84ff6f21d02b2f817c1134d054f4db7baafd92e780351bbe9b408bca69d925e47bd37c2b71f56ed1652a7bb480d5047b6183dc21628cbbccff54e75ae7e8013aab2d489d6e43e1a7b363d333475201332f259ffc1ec65e3d1106313e6890151218a7d7be90b7e92ce12685c955b36f0cd03f3a50c3fe886a1c4421ef6d47e8080642414245b5010368030000ddbc221000000000b6bfd0520c5354d26f17a9287d82b3d72b324dd3f3c4bd145294f8a42be51c6d98608eba69ae7d8f03c263ab26deb64ada1c7334c22c3d0e6aa1d1c7d57fdb076ce443bdda63ee94df0fa354ebd3e3ec3a615f80b59bde90538cd6f549e47f09054241424501015e79a45254c865e9acee18bc6248e5cff218d132bf62ad7f3be1e912e8dbf94a47939a2965c57f2e00c66ab49f968969adb238dc80807022ff5d9a27a2f8ec81c0d8b9017dd528bcf92bdfe9c276ca7cf331692cfe772f2b13a41690cd8d03515ee7e8018fc599cf5405ff2b70ade7262697825d3689f1001f0e7e0f78b517dcbf8491cdea105ed9473188b283d0f65c127f0bff537337293af028f3ea051feadfa301c6080642414245b501018e010000debc22100000000006f6659af7976ac2c3a84aebcdb757d48c122bd9242549fdaa52b1f86220b17c5059a5120a6ccb2efafe37905ea361d4685dda4cd908da66f7232c48888f03025bfd5ed6fcdd1df6c9812226480789e383f8ea4c1160266c139920ad40a4ac05054241424501013ead0f34ad97c529ad65cd549ef57d1c5f089c2ea1364b413b6a029a4549e81bd8e48302be436af898b2ad42a4bafb9dc5537d98be4a70f066c540e19491f781875eac013855d5558cf7f7f2c3396343489ebfb66df9fe9bd9fcda57e80ec9ad62e7e8015b358d9a46e39e1f2b0364caa0d034559f76f99e743ae0010c9cc559ddd8b2b728be18b0ed7164e4e811831e18143eeb3e5e183e8dae0953d07fd3c189cbdf50080642414245b5010320020000dfbc22100000000008c321e100e133e938a3df12715a0b720b1587ab2a4bfa6f5f3552ac520b2876cc87e7fe0135f4788ec006b85b1b6d134c1f11e5575a897c007c281351d6b40b92e51a844a80ea292888bca4c38d098a5289cee5fbb5b53ac35dbd24cac2330f05424142450101e4753348473acec3432a3ae17af469195b7aef2b36d534c98ee9b52207d04245531f3d44722756158c91e76a91630a2c525ff64c297942c55ec61c2bf118d08d65e6eea72e937f632e82d72e0fd3cf047a843089a732caa4c654954f9110aba766e7e801af9e3bbbc4d1455e4a62d29fa4266e87749ae02475d9450af2dd7521d96f555df2abdb46fd85a094e1dc4ea5770299694a95d05122de0f5f76f33c4febec0b01080642414245b5010353010000e0bc221000000000c4856d8ce442b75a20d2163f3ef86001b971a577c51f91922e4f8d1f3a878429e4f109c9eee619016502d97dd541e5acf5eee67d91bd49c290eeb80bf25ffa05939267eb37fc264ed37edd96096ee4cd19f6f68faca9153d97f9e286c5918f0f05424142450101aa455e31a1fcec2318ac1462ce3e08ee269b0093bde1110aa7381df548b2201145ea74f4948548a656ba186079736859a2519c013d1646eab9a4e36f80b7a08bdf494c0023ab2d1214f7bf2f9ff967768b4df691ace8663daf42b0dbe2f2bd5b6ae7e801e503ba38ce95b872434dd0656638c892a080b3421c8d7c121929f59c5618eb1f8307ced577fee9812a596ea369b804c19b76041c91b24aaf1be43962706c897e080642414245b501036a010000e1bc22100000000000db6722d10853cec73899de0932c12dd6b7ccb8e1a9ab125b613738c4c3cb5b3f05376571a90a7afa806fa50efe494e81c59f5c149c9b905011e2a9ac41ef061d02d81b77dce91c1967d6074b4f397d2540b1f2a33662f9d70c4c9c8ecc360005424142450101de47ed72031bce00c5c296747ff6b8764a032e1f69b784f0ef915471cd440717a5dade2816c35241a5c1b5f12689afc03552830e343ec481838ab4bbd2f0fe8c1d427c5ddfc610dba615d3eda5496fedbf4c5d632cbc5a4ed329cec7062511966ee7e801e4fda841f93b9359f2c04a14ab6c5d058501530292ba39c3720643116c884d3bb196093949b636c95daa01d39335f3bcd340589fa7d665d005d6cc94c04dcfbd080642414245b50103db020000e2bc221000000000b6f07c7f88170be6e09d5e595d4c9379941495dbd6596d9b30c7d4f62a2a6367b3ec207a5d47acc24f8f3bd6e1ed1a92659f53616b8808e808ae356959f845032a24de820f5ad3a224f1dc25a77d00d1d1c201a6d2a941f1422d47fa16a03c0705424142450101d215074b689642f189f251240699bb585715af6b92a9259cd113a1ae5b4e6011bbf08c158a20ca0892dd7b8f054f5838369d5edcff48ee1380d774936166b7830b09c7a8d3f461d5bf22730503aa599217484b0d47056708fe57d21c3793f2df72e7e8019eb4de6e88f343bcb69f733870a5a82db530079736fc3913a7f5a04203e71cf54fa963ee748ac328e779eb80222c28abac6ffc98436d293f3553192fdf7e7610080642414245b501032e010000e3bc22100000000002d640aa89a0b073ad34dafd30884169305c5f67af6a2f8c38451a43cef09974493f4db76b737879ec70413f90921b2cdfe55e7fbbdef6a83c6b8d366110470283326e603028d671dad4baadf26ae5642e32c9dfd6b6ee62c0143945db05eb0a05424142450101bc4e8e2400e673891c508facc685158bc3110e2c81890442fe7083799932345cfeb57baef8ea5438f7b8d8f1471ab3ea1e7869508bfe5c75bb681ea36332278bf7c3179a467afaabc70d50474cbc0766ef1513923acce39ff1802a0da573f29376e7e80170ca82d7078747a5f470e5ec4841a9d964acdd4b635f62553e84d5ba78bbd9b22339f97cf6a4e49866c9d0cb483196de8b36d5ee71d152e2125b6c8e3a6ec385080642414245b5010154020000e4bc22100000000030bb5bdd6d5bf90d0f7899d8555a8fa1dabfc33a0f31d664cc9eec07ede9f13e56a6c3f0e2d6debc617f4fb1ff59a49d52b9c540ebd93106c1d443130cc9180c77f97097b7de0d2e34de0cea0875f1a89f234f5baaccc12d33d7fe4e36f250020542414245010106213892851b93425f2d3f7e95808ad7633569be7445c559e2d44c40f01e8f482ab73d7dd8d1d42d98174ff01aeea6132613d1987cae762d08526d909aa55e80d874e53eb383c58f6bf5cb5881a1390a5b3210c12e1518af6c1f2fbaba0791dc7ae7e8010b2ab8568c573bf0ea3e5041ce66c10b91d35580b266279b31ef4cf27d7dd36f94f935fdd69253044d974508142c46f6b463d6179f6545e9ccff63c2d352cf8a080642414245b5010374010000e5bc2210000000008219572ae749a1377bc0d87ae4664a2737882b80ce8857354e7ccfdf67e498257ebe3c86469333366a3470948c5449b0606e302d715600bfdcc8c2eff4e5a50c565fd40ccdee8fa6f4195eff152b4cda7bb9233e0da7466bb813bb5bd464110605424142450101543d5c04b0cc92af681c3f3f114cde526cbd3c181e17ccd2f9f8860bf67e4e414d5e631b6c2a71389bfd2a135b257b72b21dd5bfa7fbbe1d749992a38a497e841b70755ec3d864ef4b9b533b52289511c6d6f5b6664439b2fef3152f941e88197ee7e80161176a01c58d764ffaba98ffcf2dc921c4f60fe24111c3ccdad9e7b398df0eb10bbd49db1be69f8175f0d13731270c1e6e60134c991dc84dadb37c02d2ac4b30080642414245b5010323010000e6bc221000000000a6405a6c5bf9f8d8a7e1c17f186dba7f290e19570d4dba7d6146df7c4d0f6825694598a4341aa1eb4e0212d98d7ddbca0ca549d5f5aaa967da5e1af6c32d110948bff4ee67eca620b2b1810f51ca087ba056d3cf6fb89f9c4bd38b3c9ae5690a0542414245010148fabe334ed48b0f2f0add9631d660fdadaf8d7d040b55c523b5c572934e4b3a4c11eb790809777e34d70966e95e21f75e89de88325a5beffbfc8dbe0927d980522ae0665d761f1acd2701001cd79c8af515d7974c77e5b91c5fd8055949f0a382e7e80137484a5b533523aead53b48398fb0e7ee19c9ce5872460355865cb7a02e579e6ed5bcca29933ffe5edd256986606f8007036a4f5f884ee489441005fb436cdd3080642414245b50103a2000000e7bc22100000000022845eef53e5203d9eca1c8d73b294884627ebc0a386bbfa6fdb43e0853d0a27e171520ad88c4770aa7dedde911eb84da15d9ab0b2abbf164e1d6728cd83ef0b443839322f0477b6837e17e333d20327a7b472cf8e3e5924be811bad5cca3407054241424501017e19cc45714f4b301942174c75ae24b4dc42ad5355be614b6d748bbaea111b79a7d3c2155e19b4d463afdc5b33c34d49e14e4c781e186a3ee1d7926bc3e6f48350b72aed5b5bf4cba4459116212b46583e8f5a17b3f7bc2f9abcb1e9af17daff86e7e80146f909f108724cdc83862b46c32129caa9a91555a84bfbe32233cb7efa1013b002cfdfc308177eecb818bf49acc835e2d6c7ce4ed4bb4ba2cd8365ad47c1abad080642414245b501031b010000e8bc22100000000018e66ca88e0a6402c83542f1befc62be655bd6db8b61a0b92b1ab3d7dc33464fc03a1f172bbda38296cd59d4b64db5b8638ed1708a7802746b37d04a7dd6080cea89bb7bbe24a5507755dbbfafb9515bd8179a96dcab9e4dd7b79a48844fbd050542414245010136caa93b2102e4814e5ce23d89ac2a359a57f464d74021909af8c5769bc4ca10d563a595eb0256f1b76e3b282b0123a9c8732cb1c936464173b5af94ad5a61832a1ba43b17990bb4678ee936d4e91cf814cf21a2d9043f31781083829c87424c8ae7e8018d7247cec331407df8d805d9dd786bce5aaf947fb37ecbdc673b88b478e001884ddbf99d6ef041b48b802e93745ecd3fcc828d2a1d07bba9e61bbaea29dae9a2080642414245b501010c000000e9bc22100000000054c84197344dcdce1a3da1a8e77028ae6778b1f954f8e5b2c16df866c2f8362232ad4ed95393341f7228a5f89cbad112333e67288c1b22f1eda3a1d2879d7d0cf4dafc37c105901d6f724fcdea83a09011f8c1035c9bc03bb7a7eb0e63d810050542414245010158134fa797c0a316b5848d426cb8846d91f1fd7315bfb492960d8f4be330b273b624870cee05079f5d18e3d1012f1ce9c4e3eabc508d43ed30af174e0d13a78999c822789b58cdc5fb3b7c54346745b1355bb9c2da212f5249f89db8961478828ee7e801fd749be8164b2d3e63372471e4497f02a3cfb813fea0a7f642e197c818ef853bdd81069172903853c02621455fb9d2d8c7778947482a051027ac9b5cca564f4d080642414245b50101ff010000eabc221000000000227918e306f34a24e4d1b6ac45f281423483f82fe5a072ca7ce7aa85b3f9d934ef3fc82b6d89134f4a50a1c77788977ca5ed9c9546f3b5060184c714a852440e61ad697b067f05ab5821a6ab9fd310b5d2a5ac0c9fbb38a95159417bcf25bd0e05424142450101be3fd7fd2135c77db5f71f3998c7d28c7942d6ce602a8b8da94c7968adf91b5bc835535c55b59893ada36de98c9c3e12b535a0f87741b253c4137d56a3b8958470046076d6d92d444e68f980dac9fdc91d750a2d2f003cddc78b8001f141763292e7e801fd05382ad403d5a0c13e88dc9b94b574afd1d19e7cff779b38ae8aec0b6ce476f5ace5ee22555bf00805e09357b95d89db689ff05a9d163c7f02ea32590da1b6080642414245b5010354010000ebbc221000000000722cbe419b7da39f8238dd292872e61c380cc702fbf92bf2d93aa5a2b3ef9f677591f9331171740a99aef16cf9d9becfdb58d025de299da4dcafc53befee400f119e9c61c8621f7ecdedc33a8905f63a394ec86f7221ea4a044a908740ce650605424142450101502986dc88cdfe95d293981a70ec921b205f488cde2190cb3ad8bd4279ab06633a4675478f5a3bd74f7212e6adc75149d4e808aaae4fd445318c150ae1b8cd8653e47d978e7bee99c00e02fdf9127841126ffd91b338fa98410997e5c74521ff96e7e80179ef5ee3b69e835565cfad6b7946649412c74490912636b0ac0532a007510499e948004e9a304aac1d008f96cd040c04c5baeac16423e4836b7e90065f33cf1c080642414245b501035b020000ecbc221000000000e88165172d3ef79beed6a534c2f782be75f8dd51de23e350fdd535f381ff310e9eebf28b1df15332a3ee4aa90026ff9b78d1f33f55a19bbc41da5f52e7bc200bc9bb94cf274b592f72838dac9886fa3d89bb4216bb7012a63d8c2898d323720a05424142450101ccc759b6a746e4aca08d6d409d718a99f01c284dde33bb3bfce211bde19c2a71754759a05de5c842f9693ead3eb078f797035ad8bbf12aabc9643464da03f289827a31412801a23e9e95ad62152af09db3a46fcc9031988caaeb0596f5c92f3e9ae7e8019bbfb902e34e634aa67a85dab59f676e4b37015265b5c77f262dc2d2a7a65970b67f212fb893d015c80705269db83f809631cd39495dcbf13b65603bbdce4b57080642414245b50101c1020000edbc22100000000010009d3565aff586cd687bad7ea3ce21e6009b97ec40aed4b046f14c4a122b6b926ba682781cdbc40d53f50f6d7def2ccd37a8ce231bec4020a06dae6778300cda8dd1920aae1a03456659b985938969c4a0cb3cc8568f56650af01b03f3880f0542414245010118e3094139e9f45667ee9860c18a9950534a25951d0c59307054ebd8530fc85dabf9d127c871d57beb97a998b2aa2a4e3b2b1c8ae549c61fd9530087699cd8885cfbdae29339a16c43d80e38660cc5e07415e35bc994df0c3c808b721cd2992c9ee7e8013b1be66bd5f3ab0690fd111b6826641fe86eb3b61e8a82beb91ebe6f6c69df035f30290a777b32794c88c6557da04777b868f40ce2fe6a91df256b7c6d704cb1080642414245b5010103030000eebc221000000000ec0f0b2ba7930daac73c77610ee91c934a3afd1c58ef1c13ca292d9615e5e1064f3acbb39d7896166a1697b3068492e3f2c51d09f8d3681432d2ddd031165d0caf62fc6be3823d026983601e36895d7bc694d9ca869420e47b5a28727e9d0a050542414245010170aba318c7616eb87baf8dce67bcec85cd403d037e18c6262ef27bcf8e4a417fff8ac3180f0757bc3103c12c031ac50ead38135a0d5d6d80d23d759e7f1443829159ca43d517dcb0a20f29c3c4916108f5f4c4fe73af0bb155116dbeef571b58a2e7e8018492e44092c66d7f787d3139fd41608ea22fd1a2e5677ca2fd6793cd80b8f70da6dd214ea46ea39231484b184ffbfadc4821e0b602a41cc2abe80438ce3ef773080642414245b5010389020000efbc221000000000f2697d2caf28b6a6a9b2341c9913083377534f22b2ca18b427b259bf2c303f3cf1fd62187a93bf9bf3d23ab18b2e2d3b4d2803548291acf709c8aa3c0d4b7108877ab8de8db81eb55fc711037807ca690528ca9f4a017900f1c49680753ba40405424142450101e4064f13c1f8ec0017217a8796460e60863423aaff44ce57ce2186192f3614132dec77b392c3a58e71868b40f033d6e20dfbc14e3a82b2fc4076f5c00adfc98504975e900c869ecf8b038bac04561de8b72ca769e4f93e4937163c65d140c095a6e7e801e44b1d89dc80c676dead4bac8d20ae568dbce22541090e0e652b1833809f760f20283f1b591b73fbdef932397a5c3c1e3327210588121440d8a59ddf52fc7294080642414245b501030d000000f0bc22100000000080d2b801dd7819198361ad2c12d46d04c1ad10d486ebe2bbf570a1e9d3abe63529bd765d1224614b0e05993c1d0803fd4aa181f63c0bdd9d73024f835f36810e0f800b93cdffb34aa4d31a5f8a7dd3975d81f9d9eb67108d146a52795c69500805424142450101c86559fd5ab3ebf3641f5782b90f38c4e6237a84c4bf115e48bd0dd572c9752346762583d9db4113bcf509b71803ca53f2cc26f9acd0ee518c75b6ee40b56183eb3801725a59a32a7536f0247fa761d624fd0b736e5e5564fcc481d80439d29aaae7e801736393e42651090891fb9c0cbfc81d13380e2c0ccecafb7bca767bc9c2075b830faae2728258ab543d992d24f0e1d6aac080914e32414b67535cd98a6966beaa080642414245b50101d8000000f1bc22100000000030d80755d913d5ecd371d5e82c3d7d463e150f97dc8a7deb7e188d8a7b928f4d8e55865a10e39cff784e748baf87f652b7d946c3dd19e8920cb746df603b9c0d39bf207cbfd44d19bccc5c27a20ab1bd6039104e956a51beb40449ca53015d0f05424142450101949a135342e369b9c9c12c50774f5025c21753633cd23e35e1b0c1a28277e9749023ed0b2dee446908d0d988d3a9aba56a46daa8d16e2b3acf2bfa7f37b6308bef97136cd91b2dd07e7d21e71b3777ad9c5e8bd623b4703cf9c1d54cb091f2e5aee7e80199105461fab6e8ab308e6e9cf75ed310b3ec141bb3f2a9b8559d96f283950d93190942d77962c5d3a1f1c1b54ae47cf60a88b29ffaa14a37655344feb1293abf080642414245b50103c4010000f2bc22100000000002bab806c839e3397ddbb014d09fac6bad777aa6e859329b2cc882e629c7361ab6bec2ffb28cf42fadb7610ed4c8d4778636db4b9d93a16bd11aff57868ae305b8280a5c2942764972286f73c9e7292c7739173ed511632fc0266802cc1e090405424142450101102db5f5d4ad9b92ef8add899d1901960f4af2ae93f1fc2d190a9a995919fd644629fd239148581c326bb53274eb6f20179f678b8630518ded20973245936c892404bd79f4b465c160e781d7beb2f271c97c4abf5307697409df55372d7ea454b2e7e801451cf1f0f1bc0bc50ec24c4170d0bdc55f129a14b1b5d9a66c919bfe1c2a950031f2a6ce3576ccc2e1f291e3dd9a3444f02a502de9a9e9b2c0bd54c6d9a70b81080642414245b50103f4020000f3bc221000000000904762b4cbf39dd91b530cb659336a5b751dc58ac66a84cb1b209ae54d71b631a0020b87a8de400658058100289b4a245a212f95b1593883f0c9b2a5de2b6307c5767553feca0d35333c4219abd4231828eada69667d258b8b07789e67116c0705424142450101f4da2850ea29cd292288416de68b59920879fe3a729afb3b8691de44d07dd82e5425452715d0930915d90b6bca8bd41c8c5ce526b5521f1ef413b88387929f89bfe38b46b18406c22a34b90cd13a73e84b6192aa0f82b2be46b4a1f2b94305aeb6e7e801fa02a9af5515021438efa781a5a22ef12f40193867b4e0798549f7e19918688901be34bed9f6313d307fe82ba963a1852334538383b3af914ca834e929628ee1080642414245b5010140010000f4bc221000000000585977f4c2b56326376548e3863488b7dcebb7b75b52f24ebf00311dced9656a3ecbcf7ac5cbe54069fb0f53c2db72e44ec300e9a8cd1e76786b1ed6df05d1075cee59194c01893c47291316f92fe8feae7b3b4b0074f5a635466f6dfa8037090542414245010124539fc8e702f1917fa8ab65b5e87dbf33b8ea33025c3f082cae83af19c2a45cb49a75f7e93352c3f1bd6f7548e5e78ba1485921b40550b7b099bc5e2e11a18a407126d05ca7dc0898722097fbe25b8d0aede487e0ece08837e1b029b2e04c41bae7e8016820ba55284df0a547d4d172b2442e51f703675b9ade3b4c086cf3e6c420b97ebd9253c84290f90c1998654ed0592e2d9dd3bf7f84fb7d5f58ea618a996ecaab080642414245b50101eb000000f5bc22100000000034b5191b3b97f9f460b26ac6ba9fcabbf8aca3d8feb0b2e30efdb982cf75f216c91945f1178afba53e380b40807d228159b19aef37b3772c17e0e18235cf160e408ef656dfebe0a715adb391ccfbaa0f0a438d745fb6886377b3549739ddce00054241424501019c2bb56c8ae23935c2c5ab2959e4a2de2f74c6c9ceb8af711c31ddc12c586c58080a31acee2684cb21b660ad594a9e12eab2a79d5c5780cf29dda098bbe6d08ffcb8eaeb149294c89d13db181501e01d8b3e38aa50fb15698ba7a65f2c4406f0bee7e8017457efbac7c7392ca1fc7e21b24d662a52a56238c22eb73f73a644a2585ad0242963c4c4f695462e3beed76f584155cb7483d713b516a3df4a887a159034aa62080642414245b5010307030000f6bc22100000000058e75413953b2a226e99e8345e3b2903fe6b15eea617af47b859ab24a4d2995c9f443cee26b4c5211da934cc3cd03b3f1f593d82fdc2b1c18ea599a52e67070bbdf9e18e1834a4f20990675493a4947cb0f6e02fb2c61dd29fe9749bc33b870f05424142450101e6790430af9b7db5a5dfc1bcbe7bbce20963df0db640265ce3c72e1e9b519f2039760d2a8100eff8f54a383da39cbaabe76c46e0b216f2d1e9e40ea42858aa83c88a2fd8c204faf62cba3ca04b6e9b48ad6c92b4087185c34bee212313540f16c2e7e801317efea901f77a2930a867adb3b26baba2748ea6ba9fd18b3ff6eeb125c08311080760dc47115bcbeca8a1752136096d1a1efda9400f89c3c4da027a5972fc33080642414245b501031c000000f7bc22100000000032a6c0757e2468b5519b14756bb65c5e39718b541cd5ee1c551e7352586984379b5a485c221ccae56eb637fcba33b23cd19d817f1bc21d03a27d86a61633900647f093801b1b980420e45eae4df64e02f711ff1e977846f1d02d348ee884ec0905424142450101864719f4f50335c41e0330ac7bb6a6c707e9dbae15b4b9e2782aa993ace9cc142de8b9caf712e27bec107beb7b160181d059b1531fae1ea8d5486fa494ace28799f6da3e934d2635f08ff376aa9f13f6f15d4f4c915bb1ab66e2485558e81436c6e7e801f688cf216384bbfacaa42868bdabe2da9c094f93e2a33e50a96b951649bf29c7d34a64cf84d6f135aded756d869c479fa08394ab125e6b98a40d4e2554909d0e080642414245b50103a6020000f8bc22100000000034c13924bd124f46266dce5d34ebbf5b506443452d25368fc1010dcca27b1250ceab03c0c6b06743fc8438e3c56b1adaebaf1fda18240b0b8d6d55797df6ba0fc04536651e93672af62cc17c192d35e0ba654f893a0030fa9849033a991d5d0205424142450101124b5c8eeae98f52cff3d6fc056e9e034a1d422fc0c966ae711a4bf70c865c6a49ed72057b3b6188492ea451cbc5d7d0518c94aacdfd20202e31f4db7240fb84141df770ff04ba463c5af0a094810d54fe5e88cb8fc79c81c9767ad07370dcebcae7e801ca5178765232f9a70f42fc0c914a626e867e7f67a56489630c5c1a204a1900c8fcaf57467d7c30b24b1c1fb3053e7c70aa2ac92847831711f77ebbdfbc13f8be080642414245b501037a000000f9bc22100000000088401a3748b78970e756934c09019d7812cca2713712ad302cf54d00b0ff7d4bbd64e4fc5e5f387e1e139597c06255e98e0e3186e69c04f58126b43ac5b57d05b7797716d74ba8c102b7ed9f222ba4a611fe333de4d712a99af347e9cead0d0c05424142450101288e26ec29b8bffb189d1e52821a5d1428a2b9fb3b45aaa8447ac5ddf57315786f9bdf9bdfd38e5247e4094bc0e5e66e303ef97f5c7506b6d3edee0454a06382d38ee666d2ed5262a36dc6c14304e4f32b8e921a5e6c2bc6794fc0680ea42a20cee7e801725fb7092ada3339164c1e0854d931bf21a4041a5a2dba6cde43e606a2445b458635fe65de04ba454a0679e1abda8aa6989b4e81b58e414ae9ec919af69142f4080642414245b501016e000000fabc2210000000005cc6bf518975de549c1d530a6d045f9a52a0cdbcac9efe06ce3a5e3347073c4fde40fcc6713e5d36b3769ddbc7e6888cd5049ee590821dd9a6e5f1277816ee0f9ca5ed0ffffce5bbcdc08caa52e40254616a34061219c99804d5715ead0df5030542414245010176b5171df236f242d6994fbc29f8a72292a704982a2fa3c0fa9be0c457fffe5f5f20e228b0e8619b3b1fbc40adfdf843d1a2f9e04d0762fa04a34f53c199998fdd7096514b24b704e9b1846a599eb9ad28246890e5ec0a65279913c9040f4bc1d2e7e80152777900f5c70a867209ae3f038bd3a0d2d16b7fd8bb091800bc72086b8621344038388139026ff7e911347993dd539744fd3644f20187308375d661af05c81e080642414245b501032d030000fbbc2210000000002ee19e6eaa50025ed35ce45e5344a14895001c10eea0c794f6aafcd7ae9a2b2f58a08cd13b867434406d631f98b45180a42f38fc71d001967781b9e180e32c0799d2192f4679fa99d04cd416f2a2e895e3985947d7029e7cae51aa08e99fa50005424142450101721850324fd7120c784f0eaf839dc54f7b0c0070da461fd2f86606315440fd4f62e905fc1d0ff6920bf8115baecfe09fe909777d32d9bf6643f2d7ecb3bdec8c21ec92314b7c51b5f447366db561a5c8dd093bd12e527bafb9fb3294867d6f21d6e7e80104a8fb5e6b881cd0c4b8928debd2fc23dd42017462219f1ae0fde5f086050b86f365f84f80a0a340bb5c99e1db24fda4c900d916025cd0f51c14698cb60c5624080642414245b50101fa000000fcbc221000000000403e325144542a1084f7bcd9af572ab5bceb97016a5f44c9df4499c9a9fb1f747b930cddf0cf9e0df8337dda59e5a1db634a33efc0b67f95969378e0067dae0c60361c52cd49932df014c32b2544e44875356085147c57c0ff9d8b4c2f12b90805424142450101261e9c1e3d61318eca4eba850789701aa81adabfd28c1a284b84eae46a50ad475575c6bdc25085fe54c952f6113634ab96260c389ebdbba4d40b102356fd3e80ed758bd6369f194067f3fccf11299f540c1802c18408fa2e0819c39f32b32613dae7e80142a45f46c297d7dbdb1dbbf06af583ed0a0bb16313d4350e02a192867897b7b26eb7dd4d9922cbca937fc867b1389cd039980630b13436bf86a07697ac7f0437080642414245b5010320010000fdbc221000000000362d0538f560e6e1b0eb3fb98d43b800fc2751218bfea270e38f9a43100d1f106633a8cac4d0490e48820eb9905811728514267eaef17b9b1b0d4bfbf663600624b199e259a37de3691253fc954e80b1167584fde0016bbb3c05350eca0ee60a054241424501018419e9bc0dcc07f11f6b85520e0fdd7df9d95e9cf22644550382ab7a5a1e841ff969fbff9f609aff3d3f6da65909e2155f9a96cf5caeb642cf46ad7bb063de8744a04697cb20cb3e093f92d6863de635da27c2956a9f2ec847ef5a1fef35557edee7e801ff32be1059c95427160fd58370c8e324cdf7c9b49978f71923d8d679a2da4fa28c1fbd5ace3956d6f830249611a7808971a814832d6d76e6b057fe789fff26b0080642414245b501037a020000febc2210000000008c0313402546bd25a8870686504240e515e507bed0ab44a6696248c63a9bf22f09a25c72f9125ff1b423b1175169e4492ec1392c3c70175720048aa357cf700262648a846b72618d38cd9a9a82cd62e620c019b8ee29a0387adbef21f3cfc20605424142450101c4c1f6e63532d9274ba80aa0766128ad4cc1af9cea9e6d0b625873945ee93871ee4525478c8bfc5b51191c4241b5f657cedd17deb12ebebef654fdf2fd7f418ee765924126dd0ae5fb71cc075a0be66712df442a9e806fb7b103ab3b3afe033ae2e7e80116fc7556c1154d506208e575b0c7e32387c7635cd375207031dd3529c4e538515b61c88236d68395493b638ce0683daeee5a07f5d5090a7f0b3ca6f82875c95c080642414245b5010368020000ffbc2210000000007cf97a1845a3694a50bb4092ae2ccd347455cf6d4bb470cae6a42e84efec022e973200fdb40e99574b336fb60533436584afe3849c64efae8a280508f46c7305ed2704f6d8815b23e7661a05923bd1c8a6025c702791eefeb7317be95a7f320a0542414245010110fd25a30527d0a19ff2a9c0a4b590f701c92b847582b1e6c3eb0b5bab41a82648d885e916bbb887a67487b240a66ec0eae2bfb91e51cd7bd6a38760cdf4c98e4c974a157416b735aa6c34dae012a865326ffd87a11486993c1f2d1fbf6277bfe6e7e8013023067e4347f2f762e111ddb4714aedbb919a85c2704bffaf1ec08b41b34ca2e625e3af09801b1974db66344fca2b6a7305dc834176178be7b7c4145ee0bd55080642414245b501017603000000bd221000000000e2e24a8b02af8c0155781df9e1456841b0ba69d64354e8133d78b6114b05c338a7d11a26827d00f65b83bb8a2acc095e45e22e49235faf170d86dcead9067f0897fa1104b78ecca0bde592e13502cff168f9895fdd84b06720aaa1e7200bed0405424142450101f46bf927d17f509c50bb648ab5c67267464806dab4f20f4f44676a7eb1fb907cac12c93c5ec48297af288be04614de1aa04dbdfb7680543fe62e2718251289893c274f1a4fabe0e3463554807f873aa9f25eab77def429b1f4fd75f98af9fbdfeae7e8018acddc97a79c396eaebdaa7eaec3faddd38842a92b15daa056ddcd591e197d4ced475b6dd6e71835a7b067d6b9fe4907e015a693e6175781736a6eadd87931a6080642414245b50103d500000001bd221000000000f8c68e00f91636df253112a020cf16b2ab2df70bc9df893a8e871844e6db0d090b595019de779d086e8ec38f7af16a97160b010d72501ec1070af34b3f1d2c001e1e5f17499dd12952ad1de887be1f48e0038b58d39023a096ced49256940808054241424501018c664bc3c9cd3aaffcc4aaa515418136075ffdb2c151216c54065c8147f80c41b8741c7b47d76212c40280e3e1acc83b461422c347fbe00153868fcecee11380c5fa589a9a0deeb48d8d1406d4e93b6af6d2b6fbbc720d79b4b65024769989e5eee7e80119af099f32c58f4eb19f0e7163a435ad4b81772f3555c9ca3885fde4629f8a1b85d093fe067a6f0751e06f1bad9dca6a5585726b044d474a0024b63924c9bf2b080642414245b501031a00000002bd22100000000090f74e7224260c130a6f8b1edad97a8ed6546be40a31dde502bb4876b2ca0a68a11060f06f91c1d7ab90789c45d432eb59f77e3bb3ce012522303fe09aeaae0861f0de07cc5530b2613d4ca6d867eb85cb0a0de5e1709c73861d2cf0e2de350f054241424501010a185ca78df156839da88ef219d2389642263f923bc30f3ea218f8c79074f92523a3d94ee3e29ffde9dda5933d04e5aa2e427a3ba1d9d7967058c531d0ef228020f58246eaa2f21ce66231f57605241bb3f52329a569fa14edc2dbe5b75c2dbff2e7e801e06e85ec2ba48aa874ca153b5d1c96a9439aa8953bde353379d59afa5954045b0654ec66696ec03422fdd791f057ef757d7b186156e2fb815cf92ec0add52725080642414245b501039101000003bd2210000000001429188f8c5fbc074b60604f0d9c88da0545015ac75e01abd246aea24d455466ef04c67ffaef88315d4110dbd6e878f40259335a01952a03eead85a235bf3900fcfc603953559f9a9776eff454636d8959a062d8edc78cd79a3f95b4c735080505424142450101a26f55318134c4a3ed3b2d6f956e832e215c0016a7a04aac1c0924a3474f5c4badbfed9c04a61f3ded76c0de59d6c63c16bb31d866c11545f0a242fbbd48518f3c27d9e98cfe4f27eade783598b94a023cfaba5f4dd156af0ea2a03c0feba7b1f6e7e80178351e4966df736d7ddd47ba5774605797adf2cf876737b8141ca7c7d83d8939e3661a608c8bde062e1605819ad0c314dafd2780da8bd128a49cceec9cae010c080642414245b501038400000004bd2210000000009634bd5ec90fffb79f34645bad60f1e4f8747c3b08fde83298ffd4841e8a7428987d62c806e5358e4cbd247cecd7391af70fed6b31d405aca9380fb0549dba048f1d6551773c7616025295ac262c29e4892bb62319feecd8f9dcdce318d8e20105424142450101d601cd1cab3ef11d1f7a0c24ad3d0bd192a0905814566679a30c7a646b02b02410cc409134fd0f599cbdae030b71fd987e16616b4a8fd79cdde2c0ef24324580d7f2bb28d2b9d3745ff5fde12d890cbd313706a6b4406585ce42e4828c79f451fae7e8012f9a9f22b2ae3353fc1708a847a14e9cd94782d90cdd167dc6725c419aab446c2e263d505006431997cca0024c695c0ef3bbf26f82e7251738c42e44343f772c080642414245b501035a03000005bd221000000000145ec053b64a3a6968d3bfa35479c189fb3f011e7415870132d0c917da4ad32d3e283576588d5f16fbb3accb32e27fe52e1e4ec4198e97771f4932e2bea60300cab5169d67f3e1384cfe80aea5b4e0d16222a5981d697a441ba5a8b42c7cd905054241424501014c8eac3044f79e37803d8cd044a1d232a9ad8c3e5664ff3bf7fbda4f983e0c1d80a46a24d86b7dfa96121a0b1becd5376d24e107fec279239d2d54123d42c387adc651010bc8d7b593d152ccb1c48806006993acc91812c785fc060231ea4041fee7e801c8762fbd0ef087ffdd92fea60cedcf047e564c3c99c1fd3aca7f011405f7fa0c8fc8154ce686fdf4d1d607ca2daa8c7ce3263d6bf048fefd9d4f4454d89207fa080642414245b50103e301000006bd221000000000e800037f295535a4ae35a4777d9c6027bbd37c4bdbf29953e34da447e5beeb331cfc4b3348464b44f4674d40c4f10a4fec76a6a0e28169a192c0fa230f9d770c53448cef7c029ce68268c4dee3ad65eef041b4b0ade1d716626590f2c3fe2a0005424142450101642b0f144d000f95f633ea13eafd0a6c94e44065e5bc21e06f225ac347b66c0d4e5536d556757c967af503352305c609a39b426a9342bc89592d08b5a22ffd8fa334e46a09c0ad7e97ad7589bc7b45f601af3625fd95d0f33e59732b09f7e3d002e8e801aa0850d0d241a10d987cb7472c0960b85911233b9084d0c6b8ae39578ec9aef50ed605a0230ec5340c5f03611d816089ea5dd8ea04597f7e567032a9cf273bab080642414245b501032601000007bd221000000000101747a4d6d0a76d77acb6c5b19e8e19c75125c00ba66ca3ac5ee9bc4c63b27f9f1f6d18873343a2790630b486c17f979d9fa0bf389f0da97078b7b7837eb905613449879cd77c4b0af893ca49b8f6f14dee1c20762ba76bc80f26e49325c90e05424142450101763b63fc4c1aede098e582f9b97137b3f414451b53dd6f546155c7e140912d596df4831d49cccb80ab233dfc9fcda5d25dd170c62d6c6ab2022dd01ef965ab853c482c2c4499b4f3a2084e98627951095b21a14ccd8dc552f4cd237e7d8ad4b706e8e8019f01ec99775073a8cf59e025e8d2f19309f884300e7c49c5f24e6575fcbbb325bb662c4959056f9f0ca7fb9407e4aa4a6f30916bc7820c1a18288458d429f29c080642414245b50101e901000008bd221000000000ece37ccfa1027cc42aa4e9e7e138a5fa5c320e746d19c499d745a7fd54d02a1ba0d9de6a89c14fe0f2cdfa93867aefdf96b98f677c36b6596250f014795c310c6b60487d8fe04086478c39c98515b8a567b7458a865dac18da28d944beedef0e05424142450101f00c677df8f9808edcae7506a03b92d8faf81b0f4be6db4abfb132af52a579059e4f21f194995f15f827cc7703725b49676b513b130ab55dc84c038889becc8e6391951f42850ba555491a77fa16cbf7b2e8b97f1e6612d2ee61203ff7c32ed70ae8e80173d069c6e1c1f3fbcd12d21877ed7258fa0488902c3c688b7fb158a966df78d39ab2086c22be693e337eddab0728adfe4ec262326c2d990b2ff6f0fdaa8b1edb080642414245b501034903000009bd2210000000009c86ba4574314ed18e40d7674b7b246d7cb43a76cef67368d32b4a12d827686ef209eb7ce8cbf976902712e49fae77f637374317fff52786ba6c5136fb02f7052ddf644a8c7f33d19a5bb53114aa9eefa77b2fd7d3ea41bfb7973caf9c827f0605424142450101f275fb37b6e4ba53470b47d88b65315b09e3d0ba394556613cb0af1b46ead540e4afc45cec4f2fe6c29935a9a59b436dd39eb0ef07c095f89d53f5f54402128beef3db8c4478008b61caf8e9a3a0905c154194852e40b6567047aa2fd65b695a0ee8e8011337e01c9dfc09fb3660c9aa639817b315693b7a4ef99f2a179aec22713f62cad136d8b7d60f7598e151a5b93d4af23c4efa848fdc7103b98fbcc760ddc2ca6c080642414245b50103100200000abd2210000000006697f18ecbe5798d171a76c4003bc3e201f09488658899482c0ee615e8da765c216ff9341a7767234b736bb14ccc6e377bae82af3415325279b6dd0b7af9800a7dc4363f978e77c79cf5a69789b533d40ae05725e9574f9363dc36b9b3950d0005424142450101847a214d823b5dd60c241ea44e9b170e80512d7bb4f8842ba9ce234fe0d9d06b7ae315b6d5973fbb413f681020db5191523395d94e427f9fe08c7503673b6b891482a2c63110da2d0cd651d5f330959b654382b6563055d1f19a88f98484d01b12e8e8010af385ac5f814f3d856c6dbeb7188c17347f06b8c56786c86e058c6f43b897831a2b9d948881a7ae62bbd9d9cb2e43c01c370f88bef027b43f73592289147441080642414245b50103700200000bbd221000000000544d70ad55bdcd9aeda1a64219d5af2f428bfc1a2997c1126ff0a18e0a68900ef0a3ae55d04f974ab5bd07ecd3790201bc149233bae0e8b2b9d6374ea78f2f057a2718f94a24eeb7408003547e88d6f206a11d776cec045eceb2baaa4e0ed50705424142450101c607873a296fc06fe8a62722bc82529b2949cd338afc0ffb34a9334814f76d59c5b92758659e81cfa5daf37298bc9a037291e60785ad5cd68ebc4bae92f2e78a3a68a8392fc3c8a66a79694f86cc859cb67ed383ff6ab18b44679abf1204df0f16e8e801d879eb5a1754bf4a4524818eeadf8dc55b49ec300728e026e171948cc2bbe1432ef244970c78fe4af540a7bdf18e3645611e0376b03eb182370e63e12de03287080642414245b501034e0100000cbd22100000000024cc1eccc75cabc7a25ab5dab95fa1fd30e6c75a6067325317f1cf699b0b82474e89e97edb21c47ecb363fa9195e53066b50958b93054c84ce93d518f931f60ef46675b1fbb5a6c32502275e03ba76720ca5a18c57db54b7e86d1f8ed1120d0605424142450101b4ae9e0470d221b0b7698cfe5429f2abe8fe86d4f59ff81944605de1271f5f77564a9ad1fd7b9eeb9e6ef8896b7ed001202807f1182197d375cc4d377eae9c8da38d8d2b07a58326b1348e8f7713ae98472dba213f512badf6d98ff01cc02f801ae8e80160fc6dbd097f700d71c817cbe5fbe0c32210450824a587c79ac7f7c5e9b358d4f3b656dfd8cf77ad3431e7206cc67a6a617e0f1a68ad595c1d193030e48b6a84080642414245b50103c70200000dbd2210000000009adb742e82937f634b7a8d05b1ee11dd86072779f3eeb325b4051c120ab400573bb9d58dd278f3d3ce1cb4f2e6237697a2ca4df6f394308e979ee0a1ceeff80aff9b50ce32918f25c91e239835ae342502f262c50f851aaac4ac182bc78d760f054241424501012c355ae3aba046e513427d738ab2015454edad762ac84b14c48b4c35f37d844f8821ae1c2570a1b1aa4a140e20f9c033d6e57978dad0dd02207493e45bb7d98d5190d4237596b09bb97ef9e9b4e5239cc68f75e6f17c8caab4de505e06ce0a4c1ee8e8017021b4affb20b77cb7084d5186d231aefc6afffdfec3cbc5820942ab4a144c342280c128e357fe11c22250bae3925836d004c59f694a631ee81351bee1ee9f6d080642414245b501012d0000000ebd221000000000b07158b9ae8ac90c114f482c5c3255df1c7942249da66f4af431802ae0287c428215bf7521a0f6238deb24a570055c3def89d1496fa42701fc270a018c963c050dd49387553a4d4f4ce013817fc077c80aecd4a6c574e1d2c79e0b333b7a7b0f0542414245010146d1a97a467e4d3887c1e82b9d78819289df4257c37d8ebc0dead3177db37d7de506c9786d25c7461a90297eaeebe7ab9014b082e9064db3b7c90e8c8b17f088de744718e4f44791ffcbf95cd45f3607ba7ab9d7837df05b6287a65ba24575bf22e8e80181b181514f6225ac962047bb113d63de60cab9ff268c6311e262ce7a4e6289aab2f095b650c9dde68f51a57347d1e4dbc2cd59ea1ee22a54911fccfd74b906e8080642414245b50103d00100000fbd2210000000003ce66bb1c3406e0320431e0a0b5e17ae55a9f61e279824a4f8d09b8e81633f69a01e7a324fc6f0111a299f9caa6c658a859fa3ce1699c2b9569f881680d36b04eb8d7267ff0956c69ed4442387c37f3635c7ab105ebc7a5c4e14980138bc160c054241424501010834a9e84fd430546a1e55418282e0af5dda967d0a830e0a55578e1c34af9761d01f35db4b4a5c8581eb385ccbf839631e6b8f40a348cae7f97cd6ed7d0a3783c881af899472c4c90a9e1ad766a1de62a7d4ea944198fca6653aa10efe696f0526e8e801e2f6afca15801f9016172ffc11bdf04eb78517b1c109d6474b7e6320dde4033a38fbb6a26f3acb7126367e96dbdeb3429b0ec60fe6dc70921da53af4830e689d080642414245b50103cd01000010bd2210000000003e8fb070000c8c2923b145c7b4abbfa8481a13a668710590c3ae9859f31c6014ea4e2fdd130f668ceef2e8ad256ee20155d571827ff24c4430fba0aa245e4a03fc5fa304e456b32f60ad3298a500e852397cfb2718c36364703397950d5685000542414245010130e31c9208b18e69560174e87c7edb93de0d2ad17ea71a9907ba4c199d00652da8202a077582c14699250dcf73466b93261a57cb9045da59c33f728f2059aa8762cfe84f209a700ebd76569f663f094e9a848a7a4f09d1372ba78c974de2bbd32ae8e80129dcc6f9efc223654040f85e2f9c5c65cf6aa65299053c483dc7d4cf6ee8f2ac2d1f8299a79c4e22ae367bf851c59c6f8e498e221de6fcfa2ed2ca191d56fe6f080642414245b501013403000011bd2210000000000812ea0ff05627df732c437fdf10ac46db842a3b97abc9f3e377480d6b779976c41281da3e8e5586e8fc72dd1e6f2b05fafaf82eb826ec4eaa172c3fca9a2c0eaed202bf355a040ef57b0730059d59a9a22eadf563db5fe033876305588cbd0c0542414245010118a602ae9965333c0c113d4de22fdd7d5f4006e6f01bd16cdf8ffad0bc7df03585ce57cbc223606e6d9d2985b443ecd0d580fe47a7a6926832ede3d6faa48284f48cea66eb9c403c5db1b56a089485982118d264e03d79eb88692c6188ce5e3b2ee8e80176177e842e1e308072e62ed1c399da4bf2a8784f1ad1a23aa1ba7c096fd88371a75e41552f34b400684fa2a04c6de8ada14cf29daabdb2a6422914b5534ae3a0080642414245b501036200000012bd22100000000028895fec6f5b57950d7eb1b13ba540d6d33e15dc9446b84c4d3164b934b18d5f1cc308035ae666a9eb09591e79ed603c7d8f327fe605b1f8a83483bd0cb7960fead02db7ade569328f822176f9bc8a7e5ec6547072ac40888a031aec6ee18200054241424501018e4183747f6eb7086db0b2631a78bc03f693bd9300d20bded9a8d05afd89de22fd84f3d1a5096ec4048623d740840df8d877a93709cbb9c7240346f1fdb7d0854ee634b415a14a317debc9fcf30168655bb9ad1427ada447826d8ee38bd9bc5d32e8e801b75671abd122b11006181c4b5e578bfe6014294af5cbfb7c272e90f94a593362be76f8bbfcd12df708fa5ce1504956be5dc22bb53ac27b54a8500152251247f0080642414245b501034d00000013bd22100000000062c471365d59b57752d474ab1fbf445af03eaeef11e754399e520417ad3d961a45fb6001ec6ecc902c55a7229fc6162c0bf06c46f6f628bbaa8cce5165db10042a7e3d4a260ff70ff123d82ac4015f22446e8fe50a170c32cec6cd1527497e0e0542414245010124668af1827beb42b91f668ef9eb23dd8de579796daf35d085b5f2b994173d645e64b9f5b769a59da753149244439a3923c661ec5871d836818aea9a4c733188966ff23c5e2b3e8eab1afe29b5586244573abdb043922ddc56cee9516a8eb0f236e8e80133b8a993621fea4c7aa76b92c9bdce83e0845451179e6bc140cbbbd63c4773df1308670764919595f624c6f0090cc4e5ff5ed35584f3b6bf15797b4ec0f8506c080642414245b501011103000014bd22100000000064be80ba22457bd71cc9cdb3d477dda8ee0536d9644c6aeafff9d296f5c9a37f1a368608992a81c6b219bf560c1519538f88bd36513288e860a1fee639db18054f9639baeb978775cbea3760e80e56a1e202b6e4d749bb25812e6bc094e1780b05424142450101f865141bea0f841e0e6b9bf8681ca86683d509cd05bab334d5999623648f2959d7666e06b557b58b3844184a7da6070db69b6c4b95656e0518ba17050960aa83b43258075be28ea6d489562051910ebed425497076fc015dbdd77b44401ac8573ae8e8017f574ee2e3659124062af7d4f601bcde4da426f5dc652dfdafaa298c63bdc688ca6849e53f616f1e53d31dd0c25a93a34ec6a18055ab50f3d8222d6846c7f149080642414245b50103f302000015bd221000000000e89b0783ca4ac4a7c3c1466f4b2d1eb09f0b638dfff086087c43bc1504bc907d3f6cb0de8f18fcfc7924d0a48d9c66e4653322bd0a913d2442df0e483a8cf709af59cbeaccf929218cc7db01f5282ad45f8442760c7928956974ccd0688e1a0b054241424501013634b955fa213de1b99c97e343d92c88bad8306f6e14a2b9de35d2b83f3e18196284e47cc22a0b5249cf33dfb897d8a380bffc4b3efa7d79071ddd39f8f8f78d80eb48c22f67aade45e8c1624db64f3a749880803e5ec24a3da60214479fca6d3ee8e8015a6e02255911deed81a2eebaa0da670be8a239fcbdf01753fdd0dda09eb6979ab4c32f9c3cd1f63a85d365eab7b6a83d7202ac668eaa9f363bfeb53eb136927c080642414245b501035d00000016bd2210000000006c4c38760593ec437a9123462b33fe3ea9bb443e6458e4457595be6168e05d7a15def117f90ff47d20a3284fd6b6c89da3135e62a23100b66398988e0bf3d40f493f9ed2d9c0a91f5224101897281a796c1725d6606b7e3219c4f6c5b702c30805424142450101867247ee9266bc2846044e6d3181fd316aa75bd839f878f2530a05f35f9a97446c874f0fd455a2acb2fc8d31149ee614b8192eccbcefbecdeffa16fd5520208cb0ef65eaadd498fd4ad5f322732fa422fa80a323dd507026837c493601bff60e42e8e801897a42a583683c988fe4e54ee6980043a9ae9693172eca83f818402c9af0930ffd7b889042c7681b7c67e0f7e7bc7457f2c0332105ae3501fc88babab6402c4a080642414245b501033600000017bd221000000000248b63386451355a9e93c02f702dd8c9bcf2af1bfbad35d2eeebd96f29f7ca29a81dad66aa502f401d50be020e4d387ee393b6299fe058e2e34d18a157cd7e03eefdfb973567c91eee94ce68c6a2851d3bd6190cc8ba22dfebf6191f29579d0b0542414245010186e3228cd2fe797a84d9e4998b5ad8a5d0f6d33d40841acfa5b1234e5ec219330183593ad9fc03287dcc6cb6eca1238210755ea57221ef541c19896fd3df48811e8304eff0a8bd8ac50653f4fc43a01b4f0cb53979ffad750333f8733afa483346e8e8019c8d97df5ee4d0bbfab7944bc80d297ca8dfed02750f3b512fcea7058e50b06acc7ef01e4115e94f9eafef5665dc4114c24b961e9454f5c3d721bc5db582f729080642414245b50103f400000018bd2210000000007cf151dd95e79c88443a2d25afd5c27f97377c4544eb78bfa44ca3bd17cde10cfd6372b816bdc2c84283559082a0d2c76410ed21d48a1a8bd48fa7327bdf0b097f6ad75fd1d094a3f4c580288fcbda48f0707eda087f13bfd57fa667c3ff7d0905424142450101a0c8825e3a155ee4a1d4ab8e8ca67f0f62b0f7e8af49bc619cebe6c85e27483e981d72b439c7a2e1269a3fc1640bfd30639f92cdea98f09ab322ecdc3caa26861613992648450d29cfe51440c29429c440cff16c6e990802ff4f901cd044ac284ae8e801068b8502aded4dd995236ef0989825b40a904ba1d12282cdb73ab1db0f90b098673f7e6df89e80372127460c6ac723a355e5a0189ee830aabb8a191425f26062080642414245b501016900000019bd22100000000068650283463e8e7e24084be1ca549b658f02860c9b6d14d7a8352a01394cc77b827133d4c8ec951b3473419d4824c8ed663723a37432c50e736550d9911fdb0b93f68fe7847afa7b189d244c14349a2174591ed485bf912d4f3b72c5c9d9af0105424142450101404d67278f57a1f4489056930adc3bfd26f595c5424a5e0c84cf911d7ff20640448f334b9d8530787dbe40afca9728f69529d834c3d0db39e64ffd9afe87008b7dd62374cd89b036b659b5006d03d46331748d45b62153ddae3c553017cfcf8d4ee8e801f4154334d53ba8be9f8b3e42fce8dafb5704e3675f1b0a95fc14550d2f98d9e4dd64803d90eb68f4673664cb9e82a4baea3e107f772c022e1234cec86bcaa609080642414245b501015a0100001abd221000000000d2e0e7061905248371a53025f16e65739a5523aab10e32943ca4669b101ec849bca8d3ce6f039620c1d84badb36745f4b2cc7d008f5939683be8e3a6418ca90b21cdcfafcb1545593aab0383deaeccc0edc368107162349093deb8e6bdbca9030542414245010162974517822eaaccf20d3474a5b6b8d0108cf8600b9f69802290cc8b7af2aa1f29ee570e926cd38e9585fd2de21a41f0fe61d18c47333f5f0885dafe45bd348bebdd84289303ccd3e2615886497991df3dc096e82a0e8e86a24404d1f38c6cca52e8e801053c5845d7c1bc64a99ec38044594c257a4e95ff985dadd823f73acaaf4388242b2a4c97197adccf214b0408f03fce475752daf328f1c1f060286ae7f09fb587080642414245b50101830300001bbd22100000000040cd8851905cdf7cefb5ae70b594624d56d0193068394de20b49ba4cd1d7104b7ee048e2b02168cad3502d080e3b281c353ab69fb1d04ab215c98b197dba910b37b08dbda5dc5669d74330a4ac914dcd8c5404bf3525ed0806fc43bd33b4e60905424142450101daf17845d663cda1f075059d3f8cf3aa9fbcb51af09280019e9b6c71db49105cd48ee70b9fafb6ce27c17c69cd62c308531b73dc8d271bb4adc2e25ebbc06f8a1c3354db01b27c82900ba5e5dc3011088b8901bb1e73e512ffcae3ff132577e956e8e8015106768c4ecd1b2a6073bb4e13315e09e4ff41bae3658a23bc65411dc2a75682b941b73238cabc421f27079355376cd1c718f8099653be4423225ee512fd4f63080642414245b50101000300001cbd221000000000a469c177390d63b1859612438892491c2978ecdcefd4e8478c4477ed21252202f19c256c4bf3d1c7914a5fc594d1b014792fadef669b457d891960462632510754c4304c5457d85286a3d57c4ebbd89bed4cff7ac3134d6f80144d384eb271070542414245010112fd5f2f2d36790fa99380fa5c8a377155ab7222634d12ced217f949e30788597efd50918d750b0b304863e5886ce3d9e42378c7c97d3aafc2256870d81ca68c9b428a7ad4c0ef8036cbf035bc1ee78ff0b73459f7c70ca591173c40dabbecb35ae8e8010a6bc78f92d7eb1b10f8c38065645ef9fdd7ba17d9c4d363bf5133f81e8a32243220c46a66c91ba3a8660c4e4f20009cca25a35215941d469ecf81ef38e5ff96080642414245b50103810000001dbd22100000000034cfd78ec09c62a1b45a16aaafd04bd78474adceaf40b93859eaca634e3b78775813bc7d2630a301e28c1c3bae8aaeb6e93799d3f490625362739eeb0033310269387aec0a33a96db0e93d58e3681c1bddc66ce69a8f269d5a1bfc22ef2ffd0e05424142450101283d13d9f7ee2a59c87643802d92b95fff0d97ca785d459818f30cb7370c06720b5610cf8f1b9008852f0e65f1592a6cda07d4a2b2f1fb01f1efcdd6dbc7a285b607273de51efcf81b9616278dfba6922e76902678c9be9febe35d5d613bad525ee8e801ac9c3ac31896dceed7c6b9276b1640913dfdcedba85b939836a4d7e8d23facd05315bddd038673cf7cd85dbe75afa7066997515b846cc6e270d823c3c6ca65c2080642414245b50103660200001ebd22100000000040c742ff495c3546b26ad23d359e916e48352c35e81786e3ea280a6a8e24a34529186a240bf5ea0e0f9ccd94148c9cf7f8e6281a428e158a7534862cc47a4f06c7fa0de955fc11fe6514e46970cf473759b45f6aa19b5e7fe5c95cfddb5e2e0105424142450101902a56ced1a62d6b95f4978e0be47e4cba3a9b079747ce178ce72c121b4b6a45013cf44ca1017b8147c1ed7c9a789456cd8347927667a2d02b018d795e22ce83f57d21cb079a76f6a7b6fda2feb96f31caec97cda38175f92b77fb73cbe6e8a362e8e8018dbaa68f6da754cc79e216c432457c2925b9e6f199a921b3a4a958b18434318675c30a126e83f707a39ce7c14af7b125a9aa2f5e08edf1c482e3521ed3af2816080642414245b50103bf0000001fbd22100000000056fea7d98ed3eaad6c5a3949aa7d6ebe7918d71a1e0dc44f0b9c77b7fdc74a23d49acdd029abdf1d26d57e0d25e7f5c46c5f87b1cc61abe5dcad3721acf2410f66c0cf1a7962278b73d66320d63dace5731b420fc08565833dc9163f35be0c0505424142450101aa45be9105dfac70295866d44b43eb264604a0b83a59101d5e5d7af06a4ed21eab0e20f7b6d82299ee396657b987563c49cbefe0711862598d99c7729c3895818b62e32cdea8225eb6458f8335f463b00c462ccdb40d4920340710f91e2456da66e8e801bf7e03e71645a8d4f8960219c08009cd379590c54f4586b94cab574855d19ba7466ef37f49465973e145a57da52a7019a2e2a5da7c3f4b623ddbd26365199296080642414245b50103dd00000020bd221000000000307d1eb7bd89b8e7bd63190d54aad2ac880bd50e259bd36059c4222f04dadf79bb1126b887b92014511e2c61c94244dcd8e053bbba2df08a38e2bcbf85592e04280eb4af910fdb939367e9f04e3b3053cf998952f148b24ceecc8d7c56563b0105424142450101fecef9580d2877b29f039c38edaaf8d6174e48dec1dd83e30eeb42362076b04add58e840d6a3fc1140cbdef29297752114b1a9d0a3411d3816f518eb6a14158ba6bbdeb51989626f9857cba7c026bdcc985a5f57748c95271109f9cb07b97bf36ae8e8010b2d43cb8d4316f0a728a875a755842f7a4e25dba5e54f3124e812e1ea0592525059fa01e77a411335ec799af00e6503ba0163536a81d4d38dc717d276ee25a0080642414245b50103af01000021bd221000000000e8afadae0cc2f96ff3e97095528c45ecfe7ea1b1538f09f8a4852ef0b389e0762fd81013d92e1f011623e6231fa662011aa35ef8c8d7bc6e5b98131dcd9cbe038e6d08b75cfebdbbc2b7bf601bd7cb5a1e139973ff18e018fd5942b962407403054241424501014edf74949f4b210b0c30cca86cb3a6d16a5aa291d9f50474d19b0781937d16016306056a97e10f41bb90cffa1aecbbe033fd105af505120c3f6bdd57cc908d83734b2cf189bf8c1eadbc831ccba68c2360464b63c8cb2b0066a854a42395e4bd6ee8e8012d31bb51000b6d9a4f07f769966a481537d3e2b0ca9c84017b3b68a25fe258ab8be96578a990051a5f147e10f0c79498be3444ba8c9c6c948eef3104f08f57b3080642414245b501034503000022bd221000000000b2c0477216759c0b17d927555ec1df42456b449b3653bb6bec560fd6ba8cab1e571b5b46f7a66764f137e49d37792f84198b10ac341cd0301d6037e3b7c67e0ae2a4fe3f82eb31947ee3e66646d0f079b42b390e5c3456fa44d89ae731924a020542414245010140f5a17317758d1814af887a018c180a05ec3be2dc9c64cc469fdee02cdbfc4a90c84da10bb7c404339365853491d226e7a2ba2fb9f7f672959471c15824578549a0297eb3a79fc430d2f3950a60e653095924df0a5b8630a3ad75431994050572e8e801923fe35ae011d14a1efdc595bc545372eeb594ecb2dfaa24b406eb37284db9bb80784a35627cbb5c440787b4e2361a343ca518da37119215ce70b43cb0ec918a080642414245b501034100000023bd22100000000064124d215a77a67108e3b6c6da8fe6cee7a36ea560d3e1aedf130f598cca9a28294df24a3cc4b000574ef17098e5825a03f113cd43f9cea73ccc2dc059246a0a9f8dd28b1a8e3c9cc8151dffb9e34926bb1d787a6152ed411030e265d45d23020542414245010132160c13e89711ae23eabd3c5d59b25797bc0cef1284497858342c8ddbd9fd5d6ba8e21a9fc3885054cd7a0369aef3a7d4394f160e98060885686bfb3f9ce38d30932f5d89b2824a96d7caec3594093c90d9c11c16f2a9ee29635579bf271c4f76e8e801a42018df2d9a3f520d267f957f04a152f883dcd00dcd7e505a157daa5e7ceefeffdd9be2c8c2ca1e39a6bb39a00c602f1c8cc64fe4698bccb57da4b329d49042080642414245b501038300000024bd22100000000060131e6c1cd4e84e9bea206d68f9910a8be054e3a354c30b5e01732b3a37875435335d67a8b969df4cc8fb336a2719c78f1984bba668d9430d014c94f64eee07d92d428f9f985213cab35f48a6eeea0a1332356b53b48dfe769ec50556b4cb0b0542414245010120c2e1fc398995be4756781e7a9d251e291eb9806ec612ab9d31c74ffb844c676de18889a738f9d2ce9aa3b367c66d9438a5e9995107a2c6ae7b1f273296498f3e1b361adcf933a94b1117272d463d8085112549df35371dc6598f10608279577ae8e8017ed865a42ccc5205fed86eb7528ace3ff85f0a40de5a5b7e1e97d90208db9a63a5cbee05203fbc8529d4870f89124a5d3b5d64b2fff9041e006826b5f9029ff6080642414245b501034a01000025bd2210000000001007421e65852f1eea651a054250f7e01b08166bbdaa36ca78ff091ae20e1e63dd0bae7504a70f447013bfa98ee385788c16d8c391db5d3e3b2b5fec231450093ba6263b2d035c65ef6e8b932e8baac4cf0eadb462a65ee132f4f88c4653950b054241424501019abc773bb4c14c029fd54d38a0bbe136cb25bde3c26dae71e6a7c972c9d2637d555530adceef8a4e9e0dd475457f13fc6c935227542be0f6a56e18761a2ca8887838d0c8a3c5601e22b28b17e06963ebcf4cc806037d27e1f3c5cc0f82ff5ee67ee8e801d566a370a798af6ed2ea3cf3d2989a27fe94462da491902f71468c08586d60e0a27f4f9ac6c09e41b06220c3703aedd966b18be0244c01bbc982bebd4e896176080642414245b501038701000026bd2210000000005cbc9a0a4861240a0aabf237d5830eea3376add89637205861f453f21f134713b062e508c9f8575e97556d9289f257a23c136c6e92b002fbb83deb354528210f4e9da236ae72f9b740d98c08d7d28e9b99d4d3e0e4e15282555069e051e5fc0605424142450101d62ef67ff65d8449d29e9f99db091f50919292d1c48f1e4e0055eba8a9f8a467b10e662e465df923b309f00cdf2377f752879643a3ba2ad31281adf3104e3f89856e3e99b110b97a9031706de8053a225ffabbcaba6bcb3cd698e386ff7f74b882e8e801219bf29c6a034b8da137a2745058c708ee90345f56a4d3a65cdf60cb84a6d17e0f0e7b34aa197cf1472133d9073003dbb4bbcbed68dd6495457e5a7e1b2c9d9a080642414245b501032301000027bd22100000000056ae21690952ce10f9d40fe6f73ec9b9f08cfcb6c6d7f9fb5acf8c1dac8ab240eda3f2903a3d8fe65001904ba3f3bdd90ddd1bb1678fae157cc419fa405d3601b1b3a27f3a4b7257c70ab86ace36a272c9a73d4be8f59b9092df06881ff59f0a054241424501010c4b79405c95f1d42fbe87c677fe136fa2d09b1754b6282d5b864fe40e94b10b821f902d146977aff5b7b2fe1aab741e9e2e7896b708a955cec6fe51c8fc1088b5d26f1a1bb904e4b111a66eff5d16c3f5b950d59114d99e6bc19934eb03c38886e8e80115918079dfb61dd964f24d6561fd52eede81c6b967a5f608b70dc955e41c4a7f4599d4d7a25c50465a964a7b5be57ff38e17318bff641e48afaaaa185df32e91080642414245b501035f01000028bd221000000000b0150aba4eded247eff22c764573e3baef13535097b9be9533ba8449e3757a66754d0e6ae1dac1b2c3a43ca1d264bd477cf07a7aafd51dc1ece735138364530b63ebad51837fc923a67aea702176960f05f9394d4e4f7720b20e2f8410491f090542414245010102f396c4b0a2314844419fca7b5e712f49a1254c6f8aacccf1b7b63309a51e324a875a04f006a81df57646e6cb62213ae12292c2ceb27e2d53c9f984b3b32c80561b1087c16a6388395eed6f7c8a09d7148a3ec6c90569dd48f24858adf533e48ae8e80166e440db3d8018eaa895cd92d9f8f2c62eb59c22a56edcb2a2b0d2643a40c8b5e256ed7bb8ff0dabd67de593d9e69724646961a145d38d8d4d3844cedc4049e7080642414245b501039800000029bd22100000000004d962b77ed5bc4e539446ea510d90d6d057cb2ffab215a4f49b995fdd457d37a6b1a43a0fb06774202e60df48971055d54c27476dc79f2e17dee3b02f3ba10697ac92f4c2489336f87491071cdf985f16bfe5193cb028ab761c2ebcdaa07b080542414245010114a59dc0744484e641237715b305faf32795e80d84325b3f489d44eb64293162a07b6df1e34cef1e9c9396fa3fa69c8d89fbdbc14d911b1321618e85abd7828032082116563633938d5983ab03f0971dfb4147aa56cb039568ae1b0a6d5aa7768ee8e80139d08954a34463c1749d62be538b0d78fefa0eaadcc1ed32b4d88e78be98bf2f451817a2e82cc864a8e8ee8025677e548b7f5d569d413eff9e379559443c29fd080642414245b50101b20000002abd221000000000dea999dbfd72e8b61730bae5d623cbef268b8157357d16eebc535ce35a581310a83bd03a8fd3dd01b89b7e8c9f379e951277cf97774e9acf8c30ea882318d806b7ed407226c318421c1f5fef58c8d181152665a6db50c91816471a3f5791a10c054241424501018690e51447c2ef4ef0ff26b28b6bb9ef319ed2c82d69fc6ba6d1eb99e4231f001535f64b87e8d70b0514f0556c2cb1ca4a596527e227bd15b14e7a2cdd510584de9b39430a16cafd034b84210d73f83a165907504916424804412c3337cb214492e8e801ef0808b12c387b83b147d5560bbbd0bcaed10e9f89e7b986f04793b78ca5d77cb5fa9d6be060e92c1c3b9503bd97823497ffeb61f390adadb02d415764276fd2080642414245b50103710200002bbd2210000000006ef46d7b1b7039a26388e02229838a3781c4fe0421104ee33f1c737426e29b5b95178f0e338e81a7f7ec48b7798d8b509f7d1e6ea9b5f97467629a375e94e7055165220b780b036d0eb23a3b9cece19bf202fef33f89d8bbd097775e7130010a054241424501018235b060c4d720e31a1fe9494d296af0b16d0b7c4e13aa960cc12622deb42866adcfe75d4a9d81cdb1277497d6d1f4475888e1625d4f59d885a1673ca6b7e086bd137c4cfdac9b106d2eb56a571a4956b49151dce06662d981756d26e898774296e8e801516df3d028b2866c5e3164f62553ac100e50fe0003a4e719028151e98dda5a97fb05d76e2d60c606857b0307bb24cc13cb1168c85460c427b5efd319eebb64f4080642414245b501030e0100002cbd2210000000006a388e55edbf25cfc1a234f5608078e8ae7e8127e98c47b8f5d3029fbd1e0e287e0ceeae88bbcf4085562d1fa8705b2c9622c1bbed0c3d53dcbd3aecac04b80705774977818141a4a02d4e2ee83879cec95dfbc150f9bc167f06edef7b303406054241424501016c912b8492fc1b7be742885463ccffda0066de391a944b4c4bbed9fb81b89b6c38b06e8fc57dbc1eee6cedf56bbe1f9548a92519a1f1513a155ae2a655c61980f7a74dbfcd52c6cdc45088d80497cb2a1795b51cc229ab23dca1a712036584f49ae8e80176281da624fe2615be862e1ef771ec1e9f04268c3973f82454edec883af991de4a8e44e18f46ba76ee94ad4484cda35746f16396671515463322b1e02af28a10080642414245b50103600200002dbd22100000000086c11ef70818505f50e9922a6187baad8ac37cc58b7c93c09b4512cf71228e0b7c23ca1477af0b2504cc382b20ce7a32faab1a482cca5866501f3fccd7c8c308e6f2ede335e2a8e37b1c69f7cfa85a8e68025ce5b11a72f3f4f543c5cd32ce030542414245010148faf30134ca6b2ef9c921f061f2cea9b0bb7991da4eaaec71cf61491b023d25c11d75e1b4ac77b4c1dce1b516b2d124a590a363f06d85a9514936299ad4168819d617247815f199abc1320903c0c5c0450e08dd03eff91e5c3e41556e17b9b89ee8e8017ab476e3786d7c423d4c62345421e19c92841b58f4923e315754ae6b925bbfb6ab16af7ccf4aeb16f9f5aa4e552cedd2869912ce82a5ee64c03fb28ffc13c15a080642414245b50103870100002ebd221000000000f810ff18ceb61eab2a5db9350b8d8c53fdefd220d4ae758c5bdb4a3389760f17d4be8dc087d16bd93fdc0f9536312867db76276de2ef5afb6cafd19819b5c70d33d1f4c2b37d7761328d5de14ab86d48a75083abb08f22d2bafb2edbf1f65e09054241424501018803efdd6709c0bb03afb942b0029463f8e0db3c9055a52a13ac196db8f1c647fbf3b177332f0cd4405231e2e9d48dc97a29868be834b7eb8378f1c6d2c6d2828f936c3ccbcdb552a1312ae4394482c91502499fb22e359fd1f6b4369be291cca2e8e801aa88b667e9ab810caa645c4950def034c351117929d9abb9ecf1942d6ecb2ec718173ffd721d02c561ca4bc39a737f557b5aa98b05db9f3fab6981aeab3928d1080642414245b50103df0200002fbd221000000000cc7428d0d05bf0de801263060a238bed22e877c5a40238bcb3c59ea6f4b9a20d202c5bc65b7f0ade6c414656cb5299c8e12041dc19690ce78bf6e0aacacdda0bcfd5ecbe6727d1efdd3d2b36c1ab2f9626024cde91b5137a2a70ec430399f70805424142450101a68a32d3fac236cafe3b68e1b135ff8308e0757445bc6f6d99703a86b81f050649213f82b6bde41438abfda50566b67c9ea0a8eaf9ff5f0564414c32ff28f68018162d7bca2460006a84f63cc597588f7f4c1bc2da5f7c971ce8940ed1a1b50da6e8e8014166ddba78f7c3727190f39303eecf3d08c4f0167882132f8c9a1a24c4176961d0a8e2338b654eaffaf4c9dd4ff1f9dfe9eebd616cfb3984855c7bc65be01a19080642414245b50101fe02000030bd221000000000d43fa876df5307e01edb85d4cf940374fce62aecbb4c028b61772d7e4e05f2189483573ed3ac76d7c10bf6d3180d48c9d6df4c934e4ffce27e7cd2e1a1802f0633bedb67924be629cf9667c185c11d5adc9f58254cb1ca48e06143130119370f054241424501010a4254dddcb1b45a40765a12a4caa693dd8d09e1d1644b43b8fd76e1cf24214a12de9595e2f3a859883763c4db53ab1420642151b2c664d92cf57a6f637377878e35e51c365f8ee314a23836f02068d0a34bad7f80b265ccb93cf07a02343837aae8e8019c7a7e0b446cce90988d1db50becded6b39cac87ee8f0ce890c06a72e18aa4daa36aaee37e4bdb2c0520f0d8b7f0a0a47af5838175c5aa08044bda6b8dc323f8080642414245b501032703000031bd221000000000c805cbeda19af16ff336be5ce37aed020c65a60b1aadfacb1abf4c038139c35822a08a3d311036fcebe71d508a719180f7f9d7f0fb22d1090cb05dc420ef4205b05f9ef671f15922eb02cbfd47eb1e2b3cb16f09d35e1ee481b4a752cd7f0e0f05424142450101965137ba39e4120cd6587c7fd35f3c93ea900d7328203d615e108034e1679936c62a29d93f8525c409c19f5e8fe49f3131034c175889b826e2ad532494452a8fea3d56a5580cbdcc2dff78f16144d24d36f1d44138ff169bcee9efc4b5ae0e57aee8e801f122df00bfa5812f53dfae6faa45dab4b7538206a513d378a327a09314a467862ab67797f98b5c6a669be44d6dc7e293b814b464d72eb466cbc5253404a5ee75080642414245b50103ce01000032bd221000000000a0aa6b7c75d73c3808e912ec8506c9a479254c56856adce854cf7fe28d1928788c7f1e07da9cb0cd0aecf1d8225ca9cfc2fa92b6bb6c9aa0a5b9a7907574380b95f30d7148d7bea6680dc67489337e126bc272c3d2f86b2ee0e2c5137c21740405424142450101060942a657d044d37b3417e06e5e08abb25abefebf540467bf056118574ea273a381e4280f82861a9976a23dc6c1fbd40400c2e7bec35337847d4fa66b85a08f92be4a3b65d78ec9460e9a4af6e4890075763c2db7bda13e911aeded17e1f7deb2e8e8016f5fd6562fb2908fec3b6c4154c1163344f83a1597e40b37deb9e05f347aa5b0bd1f9f242d80828f12c57b58a581590140baf842967002c75183145939524728080642414245b50103c902000033bd2210000000002c455aa0d0863805e81c76932b38c822d6240c22b68904c7e79e95b362ed6e5855b862520347d8c9b574f1b32bf42030c39ed768c25c57db3c2b580d517bf2020cf7f237298e46865d3ec6ea91c1db652798d06bc586f2d002ecff393075b80e0542414245010146c45aa93b8688c845b95a784f9291886b4a014d04f0d5edc4ede25d0677296c446aa2fc6b8393d5d99e0f270622b2f2a18de46caf89c33e0675914240d907830ea5b8043509a44c9bc79f220ca431e9030d77eec2846cb0adb673895bb53e5eb6e8e80197cc783145a8a9ce326aa7e46ecaf22269138044afe37c680480ec797d04608a13c31db7e324d8ae70c53b8ae12775074ef892195e5aa3866e2e286394645cbe080642414245b50103e701000034bd221000000000128078dfa7ffab8fd5c4e5456df9e52cb655af9ab06aa3ab8406e4c1bee64130a4463c35ffd9a07cccaf7761df120e789ab77a8078974b9e8c71afa51e230f017cecc080448e5bd798b9eb2c97f7b9cb77a264961e283a7181fdef151aaa600005424142450101fa09bb12694c98cac15cd001cda1ca0494aabb520cce30bb71b091b59f0b0136e1bd49d4a220c499985bdd2f10ec615f562ff625df70a648834c1b0198a5488394bbb9454fb05388cbb4630a1df51c6cf3f98162e968d6f1a64ef111c7a4040fbae8e8014435cb563e0dd9341a675f0057294c823b27f3ca16d68f0679ad711b080f8ef6fdc32068218178c437df3e0ef3bdef708b8e49fabf66f22b2d672cf66940b525080642414245b50103be01000035bd22100000000042058a1a1ab26434d283e989c664f8706fb31439b2084ae5287f3246e98f595ca27a9f4f242726f1452442dff58605a01e76279a46ec763746db91651dd55c047bfa2ea57a293e85bf07af7132d68d4fe521c3486bf0d23bc0b683e3d3079d0c05424142450101d6f1cf248ace58819ef65f83fd0afc65073936d7f1099a3c9f92eece85e0ab08a0f77d5ce9907c03cf9256ff9871ff4da176f55162a2fa07131fa6af2996b088873959700359a55683b7e2d9fbf06de8793759842229f3f426c04680bd931ed1bee8e8010e69ff41cc44179055a06749da2f2c69b42258ee8f0517ce2fd9a3027512fc4f32564974304ec61cb37f6a10b50633e0a92a847786aee58eebbbd453e605335e080642414245b50103fd00000036bd2210000000008e0a327ad891773eee914cfbf5e25310cf5b3693cc28f36204c1bc2bf642ee45556481158a507f33818540d4e33404b2ff874e290c5e10e24c46a62d3bb78b03dd74de406afdc2a535957907696dc951ac20fe38bdc3cb19b2b11ecaa79586080542414245010120717aab3bceb5cea6aa893f59acbad5e71359896183cac1db226c41aac328114a57524d567cd48ff53b717864f3d6fa87d05f51189c2841679490986a9e6289fb879ea15d566f3651c39d5eb424167d7aea9b6e6a4d5da9075936fcd9b497dac2e8e801f53000beffcc9d1f117276eced5fe7e35c428b65e88fd15bbf431953fc30c4cdde318a9b6154a4d5f47e313c3ac54a4339219d8351f7b4312bfc4572cb1152c5080642414245b50103bf01000037bd221000000000324dc6ec17142bc7b613abbf1a0492d599180f2e5388eb978811e537a519a23405a9f93018c2ccf092686130e94f11f2ddfba260ba46851d088f8fbecc57d60df41b73cca35d6835dfb3c4444087051db93266d94926748bc247590adf7b450605424142450101d0b255836b4a04c4a75b1b3d82aef22edee8a4fe37a70f18130b2d54be050547ad3a751018f42e60b9b8f7dd5879e27fa89321ff9fd2d8b89a799931926438873e85cc312b5931d38cc44b25230521f00962ed69bad12dc7a1f9a68c811f7a56c6e8e801cd6035e6e52ef77cc9f0a5e6933622bb82b47b01a4ff5ce423432f24ffdd1efbc8402f980017e1ac01372a9a145bc4978cb28649af9789d5da21b26443f74409080642414245b501031b01000038bd221000000000d02e194a9164798de33d2816d12671f9e8fd6a51f06281d28fc1997612a5a07ae72aace178f2063f2c8739a3c8c853be79bfdd1ae1f10ebddae3686edcac9202137fe38b011a65d8cf51c096000666dffc132b7afa303c3ce40568bba546000e054241424501019a58cf640552fcdfa595c0b0f8fc177c8433d32f9a1e29d0a7c85d914f79154dae821c9a4d171400dee79dc25aabc31065985b304ce2368bf752c0eb5577698a817b8f26ebf8e2cf24d8d68b52d5046ad956df880affde7bc7ddada55b25af1acae8e801be3f10aaace5106011a8915e8587df90c32cce5301399bf1e1beee69105a51cda4fae362f74ec6c22bc38c2498975ba2f90aa9453a43f30de7f32fd7f0b79a91080642414245b501032703000039bd2210000000000a32426d1c7bf506d6c575e26d6e84c19204d432ec888a031f3a27b047f6b31617cf6f7e94b4056499a8c0a87bf922f1c5da7199b6023b188f3cc33489a1a1098bc641d520a4c982752bb93d8165c6512562fca45ec0df46d00022077b53480805424142450101a6a25ba6014da50d66770567d87836a7cdfb107b8fc4b81ddc6c7503ac7596080d140ff066667959c32a559a84408b6a12614b4a87d5410c43d8305ff782cf85f69624ed8289685b4e43a891615ac67a0a9dfa784942fee77dfebc000c35ab60cee8e801f7d91f29e3adfce3603fa6358e63d03620fbb97115747313a1bf4cfda158bb468ef3b64de216c381274b943527e8bc8ef0f1e505442469e0d2ce8dcf840899c6080642414245b50103580300003abd221000000000541207ae0d6f9ee9f5d196cc3eda5c2e7140a8888f600be493cbc4e87eb7091a20cae2f5f8f494865d45cf7b8c57344e5269bccccb7f926ff98776472fc113040510091dba3b4ff4d0a5178d6d126b69717ccaa13f83244b5f8d78d6edbb250605424142450101147063cb3e24cc31e6ae2f32128f77813389b305afdf26bedfabd685c03db545cb17d04546a84871c9703f80670096c563888044b11e74ff3350f06c377f7c82b7e6688c3a1df567be3c4c365ce4c99076d78164970cdca4b3b01f5d135a796dd2e8e80172c1398079ec4a545d1a3b50ff7a0838ec66b456cad27fc9899ca61caae2c88195bc3d2d1a3333e89de896908f489da37722d1ab73af41c04c02df7e3fdd5ac5080642414245b50103b20200003bbd2210000000001261db0b2fc39a269a3322fd6226ed44fdca2914b42744b40468948a93efd77384a2b726450ebe6db121569b0f4a5ebb1009db28f49187ac264437cd2fe6ba06c084ffd297aa3fe62da8c2fd7d6d335395c24e20a7003d92270fe77175c4e704054241424501010eb1205371955a222edff1eded6957101d41a233bb63ce2d2fe61e813bfb0b12e0e40b684acfaf16517a5d8e4d8f78658c7e7becf2f0e84924f384369d13ab88df53ea7d26febc25f182356498f6fce0f959b2a2e9aea68c64fab1111769e4dad6e8e80111209f4a2f0931a3a625dc68a3b79106dcf7fccf5de80ac5d8145c0bb907eadce6781772c47cbff279f3d674fbcfcf5b27623d5089727c7679e92a1759dc164b080642414245b501036c0100003cbd221000000000ec5ad0c895181ff85febbbaf1e59a4116725e13d39a25787be5edc22f0d86d473d2d59d03537ff8bf95464ad1e0803fed6c1c049dae2eaff92d289c31176d2014b58a16d3a5c4a073097cf6bb6e42841710ffab47b26a61cfa060a2861a6cc04054241424501018af4fe6ec3a15474461df9e5b0ec63d381e364bb4fe8a9af7c41cff72b5a735267c6591b1b5519833f4ead7cdbe018fb3cb9c4e0cccaa51376517a75db7ada8ee7dc66bb9a1177230decb9d2b00f1e01d7ee208d4081744cfe4dc0ca3b3149d4dae8e8012990b72228673a3383bb372a46d9cb40e89dcf2011514e372586f6a0cfc4feed9aad0a7a70aa8829d415fd2165b7812641d1cdfe0228f96481338f313afde991080642414245b50103190100003dbd2210000000004c0221a0ad2c614a86d5ce2738fb052b360bdecd02b95ceba2faef6617f8e838ac9782dd07bbd1229ab018a40049f7f7038b9a6fd1ee5bbe148e2210c07a6c03693a1c5fdc18f9cd8e9ea8b16b42f421bcb31308128c660403f0d250cecaa5060542414245010194875db914d4589d935a5a13fb1757bc29445506778adcba9588a329cce81d57f7fc7d31140ba2fe399df5eb0dd472684613c727bbd76c01fb01aeee01ca8186f8232d7aab093325d5fdbe683aff89e5694e8a38926c7219d9b9165b500957d8dee8e8019cdd397ac160f0dd3947d0838a4b75a0d40148f41f854041f0e5cc669c2394311e47c1691bb3cafe9c3f2a7506889a4740e61fa5969b63123353b277a84ddb75080642414245b50103f60000003ebd22100000000084f298a1fc2799058c34bd9e9844c6e748e2d3b8c02276b4d990c652bb6a4018dc275f8dbd253aa8ca50e2eb2319e945af8ddd2d8914518b1c15549646939c05b581bd78852661b207a713cc92e98a3c6f743cc612bc7b9c6112bbb903f3ac0105424142450101884dd7c8807b46fce5e0eb882e6e906a7d95892f47847c955e12d8bbdbbac7347a0f899c1930598262eda3583b92cd9d3672f03196e3f80fad73eff355f00084fb2ca3af860d9b198e185f5fe09b7251a938ca37727a4a2c2feb5bf359f0773ce2e8e801a586ba12240ec240f1e81b415a610205a9e623684c334c5318cf2a69a51bf7ae841f4bf83d2ee62ff579d8da28abae91a2848d8a22fee6204ce49f23e51925fb080642414245b50103270100003fbd221000000000220c4b8da3093bf2f56cf4ef6c9e968c6410fe17729e3cd9e046a0c00a1e577a0b065b3b5873ebee35ba589de2d5589d794809ff9bcfb68e136248986a50fe0f63a0bbc253a755f21fe4faa5c59dcbbd9b9aa7f6d1819058bf52dd310629940a05424142450101f8688a687595d786f96ba8160a9be8c2a0c631f5b1d7610605f28725ad5d3937879bc5b35128ce3da821cb23fdd09982f526e7758f2f7f06931363c1146a8e83800f8f393c336730d5e53d25546caff23788a193fd1d983f255e667757b17e21e6e8e80151343427c1321a6a6cb7ccfe3d2030569c28cb96aafb821c6f21398da01b318ba3ce4219d34802fe5fb125650a4495cc0be0602edfa77f346282eff22a3bb178080642414245b50103b000000040bd2210000000000eba4c1c82575362428b1e3246f4a99a4a006c67fd7c61cdb295704dc1f2466f5e9e058cc6a50ef0d92407ecf64c91c0fc93f8db1fa412743e29c1ee4b5b860499b5a18bd38b16c0004bf9876bbec8517ea56869d9baf65db6eb606cc8147f0805424142450101cae28c2385fc2254be44a8466cd950baa37e36f96f3b8c89eec01144c9f6a40b12a475b44e5ee088c3669536f71e08c4204f763485b1a99ebd9499b52ce34b8b3fc48cd1f67bcef673e706989e4ba4fce443e022bc4d3a01a6ae7c17a5afe3f7eae8e8016078a254de0e05df4df5fd84509b27eded2b24002938cfd62fdd1bb619bf262647f4dc2f1225eb6bb455fcedfa0d5e0ef96400d28ac0406c674fc7cd1ba6f6f5080642414245b501035901000041bd2210000000007862774f3c5b8a7c3ded4199e3b07993fa839da79d651dcdd725d9c57170993575512af814fad279dae64b7a0fa87d9478b0046a47b76d6daa2ec2bd99c4d00c4f477d9fad8abb89377df72676aae897f51c692f5e6bf1b5a5e851d85ab2bb04054241424501014a51e4872aea09579611738c70103072a8a04522f7c462a4495b41d4f8f346682a7fa913958d7687bda424a3e06dfeaded14fae7974dad100048e09b2443dd81484f7b0be28b1be8c15c040b5e87a651ea7b40786f872f66f7e43a9390bde63ceee8e80177a16b1311ca7612ece41eccfeb12c49feccbcfe282b0a5e779e3338d48d4a4682a1f7b8ec0ff5d2e9a842cf6fe0a0ddb134bbabfc729522186273e9f2f69526080642414245b50103ad00000042bd221000000000a664f6c6676257d8dd7073b6f25fb364ffbb81f319b099bad2d0944b9346f918339f319a98b299e5dd7e456d121bad75bc0247425601ffec7fda86a64b3abb0c964220174721a33682df82eab9dfb309630fe5c8e25ec2ec07538295692687060542414245010170a3347df13d9547a220bb36ffe2f7e1c179fe1b21e2c62c1c8bbc6639ee787c693f00cf74cd075be146e40dbe26f3bd4b8e5f5a9a65c6a7e88474db9b2a318b8d348d3dd053aa9ba0ed0f273ae40fe7c45fe892528b51d1b424959290ffcaddf2e8e8015cf8880886e26d4c0c3346b7405dd0f40d5901564b0e349e4c3cde4de74410332e5a051b98cd2ac514253527c4fdd8fd73d3ad8ffb2ef31cdbd06efb82bd9ab6080642414245b501038d01000043bd221000000000d6ea38cc87858577426361b2566a4a1051b9e1f4765f12d4dc547e11f435912f7af9159e0c2a5ed767ec17ab815c2a9dd4f587107a6ba3fe0b127b856da93607b6b0ba7f23f605026f77bb8a972582a1590290b907090ec81e8188ff93b4dd0405424142450101a010f36bd5cae20707f6f80e9966a6d0a15aaa6927155201a414a453aa4bcb702e85508c3e3f4311dae3020504819fc14aa7acd99904a0e843a02b249a1cef8adf2872024b56dc204f69f5547765b52818a1be82618ff2b248d119c8934cc7a2f6e8e8015a8f265199e5c35a7c2bcdba7eccc29d470bbb45c51c2f64b8f40e3ff76b0f94e9f08cc647b58dd07160d98c47c7831188f97a27b8444c84f9cd92e73e9fb6c2080642414245b50103bb00000044bd22100000000010c9ee948f061a6045020999bb5722b7cf9e35ada194ea42d44632ec8e1a0a536b7796d2465cb4c308a1674345986818f0ffe207b04a0fbd0d6888ee53c32306236a1f8c92d7a489a8a4fdd7aaa3f342161bdc264da68209e777a8107e78a20805424142450101745eb8ea7147b41264ee9d3b8359f327eeae96eab8ebc5ba331fc47805c13d76eb80fcf5948bd5ff66e068ffeaa5434368f338b43bab7bc1528532408219e38cf6205b8c59abcb0a7ac585b8862566236655d4fe3485fa24a0df988f54f2b97efae8e801c62b8aa8aaf9890ec5dee9eaf465f212846862815565d945fc284c4eb4485360188c849e6c9883ec7f81429833c200c160b8cf9b2132af7fea5c642ea5ff3e4b080642414245b50103dd02000045bd221000000000946b7616c6fe7bf3f2832a2a355242af5fa07558e58a37f89292d10a674de4404b3c3739d6fc7754dd9b7528e75daf7944266bbcb7515b2752f1e1a9a3a5940cf190145b56700f5334c935af6dce659672fbab33df06c60b85529eaeaf34440a05424142450101dcfdbde70277430ca404823d6b6e34d24050a8b529c37a2b781e9ec79f6e8577635aa248d4c5dad782a351ac97182154cf260edd35022f3b89629e9414523083dcfbf57e126ea3f02121c352f4bd8234b22e3c393a32e2fb61813a85f80439abfee8e801e2ae0d5a65b098ba21a19f4f562bf61379ebefab4f8b7dccae7cb5f17be500ebfb9948dcadb408ac85ca7698767ae4cd4e0736c2d6cc9648b82b54f7c37cebad080642414245b501016901000046bd2210000000007e2bc652eab1dfdf288dd5470ee38b52b9c788c627ca4efaeb4564ff767a5e0bb918d4231901594d19ba69a8c7eebad330d71442323f815a4a626bea739eb208c85a8a3289ddd97b8cdd316523a2190b8f47d79ceeb9e3730b783443553f730a05424142450101d2f42da32a6fedb2068bf17b5f0448795804e8f4630a96bb5a623499a9227e43b4b27c83c9d4f188404597a9689ab0c5c35559224bc8ee0d69560fb2d1743a840c2067d15a8bbab0ba5b6c6d24b728d469f01f19aaa5c2afbacc1049650d7a3c02e9e8017081981cbe41d960a5fc17f64b6b9ca79fc227875fdfce98a686f28800efbb2a1f2853ff29a66e31dfbf072bb523bdcff1df26b56723b761a86de64410506177080642414245b50103d002000047bd221000000000f20caf82ea83053f6f6fb8749213b758f20841c6c0d573d1f759c37062242454b68149e7a9d212f66fc3b03daa4dbf5f550db477f0cbc5587b2ba91740e29c0dc17161b210dc0933153426a26041e7a038f1e9bd6af7014efb5fd0c79f150c0f05424142450101b08e0c93921251737b9f2e81131a411245d133218414e2ee498855b6f8b93a006c0ac441cd92688480dc1fbe27d0d9b04190e24836ad78f7adeef6aaf4392b8a27eb765ee4d1372b16403256096114666aa29ed18ee745c83195154fe78d7f5f06e9e801bbe752e17517b24e144f8a0e5274ef7d3ce7840a927075afcda029be0e9524939fd92d4a87701f7068ec11b7e775c5ccacb9c4591bb0777c19031a7908295286080642414245b501016703000048bd22100000000060b044beeab458def75a82bfbbabe613e0a6efd41e9b6d4f0daafe140a0bbb1b378464676eb98712654a87a0f9ca980c719a0d09458c85d56f37846afec51e04a8c9a29b6117db22f547b6e7667fccc237ffd9d180c4ea5ead97ad7d6a8448000542414245010104a3d0c0d25597f5f98a6ccc20e0696705315bfd8d6404cb31e393c85e473e40d60fade4de5eca69665c6c2053ce079b17b1dbc71889f69625950392fa3547828baad5d62574a5cc15e1bbf8a5bcea103c108bc7bef78e8c0abacb498920efb70ae9e801848c2ce759d54bd8b625106c8866e137eb8f7e9bb601f39bb9d44f721aeb173627b29623ec4723645d89312553473efa6cb256022b5432b40cf80342792790b5080642414245b50103a201000049bd2210000000007cb357d8a02f7538ebfeb60456be5ac0f2793982d0ad6298ae993f3dfc137d33f97e950dfb1bda3ab12036a1eba0f44529b4b0963a6d12bf74139b3b7511d70d71a1337fab79b8a4203e9825d128ca9e36c8587bd42eaaa061e1f93e6d4f540f054241424501014266ea8956c1206132bf46b0a4a4c5978dfd7c5fd5ae13e0a48e48f28468d74c8bf752845b9cc41296a221d394d7a6000684d64e39da9c03453bbdc82c916e89aa8db2121d8dfd64bdc4f0685aaf7c43580e5df575ed380c78eb6733e458cc2d0ee9e801b1ae4c189ea001580bef5ce85ddd43b364916cda96fa709ec273ebfde07392988433ef0639b2309eb72e63ab48b0b71861b392f962636bcf5fa8d1f9812c02c1080642414245b501037f0300004abd221000000000348ed9e3162d18628ffe9280ad7c2f5fbf099e49a55853bfe16b87c83bcd103da15752ecfef908e799fab4748c842f84514ea3fa441d57fe3f564d7a7329d30117352bae11928d02183415fd478addced63da6d3b1d71d44b68335f228719b0f054241424501012884742a496d2cc040cff0ea2498dbc61ad867bcd404feb10341f25f88ad6239a46fe34426fddb5fbbf9f5726e87bae1e061006e2cac4fa6e758a8b643bf308751e1d1359b3942ac678d1be3492fed5ee63a013cb85d2cb062fa842c85f1eaff12e9e80172d3a95cd5a01a3bf51ee9db598bc53c7f3fca3771b7555b5416f2042a20f6a6572c514ac42bb75e3156b5bca12e9d4a1e4a2b55db63c399022707103d9dd45a080642414245b50101580300004bbd221000000000660d518db48be2a6f43e7a6d804ea1ddfd839b704f21ad246afdac1111f10f5e86dbb1d4c1631eda9de03b348c3a9d949febcbdcb94b28d73c653a09f28df302b7cfe5e344d811d8f72982b30276187274073d16d1927d5cfe227b62a6748d0405424142450101080d721083dbf5b9e9f853e6ade9e97c956435211241fca51154230f3c2d910cacede3f6506ad78b3f117649aff9f0900d0a5e2ec267ba5653362f506c7ff587f12ebd6797225b0e24da924bf6cdc5070690a2e3a95a7834c4952b3d5002eb6516e9e8015552ed4861e40febf0730e7d30d87046be05e3bbd7e465a37eecac2e9b52521a1226a2defaa644a9f5e10b3f8983638b797a2b5407b7b37bd5959fe8af79897a080642414245b50103140100004cbd221000000000baf37d76deeee538176514fe19471ed133119f8437244d804b1aca69cbfd9d197f4dff4c99ed10fd07b8e645fc4829f8ff62433215671be98f7c92db304650055d96957dcc7a332e452f24604fe45004e031423e20a1c778fb6b7f9c91a0d20e05424142450101a28ff64ca8e4b2700ab89225a81f9b14f8c5701f7dc2074ce51117f94b1edd61dc52a4da504bbe0b1b2f0aae4871ecc2a5788b69b9f50d8dfd55858d37c48a8785b99508271d5d25feda7afd79237d1ab818e56119aa4e163856f6b152ba7cc51ae9e801c92b24b5d8aab436890cae998e0c3eaa77d0541f29f30a799fff04e1a034d06b06777cfd229a885a0414c222aef44ddcaa729885096335448d684d1d8e6ff447080642414245b50103b20000004dbd221000000000b6022a3fdb5183d72675edb7a09138b907d3673a11bc7bad641bfaf4b53f857c460e456cc31bd555acc9900a41dc5d8477856baae9871a0e7100b6d8be700a0954a1e99e5aa2ba2afc8cd9d0b5cea0be8143b4539625e7abacae7479d4423501054241424501019e24442728713df6867649b1afcce7f373a26cc5ac881668651f206f9fa8640fbc1a5ab24cc5e86183c1532c1e9897d1bdae62d62ea381e8e3687aeacac48b8003122249d432af858bfcdeda2bc7db6f1ee83e2dd048f647fcecf2565e36cdf71ee9e801d285673a5383f68f98f8a8c8040e481179243d5bc53aba0c3e93565f18d8de3ce2162c018b129fdd493e8352a753628331ee5a162ae1665e066fa6b3eafb1a63080642414245b50101ac0100004ebd221000000000c688ca5d99df3583052e7c540e353771438cba6ef3ab97320830515445c223106f4d90467c7bb6b28e4897b20a77a07ad06e7e62662323ce7155bd7cc14a5504a89038cf06e806fd6467bc73865b7dacaa45941380d5c8c03b81b70bb12f2e0b0542414245010150dee46398bfe46611964697a865d004e90bb48c09e1dc7435693f5b8653480c4ad4886e8025087ce1789871b54f3bda0bd542d12ac5616e5da10f912bd88c800a45ac22b4ec5d68a18b17c2f99e99cf668854d8a2d0dd81bb2c3a839765833622e9e801cec510c2c7731016fd2745d05b55814aa792583fe854ffc6a1dc347e01378717a39216f02449a49a37c2860042b57b9ad3261d84833b6c75896cbcf111f41338080642414245b50103010300004fbd221000000000e294cb745a751651ab8361062322305add930dc3691a96d7b2ef39a6ee8c1957fc6afbdb9da653766074ae553ca3dfec67f0831a67e480db58a2a29e7fd9f2027d43f6a5ddf60f71c9bf64e74c36f11d1142f42b4e56941a3f791e60de38bb0e054241424501016ae784cf9e9494f837fe399663e6eb34515d57be087e7c87b610ce634e8c335c9373631b428f1e8bb6248e815d1bbe9353405fb5144e6b9ab28a0fbe91f45589f352e04fb91d3bc7bea2080d6f6c2110207d349fd701514c91b150511202961226e9e80185e3ff73689a4d44322839c38de86781c6c7090447b0b61e2a55164de7f7a2061a6cc488b5cecc3f39732c18333908a2fb6cca23ffa59e21c361e5e983e68b4b080642414245b501030a00000050bd2210000000002e5f80e329fb53a4dadc1fb9a0cb4f9cfb628ef0ccb379dac730fed61484ce2c2a0436754edd847bd045ff38f052c4b131267bae2b4ab73db3ee116cddaa12026710b8eaea790a1fd2e9a44ed0d0561a34cdcf464e75935aa0ecc4fc6d15f308054241424501015601b4d9f26a46576d4cb4971d0a1d36d45e5c765688917c2cce57a73e349e3dae9ea3d6aada34f2460eb46acbd644cc0bd57046fe40252276791d6b011d46843b3a91b3a573be5524d437c6a72646c4abc6e81744e1db90bd45df76f28639462ae9e801cdb87f9eb5335b9540accf6ca586b33c5924118d925360e2bde0ab5f2e9c198fa23684eb9f9b1a593c33b1f5cc5c495e791db7e0fc42c6c5ca1f231ae43f94ef080642414245b501036e00000051bd22100000000000875d7fba4a4d795c0c7d973b4b149acb17988f8b1e2adcd48fdaadd345c704499e8d810c53a4467b98982d3dd93d3f9bcc1487f91173a1754c753c2c76290fa5f7ad3a8e40c627dc6372cdfd8871432dab3968dfef335859dd10b4d848f8070542414245010162c4c5142b5f5c14d37e43f8dfef91d735ee547a459b1b1141a0febd94507d30b1e35f49c1299b47356a7ee8e94b6a56fb01c774e92b689e86fae6635670bc8e2f5e67c81e55ff9a7253015dbaa28656ea8f2777602103b489b6e38e9f7e5c0e2ee9e8013bc02a0d6d20d5e7c19e918230c0210a527fe77eca78c754c4505c31ee8e288552650a980cb3ffe42ef2d86db83a9c829bced56ee0526f278e427d4fca94a1b2080642414245b501012903000052bd221000000000fca4c044845ee3fb7bd5d0df735f03677134ad221b9c0fc98e87ac8de4b2683132c0beb11d3823a5277d094c946eccde4cdb29173b01fb2bcf060ad971ae5103f2dd046c0fc524a6e838e4824c45781ab15f1e77cce8302b2df5031d9e1bfb06054241424501015ef871ac4bcfa4122215e8a67f0f7892074548c8d797c4e834be318111bac8274f37f4032877e730f4e23f4d9154b93efb65ab959392c5d1955c96686c820486efd117a0b70df73eecc07a2cc86d145a3b3ecd02729a7ceae2a799453644e18f32e9e801072d3b2f283c7f7436cb8294a31198c963ce31acabc8e0ad2c6f03174a6355f45b5e96e25c6c750e9c2cde9ac5102884f1f0c88fe3b349e9f2b9d3e7dda53b8b080642414245b501033602000053bd221000000000e0797bb52e23b495a930aecdfd1debb97f5bc44f66c175bbdc2c2f3b390b9058d8be9409df84d290b083f024298d6bcae03d85bd96a7d29fc6e61a927bde4100bc5d727799735af3eba74cba610e6c73f73bd4c53237e362a8f9586a4dad3a0a05424142450101825985782a01339b8350b47fa8d6d72617ae594d4485a9a1d84dadab1a6e9f64a10e9cec93a490984fee41b9ca749acb93096fd3e98d9eeeb8ff59d04e15f08d1bf319bbcf008c4442ed2fa1fc570be53722db8a1b222920ed4916267d55ba7f36e9e801e079a8e74da6cea394e53e3a446c8d2c1cf82e8b57ca5addb3d3d1e4a8903627899d95d1fc2d192082a214e362ce40a750e103d3bf0789159f5eeda6f664e93f080642414245b501011402000054bd22100000000070f83f411fc84d25db6e563e74b6a715f5b246f03a0569ec62cd0e4f840cc71603e71f0574529c7a3f159bfa1c80a88adf3d66f2db63f50df6874fa4996bf2078974ffd7fe8f8dc411b900d6eebfa45668677808a9854ad6cbd2db9429414404054241424501010213ea52861b2f169a03952d43f5bed515402fa39d292f081d4c2946f6ac123029a4c870baa07e00463d12e0e404d39eb3cbd55e6c5ab859f5c061ce58903d83d768adda422570f91e544f3126a261a93cfc6e62045858994f6a1d87d33d80c83ae9e801e2024160886a7100f18fac5682d5bb4503d80ec9c0ab8557f48fa138b3b12dcab96ce14fb3289996ab3203039d061f1153a91554a527c216fa07830899edcd67080642414245b501039501000055bd221000000000d0896ba559a9afaa93f48f31ef39d230c39cd192054803aa22b64dd7eeaa71435209d12bddaee6d6675d9c89c2f5eeb72abfafb51f29139ccf883bff5b8aca0610964a3c6b1b0098dfc84473cb24af485167765e6e5619c4e1cf9d29043b0b0f05424142450101583779ad016b2f91447afaeed4c2c723172f6bbcc07fc45523145a6828f1480565f9891c4e106ebce3d98c96915bae8aba42c1bbb151778c886e97775551ed8649c881dfa0a6026246f63482686681f3ea09c2e487d34b8a6c70d12beee211b13ee9e801bd766c019a090926149ad3d058c9c90cf0a27d24719816ef395cc96cbe036bc24db1e93955646d9112f8909a040cf27190d4da1de88e6643ca4e70acbacbe769080642414245b501032100000056bd22100000000016bb52b9886b2a91ed111710d5c991f03a89c61d0deb787a9291aedf7e351152b09397041bf80c19d9e9ea6827ac9ad283888cfc02239a340ad8d60591694408660f56cc423e62de42a98c39b419a53da262adc0523c8587de91bf25b3e84905054241424501013ab24967849b5c565b81fd153acb95689ff18880e70e2be0ec094340941d505ca4e1befd58b9fd9456f27241bdc4d5cb8ef6b2165ce23403378f131a5676b381d5b874b454ae2edb22ae99a92c05e9b4668789b2e64e1841dff310f0da5edba642e9e801616729ae8a0d66ffc1673c48f2db3b90445eacafddcd69fe16ebb3c32482ff83219dc92a2e8c07814aeeb2a2c6b9932db1d334c72bea7f8ed2be1791398ba749080642414245b50103bd01000057bd221000000000a0daae3f78d505388ec7b52af466b33fcba370602ca0b957b9eec623c9f76b5d0e8625be58cb9a98cd32c48317e70d6d275efb30424a3fe6f4c3b158d1f6cf0ab3118a991dedd70a3d604f0fb906a53fdc0fec68f089b2f268f3adbfa3d0d80c05424142450101bc30079119287736e057c43b2d2c759aace4609cef9ed3176ec014c8035a657e09f6fcd87cb9a31b0e6793b08ca388e29f09e126d5e3ba12c78a0a008aed0b836dbf2e9d08d4adc4b46f48ddb5c66ae01652cb77b184d37650e9fb6fc9ac815d46e9e801b0f44d0ef833921172cc1d16e473e43acad3a6f0e46c2ef8d943eaf463d2704af873fdcda9aca9badb3b2c26deb42374cfb6c70a3931be68988a9bc507ca6a7b080642414245b50103cc01000058bd22100000000018675bd88c5325a2228ad5262922b80f9df7cb969596657739609272c3df71749bcb7f58d3e2dbb95e2da8262b18922afefe4466bba7e34ed2ed125c069cd301c18cb234986a4459aafc7887ec4cf8857e8867bfd6cab4b563ef34ac232ad104054241424501016e2ac7689d0be2a297c73d51f817cf1ebf62daadc8257acf284408e48f73b4692533ccbe04b88c861b195af881a52d46f861301a422723453f75e90ffb138f837b831a95544852a11c30e1fc3c2793c447e675301cf0398590183235936c3df04ae9e801db1ce853d95754b3348145c2ab0b2be87a9f4624754931811667367252eadc333689a62cde7cac72f78797fabdea3938d48a1c7e72c44f3d4412c19d4b034d95080642414245b501038d01000059bd221000000000606bcd9fd63485c8c0ebdd2b3704ba4ea5fab11833d26089bcae2c1600bbc2307af89a58fd4fd14b0130dbf29ca1012354935ca930c71c3a7a197e1008ffdc00c74a3fd7b90b9176abf957d4deabcb08397d97af3eda11f02c34763011302d0e05424142450101c21f057dfa6500da7401329d5dcd74dcb32ee2950bb8a15a2614394e93e86802821c741f4c998f5302353b820f2207d76da4a07daae4e2be0792a6a2511d0287e756da1e7cce102bd78dc65f4a00b875903387e9c89c3af6528152449f641eb54ee9e80195740938837dd3f2c2bc41004cd09018bf1eec18b422ac91174f1870171fe4a5d2f079d71c72499b38e2710cc89c6272fd4968424c08d8eeb9c1f36e32cd4a61080642414245b501033d0200005abd2210000000007a0c9006a56c6f12fa7c4170b86f1abc08a02eb2922b00387dca717ee4304d7ab7bd1e3107dfbdb81d5dc620e76cf837e4a91d7e2447b22b8570540de127b70938a6e812ae40c575092b444288b390eb7aab083e1e306758482767b9fc1b7b0b0542414245010100243239c285e39bd0341afa5b19f652f8f3137266988a60036cbb16bbdd3b57e983153082cecd80248ed877271f5da6a7f2ab6fe253b3a7d9226a9f3c7d90804863b335775722d83967cdb1d0704b1c8e44581d0e67864ab685f46f8529610b52e9e801a70eb73a65b5bf35b3c291bfaccaae2663200067f1723dae4b7a327f4af7d69a8e1d50ea4052c578e477b421e51561d616f2e295225f32c230259a6f77d6d12d080642414245b50103810100005bbd221000000000804ae78f034f3d7e3f683803339fa66c6b2151e180c92eacc346e808a2271971c843f0d6f0128f9d4d0186a392695285a6cd5b9a6ab38bb8f13ca863b7228d067a31ce1552d7a31ec49fe51998d29f6530d8694d1e6b4dbd14dbb0b3eae2600005424142450101bcb3ad8963d937aa3cfc4cfa1bfcffbdee3fb628876cc1b8d255396d0fe694696b8771b43bef465d894064bea75062f1947f79120e1e784a6ddf725d081fab82c583b8e1735953d71ee396ea09f6ffb59e84d394c0e7dd985aaf38b98a97a40c56e9e801d6149c988d34000f75ba1e32d35004023d913b11111211787f038964ec86d1358974634d5d174dfa8e4331fddf732c2c59815c1e6ff6003c9686dcf4cf04b96a080642414245b501010c0300005cbd221000000000f23b6e15682e9a5e8fb420f8294fd8363447ae1a602e6fe319cb03c8a2e231355a8e9386cc02da3066e5cbd035428e725d66fd193b5f07c4f4ee0fa7a5dcf8046c2ffc3524d3b0f3a3d0f454aabad6b3d3e3f94a397b522bf0c4c6c25c718d0205424142450101bcc7ae4905877e0e754583a6c8a7983dac28198db71e58ab5ca8a353757da14fb3ebf5fe9e28d6787450a811d3dc341c1454c2f93037c3bdce53795a0c3f728626cf88d6fd84f0ad3dde91fe4f5601bc369cfdc14a45e929ee08e112ba069a115ae9e801573049f0b61f578b5015901bfb9fd9a1e3093d74617cd72d0d5c0b926acc3f4b390592d05916552a65e34dd1c5e920eefb73b33ebb0484f86cffea0e4986d93d080642414245b50103830200005dbd2210000000005e433d52086bf1f26df031bd633e0e9bceaeabc6f2f90c554db51bde4af08124f7d6b3374f54fb0ac6d503853fa0ee81ace319691efea8cd1fee641582cab309813dc9c993c2ec4e68523d5dad45114c88df7ee76559b95d590663acc864ce0b05424142450101f4e41318144e504707268e8eaf86d2c882542bc4c33168252639e502a7a1c00a8926f2151562f185c63a0979d42501a1a550ddacd664568a0370c1b1be81ef833ad7af6b1dd959046f4c410af5c6673276655a760c55167233ad92fed1ac6a3b5ee9e801fe30765cb8ab1f214e12a51f94d3c629ae5743afaf58d94090a33c99c5cfc8ab5b79c595fb0fe4f51e747bce55e1f6352e7bb7fe01e3e9b1ea515234aa8ca65d080642414245b50103610200005ebd221000000000a0aa784d2c2410735f9c0663c7e194059240da70025d27ed16cfa5274f199e24af6611ace592a9425eb53abbdc890e59838bc6526c87eb371a577ad311c71c0bd318114b3faccea89a5d42b028b9c7a280523fcec64e739a09077b7a3742b108054241424501014cd1b85bf78b46b5619bdf006f3bba4fef91e27d86537d2ca8519ffd0947f63519a43c5c81f22902f6d91aeb5537370582464d6bcec257cba30bdcd23ee7df8b763cc267133e37349d41d208400ebf5851ce70f2c076ba48b16d4246f98e0f0062e9e801f54a7a9a390997f7335c5a59178149e7f544646f6f9afa2ae69a9ec1d543596c66b7bfe80915ad5d01106f6f3c3b90f1c85c3da57c1e7dd843431bbaa0ebfc9e080642414245b501031f0200005fbd22100000000018b25f2519764598239baaf03442021247f64682523b19dfa8c7ca8178ee5e09f1f1a1f57e549ff3a841418f438283cdc004ba813084069b037c032a8d72110cca22a97c1819bf1ff6868e16b10cf8e16a040b4028098dce01550034e0149008054241424501017ead12c26bac307d470f6347e26413671bb886528067f31fbcf4c0473b4c6e541c63211a39bf28e02a20f6818aaa78b9bc7aa725f948ade32c698deb4421d884fc0701b257ab1d3c5fd64e5289f3892d684ce9bb736b822a8d7bac721a345c0a66e9e801101d1c61f3dc49ee8087dbd0d8e2d1ec9e8b467b17cf71026d469ac287471e5ae43d66a97620558f0f2f07bc047c8e324183cf27851d5bb771d39c799525e5b3080642414245b501030201000060bd221000000000b81409db182e3eb303257fffc2d5fb7fcb3cb130e259faeb57b59992d9b78d69000404aa4df20fd7a92b96da589cbd73394d569e23d12935c0be4ece9c800a013638cb588677423bc1a01ddf67e9d6fce1146f07960cb95113b7afba85e5ad0405424142450101f0bb78a90f280b5738aeacc0f9b86295ad46ea7af0db0766947feb57be379332d28b8fcd9bbe862d1d675185126da40d7938e43a2a1b8cbabb468f8643ab8a819d5b06ecc1c845d08cf0e8fb6f284891cfc043240f3c496d546adaad024439606ae9e8017b55cff2be8fb651fc2e60948b77bdd212255f592fbd4f81830bed6460118ab554fe44e27116ab7290282754a5245276f34c9801341447f72a4c2d68bfb0d71f080642414245b501031002000061bd221000000000eac56bf1aa4b9982b20aaf338d71944c7a02fa75e3651a40b0584a687bf94c38265dcb3dddba6b12489b140ca667d0e94a92db1cc1db8709e619440c2b9b6801b223af8766e880be3fbf697d624a2892f3e9a65fba993b57faaf8c3ef7257804054241424501014e7e1de6961ff6e209f470bb25a7a3431c335f3bad140e52085cb1cf5cff801536c7c6458b7159a5b3ea61da0f320b636dd50055400557bd8c366b0d5ba825803920f6bc94279fb910e08f2fe0676a014c98d77c76746df4e5e01c5da68a4b306ee9e8013c1c75f705b6261f2e19ce7464436451abff5904ac545aa2b50d90f67361b60cea91464301613745291a878ecf504768a43d88199694bf5c4bf69a441c74e5ea080642414245b50103fe00000062bd221000000000407398208fcb01a6f042e52942abb78965d5b4ed67a93b28e339bd6b2a8f153be8a1391efd5c4e8948278868cbbabc6319b1217bd41fe7b65a19ea855a45a50b9f5d0a0be62b6b79b2f1f7a2a959390af9a6f574c67ce5933d526e660cf2600e054241424501017453a318bf33f238ffbb38cad1128121b6d00fd0a00c3ac8629476b316fb0472c7c7c0049276cbd83bbc51de73e57683f1e64c982a04bd6d8072cac1f52a998647bd03f7e57360cef3cbcc892c29a48d4aa7a1f89479ff90050a5848d2a76e1772e9e80126b8093aaa1fb9709df32507e1fd9f1269b6c4ccd62a3fa4b27a85eee48ced8254e4e426a7f184b85d98182ae634305fb5d4dfe56e37671c0aad22e299f79bda080642414245b501034902000063bd221000000000cabbbf59c96fca6ee12c82d9a7124d0e0f613c493e5e45b4318d1e721ddbb46a707e34add2427bc807d7f3948efefdc4db364fa89e27371bb089c30718d402029047f511b1411d8cf51ebe8233cbba95a7d0a3a4f09cdab3e53925348b33930605424142450101b8cf46e71c6bbd8af411b29b1b7a087f8d03d818854419f5349ffec036a6e87ab9662be8e46e6618429a373f2e78dd7ea2b3e9fc1083e15e192d2cfce8d98687a803dbba6c684161f881bc941e107b4c55cebc818ec4d66590a4a1da4d06cfc876e9e80113c63e5008f171667968916eaac2924b9b94b091cf5fdfcc49500086ddf67329fd4bdbbbfce987b91c62b944071b1ab409801a0c824bcc0395db9a45efe24956080642414245b50103c002000064bd221000000000defe57c8dc35cd70623716ae1ea2eb9a117e55a139518215c06dee7c2a7b6d22b058a83ff2d630f08a8bdd9abcf32a6637a0d4cde42bffa0db5615957d1037055a80b0e7dfd01fd1d1444bd7f0b9bf4ac6d78c689b482b123955091c4d2dbc05054241424501016678138edbfa17cfbf9369eefd19a5516cef0c227c67b8dd60128c56599d004085ae783fb460364038460fd7dfb2cbc2f3f582d8000e7e61feabd552ad39e68d3db959293a796500b63e5c465274644ad946a9daf570ae37d3c6ad1d880247e07ae9e801247dd9eedd3bd90a12bd0a1c6baf1aab3168518276b3fe64624cf5c98a978cabb8a08eec30c920b6c618684d0b2ab3719434915e1b16b8b4e85beeeec4b8d043080642414245b50103cf01000065bd221000000000ea9856dc92fb7d1636d9300ba234b406662c4da200830a9b664bc5fea08b082fc32744d2e0dc5790a716294d2a04d7e6f7076458f0b26673818c19b6dff71104037b63387d14e90c046fdc213300a94e6264640c480b49fcf4461f9c9416dd0605424142450101085840310ea937a9576459053ff3ec4581dabf907a6aed6eae3cde6d92432e1850f2b4dd6f6c82bcb92e3084d2438385d291b61bfe110bec5af9fe502e121a8eafa35c87fb3a2929db08d31f15662c52b8af2f8b36ee917c8c242daff92ec5bd7ee9e801fce13297bf0f9d7458573df401e8aad77ebae3890e281eca1ddf9754f1ea68c774ae2ac38a705eef1d1af4b761fe97026253b55490359eb728b0333d21a3a220080642414245b50103ea00000066bd221000000000b2880ce4989ebf17cecf77203b7acc3f20c1c49359c41afb818b00f409252c556bc0f047402d3eee42e45f6e5c24a30f3fae0f4b1453f250c4828d01dd78c80fed52e470fabd3466a9501f1dedaf8f7523e50792d45e5f6f3449feda42f2ea0e054241424501017cfd360dbdb460a5a0d91ec7a8a27584d2923e40be6e7ba9597fe6780421da1cc62f016be91285eaf6994a666edd63f18c65efb064e44ed60581efde497b7d8609576f50cdfb5884af7637f350b29d768c09b3ab80091b589fe07598c5f4d3e182e9e80166040084950386512ec50f09805b5b093d2e340296dda492e3527d2ef067e19375539c8432753f65c888509f02a3f4b4d44c6881957a723be274c3601b61edcb080642414245b501031901000067bd2210000000000c1202e5146c5515f458e3995fc02e556eb5ce8fc5c835a99bc3719ec5d38c4313981149c72e2068dfa4fb9450031501c28717bc6a3c44fd7a697b927cbd0e0481cfd0eb63a09e92ebea8b4b3a280391b12e5f38dade4eecda219e7dc01d1c03054241424501014a6fbd92577c4f50db7c4c209b22e0306b85753e872eccc3e12d46dd52d0bb63e84ec6905751cecc231626e4800b5effc8c0675ab561254ea05e5699e13f758da07bb254e8617f10fc9a19596b3447e9533d86f66de758a6d71a29e19caf64c186e9e8018018939e4669bbd3d6a4fc62da990c12b6778b1dbb9513fb7d8ca500ff2cf4133f017bff129a9906c42f74c4bfba59f3aa7c2fcdd2be4181d53a6a1bb31f3514080642414245b501032a03000068bd221000000000b416e70dfe91b6fecf48effe83216f2ca9b886a1bbf00dfc138481820a66c9125237bc99d7069755a28bdab34c78197be32485edee9e2243fbbd1fe2c29f98020ae7be4c11703236033e905b941c86f8939cafd2adfed8c5f5a5d1a4fe03cb0c05424142450101daf4912a39c4c7044c5140f0d366693eb4c7ffe13a206421a825c6f732846a1489d25afe082ecc53e23f047b278759b7dbf74b5777c55072830ecd11367b198631b32da7da10c6ada5c075abaf243bade35cd8c013b71b7439349ec60d30c4a58ae9e80105d2be6100a156333c70d931a826546919c102fc775f54e2c45f92248147361fc740e9e8a4d616db313dc978b09bc0a5c5cfe22e18f38f29519565408bd370dd080642414245b50103f500000069bd2210000000001417599e7dfa33e674f4b51c5a2d2cef90430e016e939e2de232fb30c923013e131566cb043a3573f9f32fd7c503e84d49c6565dbfde86fe3dcd32f575327c013b152d1b03e82b025bdac0cf026a4050bc7b556be04e1287610717c69eca6408054241424501018abac6b17cb5f9ead7fd9659ef34958c1a5eb535ccfbe198df67151e4c8c8977e7c990a82994b7997e1aeb4682b4408d188505e8fca8159a38dddf1c7ec67f8a996a8196e965ff80593233f9ddec0d62a135901ec4cf1a95989af28ed5b8e8408ee9e8010eade6613368d890f55d5f614976dd55f739c594fc3c91adfd549dcf0b07f827bd08e56db707b9647e5faeb86461dcf5b7e111219d47395698d82544bf335c65080642414245b50101530000006abd221000000000c48b7ecf0bea91273743470a2c8f51f221daefb3533d3acf59c36f6a630b4d7190683374da6bce15e0eafa69382ec58b0a7f983b4903d49ac4d877a7eff51200518ce68a6b20b4751c5277603a5a16fea167552d3a6ea8b64df7ee62c5880606054241424501015c39eca6c0e1520280846f36a0895ae0dcbe0dea2b90a5e0d7f898d9b1f29a374b2ab222a7889657341c1625741356bcb3ade32ad5b17d86a87e10436ce68f857a21aabdac65088b27f0b75b105c92d0e22a8ab33f318eb5eb487e4c32f1419a92e9e801c55962e393c0eebdb91815769587267d8f147b4f963876f4063eddb3d8936a3a14bdf38cad0c82c05e9234173bd0cf62b273a05de9e9babb1e49b6ec1903dd11080642414245b50101980200006bbd221000000000e0de96f64077020596e8eb6fc796bf1423b343bfe3575ad558e6109db87c9d32210afbceeb8002ef9f02f9f574387fbe0c2dd84cf8e6c0bb28a3e701c003c903800d020eb5e69e19b3269257201b6e8d4f2309fd17d84f9277d737f11936640b0542414245010164e8d7867088dc96abf89f5af8385abdfb6ca03ad7ca28bc1370a77ed8ca7448c1d8612de1bd919f2a038861f2b888cfa86825a17cb439cc682e64317c9d198fa07ebff7145f9197e7d207800c10b0bb06ea0d59a35b3e9ceb94537b65db6d8696e9e801c7e01f213b7b3434525c7a507adb1f5d3eb031126fef6bef9afc89799131071ffb7699677b3f6c57a61318760d96ceec8a1303112780a4d1fe0e8e3fc0cb3d70080642414245b50103450000006cbd221000000000c2ea1bc23c3f51d3afdca9dd760583c0e3f191dd781cdc615a9bb17e2e004a372f8cb5936cae95b66f86dbe82d2acbe21eff402d8b387bc6cf3e651ee7e4c303f74032f494344cbbc5914c1300e801e5a1885d10977ae06619fab4b1b4273300054241424501010af9a2fb6efd38a88bdef6207be6242077c50748484491b7488d84fb2af76c2311fd3aca8487a4abb5e2c56c5d7aed3b986ba0e8bdef0f50fda64fe96eae8a88a6c75e2104ae1210352e1712909bdec7a565b8e4798485293c331546be5343d99ae9e8014a74651eb97c38740bb31381929187db7b64b4c16bbf16fd37d46d1acc74390db0a0ec0bb8a75aceadb0f081f2781bf776f13830de9749fcf2c544ac37d5d9e2080642414245b50101a00100006dbd221000000000ea2aa0024330bf54f59717e581492e2c8a77c16b56ef2dca63bbe310d80e110d96c69b5235c0b8e7136d1b9603c8bb970a2f6c36e209e941b8ed52f6953a6a04c5c95f415cc7c61b30f335ef2ec8673115a5c8fa7d463d92fbb2bcc510c5930905424142450101f60f2c8f9694583c1e6b547f7b8d7be6a3a19efdf15c40bc704673063377403060dc4eac64dab576880c3dea58bc5f6e8d9294e67dbaa96ac6fcce13a9e0bf8c1462d751b40c59e4b54469e596a5e660eede6c86b7984203f201bb0e424a83ce9ee9e80173a61e96e2ebf04cb36d68ed8d5382188f0a32e0725c856348066416c3515e238309c35728e02a4904465a697d205e7b725e7d485fe8967ae1c23ba388e7e3a6080642414245b50103580300006ebd2210000000001a81c8a23add330c4683b40952fe77dda829426427c776b7438fbe5e83d9b413c515609645849c7571197e027a747f7cc44d50ef4c452abcb2d6bfda4a479505c4c4bbd7f3cb3ea254c739a3fd26d4ccd1cc1f1079bf18a71983ceafa3cfde09054241424501013ab6fcbfe83f34ca4b9cc741878d6b45d524f45d837459976df85af8a461a9120f5d7ee7a857c1e8e7fdce12d28558a60fdabecf3b332c6bc1d3416f4f2992824e29ed04b74a26c05e992b30243e261479f67d30ea8f791317b07726fb946950a2e9e80100a722836240174b2069def823a17460cba3860f09c3eb2dcbf909f2ad18ddd07b0feb16956d4544b685d1d4b93034c7b8806610ec2084c4da67de460396a308080642414245b501014f0300006fbd221000000000aec4fe4fe7d65732a74ea3d000daded4aa6b59bb713680dc6f30ac6ac2b5743d42b85ec424f9eb200aa2d3735abe21a250109dc560a26a3e1902a4bbc414da00013322878e23559705b42ebbfb2759e190348e5f3d6326cb3c90936cdfd4c50c05424142450101f2e210d674f8dea8bd63fad1260a1dfbb90e4f1a40acc6f54a0d58ca45655713b634265b76b8bd39591b854d25432ce5e6fc791f924202d8275266a62879608e8f5e242047239525c9bc5c8b2a09a6ab7b0bee8a03d8254f0c247e4ae7d35e1fa6e9e8018bcee50c828f2412f06cd1dcde7d466e5906775bee62e0c2100e79adf4c68da4005bddc5c8269f8e7fd6799f97bbdfd6a47ef2f0af2d5185aee167ce0f5b7443080642414245b501034801000070bd221000000000f67dce0862616225ebe8989d67b76f9d496d255bd403bc53711a6059412f430587db41657a50a78b4ee4a64a0c38a19b3ab123121f9b0241179683828f4a5f091d201fcf76243d5d36f5d7b8b85869cfe3fa1bd9b996646ca9cad22c638abf000542414245010148ff75efb1b3a512ae305534632a412083cc45faa8ab0cf58e1a468cee60e67d08ee06df9ec15bbb8f03d2008e9dc1df0b6db1b26b3931215a82ff2c3ed5698654a8bad617ae42c33241791818c12f3aea64f1f62a56f751db2594c732d61b50aae9e801b36a08c64ba327e6a3a73fd423252887237e6faaded7b556017c39c402bb17fabce93083730329c5df18e0df73e623d6553edb327dffaac30a86d1831d2b2c4e080642414245b501030e02000071bd22100000000022fe42f6f4834825139f5cde848f7076b354e8dd6e0e578bde6b4062e502db3e220862ff2db10a4016e88e335a265a40b27fc5a5baf45bfe9cbbda160ab25b0af15d6e2ad7289bc0c6571ed4a94dedb1edcc038da5536507a9c105f53afda8070542414245010112f3a97c896da81e735281eaddb32c912e21a9a891ce52a3dd97ae5e2989dd6fda6458c01aa7881d745321638ae35eed2bfb309c98d79d3a86db4b8ae1177283db7f01c3e668c7719cadfc5d5a6b24efe23dba8b32439e901099ad6dabd1abdeaee9e801108a309a9e041324d810342722756165c8ae2206c1faa85fb0e297489730c008ad26fe642fdac5119489876a5577e6b12b119023e2cd17948f918e71e3cc7046080642414245b50103fc01000072bd221000000000d241b5eba4c9e1a81085d1bac2c2095974230a39080ee47707eec3fd80213e5eb34e44d08711bac0c4bcbcea3ffd7474786bc78e5901e2c9acd9e97bc7b9f70c2388181d993c6a8aa9d94ab8c6d12a9a348555ae6b1de8b7b9a781a74981390005424142450101e22b1962aca43bfde0353a5253904b8ad010e704cbf57a7279dbb7394a0bd44ecb21da0d89116377bc6f914dc1c10a47a3deb1a6e59351eec1c9a18dc1af63810db5bb2b471ef9794a5259209ce2d6c29232243a3f4b11c085c1b393cf5ee9a9b2e9e8016722991d45e4f18f4afc27151fafaa7627769a5d272b0644133c5faf2b157070e18cc28b7c49ec23120e95163fbce8099cf380b360fd45261f816d2538a6c8e4080642414245b501013b00000073bd221000000000c827bddbc05db47aed4b4185dc948f09ca8e24d1064b0236ea98b6f646ee646e44b663994924dbc15dee2f2511b67aca5d207ee2ea4c1dce930942c46e738f03b82d2cf520f43bf649fd4aeb64e51019b43bc2bd0866a40cb325d2310618d50605424142450101266b33c783fbc7629bffb8671b8bcc4bccd668f7e1f0b5f1ce360a26dbc5ff4f49b511162bab570bf7a70bee2dbcb51c52633095aae1082cd6bce837af3d7d83d8decfde726b99a27b4037a6fd2ffa5f6e3a663624766175c59149dbc729fe43b6e9e801f145bcac7213f0941f67002f778ba4ecbca0951b5df38fed2c09adb7d4192457bae76179105e782092a5481535495cf15b8c408121e5e61d5e7a9e39ac37be2f080642414245b501019802000074bd22100000000086ba2a5e158bdfbd2e508d1605e3b3be3fc8d60135604d421fb2b64c90ec994e314ae9badcdc59f1e76d83bf34627fea230686f72bb9d1c30b337e00590a9f09e7d598b5b33839bc73e386f9f8390ec67c408fb335427405e4b89dea9cf0190505424142450101d20fd0f3989a16e60c271e5506f02bd35fa5e8148871c78ae6374938604e124066011578e55436197aa85887f0cfa55077d2c63d2ecba1279ce388a9c2faff88159f3852526155a29dc595041f490a565d50fe25b14a2fbb163049b7c218276bbae9e801b026d2cb7cab9102d5f026208fd82a60b13b39265d011c894c74468bfd29ac28d8f39369722ac3ef615b9c032c2f341ef3c697ae5f1d0ec4c9bc1a1a844e6a8d080642414245b501030f03000075bd2210000000006278a17f3038d2a0e688afab1a4890c75a01dc8f22997997f43fc637b435df7dd40c1dcaf8671ee2fd2692004917f915a1bd0d8b128d073cd7a0fa2c0ff1290719f6c324585fe3f721b3b8eae502cb085dc215902cf69a09468a74f7e6c4360c054241424501015cfd3932aa143916c3fd3ea030c5ac60d11c36a980ee17af941c5db31a07e00274a83e386bb6067a46ecd93e319d105f58c5b2fc781b102f98f8b650050a0b8ea8a268d7137c2af5535024accca0573f0894a35e34d19e570ff939ee1c7983acbee9e8014bf5509c237478a7663a2d16c10b0c9100e6132b4901990742478da1afdca2c4aff303dd5c302a0ad9be80f97eb183d329d9cc6cb6ec146f920f5044526efee0080642414245b501038702000076bd22100000000002c814b986ae57fa7b7642004a87b3bfd0accb890193501f7d22b116a9384e293ddbc136a6bade0daba596784b44cb7387020064ca0d1837b56e1e266545840c578f7b630c285e18bd0c959de2b9eb7fca09f486779d232d107f455e1565560a05424142450101a2ab25faa2421d1ca1b3bf8d8834b5c76cd73cb8136b20de71e08b5305c1e648976a9ab09ad2aa9d01c5ee8fadb862e1aeabb9ed3beac63e224e94c1f8a220830d98d2199a599e56f160e6ea54c48e4f60e251897df06b5afaa612a1803599c8c2e9e8014e933cc1d6d1a14950c91bd26e5ec1007418c93bd7b7062096aff70930e96bd0736ac8fc084f19588d32008f8f3123b3a1b69dc1e28fb6b6f219635b8bf35e5f080642414245b50103c702000077bd221000000000702e07e0a3a6142b78a91c5754c7e9af3bb73d0f93a62499d9aa385451154761841d708e15ed9b950b2d6385ea17a64e78ca8612fa628e6f757b7dc4deaf6f02191fc3f721bcdc3de449375b6d992d9854912b96a84d562efab7dceb386876000542414245010172f5305404240f2beece61b558fb469b2497aa44d42b815d481098082d17fa405f19840611f499aee7b67568e66869e53fefc627a9d6de9b8e6699d1daddd9838df3bdbf964f5d50bb3ab543d2fe058b8d3c4805858de12837f7537930c68f42c6e9e8015c4624fd8884fb4b36bc346f1422037f3290367baab54c2eec340d0fef3d9e10e8230406b88e37f6c19a8d6e5287513192fb7ee30810fda387ad1f69ce6b36a1080642414245b501037300000078bd221000000000a0458f6ba3195b05682b43cc084520d329268982cd5bcf2ef00ef4af9496d6016e200dcf57b25388cf69f7413b30d8d406fa9ff81721956cf3adf4f7a385700104047d24d453268da70c0faad0f5d36bee9b48f3a8fa57f5341d7fc40df03900054241424501012463ca928b9d262f25fcc455426dce636de46760cefb98de9b4141100cf1735b4675ee0173a3f8efdd8e3d4b5820b879e26fd76b2905c2b0771b9501424a9289396f280526c64bb90f07b5f826aabb960bd9ffe66be9575fffb21dadbec53a00cae9e8018c5bc470e84b58ffecaffb86fd6f6a090005658b220306dd0a0052826fde12ef840913cbb1d1e88f407f7c5acc18897951992291e604816e9a166986a917d4f4080642414245b501033402000079bd2210000000007a68df07b3380ff8b806c5702121882bb89c890cba88db39e1c904f6875a55391915f85f18164c0f85119ea58df9a64c280425a67030d81383bf41328142c10fc3b326b1cb1cc0da3d03b7d684f4b184c1398d716e6cce7c9907485175f70203054241424501017c4853e7d0ad4dd1403ae9ff7dc647a10760294a462531e767fc7d2b377e926ad29fe541c212fd8cc77305a2ee63ad60a983876644bccce93f3a183471a7e98409dda55141a42b3a122fe0b2041c40432abcd84da72128e80047aa56df28ec42cee9e801f74009ef19df69dbf09ef3ad147380a42bfc2fd85c9ca28b26f8f9dbf76eff44738075b4e50a035202f06fd6a26b46c3ca344f037d246eb8a83939ed1bc07358080642414245b50103ad0200007abd2210000000000cf3d0b843b41d3ce1cde6adc89df1c6c1570d5661923f1482f7edadf1613516aa540397f63e454d30aed008e89230ce2805f57f0d98afa50d01a0b3ab72a20eeec4816df974b8abc398f66e81ffa1524467a1719ec8cd3693007321e5d4570605424142450101285cc9d6d0f6a104486492ce40726ba09d6751ef4dbcb7369881e33327e665202c4c3eed4b2c0aac8ac663fca9a96d61538d87c94ca25b3763c2c9770a5b4b8dc78cf33ebde485262a9fc17fa2a3473e787f221ec26db006d189993d4797ff67d2e9e8014d2308b14f46f6a208691a6fa4792155b94525f801e712fc1c727e675f075dcdbcb999f6eb6019d3b7f1d9621412cbdaaa8a6b049cd1ee70ef64ca665ebb84b5080642414245b50103a60200007bbd22100000000020fcac731fcbdf14bf9cecfd6ee422c53e5f54b2e082a8e5c259e6d5b0209117a551d66e9cf5a5422914c9fbcb019e724c33eda9928ce7d83c0c9378c89a6f035f299f81935b3d53cfb4e6e9546eebf11f9bce00cb68a2873054f22ef74be10c054241424501011af3d634a5b2fc12ac25473b1ae4136482fabfa90554f420db2ecc504dec8311f98d68a1ecddfda6215f07ed92a4e637ef867fd5c0aedb8076b9193eccd9a986bfb66cccbb2960d3085180d198b9da2fdf4c8992e7947b593c8b2ab4177d2f5cd6e9e801ae4ee22dbc810dc6210d8416b345b8e284cfd50c59d9082483a4fe75d550b432c6c019166e3200d28d04b8fd4e38572e1c4e8c9ae4b9fea677137ac107a8d2a2080642414245b50103270100007cbd2210000000000825d8de8db17a190e9420098e17d0cb2a87c359709195dcae785b5bf77f674326ce9db29da60745abcbb45846860355e96dc113e5d3d96ef45c40d9f9ac2f065646a6dce5cbdef85ba5b80f9d7d416a4576ef2e263755556aa79bff9b7f600f054241424501012891378ac076086845653fa176a4419b83e9ea3c3ac052c3a2f4172a87c0bb4d3696f03348d850d7ade9a677a46a682e7e9b0f654d17ae50eebe512d2e8b0281d14dc2615db2794a965cc4ffc41788bfab644f6736098ae52e103ad972f3cf1ddae9e801ead98e96c88bc53d2c73899c3577c4f46b33bbf8a0df97f3e0c1af62541096ac871e9ab9953cc223fef7dc97dd179f26909fde4006d29a193b2f75289855b2f4080642414245b501037c0100007dbd221000000000b088a9fe567d94eb1ab144bf69be9ccb582b6ab11714fda4232783346db50102236719b6e5356e5ae949924bd94e9aab5ff7ec1f5523e31112eac006a810440780191876379d6cab5e0eb1edf9bce7648408952392a6b66ab9cadfe722e386020542414245010116808c436458a83e1279d9b7aa22c8ad36510ac26438e9a34d677ead6c870d123fd3e9f6a19d0b2d20213fc980cac1412c090c74ea7119ae8ab0eec4c7b44b8ff1a6b1f56ba386e097acd3f8879d029ad06e59c8e0d6278a2d357b8c8fa744fddee9e8012f4190d43ff8a8113590bf5c75924c70b969a6bcbace563d9169e3cb794d3c8cefb13cfc6f0ef9ba04f1fe5bda476d5bb37ff14681d4768992bd3bb2c1995220080642414245b50103750100007ebd221000000000dc87253bfe886644537dfc5f398b421ed69f84148db22265e38261a621a2cf3a0e116283738dc2a567901640bb17762bc45d19f262c49ea6916bec65d09eb70e4eef39ee404c2c4e50450fa00bed32fec47648b64485a20c0ba7ae729454870d0542414245010160a79716bbda05e4bdf4e21ab101432c17c0117b8bdde191e81ac07bb549f638cf024a0cca0c76323335eebf223e263d93ee2b7569f1e226cfe35fc19d768e82f1408b56e5a30c60d5828c534fc8be1fb8b2c00cf84e60dafbb994c9ae48650fe2e9e801b16eea86e9e9892c1f9c2ba3bc52a32be6ec2ad191bdff0e5b7b1aaf55511fe2dfa27090a87fb184bc257fa11c569c08a54ce80cd762bcad48d1d61de991b6c2080642414245b50103e00200007fbd221000000000d0e95a13629a659559d46ffc8b4f453ba460c49805e29a5d33379d08f261ea60cdc96c5a59e9bc6a5be438c91e144c58550de9e64e69629b5cf624eee0c5780267e3640a4c6fd53f285a991a60d2a37f818b1d5fad3ff6184d01fc401fdd940b05424142450101b2426a861a6c9cf7a9974d1140ae2ec396962acf9492cd3e707602f42b045d57aa54be502324f5b6474a193a816a2ff4627ee636164bf8cf873944dae0db04892a425307abc7727eecabef00dc8c8a11d791f084e00afec1200a3e37016ff437e6e9e80141956b9f94cd79084a67a485050913a71be3fc62d5371eba8844ab56dc874fd7f5850fb43c977a8431bf25829c66f1b29cc73e5a7dc3c59bf8cc3f518dbde580080642414245b501034f02000080bd2210000000008a3241226f9af66aa914e199fd93d7f3058d6557e8b110a0ccfd6ec1608e39311dac3421310328e3d9d3edb9de5856772fab0bddc35b2d884c5071d603e8a509f284f4663b4a9004b5f3a7c57ff495707279befae6be18524814fd0886fb020605424142450101b48f39cd8d0e63dab6d7e2b68c09938f2aaf50331e0da38e751863a06a13a10a2c360c7d22026a223e562d5f49f5aa3d20eb1da0c1ae0ab78639c3e55b65eb83b72117aff0424299cad8b104277fa8d64f30006af3f15a565466aedc29660e26eae9e80188be9c9fe1b21fb06f65f655cb9dbcbd34c751a94dd01cdb96a0610981af081718de550528c7bc397996ee1b11210bcc3a94712d3681523fa8ad2c42e89f80cc080642414245b501030e03000081bd2210000000003a14fe4b1a84562406c1ca597fef4191df39183add2b3a53b582966b72b9bc0bc5ab008e720b67a6f886c76847444290dcf3900490a886e801fd0c3b1376640c3cae58074d45137f88aed52467a2b338e35e774bfbc78b16fa3c872a9e9b64020542414245010178008efa47ca8d59b8e0160e40fd9a456f8ef4a7fb004c5d69636e3ad8eb8725a69ee71e1bc6c4e029e8470b434232fee303ff41db5e1733e1c63942db77c5827bb4dd45f642245b84ec0109b29eeaa4ce0d5532dd79502e46e1ce334a17e98ceee9e80189672d3dbf91011b434e78aa63b8412976aa42ef1613c34881c5c3b30801640de4ca4bf00d0fcb1c8b536355f8f14184a73b83755347ebbc348a6201519d59a4080642414245b50103c402000082bd221000000000a44dbdd6968b67af8e27b1fe37e72e20828717443da3d418df634f0ebd7a0c289e075302291a7703a3949e712d623c20b781a90c85b858ffa9aa10d997984d0372d1a68915017139ca70a810c040822f4c04b99accba364a013d7dc45f084803054241424501010c0882c0b65d15fcf62043442be8c7b92b4ecda82595a09795c7e7b161744e5694688246a819aed377c12d213111ef3a1432fb00e3186ebf9c4070a2d585428487a1a83a38012825e5bc29b4cff83d498271500ac1ec25948a6b8ec583cae2c8f2e9e8010098029893f6b2b8553f52119ae2337820222619537f5f1842f90d3fdd3af8c9fe6bab9f29ef31c0c506e566f299e4351169035462de079b5d6479060d394400080642414245b501015301000083bd221000000000c057af6b6700cd4b976e24f453d94285275677c22f39a638dbec03558ac4681d5e8d7ede1106cc253b83bb49229268cf43001c02df330902789b448187be520a8d888c970ddc1c395b6719ea091e208e37758005b18415b24a67948796d3de0d054241424501017a6b8e06b5379d6c45c57303c06b7d47f601f95ee4e438c51e664b015bb3392a4225490b28e2d6686ddc3be6651a79c1e1d1e4812ae5281dd74caecd93ddaf87480019b303a35dd19b012a93e26484ec7ac127c81da330803cbcc0cc99bb0b16f6e9e801bb5ec27adf7488510b0ba041aff2a0105f86c3857c65764771fb742efe721400e7867db351260505a5c7a4ca8199f330aaa69ed46dd9bd36f5bdc43bff73c306080642414245b501037000000084bd221000000000f27cc869d33ddb4b17dc73dba1d091cadf058fba1d5e6a372aaa2a0e4f5fd1785cc96b30ff8bac2a0241031411216988a47df4880a1dae6d06107a842b3a2e0643cd9222e4f0aead51001f8a0e741dd84536bbef6f42902c83349b5bfd453302054241424501016c88693fe2a5ab8903fa0623eaeef71ea61305102fc1a3310aa4ba8a35256022f702f503cbd93ef0e22f4d9f598c0f523dabdc4673e7fbf6cd92a31838a15681cc497ea1fe3e6501ca2a95eac8a0be47b36b2a5593fac783438e7cfe4fe4203dfae9e801430215667132815ca12cb6ee00954a6eb94a6536a109cb057e9fec07e79e54e57a2541d835591f340bf1d809b83078bde64f5b57e85434f1854931a7d4fa8412080642414245b501031a01000085bd2210000000002c1b064e0f5ce169991630069114eec4b4d0ee70af4385f624a05155ac81114fecc8c58b939b199b1a14c5e1b21c7172a67a29a34b55e963424d2c8b53afad08a5dc526d2124e441fec1de48d26f5307c55d796f134cb360fea3eb4189676f000542414245010196fc495e6d921c339c5cdcb4edb975b0be6ef6284cd37192451fa369b08556107795446115de6799339e5322a858a3f514062d9a1ed883ece35f27671014838b636b9763048aeae0817844bfdede1f372dc9822638da93b4645991b2257e6180fee9e80199746ab7385ae98c74534c876259aefd757851c9cc2f1cf8a761323b70c911bc446f15235bb38805145b5f453d5c706be108d9846637ea8db57d50b7ecaf089d080642414245b501035a01000086bd221000000000d85a980b22a35eee7700aecdcb75eb5971bd9e153d8b5ef08f650f497aa8b3000d6e81b90d13b7e6969ca44e32765375ac02749350044250e3208621caaead0b3f7488d6e4f54d4a96c47f55d1de4c34251667889ac045bdcd78b6af04d5a702054241424501011a60029f1ec0e7a10fa74263b0ec9a555b0e600a02ba24db57da3fb3cf31ae7db4278a02bdc8784334a06f94a6c84e70fdbd1642861aae022264fb85c5b1c9898b5e2609e936bea1cdc5425d3ddd311695dddd71e8296c99ac49e117f0c407d502eae801d77aa40cc55958d7a9ec41c4e1f300e3eeab499226538c1becabee7c60879051f9a3e9dae301bd15e68c6c2b15f2da493afbbffd5d3fd5ce1647514f6f5ad385080642414245b501035002000087bd22100000000026d6ec67986db203c618f57d6bb09bfdebd20f28453b671d6efbc6dcf89885200048c302f51ecca8abb94764c7c413d3de260c8aa71668a4c1447a7006a4e908f72a8351fe4832ffcce7c2d79b5c11020257ac29525e6621702dc988d91e4d060542414245010164dd9bc98fd2fc15c0ad7cc5d58164c1c3dad0b32186f8c688e93db2edf9d9790bae471b50ebb753e3688f4e2656641de107e25d368447e5b1a6acee5e28078f646c8cee79867b2ff93217c70bcb8d359c38a4de6ff24f9bc85b3fe743898d6506eae801b4186fab1b8bc76d77c38b2091d9b33cc711c9e5dffcdd3dad399158af13570365cfd48dbebbb1303412e3c9b80ca2682edf92d3fc1e9b32685690ba6af8b855080642414245b50103ce02000088bd2210000000005ae85cce1cf1fca6cf4f366d9f6f84625d4008e5f2529b6d291874e199f79d428a67031ac2c96bf6bb60baf546ca653f77f748835657fb915313cd51faf8320b4f71ae0059a32aca4ce4abca75ae19ac81b89b2caaaeed8ae95bca0652c6610b054241424501014e217889541c8ce009b0d7c74bfca072514aba43a891c64bb5dfd3c6421db81c7490d175e485953b844602722cad266e3427acd0818174be6810e2b69cb000860b291bddd362e0af55c0e581201c52645c10fcc518a9983e95d81626a23e08d80aeae801a0a52f51adad730ae5276f0d631dfacad49c40aa84f4d7e8aefecaf4d03fc960fc85f33538866533c343b92e7cf3ea0c942203f139fdf7cc68fc62ade7de280f080642414245b50101fb00000089bd2210000000002a6e228ab6be2eb6713eacf572069c29bd247c9e28c1c80c622e3bb8074c4b3837aed56566ce4e79323b96ee98a5542b8f095dbe50ad76f9e8b1dc3c269b9c050ba2a4c91476cb16ab374b97ff014040b7188cb57e8663955eb93431b808a3020542414245010170837eac3b88c44d6d8b2c7d22ee23e8b6d5163c870b305d07b49e3cbdfe30077bc7e0b6b0cefc3dff2e8fb2b965cea4a0c22c4f34e24981d66a2a09d6a2a28c18bd0b538bfe7804a4f6e98e462da178ffa2df605f91cdd1988666907b33ca6f0eeae8019eca134a90906cc4d5e7b1010213738bc339182349b42df7f0adba70321aa81bdd63a7aff4925a30a64c94811f3160eb7237f731f4196586a132d49124e8e333080642414245b50103f30000008abd2210000000003c8b0a69d4ca0f57298d034afcb3f639febb26f53200dffd3d94aa3f3103f320da81f2162804ae699aabfeee42d0b16af94673ce0181c8e052565e0d0d3ddc0f7669eb8fc9ff125263ca1a5470bd381cf3506db3fed4bf1771e2862518934d080542414245010158514043ca857ee359bf48b016f529e45b37eb864fab8f63ee9f547e18e1dd1509a0ac643ed28ea630caac926a2289e336a3bff15e7d487a51eb7d0f1dbd578e3477e8ef6f312385b4dfb08eaf8fc458e6807104e50f3ceb9ce15c98df00d47612eae801e59feed175a64a80c63c70c6b9704f3b46deb71b8bbbfce8f355e024ac1ddd876855a5540cddedd074378fc49dced78e6a3e415d6c6bbbfd1c28f67bb9c4505a080642414245b50103030200008bbd22100000000078a1b82d6a97c7e0e5b511955fa851d3b7fdb590d1f1db39b69225d7180a4d4396e253bee228e34dcf98d62be246d46a0298da9c132a45c50b5ea7a0eaa21b0e194cda620dec3806de9fc8130326d5754fc4bb1f07b6624096364c98ab133d0505424142450101f6fee03f04f0145b58aee94356beabd5df5754a6ada11550f237359e770f544b91f4cc1f7750a636422cc5cd8431b6b18ae6ef30d65f6aa5ddbf73f3137f148fcbd66cb51a81c4b23c832bb401a731778098709e2489da86bcad6ad5413d404016eae8010ff4376c9ce1d44c860fcf1b7dac459a7cbeff98ba81b95d00acb3cf191dbbc1398d06f1857bb78e4b42a707a8ae8e92bda4b8fdba1dba1b68c2ce7f9f133f50080642414245b50101a90100008cbd22100000000024c68e01eac922d867f055042b10bf352c1b3dd463e4a5be01c74c8c2948844a46cca5c0b47fb23de78e4dcd1de4b48118aecd11f29533d24c7639a4b0e4490be5ff77f87c87aa2ca19e2b3a9de296bc8fa988474bb3ea89fe376cc9c328a90705424142450101ca9fcf045d207978ab45d23b98a401f0ec98117eef7741f7aae2a429e5d2753b95acfc39ab14b08fadfd4779cba6a727405d4f13ed7a4df626b8a264f472d782f9153be22e26e481dbb11554130aa5fa748a44fd0e66625c43b37558e1f902201aeae80139a03ba9603d34181967b84a104eb029a0de9d799160fc8f3b863e133c6c91d91e87ccd40cd2da4cb28d61629072f353f8dfc3f4c1f47cf3b4cb05745e785e5b080642414245b50103ee0000008dbd221000000000ac50942f351eeb1bbefdf2f7ec1042950615c1a16202cca56123f0f40e2bf450d9d29f74fdff1ad134626b5a4bc23a9fd727e679afa4a1421ff0e6682d03f60eef002a94eff9c35d3d4dc91305769cde446245b3bb8068156f3e12974a01b201054241424501014a9c539f9cf65bcb0e7989be1f46a00f0ba790ea89378c55ec2eeb614143a35bc82b5a74e7f4421326e391c4af79372a4ea1eeef01365212f3964e151bebe18654be2c104bff4d7ef56a9aa51f689348e40f9654aeee412b7f3a0bf52d695a701eeae801457c07709bb7be8c445919bf3eb3e69d8df187981646a676e9321c8ea79a1ba913b35ccbbfb2bc588b6ed756fe82ab97b5ad3bd155e183074dd80204882d42c2080642414245b50103290100008ebd221000000000c66c17052194c21c6061075da172d2871c6da132b76118fa80fd6e4f701a9d7dff8c63b2033142b23d29e793ae6ad81b12cff4bf2ab811b79e5aaf744a671e0b5837908498c05049e441a9379acbcde136f3a65eaab9a8e8ce09d5dbd37d6f0b05424142450101feac057eb1f52f3b6e5ad5200290db0a1d463956c99d43a910de69ff3c3b0d025cc2d31a78ad31723d06d22de67474f4a23e89eba006ca5622d64ffad3130c8c7d917e4fd30c4d17edd35de4b7af9d1ed1ae2edc3da83e741fe2d2846dbeb4d122eae801bfe38eaf20d3990188250f0cf37e9909fb7ff29063e762b38bda800c37553a377ba73d971b1faa14a41ac9a142845e238bc9be8e7077f1d1d3347b443a44ede9080642414245b50101990200008fbd221000000000be50ea2bd697013c93647c95cb3c2656eb2d1c23185384a9d291742ac8c2f77b1792512fb70b82115ccc1a71cd4df24d9eb9b5bf90c2c70417c3d7a07ef4fb0dd86822dc48dd7ce93c63f0589002e909b7e2e7ef381355db4ff21e124fed9705054241424501012cc6d6dd2e3bf6406864175d3795d18bce4d50ea648ff43d2520ce8c21ade13ef44a1e6fcc18edbc3d1cae6331bdbe6fb6fcb18b9b7eaace82800f00aaf3c586f5e33ed5546a7e9a53b0f1be2428d4c152ad9400e4904ca70601334f33d7fffc26eae801a2308455d0ce2c942900a76d62750a75285384f40839d89a0f02321992fd0c89dfa991a00c46b1e59d5e98c2bfd301c0d0706e58e683f3071300f727335c188c080642414245b501013500000090bd221000000000d455bec92f7c1e9691ae3487d9d618eadfae0fabcd94bd6c0c200dcb6d405c286eac33855fcb7d92aafc0a59c5fa927ace05df62dd695c38d712e454eccebf078be598fc5e71f9e9afcf53f47338154120342707b9cdcbf6d5f4eb23f5d6d803054241424501012a787dfcc74a76a23d3528f44516de5395ad414a8b36567f9586313f5d980b17b6f8163cd597c084ca712f8fe87aa57acf7819ffe16cef500c72350b9845c9868ac1e3b4ccb5801abc3ffc32099393b8bbca44e53fcb909a73475947b593ecbb2aeae801ea49d70706cd44fe5d1190f5f05dbdb34f9579fc530db7a6af6cd9b1d772ab4385566d2d70ef69864ddf4052f9c6f817f452f6a7a991ab93c8bd7652c57e4700080642414245b50101cf02000091bd221000000000fab6d8d868b39d145fba32446f0cc832121590fc6b0c0cbd272f39e48524c633cf25e51546649151336107bb40b862765bd1548b60aaabd0d43e48beb166560a897c12b89e1428187a0c5b45c255a6e0ca97a468699698661812e2243b751308054241424501016e044c02ca76a579bd9b83b36cb383a7f1aae3454ad9bbfde3125b687798887ee721d241b34a8fe156310f71151b7077d42f5e39bdbbcf0b486ada56585e9b87996c67b45a77136521e75368bbdfa9b704c8208c7edc44d357bc9e0ae5de52a42eeae801a619c368f5bbfd1a79e8b410a2674eb7e3155cd0dace389dc1bc45f38f1eebdacde91a9d7726f385247fe1e372c1cefa77aa47c9a25fe4850f5fed4f0ddd143b080642414245b501033401000092bd2210000000005e19952cdb46a471922f4429c86762d84c8fd4b24aa7cb0415235058d8d6f07d8485e286cc30e724dbdd4930877d02338800dfe63ff810fe29527f068fc85a0545ff578af9462696edb9c51fbb8ee96ef78eb29e9ee411c8e0f769808eceec04054241424501013a2b00bf896f8dd58384b4ba1aee28e92dc43e84cdcd8d59b8a904ad784e4f483686c352297e492cfa7a0044189ed249972d2c428d71f7c83bdadd6ae76b148293ec907a04c1452b5f1c1090e9b11e959f4688a1f9e4810526b6ce979842ee6332eae80107e05237e34e05d767c3854f9a23bf9fc248f2c53c209c30115abbdc425a0865c8142338dd2cb00351cad8aa87e428aa3323f8dfcadb8e04541adf040e2dc9c8080642414245b50103dd00000093bd22100000000060889a6dd9949cf24c72c757a1c0750c3bfe8fa91111178e632eeb6f4c9af2228bbd834e7e356f53083ec833dcab5df6cdb0e11789a43a27a1166dbd3108e608c4d72ef15904a29f98179efa107970bcaa457f31d557fc83eea30c4fdef5f70f054241424501019a97883de0878baced99e852cb4ac25960352c8923fb08044eed42a3c514a77de68bd5d5b22a49daf4caf14477f3ab55deb1cc2f1c558e8303ed8da8239d4c801e56c6b5527aa08c568818c8d96a78b50467166fc9c1a3a6dca11bc7612c619e36eae8018908882cb50c51df76d00d3d93aa488fcd2b2436c0c8e42ee8d25bb007e29c055b260ed6e67bb3dd73ecfa4e556c1ae74941a1e69f53f6de4615d2d8338ecc69080642414245b501036903000094bd221000000000c8db0c114ac0eea80d0742ca6247cdcd0cc429e7eb5f60d2c14547fa4f2ac8668ac8eb537d9f58a18cef1f3c120ac8de56bb5e70d5f2b7f712b50ea55892b7028fe7f512200de65fdd7fc021a3bda7a7c7aff832a0956d862f6850ee67eb830905424142450101e64fd64416b58e70d24334e2a9d82b2373c8e52c4aab27f4943b3ccd9d18762c5ca87d8ce1ffe093e54e55960c364cfdf2d481c78d05b8a7bc4aa39faddbf6873e21cfe9aadf0b37f95cbaa2a5f29b70289ddc446f2c6df798eabae4f2bf399d3aeae8011e33b801b0cfe4e300539531b1be61ff5ab4ba81b6c1443fb1eda1c1361d4c9a4d99eeff23d7db96117e3471cabca01da12d6c746a28482413c49f057c30cf2e080642414245b50103a202000095bd2210000000002e5fdd7263b9750287518b7c38d5af26f626a602e4b4ffcff9c5b46b20bb5178832c9cef287288bb6996b2a18f7defb247ba14f3a507d84f40864832e734520bc09f10b23e1e1a2265a034da3af47c9e7f7e9be44e3ce223cc93be256a6dd70905424142450101849368d56e14e59fa1e8bbb4f61c4134aa8740469bb2b72e2354b1adc18f713e23bfac56ed39714236a878d275ca30f25a05a0d03fc94202d8734796ea52938161a223bffc4c6ef5dc4c32d269e1a96ad7b31dd2d479e5d86907f5d0bb9ff3ab3eeae8016e0dadc424ab2a6195ec4f2bb164aaf0942293c703bb207b22a22dc2aa52324c1f8d0c8f8dd2780568c57eabd61e38e1f087a7f3ac08f6c30115c4b5dcc9dfe4080642414245b501030e03000096bd2210000000004287c05db8aa9a7171f77681165ae0f4c864f5e3a1899d25a222f08613fa47290eb5ffe2024f9c02eedf9871c49c10757890b77c8771db1271b18dfc7dc9d50d52145bb0f16e6e5446d516954b1925da3190e3fd31cff10832de731177abfd0705424142450101905af26009d00d51c2e8c58b7140b9bcab200b084ca31be8c08ed4ec293e3717b86a9ea50bb02bcc321eca0eb40a52ab44e7d82a2e5cb578311d75448070c78908cea7ff54c06ef1ac64b28a16442c3a5a630c48e6483a22cf5739123150bc5542eae801391fd0b155faa8ceb0ce47a7f50fed91390e1e27161542669bf56ded2ba9e3ebb3473fd7b37242f90b3378bb6d0e20dc9bafb7aa913db4bbfb6c4c1e4f2fd25f080642414245b501032400000097bd2210000000002aa8807c516ae11380322b46447a06fab3c21508c3038488a0bb3aa3f702101c756be3fe4728d4169b5601adeb1070df721a11b9b2052d844a8b34692e28fd0950113a001fa203ecfb712e0ba61c92119d0873cc56841de3bed6b175586294090542414245010160bb54f7114fd15550be8c0146554c96478808a4144cd1a34ef2e80f28934a6c8aa53a6d92e81c57012e47b57fc697d3c85652edbbe8a1a24d901205c5d2118413cb74a04e53d8feb3994b52f15479dea1e3892db6aaddff24665ba4d125103c46eae8010ddc3f349cd1f15f1fe3ae7a14bca11082ba61a11148f2bcab5d0b7fca9598e90a70e45c4df4d22e07928c52fe22472985fd5e7d1946e6e451eca89cfbcbfb55080642414245b501018702000098bd22100000000066e416cdb02bcdfe18070cdd69267637b00fff4dd4e210761ed4039413445b2cdd4f6f748079387617be14e4dd3dcdb2a0e85641dd239efec8f1f5f01eca1c0cd16c912fe0f476c65fc69f68676478d43274515883033f53b58237ac816f8f0a054241424501011cc4779fb78611f157e0cc3f61352e42ab3bd095a8981dae1a7768837cf2383b68907efb8ded890f887415a1dcf9ce9d8ef4f16e098dc65c0778bd7e6b697c87f734b3558f8f174e2901ce8d2be36441c171d8317ee6aeb428599e7a929bbc744aeae801ce0785ee096bd234297168eb44c66d8198bb3d1637b07ac0a8a0ae58c41096564b5b94075fa51c67ac8f2bf53ac3052fecd1480986c4bfad3256eca19ae266da080642414245b501038103000099bd2210000000007c7df0ff44968add640ac63be2b758f0871ed2d0c8a5ecb0225880411447cd66384e4f7d617556c0cd7eead8a0e6fd4fca3428c998514836082c4612de3f430cb116eaae850eae19dfd2721da8c28bec6ef75a51cbd8675b52e99cfac0db400205424142450101509e807d89e7d66090409aabb26dd0b4ac337c6df4e4e0082a8d8a6013efd2094ac61de7522640e1c2bff8b2379293ec9d8d5bf1a821f1fd97d49b7183e3bd8f4f86100d00cbe673b82453b3954dc099dda126a25b827fc95a46b84f5233dfb64eeae8019f674ae4bd1904f17e22a03e971f0edfcaab80bea71a0a608262f33ef38563e0fdcdc49ebc043684ad4b018abc07b31987e1bfc40c80464208ad374ac0b5d52c080642414245b50101b30000009abd2210000000003ee026ae66aa8c691d19378e8a9640a3a8b79bb1831c17a66ae50dc079db023dba3db5a4a0dda7dd219dc8b215e0f3ac4ac872d94dfee337bf277e38ff303d054740a69a478cf19e05562418b6f5ae6cf6e8cc7d6f1652840ecc27fe6e991e0105424142450101a48dfff1bfcd20a8ae6052b18ea7149bd117fa1bd86ee4e835e2f99736b4d34608ed8e8552dfb9127f75337dffe47077edec980157405610da6904d41ec11e8e7a65468770551abe28f032b008fc6ed26e7a1e1515252cdc9563ae0237cd0d9452eae801a78d5223a1fd3a47adc17ae71896dd22e52be1b26de681a7a36797d069266b4863405fd8fb7f6193c65a2fb8d22e23ffb0cd97442dfaae43b9fc3e606841b663080642414245b50101d10100009bbd2210000000006a2fbd90fa82899c7b5963941ef0dab1ec904695e53552a845be39ded0716a6e1ed7013d61fb6a4cba769d206fb4858a7e9add591cba42b897dbbd46157e370d7c2ad4d20f3f3e0f54ad97d070ba39e3d629b35f4f7e0364af2d70f639216d020542414245010166d9759935046040960dc38f19b5a768a2812f3e318fc492e57acbcd27d1e2083bcb5bbb4c1d917543f39e99305d7738cf96fed4bf8f293775f83b296febc08adbe94c9b87926dda3193cb6186967e1cce6bc0462dbe567b9ec72598ca00ab8556eae801a297643c0d84b42d9ad7dff94199d18b2b6eac085a3e4dadc36b340849640bfeebf3ba48e5cb515d10ea2b52fa62d90e9f93aeb117abc5c44a8df124df51df88080642414245b50101920100009cbd221000000000c419178110083e6d6216100dc54b6d1109ce7d170d54725d724db0b37e07cb59bf934d4efe6a77140a5c12f174740e754a3017979c10cdcfe2c70ccfa0c782078c718f981833d8d083f7808190812ddf1ce364d53724566787ed29316cb68800054241424501012e543111b10bcf9c8949c58daa51aeb4b6707eaee43c62bafc9826287306dd7c204773fcb6b4675501cb8d9a7dfa9a8a427dc36915099f90ca946b4ae260748b8ba7212f885fd30e8ffdf487c57ea3ac9efb87be30d5f265a3b71430623ba1b15aeae80171ed6ee16a866297c5e77174dfe6eb57947160068455f929ce6cf30b2d64e97cffe3faa575e85ce13cfd3dba16d5a98d635ee62730baa83150852279024ba1d7080642414245b50103640200009dbd22100000000042dc978eaac89057ecd08f1f2a6f73ed1b9b0cbf060ab7d089201fcbe9340a4ffb6bbda34c82735e0d1956a34455816d187b973a009120fb1013897eb78c8d058e31916346f36dbf6fda3f8fee72f0e3d8c0b0fe65e9e4c2ecaeab5f0f78fe0005424142450101ca3c509c21f32134684a9e57992eedc2e55c352b57b4d62b96a855483fb2e36ffb1e76226472b6a4ba285fb613b6275b9184b20a84ee9308e62db22f8cbd5b824d05e94b2a81d7c8dbfe668a340b582d7caeeb29b50251dc0394134d94143dc75eeae801b6b973ef3bbc7fa1ea1a833dce0721b099679ab003a87b546898e850a43bcdb8a23bbf8f9f180b6d220e89ff36b1ec1031e0f0c08924275f01c00793f71c383d080642414245b50103780200009ebd221000000000021ce0ff5f4aab1ea26ce2707daeda1cc114cf23560df2ac9c145930ffea9404f3e646ef1f6d569e08033d29f015e3746f3a8c05547b485631bae1716ca3c0076d6efc199556ddd06460abdc41a7dc050e439ce0ba587524387244e437c1770505424142450101d46c56ebb5d672d752979f9b0f6684ad9351b4b986126f1c50a19a14a669d747aa17359dc6018ee45324f9636130512160af3b6fc2129634be66d800bed59d8d6df2785eef6651617dec9b6933ed493b8d0903cf06af67b3d9b900eb95f927d762eae801fcc1d4a0e6530f0c602bde88b62b1b917d089d6e5bcb3b4262e5da291d2e99eedb0849bbe33a41dba8d6b1c5101a34d08d12ad5c3917e74c36190a57da411143080642414245b50103140200009fbd221000000000685239f27c9dcc639768f8afbe96aa6d3753eaca77a3b662384b34ac2618e70e3d3176453ed3adbab36f465e98266d72272c69b174257c861bf27358d64aed013e9e20853c8534fe0407ded733eb862c2c2fd81b77e3d2dceb3b5fdede208e06054241424501017c174373e8cbc447162867023469f69f073dfa38a9a4caf577b862ceabfeab41eb1404600c4a4ab7afaaa3e2174e4be0ebb38dcefe9a27a021db5fa529c6cd8a469368234724fa535e2fd06b26670b355152042a6017643284f040b4de3b6ef266eae801a99590de8248523c5d411b9eb5b9388873727078a570646e00532f0b33f49ddd7fb0a1bc083cb73919df25ef201db992f3cdd8137432f389e488fac5391632f8080642414245b5010369010000a0bd221000000000e8366c1369eacc4fca6e09022a39a9967507e4782bb04acfa8618a54a31597208f33975a99fc7d450a17514553baa8ad6254e2ea6e9bb66a9d7825c1bf9abc0763a9f69e20737eb5b85cfbefc6041343887be6364da2e0b9d3eee7a09d9a08040542414245010180be1786dd022b6fd23609ae0e5c5eb33c09e525077d36fe59a3dd957fcede0704068a0ed8e8679ab600a08cdc2ea4cf0011a4dce9cbff77dfc2df582dea7d88321c871f2d81101422fb24d4941d4e873cfa88a17b1f3007da4fa9e365b32cb46aeae801aa7d472562bdc8809f5cc1592f02550978d4695d859110eb827caf84d68b0bb4c09a7878b054a841dd6c2bab741eb3a4fef21b49ff82bf63b60f583d4d5f36a6080642414245b501010c030000a1bd2210000000006a84b79ee846f5e104cff301f09e0940ea17c6851e653b459f89b9e6b0a668572ad3644e969f804df2740fa35108fd5168b40b3238e01942b3e8c23b54d67705e71405c7db0686290d3901076e1b0f088621b9b12a1c76280abd50294e72f20305424142450101d6f353cee4d1b4868bf40cea652ac8c34328b70a232d4c2cff8fd867b1124c56a357119c3076fd224b84e3ac600c71410825c1955e1c10cd631b0b09db23b68b67daf716a0df2884fd81ec2b923a773f10f0f90f7bf294a2ba0e2a94065354136eeae801dc27563f89b6d889d18a2fa7387e5eac87685d103e117ae561dc230412a045532c7ab87f6ed74ffab4130c276bbb52d88daa8884e39144c1e32968e7770a657c080642414245b50103d6010000a2bd221000000000d44c2d7ea0913814a996f3f51193bc7c2a7a36d9255c1aac54ff53dbd45d582d8d601ce30926396c4964b5089c86983d59f4980f99a5e3ce45ec06223dd0ab0a0f3d9b624f107c39a7e6cf14b6412817932a69793b1cab60e27332bcc61d8b08054241424501015ed8b6d03925df2f59e1ee5dc116e9f48dbf0fdd829eae8ea90ac32bba582e3db8c49f7bd637a9f5ceeabbcec75344f7a4f55cee3ccc58c1522f43d55c3d0985d828c4aa3fb993dd1ddcfbb89197526e9371b2d8c90b805e56a3fb4236266e5872eae801a273f37f8586a131440f3a571d5d13572ba6fdbdbfe78251083cf3429dc22c2a9c4142e16761d8ef7fb89a3fe248e8348104e49e5a5a7d028e92d90e5a25f577080642414245b501017c000000a3bd22100000000040e1cb7b5274c4fe0278f884f1bbe5419b98079759ff2168aa994cdc6ec7e30e08839f83945207eecd70277f72f2a4e8a2534a3183923f2e5ed7d5a782abfb066622027c8aabd8323f112831b3f7cf3334a71d3d85a13f16723830a2b1c67502054241424501011841eef0d2e126010ab6c9b94b817116a499e5168372fac49827d13a59ec331f1a12bb92e3738a1355d12d3a96e0134975c04956adecdf23e636481127946d839264b8eaf939ab73574b61322579c840b8a01ce403ca6cefe3082c580ae734c176eae8016c54025fe2f2b9f665ac8aa6503bd22b6bf59074178cd6e51b1c63d66daa9fd3fbfda2d40fb972dd61891ad5566fe6687e2f84b8048493be9e6883e9455c3dd5080642414245b501038d010000a4bd221000000000aea9c6e718a53c72424ed94d5ab15e3eedbef65931dd9c950f5d2f5ca2462a40223cf47ff20266c1a4e702521df11c26bc9a465dd5dc37a01b4faf4ebd9f8403e6cf7dbdeb020bdbbf53df390c117a41633a672699a05bda35f854462d96a3030542414245010160ac703ad3e936da74eee62494c7e89dbaf29e97f356772bbe1fc989844a752ceef964f9a28a4b26954160fae4b1ff232abf41755cb3630805725254905aa2806013cbb26810691b5a3bbffb9bd81f506d1beeda158fd730841592bcf20550ea7aeae801da5f7b060063278240aab71b415875f4a238cfb41b56a3ae59efff7c5af30d8b376028df3b032d2fa5ceceacba11f8d160b3f675ee9bd41fe7965e80486094fe080642414245b5010312020000a5bd221000000000c072529fd90f068607e0c644f2adffdeb4ce5d97fbf5d8857d3230995af9354a4fd51e7f14543c093807b99f70b3fb063126120e4e5f16a5f609c206fd9a9a095369298b1fa59189b825d8c77df6afa944bd885b2e2b8d1d83e5c98381021c0505424142450101b4b76abda6d42cc13957f32d1b0f382b02ae972d402ec4fecfc627e55e0d0801dd3778e473fa184f30df3035fa73dda2b9f0a3896e4eb5e9e2ffb3d0cf39628e40440b37912f0fedb12c553424f1035efe9c338a5b1b0c18d2e70f05d6c8811f7eeae80133759774678564a2c7dde3752db51f7f6ffb19f33409ff0d490289b22cbb5bc7254e73573d49f244448ee18148a96ffcac8a2156baa645cb723de63d12e0d4f3080642414245b50103d9020000a6bd22100000000064fe0b58a3bbf5318c963c58331e93a919a3a1e3a564da36b120fb3f2b454e1537804facab54dca77e1d192e6d2b6f8015bb0dfffe786e8f90032b1e0ba1c202ae2d0a4ca72f1e0fe3fb5013e55b352d15996367fa6b0f893865f59ac23500080542414245010110e3c49b9e6394354d605c1e4484168ad01b4cabe1bd58154303a9f51411af1b0f5f89c0b4fefd33ee8c0bf938a09790893eee2aa8928e6c99225cce15b6788c28e5edfbf90c807c9d4f24c434561f60ccf2a7d96c659203be10f72ec096357c82eae8016bd1c8de687f2e9ddf24b1e8c7ac1fee124aa79f0e1d364aeb158b874322f8e7efbd3d7acf18bbe4579c54d8fabad9e40a5e7d132248305b31959cc607414953080642414245b501038d000000a7bd221000000000f2a83ce60d80f6797058a701802bfc2225d2d0307e65a20319836e1d91212a3fdda1c1d9177b385c868834e7db63d7a74cc5aa6ca5e5611bc847de533469b4021b84789ffb8a9af54618fc17ec9f1befbc2662fac7f941686508669ba0c2600c054241424501017e196f8bbba8d6c8c92be54e7cf2182890b347dffbcb50227753f62589389b72f454ba13576a62c7b8fcf6ad6d93159e460478488795bf08d98414c1956cb489739be7058508bda8a5e1cc288e89aa1f4ac1bcca0a211bef6d8c5338a7cd87c886eae801ece89762b297d87821a62e8388d8e6b9d1eeac0b8a2726360fcfcc254ca91a3c755fe7cc7f210ad43c66a058f7b97cb1eff08a3187964adf217c8bdd4bcb3431080642414245b50103e0000000a8bd2210000000002a94cc8da57d1c9899ef66f47457cf6c22b78597370ee0ea9f10e03ed453174e8cb84993457bafc49750a3063d965470ad5dfb0a9b72039883815693447ae8010ebcfddd31d218c6fe9255f34f07c232338560b00e06cb922bb1eba19917830905424142450101846de95a391e9568c3794e43b652d705ac2ca2a74259a60cb0b94f579f02912af3ccdd79b9d126183db81b691906ab59ada3a94b7d1a694b40683b257086e987cd4a32825d71ec03c71546e477efb1466f3528086d5e564683cbffbd5f83ff208aeae801983a47c8f14fa62d6f056dd58e93b2ecfa7944ded68cd85dbd1697a5133b5fd3330e2f6103e230b91949bf5981b8d3ea6f72d91096d0c224f1389b7197c44099080642414245b50103f7010000a9bd2210000000001e561ae920b590f8fca942ac96e8037281ec36c58352e377145e072451ea807b82e9560f17a9ef2688ef0c15188c89364e8c82608f856395ecf1662126217f0d319718d2ca6bde1ef0cfd3c96b9ade8f9897cf494a45518c6b6721021adc6c0605424142450101f81c0f11255daa6d2b3c04524e6d253ae10e8855ef6f51e381eb828c2d0fc551a10a536a2e1f36ab045a5621f10dda646eb45e53b5b5049e8fe57f41bba790862bb85ed1e80b6c893fcf79472f070a8e1fcc1db49af8a66e0dff47a344e194d08eeae801b8b323d6a9edce0c24a7bb88ed06615a8fc9dd5b48f980512531f96b61271bf1c52967c81cd573115b8e9a9e9cc9c8dcf9019b27845ac6727bce3a25b93c314b080642414245b5010352020000aabd221000000000d24a0ee27630f06c5f673e7ce6c6f451ab9af2552a2a50fab339df2fc6ea352cfb4a2dd8827c8ce978e944de58cce0cbe39f795f2bf3b7db43573c7d595c1f0b9fdf6842b7b1e4f37eed21ddc5b2d4012dc99894ab56b7440c27dcdc738b900f05424142450101385b659cda9a7f6ad0706426b125f6b4977c4c56967f7517f6bb46be00227b2935a28498bc441fc262f9c4da4513fe99d0125734dfbd8d7f6ee2b37d2391eb8ffedadcc06fd3da7bd3d4f4eddf5479d3ad8f0ba365276148d9f12f28273a1d6f92eae801372d3b4dd2a78b444c3f28c3294e8c49d904109ad7875846350a451f922783fd9d0265e5b36af56135caf675db3ed9c1a2496f69e4e4cef51d490c02943a8189080642414245b501031a020000abbd2210000000005498a91af0b1004408f8914b5798452bfb0022b33a75dfeebb8a332241d0964c8aebcafe371c215f4e5b0b660362d3a143669c9d8b1f64d80a1d13f584c79808e9c7bde2cbd4814c46950b0ec26cd662fbbc0a083aa2f394c539704d9ae9a20a054241424501016abfbd248fbeba8717d16790591211aa80d5c69b072677553ff48ace9932ed6e080526d34aed3988a260389094bc7ecc7a90bc172313e2b3704cefb58e44268f7b5488641d9caeb108b052ef693da3fb7d50dd04c6f44344f04e9eafdaeb842e96eae801e28c8d075ab44963800e876aa1c65b840799850065f6407a86bb9e8dee1171a81a39937642c0b7d1510bc373cb3cea8f90415128b0e95652aa84a18a7a9b97a1080642414245b50103b1000000acbd221000000000d8f809f9cd540710ae6d754095d2a1aac8f92895e0b9864215fb955ec0e29e41fdf2aaac27aa22050162bc6b7073615bca731dfaa8e8ab1fba9d9bd59deb4e0a011b04928aa3debd44a7252dc4fdf49c3cffe7b5f946215f5b5b0568a902630c054241424501012c9397979b0b8247ef1cf35c51267d1e7d38f84e848c4c093a977c1b5f604a2772bf85a3760e1c43ffe2aa7daae27aa47b57f45abfab6e0f39961611a5a12c8e5fd3179b6475d431486f0046cc985fb7d353de2e867ef24ed1b49f271169b3609aeae801f56c850decf85ad4beb7d9dbe187724c5eda654849ac0d8fbc43a77108d90bbcc2953e65e421029484a72e44f01eec726d92b126aa9c0689469bf782aaa34659080642414245b501030a000000adbd221000000000ea211521414a8562acb86fedee365d906553dd9b537e4cf19832b0a24106333b068af7c5d6ab313332c7b04527a48200173606cff64b0ed3266615c4fa4ab70a9a488ffd6843cd779bdb56479ef856d8001af168dec8121b2b719cb36123820e054241424501012c7002dafb8a1745345621168cc18bd01a2d9adda017be4ed3d431be2b0af25ad159c9746a585d721a0b01bf488eadffffce51b773e5811ed413e74eeaf9b087a19e9225da7a2df988742680bd8b405f3666884c89b488de4616fe90fdb6ba299eeae801966ccd6a1cdb2a94ce103d5aa62aea0d049c6fabda1e3ba85e7fdb0681a8bff1dfdddeee14b10f262858be13473b75c1bcaa8dd6425389a09b5f8dc5217e8d80080642414245b5010336010000aebd221000000000441e0032357015d402155f751112e9d2046374869e7e8ec46637b0a80228ac2423d42ee6bdef08c1adf4696209e57a9ef21e7a0b4c86e7859f380acd6cfe4308ebad74e9f283fac9d0a1cef4cdfac38e8d9a908934e5f1877a761ecef643970905424142450101c8c0348731053a327721467416142ef9e7e369900e5a9d433fb4020c106d23737cd15c5640e5c538ac49c8c5eb8aa11bfccd6fb2a8397db7523109f6d17fcc84a50be84e5bf0da66904942849f378f5fd09ee2d5981cc6e1cf3e1ce194142138a2eae801e8b0660a4a2cc7557c841bdba8bd0dce0fe4620f890d5057cbbfe48df1751211f6ed0a3803cc61e3e1860e78f3ded22b2db7d05675c2b736c5ae9004095c1364080642414245b5010351000000afbd221000000000521be6462171e84d3491789a6c7e69ccccc96dfffae51f2a4e378cd621e8d92f6b0943b04795a1db9d98dcc98a0de099ea2c1fd9d17caf2cb2e3cde5e1941c0f7ba0508a2e55c9e24964dff47769f4316916ef9dd732549f891047c3236bea090542414245010112fa30af1f9b43ebe0d8a1aee7b60cbd7d39dc412d9dee6c926df8449326013e6a271324ccdc04b515e58b36e0498873f2f3c3bfcc2827050ff86e662b0fef8290b00d063d6ec79046afe0cfc4598577548d53088b36d7aff7ef152e3edfcf05a6eae801cc221dadbecb19c86b361680340c2bf56b507ccb14595329c1a99ff93a6bd90d4adf10da0e3c36b41f3071cd91cdab2dff7574f537a212ccb3533e6cd8373492080642414245b50101bd020000b0bd22100000000008f86caeb73fe10a423f7bc3a343fbf466d2cf8fa16d657bf4346474a1260c138e99ca529300b34a2d26dd22be2c7c1392377c0fde97b339ce2972bb74da5e0efb5e40586f9a29aeeccc906768fb08c5e622ac7c0c7964fdba185ddf02958d0d05424142450101f6a1390ec36cc8b59668b1eaf4948a449acd8f3685952310d8aa027e26d8eb6ba0619df7ac68a8438f976fd4f1df4d895922c29b50cfb3f4d70379ebe20caf8e90b20bfece18357987f91e1a128af7eb62a0c4d2501ed4aed3d24c6f4ce3d512aaeae8011aa30f33d8143e9f2a88ab73a2aaa63ea05c76fd1b6e87b4f73bd169d910479c2f14c20c8e7c98a3ad05e795cc1294005f4dec64d1313b5da60676d96ec74c80080642414245b501031c010000b1bd221000000000d0bb6d512387fe87a9d2a526c8c16a012b28c9aca887aa869a61fc4bbcf9917f0d4590281b7680b79cbe15a9d4d28be395011fcfa846fc1c169ce4d96bdbc10acf1af85a98ffd954dde648f0d32b8cb08a28fb68a5d27e4acdbdc471bbe64d09054241424501017692969de12aa93443e534eb57c98809fe7ae2c10725b6d79936e353a0c0302bfafdfd4754f3fa4e8bbbd423ff6b5443d33c09da5bb7d879c241e1c1dd13c8809b38f2571d009490fb707c9f2b682107c11a95b7561c08e0c165d8afc21df5caaeeae8018b13715758aeaf80134bfa73c5e2cfa6544a7299eeb04bdb484f3ee9361d489fedf4f9b83986d516ad04add7d062b8a089592348ca3745bb85d8e835acf11bbe080642414245b5010307000000b2bd221000000000f48f63bc391a7e69ba67f3e6d375a7f8348b355daa52b3615ad0aa6dcbdbdf4eab036c50ca268ea6b17f4de79cd6c8e2d9ac3d8202331a9519e3ee8dc7bd480325d7df43a1e47b2ac9d17dea5d82c56b7c04b163352df3d594f7a4e91d7c7601054241424501018603451c69c7806ed8e1ffdc489a679012cda04e0a55835beca9fffb225b4934b4a3be13cf4438c6011b47a9d2fc2ddbe81f8395b12312e9100c7956c8876584c60cdc8866f73808129fa2867d589e124d59854b68b60612ac575bb580c48ef9b2eae801285743a643734a36d900d4fb3beca9325483f4b2ecfbc77985551f68301de96b8e0dda54a32f40f4b6c1ee81b245d7a8a61d74a0f9ffb358c3bc0b8be8501411080642414245b501039d020000b3bd22100000000064c44838d023228e62197400eae0d1575f6c0c0914f567da35eddf3d90d36c643f4d1e040ef3e11e8648a75f6e381fba65537235ba6679e2301aeace77ac6f0f08bf9f974c046aa87958a38b7c8b7be65cd41d37a763711a242bcb7df585670305424142450101dcba31183a23acf8a40db11e8ba867666a2e7fd0a3e450a2918066a27e152a50ff61cf3df0c49b1a817957c18175f4fba412a3af3cf96e296b826ca4a2313b855cb785c0f267a7897c97b5754f691b37f2eda6994ee4b5f637aba037b66b758cb6eae8012d2d1b41aaf323f48b50c10061d76fa86e7625d570fbb6732104153a96fc171a7fcd728cb462509c7e5ebb525618b21597ce199ae25465bba05fa88f70a03de9080642414245b5010307000000b4bd2210000000001ea59478d0b0b558b9e07703c14bd22512b69eb947a1d29a75c84b28dc479e633bf61bc2c4a69c1927c40538c61eee35e525ba8a6549f65a8861197fc555f60c6a4d6d96b7eefedae7b3106e41d1b33aaeebd8b5ee58c6d2c2b9b24d87453300054241424501010e82289888be71b125c057ccb499a0be01bcc90eda5084aea633b90e53c4f773e5c660512cde8473269c445376b0023c8cc396119cb88811a655b195e7a3aa85de535903ce67384dcdf1c434862e1105aff611bcae7054474655aa32dd09dd9abaeae801bc0701d0403cda4ccda3e86a63d264dc5c6590c17ada46aa6d15cdbeb8b24c26132869749f429c221c9dcc15e3073fa834cf0a089ad71464c10b3b72565a680b080642414245b501036a010000b5bd221000000000b4e5b585a2e561e47500b207e2026bf749a7e974306728d7acadf17f8c72cb35f058bc6d24dafc38fa7fd32c4b275dcdc310e3371f1b752126bae4298f7e9d03ff36f2cf0f7d5febe999331d3db8aa71cbcafe968c8aadf65522089a7fd68405054241424501018c8b353fbe1a52e9f22e182f44854464aec7a191bfbb669f1878f6dae6d25357a38438b296634ac42098952b203be34c91a24bfc9d2f064fa9916790e2b6be8961ac43218439c8d2547560e5b56281459657134c910a7315e5e2b2eebf55f1f8beeae8014beae02003a5b28d19513e34e5003685aa18b6f7a9296370b795cd0d17d6ea34d070649ac4e3abb06ff9e1ce88abd3ca7e0d02265260700b8ba0cf9a66d60673080642414245b501034e010000b6bd221000000000b42abf99e7dde8b7e8d0e677ce19a7a074174dbef2eb99d3390950914137052d82dd5bb0b8641497f32385e656024aadf1e55b09bf366bfbe8cb814f67a5370a5d7e415759c135b1b93075def1333961130b8298c9a5c1d82ad444ef88d76b06054241424501016cd9125448b92fef02509b24ecc600d0a0fe640ce19ad17b2a328b9c8947905b55bff52f36a60d4523f49cb088e276166d0dd1234aa097dbb0841a6010e9e68d56931ed828b6382648092b8468bf328f3033e4d596e32780616dad10a801bfaac2eae801eeed85a8c4c3ab44ebfc764d6a5983408668b07a74e62ddf0120bef671fd4784548919168e9d7ac2fb3264c05b2df8661f8c7e0eead7a9f15c3da9063cc168cf080642414245b50103c7010000b7bd2210000000001c6d6611155fc826fed9e77e2dfac35ac982e6bf11884d15eec77c6a4dc8d650b20991e7e1e1190b4eb50c64be0c673cfbbf41d59732499ab3b32c692fce8500e34fba563454518e87f303d2dfbc963bc6875377794e7d30d3dd624ed8090f01054241424501011ec9849c6c7d32f88a73796f9be869f83ee1c3739e5aaaf3566d6e38bfe317123c2779adaa7d14cd997d049e9b7be78cf3183a7e487d0a6a534c5f2cead6478ff022ae8f0ec818d5aa09a6e0614a51180c866af6b0bbb5a54ef1270c32fee444c6eae8014f50245643e8b2d4c81cfac0022a4d9e65a53ab084a8add3b4d4e53a6833250dc269b89f03d089cbd936613d30c0fbf744fcaa02b7b0351332168b06d753935e080642414245b50103bb020000b8bd22100000000034fe3702432292644a3a9976b645cea7f60791b61c206d9c666dec06ca1b746421cd165585f18169ab4e56e6f844070eff177eaa00f792b7e0c5e0db2a03ce05d3ca5524035dade181317c351cb27bc9fdabe30f4ab6e022982ba5368a551f02054241424501017e47c6369ba9493878df38f27897347e286ee3178a46c037fd2c46f4e24b667c8da7a27bada2c8024ce7eca51090b1530d30fc46b2b2aa67b9941f02a596438ada5d868267d2b1a9a217f6077f8b753a4bb5fd229267326c946b9a73a3edbdcdcaeae80138dd1f2ad7f6b7302a58de5ec02422530aee3fb758f341fe85e8c234af270de7f528c919703d231d50d54e511642047f172e8bbd1e5e2b271876253cca3b254b080642414245b501035f030000b9bd221000000000565b86b93d3489d6954f72ed48863047404d082396866ec7bf89db61b2ac7c01922058507a18dca7a95eb1e2417fdf8bca95620f0ca61cae59aa6acc85ff840bd019b7f48e3ea96b99c934f3a737b099903f4a4c95338fbb35f35cd1cd82f40c05424142450101a4a5281a3b6ce8dde124955356fe91a5e93e823a29f34c7569a3f755bc62987fb46f6462335d2973272eb8d03180ed128189fa54867c8ee1f42ea82e92efbd8c05480964b0fbf1d6a26adb1f3657887e4158c08a1386157aa9e21263076340a5ceeae801cf2e9d8daae32f0635c45c3d4f56421c0d1075e148c4aef374feaf5c826f04ffe77aebb56f029ecb3b6d638e7a4efa4eda5e0d1b843f25ffb4bb35a9fd01d02b080642414245b5010352000000babd221000000000f8239d722bafad0a971612e47e0350f6f5f270791757ef9f58f4cd61d05d8a75db9c5f4ca9171a06ecf21810176bc43127b11cb5175ea8e5958470ae563c6a05d703fcaec7480b71bebff82820dccbfd6fa6ec2a0de90aedf088e758cd73f90b05424142450101d2a446d6a9cf061ad1dd7d56ac34fffa39341b47319acc7fed0bdb6ef7560a23f71818cb430e36483d27e1faee290b27a7d6d81ffb34942849eaf61e0ed0808a4414e549550ad07fec1178bf21ea28d0a32cc8bd157bb662233d193d8cf51b23d2eae801c629c27dfcf8e83322ca55c8a7b337989704821d74ac5b0e93c4d1d43ce2207e604c7dffa83dd1c1b8e16bdfc45939fe861a5342554ac10cd37c943af1d25176080642414245b501038d020000bbbd22100000000028ff189a3f9c0f362557c023b8c8f936798d033e9dee88955573d6b8bb5ee202a8baf15874ecf9346d3f9c82991b2e650ff22df6c5d3c88e8c65efad69b5cb06e7ff537e16a9da5fafd18cbe0f69cacfda3e838d78e09919ef61122a1d55310c05424142450101f4dc6de48fc6376ac2f57294cc22de664d220169301ac253aa162b25f57faf669135a0fb6cee58c9ec13a0e553a8eaf214e9c9d64e3411e328c834f138da7184f4a89703485f48737ebcaf8295d1f4e37e3693eb98eae6b2ac2b44099096ae5fd6eae801f307010f6043fa69a51863d082eb270bb5721648786ef13bbc7baa76e2da9fba46719e8f3cb8bc6558c7fad4c72a0a072c2731f177108990ae714ddd9de75d0e080642414245b50103a1000000bcbd221000000000bc519d724e1db21b269afdacad939155b99a4ddcd6f290f06b7bb31d8644a26c92a5cb2ee6a694e3f1aae62218719b5307fb6755f83629048e5b63a3f02c4e06247a077b87f9af1f050604c75146c02f2df391c6bcd391d095a6f10c18ee4e0d0542414245010148b4fd11cdb4924c580cb33ed97b31f0956500097740e360ce529a83c3f8d76dcbc0e7c48e5aeed913fd830f9414b0d827a4a5377f780307ed4ff7cace04c889057baee4e975c3b2741e315cba9fb8c1b331e9081559e3d54a889ddfc0c58b6ddaeae801ef3f977c7c158e7706d484a8d7043e0d2d91030acad7ccef43d804fe1cd33c7bb29077ccdf43a4df1a93f1b5e02a72a0567d5331ddafc7014ce78fce9edf8937080642414245b50103ae000000bdbd221000000000e645b9ad749ddb1e75de0a105123f2ba3661c3e82d5aa35c77763af6be5ce274c4f57b224eda3c66a43076b2c9e2bf44b1e3726d43df6d10b8de163b034dc408e9d395b6432b5afe929a99c80cb8149f29d8f139790bdf65bce0c40e37ea690d05424142450101f43d6e8707076ee6e50d27aca7ea81a552569569807d6083b5de0eb115ad6a251cac6bf543cacae9500c6477e965d854d8a4cf53987eda1c75cfb878ec7ca989dc813497a22c50f010c7a22112539a46a2fdd96b5093b6b8a18e8da1db2fc32edeeae8016e0d9a7c3198cba76ae20b61225463fa0d7cc48313eb0ce35a315d57b33d014a6422f9d06aa66b09ee85ca0b7c2438436b107e13559c961fcada32ef0c3ac23f080642414245b50103b7020000bebd22100000000016ad57f07ca45b08aea615e321b214f96f4107997fc50a01344181bc74ae926b20543d191cfbbc4a68e3833324b3618bb47c0efaa3884a158115d1b349b125072a3eff235b166a0aa59d222fcfbba615ee6d75b0014cef3ddda13c8fbc0d8c0a05424142450101881cf7a072aacb663e0e110d6ef428a7add1a1782cc1011c83d6dfe2cf34f11f70533a9a100e94971456cafcf796b3d78068fb81fc3dc14e5e68cd64b28b7e81f93875e21324d86b943ccba9e863646d7e72b298d6785989a8ee32118fa1c46ee2eae801e112986bb2b055ad44f3edef8afac2c4ea39c534ddf1a801a8f1e0643d4239bed264c8ba3f3de42971bdee51f901ff31cdca011df47b8961a0fc9c74eba0c624080642414245b5010189020000bfbd2210000000005cbc3fe84b1641a8abf5dee0b43cc348f66a040790ed98866262c202e5ea09398907dfe0483d1730f0ad801a54f8359a9a02e7be2051253d323d29d0ab24f00b819a290c1cd3d6a0006271079089b4f24d74fc1cecbe6a742f16a3b69684b008054241424501017abed9fc8b77f1400b15d03db96738883e84ab8cd4d9a065c41026c92ebd523ef7d2756a574b794adfd802be106cc51839ab26776ce0b7893d9ed9c6627af48386d9e243a68cfe6146495dbc56ad5a9afedabfb80ebc449043af5a35da6cd378e6eae801c04d219542e3424a84ab12850de03cbd2a4fe9eebdf89fd09de05efcb780afbf0a281d8f710728544cf7bd60142b25d199dc72f5c753c67e1e92976c15923547080642414245b5010342010000c0bd2210000000002e5a09c47219144978f33d451c7e461e30156e75d51e9499fa81c65a5b8f7e1b1da79918fb68dd7a4f9b22aeb5067d2e359ef8cbdab30f81494682fec638a70c1a891946b559577460896724f5c29298b3228aace84701d993968869adf1a800054241424501019293eccb163b0c6f43f34db4009932c4b0d59e9993aed0c33a453be2b0a1e72633804e302b863db48fba17c68a0102e34c337d55ef0b984f99deb37bc18f71824b21a0273940b76fbf3ca5c488caaa6717f61bd9e0fa34a18c2bd5364610964eeaeae80195bc87786acb55f280fb722a39e855eee983bc288ada9e8575ebb96c9777dbcc2355e2976d6ea433eec38f5e4a9c5e53e0e5562919f85704f86bc93f9fcd39b2080642414245b50103a0010000c1bd2210000000006c773d227c9ff39659e68f4e56f04aec2b3594ec641cc21846b2f0d8599d3e7fd5f781e41a04d143a7d4abbeb0f357a2f45eed6bc94b5e32569a52d33b210e0804078429d65b54dbad6e22678b4393e985e11736561031f217ed1e55c3c23200054241424501017acfd4338de4cc414bfb77aaece876d53f273ef05e315537bcc087b630d79d687cee4f025ead089cc2aaf676b1a50c6968d3574df4ccaf210a351ad7c8e41a812f980a646179e939e6761b3843475e7bf8aa639915ebdc4f618c960315fc5f76eeeae801b460ca4613b007a293434801a1e57ceaeb913659901c39fc0c12d31460fbdbd21d033ea74b840b46f57d20a8be6c4fcc1d75d0c0d5394be6e1b8c500c42ed0d6080642414245b5010113030000c2bd221000000000404fd0266814f4179d7534c8892733756e9495e345465e9fbf68481fc3bfd41a83542a7507293959f73dcd2289d91a7b975376282433e2ad32ec63706327e4043d7a0f0613f4e9407e96476687c7c9c65c8c93acafa38a963712fd2dbd62500205424142450101f8629ffd748788729e09c43e8ce345502ef7246685f28b1eab1faafb1ca25e35b1d741ae93e96c2467c1d5e0a1eacbc471c98b9fbe4328d805e4e0d48a56ae8839bfa5b10c5454b4a16c98cdf3186403e30011fd6b27eb98b35935d0b1ee7f0df2eae801c2fe4e4deacff3dcb90ea9167d61a8172e175ba9115f4de0142006a545d344e38cbffd31c66c30004dbceaa67918e45db93cf96ab3e6781fc7bc8f0ee6e417c1080642414245b501010c000000c3bd221000000000606b367165e2ba63a03cc41cd8ffd127eaac38987e6284894b4a76eb57f54376dd39299ceed224ddc2c1e644f6966a972c6cbae5f5f5c0317f3deb6e2fae5009f33e61059d5f5e22ed8cc4de87d6435f6d705336135fb61260c9f90f5f70cd04054241424501013aa75a56a5a07f1a5c241f5991cccb8d2232d5cb34601588487c66b7b24f710ddd5e57ec52b8f8bb4eccb5782eeecd9c12dde4bedc221a022de0942962f58289dc78ea07d6b51549881a01acca218691f0f44d40f97660be9499e84779292c97f6eae8016947fe6c7b232ce2a61a7bb4b9a2c3611e090b32d595428d235079798584e3a35f0a115ce89a9f89b9f59971e674bf8dff5cfa0beeceab2ad474628d3bab05b8080642414245b501037d030000c4bd22100000000010bb2e77d3475ae9cacc0da461f66446b7d1bb66ca92edd1c6166c60cc8290433d74c8b2d56283bfa2a0f78c208e75b8cb8ae1ef9c7432259afb89eda192630ea36993968f1a8a56955f54cac322821d448d8ce5bec7f35ba3808f67b2742a0d054241424501011cf446c956d38833d37ab8e30e73c61ceb9388128c89537a035993cd930684275e397075d72f75ec35dee802ef96828442b91a3f0e98ff5271c29d87d6fe258e17e81a974c5e061b8ee824b1df1aad3a3899b30d230ae1c5b554d477140729ebfaeae80137060983d072c3aba3e6aaff2ffda61ce2027b61befea9aeb714ae1b1d35424862609571e5c20fb2ceefbf84e4a5cf7eef5e024756575e86cec1e3a84788ff1a080642414245b50103e9000000c5bd221000000000243cf4cb3c6110011c69da33e452ab591b996b0840aaf4295dd3d21b84c29f7f3bb5bfbc0222664cbc7e060db47f866f362249e414a9e5710786bcfe1e278101828988f094960c401263107e8128312a412ee7b1e78eab39260009d70b723d0f05424142450101809b61b686a16e7a1238785cec0c1c5115f2febbf68a2ed0817997567f07362b3a4318c09cae7aa00c7336e9783926275b4fcbf7d0d07bc3e6347f62449b478b0ed1000795a419c91306e378782d04acd1de4a4a81bf955ed8b94ad390f92f65feeae80136b2be207bb2c59dd7f267b0198a898bf1b10e50708ce97d66d2e3f2401d011e9185803577e37a93088a6d704bdfe83f2fac25e43bc9e685f2d89abbe4f62706080642414245b501031c000000c6bd221000000000f278aaa75ec4c50a4e8e5d69cd30d5452560f3fc5016b95bdbca67b759d8d271f4d61714766e96ed2e7f91c44db4a54a333f90549e0906aace1b95816d20ba09950972b4895d701fef6e333a702148bc5186adb414ef18c8e257d574f26e890c05424142450101bcd8f0efff80f26c7f141f3a0b94fae31e636c959ecd92585f81eee25f62060d6243c9eaed6278fdffb807b037cd2ea16b39cd93538a2434679c1c662003208cbd160b2794222e8e2a62d32e701a0e777d353079ca859d416889d8e5f931ba5102ebe801f4b6a01dbab6fc7a844c8b14892444c5f11cf5108b11448a99e477f0f1368cf0f0c81cea4130522a02e6208692fbc42db3fa5ca3a409b236c5cc38ef3e4a90ff080642414245b50101b2000000c7bd22100000000098f51fb9701abc797b5b53583848a17ccd0d10521e79efe5d621e6c5d623470bea8ecf294c09d7aaa30e7b7adda259a1256c1b9e3a094cd5a5483beb021a490cd1be9814f4267397322c54794c460d24665042a4f4fafbf6adc7c72e4bf62f020542414245010102bae0dd6a42abc7c6cfa757857b30b0e78abbb125f903d6befeae70bd413c39fb346d960915184c5bb52b9422fd73bcfc62c08ba6c5597392981b0da4f01b8eb81268b3dc91652bd333372b3da32b167e31f6bf77c37c0fb562e7d2c4015a7706ebe801137e23235c60211c2b3b44ac6f9154498dc810eb780c5608aa3f2c2d463719d670341a0bab99efbbd8511168422234f3c4e06f925673d13cd33435c775bb72d0080642414245b50103e3010000c8bd2210000000003240cbc602644697e87529cf52ec54d4bcd0fff8178878eb1a3f9c083239b6526e1eb164724a9f1f94af06c5972913fae57d760e5b8ca9621614f704122ac000df801fd566d624a10a661b98629a4812ec3581220f80b373c6adb2ae996ba40505424142450101e8728a0a73d20169e054b96d0ced1c4c1efb86608654a13c30fef4833aae3f32958c8ffb85a4df54bbd0f1325f928a9b08fb79ce100ef5f720881b5cbd36b286c05291aff684ade05f54c143bb04b05ea0a45d2ff9939838f9a561fe6621d3ba0aebe8018d2d3b3d7986f5b9a42def1db015ebd45644f9d06dcff0aec67c202316f3a114103f36adeae038cf9f888598dea770647a56614cc0ec47960addd3c3fb7f07cd080642414245b5010108000000c9bd2210000000000890f76fb8d8c6952a78133b477d4f1e55e808f4b04ef8eb57ed26f4d8352847aba5e9f6094eeb9337dffe1bee431dcb3c77cf4dc354122bdaebc66565b080095afb6f24557b200c350b453ed415d70d9909f6aee713dd6d6f1f968a3172840a05424142450101e210e09e59a973f162cad29c910d5f813cfe88cb1dc50fd7c1cb3b9c32ce0f742639c4638e23245e1706c92ac29e84b7977aee4b7210ef273fa6d5787879528c6ff01883fd5baaa08b0f9b29b0bc8cd1c63a5a704ff1f4600124d6a3dfcec3a80eebe801e27340308b1aa8c97fefaa91656fe5e5c4b3d2701c33582ce1a0bc5f7a5ddd0da0d623c74c69a524d977a42dca43877666e8995d5f6b4e10f48bf000da8ae684080642414245b5010301000000cabd221000000000fc9bf18653d01c18e2a79d391ccd3472437c6cc9dab2fdb941fc6811f927da0f8a169284f833b84155abb1a85eed7038918df748d8e176b07934ef4335626b07d353cd3a253261d47f038e21070f1b2a16b9d1792bc8d580ec6cf85eda786909054241424501010e746e2571681bf194c7079ea22556563d9a53ae214007bf8eb4f0a41073da575915b77f6ed3eefdd4fed8157c9cc3a55590d3243f606bc03e0d4e6af206f887a6adf20fad31c5d4bd74593f52296ccc0c935307f8b44643c9fdabeb5f8c0ab212ebe801b441603a0560ab9bfae5606f8dedd802f581bf83cea982c049bbd64ff5ee38f828648f26db9450017bf7eded3d95247c42b6e512e29659d693783bc001bcbbb1080642414245b50103e3010000cbbd221000000000aa1d500d2bb2bcb4bd5e73d97e6cbf3984e448221ff963bdcd57c2143077a346b7b8769f44c82854f4ae685f89719ba524a1eea324f79a74efae7be30587fc0e8a2f75e5408c9989826d9870a10b9f5a4ba0d678a270c3262ae6f34d0b1be40505424142450101a6b26dc7edfe0814ac0afc7dd89d99f9e91d6b6db2504f7128427c324bdc003eba17378de497e96df9ec87268a5003eb1a443893f191630d0a6219e711a00d8e8b087045c00ad6733999934122e9e33eef67356eb2748f2e4c651b20a7c7c89716ebe801cb7cdd0602e348d06555008794f2a29f4d9292bfb9620e8f536f8b22dd31adf56ac03edcf27d0b7c6787a5fe0503f4925d7416f479f0d1767de92e2c1a39d691080642414245b501036b020000ccbd2210000000005e346605490718dee57cb1d5563f777d799e7c1b10fff7566049f073c8f61758eeb0e33fd9c202a0ccd146813dfe9580b0bd2deb364593237e832a096d64080182dfb1207fd25377bb1f1c9c0c53a8f7320e700c509b9fc96598f05a53afd104054241424501010604cf4e3e7438428522392b7274c2b925112f8e5b7a4ad7f1215f7f04cce655b1d4fcc7912a248f21639c2106f163f1f8a8d8e1fa591ab044174cc0e2809f85f957f271f39d7ed5b4e3545a30f9e62a62b7569089cde3f75c3caf85a915d7931aebe801d8e1079527826a12068561bb52ccd89cc180a49ebb38fd025136d3f820787639434ceb15fd8154a509555e9f4c5dad624b99107812d01763986ea823551c6a49080642414245b5010337030000cdbd221000000000a0dca2487d614dc100ff3a8be284c9cdbf8e869c8d2de426ecc3100675dbe67fb2f3ed6cbedd5afcd88305d226ac4e637aeec76182da5465a2f44ddcac3b7807cd5e5cc44144c0c6fce530edb72adcb0f9177307ff0f37578f2e54537f45700e05424142450101c4d9e2d44adaf213065df1b047576b064b2f7cefcc22935a77d18d4320471d414d7f841285e1f4f9878d31ef2d772d9185c2157ca88a3a739d3c7ab54825088b7eb33f4f986633d66c7a7bc30a34de449e8ddae0a72066d9062cbd2cef925f1b1eebe801f09a51bbdcbd3e0392f89271b81acd415c2515599c25a70732724a8c1a85fa74e910f3128c02c30282fd71b5f708171f7d43f84e3927a3be5390290564ee2797080642414245b501038a020000cebd2210000000009ec7ef892089ee33ef1f1496cd251826daab78e419e292c1b43e6859c7fc7042cbeafe06fecedb7b981b4efe598bc3259e2519bd35bda6eb24cf8f81dcc1620ed592db73cc098fa156c337e454b89fe8ae42e515453f0ab8f5bbb144b56ca40f0542414245010102eb8fbf10f8d85cce0106c507f4b97bd4897895cd994f404155e7935c59b0167b4e7d713bd11422a22f95ae342d23af87ab77593adc9528c3cfae7614b7fc86e7b14bcc44bc5ab317a4ebe634600169c69f3433afb410cf0f37f16a054cab3f22ebe801a224ee3946f3b4e0f7adce413596a15e9cbecfca42284b8dcab0fbdf6c5b6c8ed0b483c4bd26f9a25658d7a5ea554a58d2d0d32cf33e4c3c38676270fd6a7a77080642414245b50101e1010000cfbd2210000000004e548ed21e99780d9eac36e69eaf947c53939ad519266cdc2e5c997413429a7c2aa848be699c62818259b7a0c40d0e82f26decb391e1de38020503870369d602f069288e8bba225f2a0bd6f327b71fa89fd1b4a1ebe2cf074144d73c5e1a1a08054241424501012cdb5c43a5a86e0d7cc9a5a9b465e710c1c628c6358cee5f83a073910b9b83092e1376fd7bddcba1ee45eb7ce9684f437365df07787cceb856f618a467a6418a1c92840f421a65f0e21775a9a3ffb1c4acb0304b9bfbd41bbbb49b0f719ce47126ebe801b4715459bcfe4a162316def438882426f96973d5bd9850a3a7d4a6693d12165d98b1a087cf2c71573b1d66649d99625976fda2b5031fd82fc99b9d78b4ae02ee080642414245b50103fb010000d0bd221000000000bce1a9759fb20f409e4d70612403a7e7b0ae63f6cdf832f7ba91e030f0552b57bcbb4472a41d87b7fcff7c04e7d9e3146e6b0297d1ca79b87fde7f7d6ddad30f255ea6ba62cb13992b2111f86a22a7fa55d7da5455a9aeed4c8b5ec9db7fd00805424142450101cacb3ce0624fa1d0313f7cf2b02bcbb683ff18aec953fc0c313623a54c29542585107d7ef610a809ff76683344153ca951b22d9c0fbea5c98fac66ff13629d8ec8bda83579388e32454011d1d5c5b8c4e7c7d7594d6657d6be95531720af5d3f2aebe8014ae98ef001476b4f99a6d5ff6ad8ec441a7464e381bd148196a80c3dfdc055c2d4e094a274f8d451f636400fd6358ba1263c11ebaed07f5ec59520a9e363d644080642414245b50103b6020000d1bd2210000000003a5e475110a631573aa5dc388bd0faa4279cafa6cb9de4bc443a550a3bd13137e6c2619ce0cd0e09af9122baca16f3b8955030e47e68aa0803e861e2f9eaa0097068f3261ac39531b2ab126681547c81810c69492b3548fe85bc5856b08e5902054241424501012c1b52d07851072c35e95e92a86b98ab4684134cae1059ab183d8c69f330037ebdbd605c31eff31366f9ea43888965dad51942a0268e6cf75f7868a841baeb83879edeceb55936ffe514bb7d9300d9001a69f4bcbaf5d58e58a9aaeae9fe0f172eebe801cefc64ee7525306fae07c5c4b4a87d4399a30c7fa4b527fe278db6f655745ca5d59918eca7089bd8c1b1fdc479aa60d9364cccc3226a6a2988f153ab93ee962b080642414245b50103c0020000d2bd221000000000c62568382e3352fef4defbb8737d34d386c41ea0905571b15f9b0e6c6e95641dc212f62e62e4b6d89307741c67863f4928d7d2770b1c97b08e0dee36e50c1906aa06f5ad9ec6965fe3e4f39112e9228fc3522ca06102cff7efdc796e5d5bb3010542414245010112a31644816c70f5281b5d3d9dd05f6a98bce2cddf3375deb151189543aee6782da4f632f45dac0211022da84f34a4aa706c44291ef17304f321b9391401418b6c0f32e91dddafa0bf4131a19082d64394acd2678e372b7eee418e9c534d58dc32ebe801656da4f8eb0cb146bfe9e4c7e7495520bb271c47fb5c09fa53b7d213759bb28c08cf764a2b9d202422da4106954de9cfceeab34a9eb22d55bad54ff313e11c89080642414245b50101c9000000d3bd2210000000005c67437939795ce57a7b33c1c7ef6c547d82a20ca76b0f27af79e8767dd17c51624ac3319804adb1f877debfe902004fb8302932a1f8a19fc07d439e796d550e2b09e63bb4b5b658e840ce4fb9c2a20924023991cc37905620af42b2694b5c0f05424142450101362cd18f7e10e439c69200063537d946011db454a4cd737b02c1952d63d7ff05ed34b806e654b8fa027b4258062a5430c82a933f161afb296c5047bfbd95578f7f13d274ee4c1f3f3eb7624bd422852c775078f738b4f14c99aa90090aab3f9f36ebe801f77e2463c8d1617b8b194e2ddf245f6e90db4ad34b192c57b91d390114b463705af7b04b3779e58e2d47607cac1dad1dc4708ef39269a4f2a3d800f0a535d716080642414245b5010385000000d4bd22100000000096d0f03327ac8724c0ef515e84a759bc6925753dde354bbed31eb0faf4e9d62f5836f2ac5d450036a608b165e8165904e491834f796b61c41f8dc80b7be7f0004bf29b86f7885980d08585631a40fa757213d41fc5127c94252099bc9965c20005424142450101c2925c1870718fef9c184919b56f5e21bd667d97fdc411917855586ed676d11ef5c0cbba0b4e1aabc1640dc43de0d0a9d2a8be994d6451c8508cc5bad41d3281c2d636f300ff46f52441f3be294f0a6a3b06d8a9b2083e62fa7d05b08de37ff03aebe8014f9cecc1bbc429349b09454453b3081efa7cb1bc6eff8a282c43f344ffaf4abdf2bf04fbd3eb7914cac224d2412262e7e814cc93d76fe5681741b4f4a95ee985080642414245b50103d1020000d5bd221000000000acae48f47083bf6b4cc0a5c0ebf68a7fad1b01a6119c3f3b50e881bb1d26470a4983fb55ee735e87d527771fa9c7eb48aaef5d9f610306d75e432e16da22d1052f90521bed78e68b34d1cf5d47e10737b80d5f4a088da68aecc649c11d2d520205424142450101e0ffe71ec57cf1be19f3b54fc90e156a441ee64ab5db08df29b9968215f1e2578477de228ce9d873c924478ff7b243800b9d14abf9d7aa20702cc6c7fa73aa83d40754ab5eaf25549da3d5b758105b6347ce3394ec59a1ba10e2de323e947fea3eebe80184133aa0aa500928d6ea429e082724e967da533531f3167f88d79b4cb22794fa11de75ce2cc2472f7c6365cd1fb35ff78de7a78602593a15c32ecc5a2270ca9a080642414245b5010101030000d6bd221000000000c0bdeabb1daafa2248536f39258af1c6ddb231d069b42fe2f1923d4bfab8520a8bc8055811017e14e46e100c07c79b56b09dd32a135b288ddb220ea9cdae770914261bc4aa99f87652509cbae571c6f3c066d8191663d0014bc88495c75650050542414245010158a7987203687dbde68ee68f876caa218eccf7585c9508afb826f066668699567845e47ab90770f48c50e15a825b7961191ef5e506651a3736d4781b16968f8d52ac4236243d6861129f4cc76ed85f7ec32d1e52b2a946a3ec00b3f5d2f0d9a542ebe80105ab7bfb164c579532b9596cb87e1900ae74a3817a79b2c8b0092a299c21675f984af9bac699476c1b9de2a3b6d59668d168fd02cfdf341ded06bcd8b684d127080642414245b5010340010000d7bd22100000000094f53a19bfe744773723c97ac09ed5f377cf5e40b33cd97c3eda2c957a5be15c1af0078ec9d654e3a2c266f2c677622cb01b8f04271d3e6820c4bbac1b27880c64aa17af529a6b4177bc9308fa22b9fecf8d23f76fdfeb76689e8bb98e878006054241424501012ad0110f8431eea7aa59fdbdf6981a15427f7bec9e1d77f99c73db8c96dca86bb78a33d7bc693ba1da53e288c983b818bb3fb4afa17ff2f9a338c0680090c28fb3448f08fa0cb5e3e9c4956ea6ece482e69ebc73687c4e24b020d89d2e540a3846ebe801b4b70d889a1c1b913695ef1c448371b1bfcb71268398d900321acfd4c39bb1e482ca1ed4e01f08f250d93671fa95a2b5c0b219faf20167ed93950f438a80791c080642414245b501039c000000d8bd221000000000a2d6e568e88716b8fbaa2c73d80a6a9571b5064f281408ab864285357c14a017bda405f0e973ca1541015ba721d877340aba90068db6fd8039a2d0a1c1e9880fa892608457b259b54388bac84c117eb6820ce38b443207c3c1b7ac776d94aa0e05424142450101bc38e97b5fd0c7a2196d7b806f92a4e86092fd4ace73bd392befb5c73419a4295ca73d4ff13165f67c36ec098fe5df99181811b85b0721ebc8fc4400803b1f8d463503243eadfb580fc2841cc2812f1904bb1ed45f60d4d8f4df98bc0ad6268c4aebe801b5f1cb7f2cfe09da2786b7484202387188569fe324f5f094269868b46d045a1ab7d801bf23f3a7b2140cac82b188438f9094e4b52b20f5769db9dfd4d6b61ca5080642414245b50103cf010000d9bd2210000000009ef7b1f6fefdbf5cb31486078c9db64e0eacba73619537d0def62ff7a1d01d5972a620903c874e91212f1005f7ed5e11a94df4c5ef8c163d0377faecaf042b0eed859aa51dd439d7015016d01ba79d7351903b980dba2ea0a8d47d0b644bfe0505424142450101ea45b28c47242749862f61bd6c5a64c4fb364bb391621ec9ec2e583b9967b44fb7e87f1734e8c2abf26dc4912b60d5c7eb786cf53db869612ad3e74f8431e08a3c723f171374e6c68306c2139fc8b7b1b8c6e586b71836144c4ebe2cec7a45fe4eebe80165df17f91960c5ef37ba16c861778e6de7c4a55e6c7673e8e3dc4e92aa30c127f7a83d25315fbde31ce20bfc7ef12564d908a2c28abfbb0aa9bd1c84f5650df5080642414245b5010331010000dabd2210000000002c2a8d6647963919d1bb435536b6fe6798e08fcda74012e16d5e94e63311f939d45a9cc90293b83ba4b9844d4ab9b9fca4f45bafb03778555ef2475aeab5cc05749df3bff45ca39ce4e44fef37c17f41028a308528f6ee1a5d1c0d75c370190405424142450101b43dd4b7d802cc583a8fc2f5fa03a2cd79c29629b549f98f17c1580a4977c474014b6fcd18ef70e1f101ced90c3d3396c560bc4651a40e58d85e19e704b90e800b43e069d897a715498a9814943c161e26dc3eb94b44dd3decfce4ac08b1827c52ebe801eee704aff78289afe188e3a9b107003d574fca6e7806df256df540688374f0bdc80a525cd9a743abdce5dbac11964f50fcad97b76b80a1724c168aa84a672cd9080642414245b501032b020000dbbd221000000000124199d1922e9f408e031ff85b4fcfd423396a0f1d5340a4e9b571d67411e72f5a5a27a0cba89e613d02acbb9a37cf8c2fea2b40920463046a3fa642d47374014928391760430e73cdfd20fbbb655bacc566e1faf2536b0210e623d80171610b054241424501018c1da38a9cfd45dd4c027ba06992bef3fccf758cdad2e44cfc26b75206ae877d5c0a653e5aae14cbef2409c7ff495392bffefe26e0345fa9659b8c01b77f4181bb5914de5dc5a26adcb963d88958e8b0a1427eabe87009f10f27c40898ecb2c356ebe801b94dbe51652600524e40830e940de570e6b5b7d5ced573e382db0087e4b30a33dff6f42d87e3e39e79664b74fd12ce7e81c52e7c57f8b1f31cec2502c08f0772080642414245b5010336010000dcbd2210000000002aa43251e39089094757d43ba1c9068f49ee366013f82dc8760423d009656c6999aeaa36e0da45830a05dc4b3467a1211b64394228bdf233ceedb09d030ed8073fb2c1c87e566145bbfd024ed075e2ec852a16ffc167466a7b8fa6fa60aafe0905424142450101a2de3b52d27e1517853c7d7f5638ee173bd1d4bfd3dabcb21e86ca499f31a10f3371e119733bfbc884ef5cbb08a4dd571592f9843e861b581a5c05257afd9b8581eb392e3487c703a177f3ecb5f4031bb7d8cc067aa4ee4ce0161da98d4d615c5aebe801fa96683a273214931824be2f3d2106bb851a4c0d7266abe4def74a942b88cbd9e829418afbcf7d05f3e570cd2c3c4a7c1e09eca850bffe25ed669893b6a41950080642414245b501016e030000ddbd2210000000006c41cbaeb41f79eb4ac1c14f8507e712859f8a5b2e48a550e4609c7b278f8e2012437eb52062b6cc7346c7df3ea373c8e2919e3eeffc815cdb2edcbc1e762204fffe9d9dd19c1b072ffb1928978b9803410e26db0416f48992f9062383d85903054241424501012e8ae105506cf59be56a892fff0321e4bb3752914a66d61bbc1e533f79239560ae571dbec599302c7a7d46d46c93ec63e2ff89ac69847709f549acfe082abc851125faf2e10a4b62e9e8a5e882cc22045328a37c38c4bd9d3a6806056984894e5eebe8012b1292f8b53d726bb27040b9aa31e61d46fbddec9dfc181fb9ecdfd9ed81a4b96a8d5f630c4398dd31af49acaff32e73d19355aeae164cd482c92b7ac6aeb72f080642414245b501019b020000debd2210000000005ec944eabb7f18f45ac84fcbd652d015139b45cbda79883fcd8d83a996f5283473342db941eecbaf1990bd8e1729daf1c749f1068beeca15643a5b7d018e6805ff4cb7bc5f9d88171ae0483eb4cdda8b5182507497aea8eddb925062cc412409054241424501019ed15da982bb94bf867e1e3838b64a8ffe2027ea271448c1acd11254d67c436a37809d5a36bcb7e79093c389e8f407709b6d660442b7a28e931c8b7ee30d50849f0162ec03db4cfafcb18e527696bf81a1aa375c634e98b23f3590feab2a141062ebe801857e4e5d520e5c6e4f20e250838c98365861bba985dda1e466e0f00b759738ce6621afea2237d6d36e6d33e8a993648956eea46351eaac293500a91706ee027e080642414245b5010339000000dfbd22100000000024f376d07334f336858ec71ee56b48d3246b1fd7bd395721853bb38b6e1cae2e9cb9c5bfb993a1df23e05068727d6d9adc21fb2abfeb1259e25a7f6856fb740489d0ea2c69e24d132e1c792586ff73237f0c9c1c56f1fdfc7ccbdfe51709e80805424142450101cee7d433fcb213e80f93a3e2297934bf7ba5448653ce1d9ef91233130be651489c343caa15825c990d8b9acb0b08d7a96fd1cdbbb77af4b869fc04f3d28a8d8af0a04e4f81171f9622c7a0a83a821303e08418dc56eb80812a6e690d41f4cd5866ebe801e6f75859949a5f8d44bbb24a12be2469ab3f740a96860e464d30e46c886a867df92d5bf7a5e677876bad27801c0243144fb2aa0030b53d35c80a1729415256e5080642414245b501017c000000e0bd221000000000fa806e6bbc5fad362f68c223ce65611b2795aa1977d3bd8ef0c5b83019c71859a6e300179591c18b64270f2b0e2f91d92d429ce4804263a8f48f4e5e00c3450c6c07739003633efdd46d80555d5d6083cf8479065827d17dc03fce20242e020205424142450101cc22b9721654ee535d264fb568990c9733820c0addb3e213b696e771b319652fc2182063c7bf543acafd47865d5d8d16191f8891146aa6e3be1aa0925b76d58b189428a285988a8a7a302a6ada6d5d747ee17eab55be7f64ea8b2b92da68bdf56aebe8010283faf1ccad343cabfe76799099270cc71fa2f55825b15c15760593d83b853d834e0b3fe26dd00310182dc3e27234f9081435e0cef00340eb2c661af5dd4857080642414245b501037a020000e1bd2210000000006cc3ad21e43b2701f83b7c54c761526f56dc9a5587149fe36a72f255ae13741083f768e94f687585e1231eda0db93a446ec4dbcff848c48543366fbc6f5b940eb095f12aa3fdabc875386f58cb690e9bf2389ca9c0a2b2509527fd41d8520f0305424142450101084758b95f56d82b3900222750379ae1c27d0ed72d63cea718bac41c165c3042a06158121d8e683159e62a4822a874b4ad4ebd39bc6797f56daa09ec49ca2d87538e3d939a89331958ac754f3e4d96f2bdc7408a99358656d83d4c9f482479156eebe80186f6a56356c7be0c242ca9521314f901def6bdd4b996e2aced8ace35856a1198ee440e4e023ca9075f2484c731dbff073262592a2294bf1b115c3f7970930c5e080642414245b501038f000000e2bd22100000000062915afcb8a250d5452945f66aae575fe1f72b090847cf61f70d4571ae00a00d8f2f005b84b8cdb183d87639a8285fd2cea805c759d8d71dfbaf25f28cbaa2051d5a98804c42fb8d1b052832d508810e6046d80aa73f19f4373376826e4b2e0c054241424501013e87c53c7bd39dff19ae4b00ad917cb1f671571577bcd0a04c73cd697fe06613850fce77b25a37a365521ebf15a8c9e2db0fab0502973123373f1f25d1d73e8dfca0e8167dc472eed96a8c9640c66c41373c7efb3349dd01043c506c65e527c372ebe8017c202a0a8e276b97963f7203055fead166772a493a070ac227e15befc0290ad45319b57ab5df8580f9d0eb9a1dca64266f145184fa144dfdff684eda028ed5b0080642414245b50103f4000000e3bd221000000000fe3423e72c6decc07cdeadc74b5cf9b8d90a55848d8669c29d697700de09eb5073c4f3c1fc83765b11a6423b011cba12b99a573cc20ef78559a42e2ced4cb30e4f2b854355d82814494339c972d436856329ff6d7a653761f3205fe979ad270f05424142450101d42c8d57c9ecca4ae25a1512dc58f6815a954824edadba89970b8b192e006e10d941caf1d76291c628bb96b81a9717195562b0c855bbc5e1be413e01e212b68fa798a7b7a223eac818f9bf02720ed95c9eda34989826b36fe504bbff3b3c026a76ebe8011aed5be2f33d75ce4ba2d29eff41089d24323211e686ac9e365dc5f5ce1ccdbdb362ff795a10644eda6eb37e2050b460c89d5907265905136b81fad45311187d080642414245b501030f020000e4bd221000000000ec88e274220735a661b527362353f822d0e282af3341288a50fc7b8b72b4fd2703ec18445dd6d4b9607e1e638be74174611645fe83cc07f301663d12f6421e04817abeaf6f59aa05dad626f43b7995e3641ca3ea20f027b680ddbd00dc445100054241424501015ca1f3fc66b94115d72af9b83ee56dcc153e9c1618a42b82aa299dae052d9c1c43f373ac6e80f2ffb71e57c3cd3a7d3f4d021cba68e972b4966e3af2219f5688a446d2d9c19aa81ca496edfcd89a4d9aa83cc1bd4421b1e20c82d8fdd4fa72aa7aebe8010f82445feebd3c3091fdb134b73ecb06ae35a4448484ee68ce2f4d5064defd7550c8c9fa5cc13d9856324f1dfab0950538e77416e6f88f813528389ecb5e900f080642414245b501035c010000e5bd22100000000018a201e197352870c51c940ddcfcb7d443940252a81b3a4676874436a6bba95130621cae3fcaaeb06e1ff3410b172ca4c1889a946ac70d01725ea076a141200b1f580507c85c76eee309642a4ca6d4fa7aca51b170a00be992dbaaf87d715b02054241424501018845d8e590f548521218a75d2cf87e93effea0872b9c5ad23e04d6b6be3559026529e392a23920e8e4be1e2d713ef617c9dc0b3ba6367fa295ff8db80a4fe9816d268a2094f1919343973053e867f7f16de9c1d9efbdedaf95823a28a44fc7b97eebe8011972fb7d909fb0536336eeeb4850b980001ab65a25e27ea66f10ed6edc8c7069da9f06cf362f53ffc5303a16fbf1683a0f93a5a56177d0e2f2b029d8695f43bb080642414245b5010320010000e6bd221000000000aed7fbc4bf473484256be5425e0f8c6e89d3ea21a7664b6020f82b6d74fb6371f83fa381e8ac753e993d15e7b855d191725c35ff1d368f5ac91af5576958850f000681d81ac7653ba991ab3e40d682096f0e1a319cfb8a6c32f9305e1005e70d05424142450101f4dfc40a86b2a5f0b89087cdcc012843977234de26426cceb9eeb28258003b6c2946d2424f634265c36abfc0c5f4545b7c9acc6b2f2797ad5185e5f5bfef878658de295b91972b17eb01bf53a6cf8f56a2d99bb8cc83b9e20323dfb43de4869882ebe8014f1cd9e756f9fa86f00bba9253b9c61ab9db7c80034dbe8a03fa549679520aaac65854d40e86fef7ed2808d4615794717a872ca1da8fb4ed9b93f5c7d62c3625080642414245b501030d030000e7bd22100000000072989bf65e6b416ace49a80be79bd48c17c2bb601e6cd839997e63943ce5016d4694ad42a9cbc24f2de9d74c52236776cdab65e0ad16d192e36d25cd371e75046d1cb2e71e69c919e2ffc31c8f031c01589c4e1f0087ca84ef6ab43ab20f3e03054241424501017a170065cfa34e5df6a9f0439c07a857d781801fee2a7c97bd2f1a0da427c96a0abd4c55ed186361879e5a64570c29bf9b17bf743c94f1c09c65d1bc0a300f89b1c0ad0ff116abc2a303bbbc4a29394c95fa0a4396bc98ac79d13c099fb4325386ebe80189795d7e3e5087674a890e07cf425c627c8b91437b0b68417e912daed4145664ca5077f384575a51cc438440ed3d7aa0a202561e927e7af795cfe6975bff870c080642414245b50103b4020000e8bd221000000000eed9b009c5a40e96691c29ce7f228b9d60f9874eaffb9ccb1dc1f8d604017834eb8580cf209d89da503b9fa008f52be3e2e22967f6b87950ffa3bf8e8cede6080bb57c321264c3647090e870086c736e30e339b12d95eb569c06b7e72b22130f05424142450101364018a0276c4cf9c116abadaca8bedee1bbe83a55b1639441dd59f410789f10b8ae1bd5cf182451dffcef7e2e2c0376d75c35df08e4895f82903702c0e6568fd9ad914d1a4e6cbb3c6602db750906505ddeaf6afe72fb37d455f5626e43ae6b8aebe801bbba745b3e124910488fb12e4eba0554bc2817292a74bb6f45568518ca640722834a64aa079e3643ce27d16ae3170e144fdbad4ce86dc75d1168e322fad2ac74080642414245b5010362020000e9bd221000000000c043dbee1e61ec9930059b908ee1902e1bd6d9d66c8cb2ba8ea96af8fe9e8a1ac8318fde68bae7388c6a3c5cf2ba24cfed60420f851dd83583e38b5cb2c531045dcc948f07718908d1f25e1295fbb840e38465b7b49e8e6b5f71a72ae379a206054241424501010e11846b088965ab553eea6edcaa86ac4d9987328927d7d404799123e4e41847310bd65ff58dc6cb7323c9a33f0a7f90c88796191c16099a5f2df5e0188b93817b0d2ec1a4345a0ae2674cadd5202af7d53682dddc9eb4db39653fdccf6b524f8eebe80181158e459265c9da6d86081e9473ae251e3dd41aa3b8d6f03a5faf51faafbabc6d3792687cb70dd9aa60839a205447ca5b2eca7fc02d7a66243e3dc3518d7d50080642414245b5010383020000eabd2210000000008a4843740e78b91fc27ae2f1f882a76f4c2f1e11217511bfaca50d5702278c48a06dd1531c7d69733b54508f39e4bbd1ddae0c237a84e8bd5bb382575eac71013dc3e8f1770df52c73ae2ada579bc65c95919fed81b6559cdcb164754bcd0909054241424501010aa83edf309436001bfda30812ad9dcb27fb78f9b90eb7d373647827cd53a72a453010b4cacbeecf37914f04ae168b8053b6456374b9a8bc371832b229fa15817d3f35fe8ea4cf70d2076228b9cc03fc573bd0a802c58325e63063b3e6df883892ebe801e862c17872e6cb26f03064f6878f5b9c55a80fa28ad009469cd568799abde54e76d5177cf7b6ce22ce39b78ecb556910587489b26fe943ca2b2e49defbe54fe1080642414245b501018c010000ebbd22100000000024f8a97a43d34f3c4e477595bba9a15b164d24c2146499fc77c75eac763a112fe935e79a92a7a5d877b226d4a12b3954d2a6ea6bda4752f3b00ada042105c601407e0a87e56e2858b1eb43851ef4fde6e8cbbade5805ddba246ac1b07f29aa0f05424142450101064ceb4a3b1f52b55527528875ed913260bb7f302ebb9763c361e26f2f18b748c357be1bb39960da8f0923f0372567c431340d34f22a6fd0c3a8565a1d4f7e8c054edb46ab4b9d5513919f746fbed3d4489efe52ee33a9814b37869302fad93296ebe801d128a663215fdca9824cdb387de4bb9aacbf8ffb1268458e45b8604ff759a9db829de19cbf1bff422510b56ff0e432c42c8c0cb423404b454ef3a089a1194fe0080642414245b5010368030000ecbd221000000000d6c3540e7c12e34ac1837ab8f7d97fa030d1f6c99e12ed49db1870625258217bf3b5c600b8159ba2487532276e774a1f7d79f01780cd082c5721e6f6be6d590bb47e37a0a8ef06f55afc5ea0e4e26748b3477895e80e98e8b2449d8a75d4ad0905424142450101049998f98bc2947fc9341343636e5b49968bbf092544c1209c0e8855ec58d90ca058725ad5a60f15439d451249451e42c62ba725e97e63cc07e39aa7016c718a70403605baece167cc634a04345600b1b91984e95388fc46adcee879c8328da29aebe80164be78f6135dd40ebdc562e65724ef9c9e494a7f6f28f7bfa70891162c1c891d78252f8bb40354bde83e36a7d1d87dc35c46b9f73ad820b30131d08d2e7a0c31080642414245b50103f6020000edbd22100000000096c1ecf5028e5822beecaa70d703a3fdd0cabcb9e7d934b0e0a3d2e76e45b74d02805f43b968f51099f40a99baa1c816dfba701599cb3d6fa5959879ba3c1102aee407141a5403184948ff7d916fb8a4c01ccb6cb1cbae21fc494ea7d54b3f05054241424501010ee2a55e66e3b819ef9e68965f126d17c8ad065084c892e285c87c3bc1067726a026b68637e704c38805b07f0fc55494e5cb92d2c9d89803207b2a1ead0c6884e380014e99d4c6c0c69a4f4233cb9bfbfcaa49efc6080c06f7409162bd704f619eebe80182cb288f64efa884ca280223c13b02e280d9399fb88d6a81847b51bbaf799aebe2c67a22bd8b1ac0c5092eb2dbb67f114de3f622985c7de6da6ebcb4e7473315080642414245b501037c000000eebd22100000000044d2c8e806910665fe274839a95f75ad57756a9728a81a01b2fe6dee2d874831a27529629e90982f75ea642344bbb5ee898d68f53a97829aea71587c5724cd05a87427f19d5fcd9af14bcf453b62d1e04a27ab632e8cd3f3fed205c808f5340d054241424501011af81a1900f9de281d03412e0ca129402cc1d6c2fa3f03dccc30a396e21648024853808dec819746c7f19387ef04deb3beccd264ebe7375d5f3924206e899986754f1cff3b1c77cf64aff565a2cc271d7d578033fb54e825737a7c23cf56ed9fa2ebe801fefd70085396086adb8e6d98407b19d0798994e1fdf00eb25628801234b8611835f05718cc22cbd3bf2d58866c05d86349f7261cb2abe405357573ddba55cc0f080642414245b501031d030000efbd221000000000149a07f1c0bdd5e53a1402e558562347d7876665d1c4a5265644ca0b1dcf664227b1b8025d45c2794a2e65a42fb30bf4981ed0c7a7e0c32b73a6969c66e69506f9c9f1538dbf0239ec84975d3fee8224e27d135bd35db3df5b432457fc1653030542414245010114cec6bf158a46eb683b7534e142eebb9a39415c4bf76306cb97661fd52a75625444b3cfefcd188207547fa3bb48bc05a909264952e91d9dbc3e8029bd1c5e8266edb7f0bd4f3cb569625390ef0733ccbd2e4ea02cd9716fa115ee27138de0e8a6ebe8015747eb6e3c0dc79bf396ef768e242fc27a6190095300dfca6a419de6f378ed64ca9d6ec0d8f6db5103397362a8946249fe7c795119b35215ad895bb4427b0f49080642414245b5010125000000f0bd221000000000f4cdc2f1cfb1a7ed48880b454b4e92e8e26226899684c05a6fb5a892e72015562fce2f8eeb3b735cb70e74a86760111999a8ec0a456b006bb51eb938a38c47033b2a5355572d1da5956cbde54d8efa28286ca923322a8622bd11511c4dbd2003054241424501012cc2b52e7885d19b4eacbffdb97d4a7512996bdcbc76278c94d5f1fd74e9af1c459dfbe019cac0d56c780053e5a751fdf200087fde39fa5786e07025e1950c85ee3735a16a611702309486c51072f5501ef850a6bd4bf7c67bd8e98a2a9009d2aaebe8018a4421ce669c8dc523c921d3f38590b4d313ee87538872d12de3d64c6eb4f8d6807011867da1fe04ce00a771064e872910f98cfce080009a2c546037aa01b4af080642414245b501034b010000f1bd22100000000014cf7f20554aad6bf940f9d24da735b56f741973af69d6b253ec60494fbdbb3528ef7bdac218736177c2f0126147c606ff04d00e9cbdf65cee3821f2ac29490bc3d7cdde545d071c82342a9a1f8685c9e3d88ae6d60ea71ce33864d4fa9e3d0705424142450101666cc33ef1f3250fc765ab9632f9b348afc212724110aae75f82ee85e3c6576465aa9a21e78504680aca40e05462e17da89771531cee006ab7dd260c8a39d28528708759a0f398e711af724dd27af91cd5bad845fd61c6ddfcb03dbf2ed49f17aeebe8014597a4b920deccf4d2d4e9d40934fe09550c9602a40a3e53e7f35558ea0c3a7577fd1b9a1c759ae109d35fc5ad2e2b8ef57ec6c1880208cd3f10d336e9335ef9080642414245b501031b030000f2bd221000000000aa2baaf4bdf1a920d9cf1fbf30b82ab2bbd071597386a204e49e4b184f190c1c52a1f7e143928fde634b006c1ddfc0ff1fd4b90c142dfd6acfd04311097fab003a5e31fbf293c2b3324701e6e654eaac5f0fbd9593c65e880bf0d1efb946af06054241424501019e00752f1bf7049e68e8f4671f8a06ea2c117bcdce0f6e6bd9b6d683d92f0b642ec1195bc40b735e3770643a272adf6fbed70b745193f627c91bd0122dd0b38fa92410222629b027e1a9ed30d96e93bc77a0278df7f4a0cca4f01d9e024a0d3ab2ebe801469e9d3ac6ea0bd45abb776eca6690b592478bdac78f419e36a816176d8da1490f1f41b5d49755ca90822da4955d08979df944577ea27f70cc41a72bcf06a8f5080642414245b50103a9000000f3bd2210000000006e3618abe5c701e9e149f6895670db83fca629b35a27d3731da8348a6e2acb60d075b2c6a73774ceb09cc628f17f82f60a32d75a5624f3e6415cd3e49862d1002d0eaca2acf08ea417099b03bd6d0225ecee3e92441269115ce5f47ef0888c0405424142450101123a00493a091fb18d807438c3f01b9e4d9128e0252bf3f457a03d5e5444da17a5f2a04e307518f70ce7ab5004412cd45ef608d9ff032565f01bd97eb016fc858edcdd2ac18c44821f16400f90667330852b7d54450f1cfdfb014aecfec01a82b6ebe801c667cb9bd57ae4052b20f6c41a2fa330c36da063a91a46f78768d0554ab048abf9d2f9fd1e733056e85f48b02649bdaff7a749382f2d2225cae4070bb6bb6735080642414245b50101d0000000f4bd221000000000567014bea3f506738ffdab70858a67e0ca242ae10b31d93a954eebd0b1b4a201a43f77de8f131aa080d492c2539ceca136a464e0e0477bded77c0872faa8b80b7d39c59e624656998aac1db4f54ef842b67c5133580806d230ae4981cd91440d0542414245010140d23c1f25c5f9a73c1abb638540a2f30cc5c83995c8cb51b73f4c93d3fbe06106123a0d629611512306914cdf2d5a20056e88ca66c34ceb33c61194482fab89a02564ae7d6e9d93f3b22dc333baf4ffff537f8d296df2a80915fc296c0acc6fbaebe801c3e0c0ab78fbc062f50746d15595b9804cdb1901eeae0b94e5c6d6b5a7b99ed6abac9011e4594bb782deabf5b23a60171e54e9e3f4316a479da8ee604e8f5189080642414245b501033d010000f5bd221000000000b08669943e6a52801682ea17a037f62b5cf39cface994feec6ec851bf535097fffe433541069a0c379859adbddc7c6fc997c339c8c6dbeadd4bb033594145d0f80d1ab350dee928ab12c8c2484f5b29bdfeb96dae5dc56e273cda21737616409054241424501013a4a51e0ec123751a8daf479f13c8ed9e0e75c8a862bf0c1e25d59062ea376544e93433c9f77a2ba3f1afdb4e84f6b37311a87d6d1c08e4c2d90d6cd5328138aa0cf99558d36f16b4f22a6cef9de404aab268f46103ce36275a6ca063b6c86c7beebe801ded9b4e509f2a2742a3156d5bfcf773582bdaceb696027ea8a5118ef97249121d2280657e489ce71b51481e63d5f2a301a9d01aa1f082c01083003092c2e5f1a080642414245b50103d2020000f6bd221000000000f05a99e547ab21c0457ac8e167fba52a221a06cd956059318691bf9cbd60e34823e2bb2950d24092fe97261b74d1332398bfc688eefc1968b98667a372a6800f338c985d2893b814ea2fc799d96afd176628cd84c3947a49569e81bd99f0980b0542414245010102cf396d2f00b9751f97843e602e6df7dba7ced98dda714cf5789e50d0356d09ab513ff7663ebc47d4f2c00b60cf7e68085f4150fe65838036d7e80ed8ee218bcf162f4476a4f5e86009681210c5aa6728639ef44faec16208f2164fed0af8dfc2ebe80178bcca5599efe4c2a416fb8d35ae478eb60a7dcfa84655f5573d586015896e62a88f8d2f89e46ccd72a198410dabddc5e58917f3503c7e9eb0ba459264680f26080642414245b50101f2000000f7bd221000000000f0c639da7b97296ae593b5a407e907b1aff16e7c91ba008909dda278cf2fb055951e0931aa8bb2a9b5399e7dfc987d478f86acd004361583dd794b697afeb70645ce78db6ac23d4106a2f86be75a85bf87ab3cad624f50d8cc025a689828e40805424142450101ecbfb4d2b097ac6ae7c33e8041a0c9cc6f02c194ced7a1006fb82216c241f44f58209cf334e696b8e048d6a6422e70e88e7149b85fe90749828bdbcfbd19098079036fa045b7057a3d56be8f6a9c3e6f88b6210996188f243f055b427260655fc6ebe8018fec5a542e64eb774b44c7847afaa8db01d622c533a58351e86832e37f8157d3bbcab45c2549e081e3157f5f25145d899647bce82fd49b47c1cca68cda607e22080642414245b50103b0000000f8bd221000000000e070fa3121c1350b10c555704c81b53f447b8a21e16e86698117525faa73d676c453542a89f2a0b2a67212b029e57949a17fc2ae3544b7125e78b7ab55cd5c0d309570a8652350d2c35cb44b16c7a29cc08cc13334c842845632c906a56c5201054241424501013c19fd8a0c68188448840ae5ad3861c75c66149b8c865a1386b2629328e89763b1c2d1913f78c6e3ccaa6e3a4655068d8fca6f9ee19992bf286fec0024a20a847aecb36c78b56c0e8e67d051558dc52ca6a64a3324c39cdab7253af56cd6aeb7caebe801f752e9204d07943c4abb356038a2e436a3455ce063cf9ed724403d5d5c4e954751a94979bcfb8e573007cb3d03358e1b445dc72d711a8b0fc9fbdbdf142234dc080642414245b5010374020000f9bd221000000000467b35f02cb154ddcf5ae1039d2af27b6e382f5ee6b4789a5c6d221231091b7f55e7a05c5781c7e51f8bb285a17f1fa5ea0d6a20e43f837f8428d7010a4d1a00bb865ea3e40176d3c6405ab2d9280c280bad571f429d315033ce9dc58adbfa0c054241424501018c6d10f224da5254d476fc4c8190231058553b3871fdc22020e6752dc458f965db06eb5ed5acd8aca9401abafcaebb490ac7f02c17c9d6d9e222e65ff37aac87267d32fd55e0ad81f6ee6622bdc91552b47b754146704fe7e5c68ce478e4d4f4ceebe801ee32eda7202811c065d7ad9c45e8cc5a2269f9eb6a993a7abd95b250185ecbf51488b7212b8a137e25cbb623b1ce64c0fb93908f0bbd0fc5190bc2a71d25a541080642414245b5010350030000fabd221000000000462c5399a1aaba65422ee551394e21a385ba7605142466c4efe4b74331027728babb582545442e83a11f8096f96ff8ee212d4810696a507be6f6f429786b9a0ff64902676a9dae81fd9059d279363d88d55c3fb5a02e13b4ec87bab30b3e710c054241424501012220657cc9c54f43d9e446185ec246cdb522d73e0a37fbdea54862fc9585a20de7495b37cc05f3503943d2c8118e0df6dbc4ffb3ca082ec5a04f6e0f5478b18fed2699e36eda0a69fba8903ae87d20a59de6977b8c23bc552ed3de41c5e8a291d2ebe801dbe945d89d926dd6df88ddcb9c79d7f7b15030a5121816295a7b3b8582a4fc48c4d322d1c091e2801e99f772b5be89595e4d101248af95f7ef3ab6ddca777f60080642414245b50101c6010000fbbd221000000000eae29a084f98b53dd48d3ae6753f53e64ba9dfed51af72fe9b074c4634137828c4d9cd35c668f962cb0f946f28011b8116d9f073592535a0fe0d7306ccab5c01c8194c0b1851ac7389332c5fea7de874690c5e88e2499191ae255df2573dcd090542414245010148c1d1cb934a2ef103cf57c04f989701172d02f240770b6a6fd71dd17258d962ffa696fcec6abe3b0a19a86d035e41e4c9956dd2bc7e00d035845b1a581144827b39cf2c54cee5d1cfe8c049106ef6a074391c787332cdf524968ced61bda046d6ebe801bf90f9dbe2bb783df5d82779030c3c91a4cfc40d8867a00a5887108ad48d5ae0b4efe7c59766c31989dbeb888f0acee0a849965f3194b7b6b438332714e10652080642414245b5010331020000fcbd221000000000e4f39874a5a9ee10a6caa587092dccbf7a9bc0d4096db9529d84c76dd38d702194c869ae33a00c1ad233031d7c301e56f11dd14ce8882cb690be6740f34aed09cd6f1bfc6f3c2c27768a7275a347ea9d3fddac2d74830f4d02b218986f33b1020542414245010132068723fdd401abe5929a46add8d9df6d52478e893b1b527b047b52e1b85f76c02bb12e22117d6519321ca572ae3a7223e6707c14654d9c101d1d891550ee83b506474a38530725489ee98bc77cd2889571cc6482e339db0c7ad51ccfa07a17daebe801d612a5fcf00da86741a5f5a883820592223e4739c7fe212a2f13bc216f413ee6dab2b9942edecd5d79f5e785182e2d1409ad2ff9ce99df11b200c1b7addcfbe2080642414245b50103dd010000fdbd221000000000a85137e857c423873dfdbba142dcb7deba944b24aa0388f67706eb2efba06d2bfcdc772559985008f9b0d9ad78132812d0aaedeb6ddafee6fc510566517c540310563bc43dc57d572258f624376c215ef315384be42a82500eb356c8a4ba610905424142450101ae1c3667c1450e7ee045a2d1900f22b09b8cc406440a88e0c6f5a2972b3c33481f8b651e79115f46383554fddebeae50dd2790eedf423c961c675f1775412a84ce0bbcb26041217235947ddaf6b1cbfdbf1be2d3901cfe03408562d98597423cdeebe801545f4b67b644ec538ebc4901b0f775a980ef446fed7de3f61fc6bd570c5de164899592a0c1f9f36c878769601beac0527119a2f71500f95133b13961b908ec24080642414245b5010332030000febd22100000000064d03678c5310066cdd31cdd3ea7c46d5af555f41f959b55cd056fe35e588b215aa450bf84437a38ead64070e321e4270a130b8efe4cdffe08d0e7b4dd54d30c8184a03d7fc6b76c6ef1d1153c09986812ff9045df596f5db4907a6150e58a0805424142450101f22a2837da86abb33a195019b346a1143ed3b917d1e46f905a71f704c7ceb92b551f4161e759ae7509c2dcc3cc03a24137d844ebc4e7d2fc1bcf86d492f270889e07c68d484a6c167b20fde217bb32b3193a8c07173fdb421d58234293f649b2e2ebe801c9b71ff0e65f59564f5a0ec690c7c222d1738834660a1f1768bdaac695aac0a2c66327e92cad0d0d0e1db85187e6ab6e902b7a617e9c3d268b97ac48ca83efa0080642414245b50103a5020000ffbd2210000000000e0b08d047f6d39ee656f34dab91c7f5c54e3e72a28412af9a8afd02f234f714574cd4f6a7dca8c2976b1d070ad6dbf93a1b662a467edcfdf6878a1a6ddcfc00c6e5163ebed23541fc1286b160b8a9bc058887f02216cedbd6257f2e756c2f0505424142450101d83f5c2297bcaa0b2d065424112a584b84d0fe3fe878bf90e31bc1489113256dadb6b470534071a3792bc628be2662e4f34bd29d0efe61903715fc2f7969af8efebdb60d3fd8c6af4c9a181e5b7a5831061348a6e5d31ebc9889c4659f00b65de6ebe8017b2976c99a65240bb0a9fd3e876af935f28102d6ac2dc6b4cb9a394a6985103e9f3c3627b58408cb8408bbae535fb334fc82bf2c8651d6eb020cd8e3f92aae14080642414245b50103b302000000be2210000000006c1f72f8be94003e89cc2a6b0696d06b20086b2180cc8c5dcf16d71f2c0dea1adca11e5a15a6de9466cf1879d5919f718316b230c3b451e76c38119f5821d703227446bb12e9ddeca421de4f21fb383176b1d60b54028af3b3ad89cb9e6d2f0a05424142450101ba1a523b00f6fa881355277712369ee79d46d8909b00ebdc4ecd8ad553316b7d67086166b61699f2308522bbb1cfdb43dfb9faf9c82aa790c2ead2e7c36f418fbf6a3c577ea0eb17c327a2bd524ac0b5d1cac0c8a7c5d19e3e09b370806fa76feaebe8013457a8c112a4b21dab5caef6c5bf2bda10f1fdf7d2ccc140a7201bfaa33bc58514dc244318f15878150ce2eb3b60d5d11b523b130786a446626f2866ec2f4802080642414245b50101f101000001be2210000000009cfe957b5780628965f55220f75c4fd64aa374b9091b5242ca5a8368dfe0fa44c36838bcadbee7613547f89ec954398325e724e4de2b9b240680430b72a64001c784d93ac6a2063b552865c2e9b8c2b1154b55205bea408b52c44718e169050305424142450101c63a07eaa36f10956523b52efb45b2c0dc361c063edff1a4c35695956570b32681aafa38525586fea949a9aad6c3b7768dde4d481237bf1dbd9224d5964a018460c29525feda2b2eff6e015c6618505e9d228480e4c1002e45dfb75a1734e2edeeebe80116298a10a765c9e5e8a7f1814bfa212c6ed35e7d74c6592fd2f13d12ec88b1775b5e4b9ac1465c27a1fc58e5713c7071eaf9b34af39fb9ea70e4c31fba5f350b080642414245b501032800000002be22100000000006a43f0877d8cf6cc227263888b05e374be6ec872073a4bac16d7be47cf34b3ffaaf81a043a06b43e2c9efba1517a295a6620f3133ad670c6d5892ce10d5fa0937a204baf8ff8a6a7008284b49ec5d179bd9ee875e63eb11fe8d51242854700505424142450101b0c00d653abcdf717a529942acad8d5d4c6f70858f0d3e3bfc5dd20afb1e2c3bc06df753689e9e4d1b0d2c9e5cb99b28ca086ed023ddad937caa3cfdaffc468467f20f90670278aba43aadecf561c7bbd16da6ee5732fbaf96ae212cb641f913f2ebe801567ab43bcdf7cf41e70e2d1fe33f1b5bdaae76ebd10483368a74be27748384e23c5000a57c462552b5eacfe7ff52aece9de21c712f6d5a86dac945eec37026b6080642414245b50103c401000003be2210000000002acbce0e1ce99c3cf4def596d034622627de1dce670391690d37c420e6d8d91c5effe51d1ff0e03f7e971bda7dceb06a124ad187ab9848b1dea86f1465397b0ac142c44f5570b7792cfc812afdd06b61455459ba7e66adae60c2f0fa1a406d05054241424501017eb2d1eaa1de659406c678fcb00198b93d43ded3437e8c3d5e76db8e7267e20d7a3d863f9530e35bf9fe854393429852d6a1acbeb362317e72ee1a4c1ece4e8b29446a255731a2eb0d5cf293241604e4565f398f57a351d03838c34f45a81924f6ebe80184f4e92306d879dff422fd72f600422bd3547b99bf5fd7d37daf5f04b68375668b131c7356660c02832959b386c9e4d854888d53e74f310f96e19729017f0a68080642414245b501036602000004be221000000000bcd41c92138d0897d77a937f224e65ac7e2df74fa0fb16468b9cc8496f8ee73c7e3e68f0485695ec363a3ea50415b9f3cdd66cd5156aac93884283a6a635760eeab9aebfb61cef72e7da1e3f755a46cdf2f1a487809467e4fead3174c1056a0405424142450101d84b2512fc151e60931e662a956a21b26e6c515a86cc0555d17f47e8a0340e7dd0b35590e9ba467b3f0332e030aa3f0cd0df4a6be0d6c54160c196fb361cac89c6b996af816df2aeb09c07a93790ad44379b20b8ffa83df8a43de978e1c9adf6faebe8012a02495061f967cf625308b99ba2adb906d27a03e4bb6000fbdb93e900b2b8f6bde2e9900bbf87be4841609f4aa11a3396370f616cdd7f2e06ba48d3484baec8080642414245b501030a03000005be221000000000dc8e631feddb36efe148ca5afd0807669310cfdcb097070ed8d62d58f7ade4618a1adbb97ccc5359ee31b69104b747be5cc6232a6c2047d9099ac70eba3bad0242035356e52191b2d9736e170c096b0cb7b16c5acd7c8fcd82bbf4243191970a05424142450101ea829495a31069c22cbbff3ed858c81b53176791c48fdbd68e8281253713e74f8ddc560d8ee5160346ed8d177517c20aefe83193101cda3009b22d7ab30b08837955e59894746e26a68a1f39cfc24505dc5decb487d452b3586bdb1e7539a46dfeebe8016e9cb5e8d7ab5536224ef13a5046e74eaa0b3f9e771ff113b54358101340af3466e49504fa48465a996f8a99a68572e246df5a2c1379b8ec0ca7e3bf25cd5cbb080642414245b501011e01000006be22100000000002dcb0034fc2e43ae6349a92af4dc2ebce36c25a46ac6ed7949348917ff0b141fdce9a6a7147adc482c91a416432d699f36965b4a863d42e563598576bb80705c6786642cb8b69830d1337e5fa0463ed1e62ae7966f54c7b4186c9ad863560000542414245010122f1e5c0b077de4da494d30dae9457a76359d7ddc582e8fbcff5a1b4f9b5d3753f3fce614ad69894229a8cd8c8c22c7e1ce20e413e57ddb24339850b8ff68b84deb238fc66ee00ff193feabb9a6b9e1c2495b5c848b5ce978492e99524b80ea702ece801f4e92882409b34336a7dc58f394735c9a657ff6463d197a86ca5d154b6ffaaa89e30924f4ab8fc3e3e942cfd3c0c546094dda08a08d6fc996e9675250745671e080642414245b50101ce00000007be22100000000006888d8ffc70116de2335a3e554b82edd4bb84e312689d8058f901d959960552fd828c2c650776b1e3db5db3bceed779fe2d8b766047fd70e9ac41640e4ac40112f6901371a63eab44aa8557b3a99b12497b51d8faf8ea66b1bf074a31e1e1080542414245010178642347929929f227c2c2c9cfbba7634c9c0bf645e067e6c6b86779a5485746081d087ba49f73e001e021463def0157714b522db58e12ab5dbaeef2cca83c86cd7296fb4f9aaf27a6f3f487da36ce74ecb5fdf7791689a7b387023b55edd76806ece801f233151ba922c9a149c212fa4d61998489d6d5d1bb581489f03a153d9a8e032bbf91f6345d85a1d7d23d8c8aa9a2807a53d03c453d1423998cb42d2f2229f40a080642414245b501033403000008be221000000000d2cd15576707e4a19f3597585829a4815ec85a857d1ff14a2e51b7fd1f7c8a0043fe770b6610a2783a28bf62014040e03a8235e9e2f26b87bc94c0c3d2ba7a0c9f5703aa0a5ebd14b0872cbd8c46ff37de0cd3d8a386a5886b6d9aff2369820305424142450101b4ee1771a8b8bb95f25b0ef24471e298c8ae1e092ece42b6c4fcd2c500c41e5d092554a75031c16f75e2f7df980e92cda62a0487429e00e72038d4f720c908816f47756a5bbd3ba043023a88398f38a67c08466b5e5c9dabc5415f785e46d1b40aece80106440914db5deda47fa39b8b17f91338a789153d73aee8856932c0c5a96aeb308dc31b192228a81ebd28225bec6ec686cc51011304135fb736a24bd584b38b72080642414245b501018e01000009be2210000000000c9b391079dfe37d1f60d537586490914084f78044ab8d86c810ab93ca0e69342e5c5edac41c970af63d1898a7ee6da9b2b2dc50cd5be1c2286ca993924b88017e691fb14e0ce8fa316251e7d9c917c2607b354ffbbd999775f7ee2f61b5540d05424142450101e6b2dec4ec127c3fa73773417e0c14454e4dc38a160915774d0bbd64172889120c69b99a46dfa9369571aeff5e9326e1e598d18b7fd8b00ae91dc139b3aac28748393c759acc39ff7b3bde9faed2bf58f71ab5f2945b8326ec85da2e57481f8f0eece801a954830e0381b3b1c70ef108def8651c8b0f3ebaed61e177e730697d8905d24b90e54e8a7233aa02e7ce03711d748db0cee82416dc306315fc6cabdf82d0941d080642414245b50103cd0200000abe2210000000001eb1f1130ed15c48fa1f2d6bb087cd840b9decda3f0fc09f2738309974c4171d4c2e452fc839d207b24efe5fdc7c5c125aca36e8ec1a69cc3c35982ff950f50c2c2f6c90a5ed79849f4f7411aea6d523092d8588de33ca750d4272de84d3a70305424142450101067011dfe48f539c3d1217bd12ab0d8e1daaca86d6601bea75fdbfb00e0f7f61cd67456c193c1a502ab5c3467450933fd1709c64448e0acfed550b7221a65e8b88f637e06c02982a4aa6dd4f5534f186ba5c6c26721d1bb034990d20eeeb866212ece801fa3d838b0e2a0dc47837ac3b8443f97396df727085542307102f17051a0d042b95d2d1c06ed5c21b4461b9dc0aaaa296a64130823e5148c817e1d262fe704795080642414245b501014c0000000bbe221000000000b80e041c679da2dafd4f2d46974d702a2bac281dbf5998628bea2836ea00bc630e629b70790fea31f228fa32738c43b21ddc47cdf850e5f6ce503368a7bab9066aee49dc7b55d7aa55127ef49c97232e28f24977b3e7fdb5027c15e7d6fe250705424142450101002ff6f4eec7d7a24b5867647780225529bdf2bf09e85c1bc19274fbcabd6676b10fb97c8814c7acdd87f1ff51a5c43a80acdcd423ddaeb61256d7124164ea89f4c8646758d85f7fd95a4a1f388e44ad5a9fd0ec0582110c16193f2d79dcd73516ece8013185207c1c822a38459693374450ff46760c38537b5b1bf169fdf8918520d297f011e2cc5bd8f8c38236f1b2adebe2fedd6203a8dd6b3714ee0b925d393d86a1080642414245b50103df0100000cbe221000000000f4e2a68e8bd7b12da4f757b2fdbb32a7e2f779a8e494bd119246926ec5052d5c454cd094205d5149c4219a64d9ec1a991576a8842c83ec251991b5a539310508031ac229c73bd2ff454ec39cb530dcf069703787316809ce38cd04ed024f240a05424142450101acad360bb4c7e4467fb75a09455b45d0490ee7dec011008b1e5e7286d2ff33245d4d4c3c2082752e3eea22e7ddb6c5ce3c4dd1b7154d472bf31c981d6dcc518e6a612258fa901b52370f0aa7f5c21c6128c9d8bff0fe6c73e1318e703bb7bc691aece8012280f9f30cda998a71db52f72f396c74e8528059bd048d1a1f79ab7ee9f5c97140292d8637be5f869c13951c779fe6186fe579f0f6c4bbc4895a4f0b721e4491080642414245b50103460300000dbe221000000000a2be99037eb6bf8dae558339366b27c0cc329f5054a54ab41772961992961418574770797031dbcb06f5e2777abd25aa37ceada54c59a8377099c75b973832021d65485bd6d011db4153485569fcdaf48ac4989e749b95961d1a1da9037dcc0a054241424501012aeb31e0cae1a21e8e15f07a69cd2ca517102129bee74f809c5151590e70740a5e98eb59fc33c690d673b366a94cbfe45701b86dd91bf26a2e6316b29d62d280e15aef5dffdcc97f8a1d55c08f140c3647c2c1476b58e022a00f5460238fc8031eece801687463cc0c039b45f868e17d5b8d03de1a6ea60f4c3f50afeac0bce435dfdd012a5edd820c9e3da182d5bca9d2bfb22a1011dc0663e49e70f03024c5079e40f4080642414245b50103eb0100000ebe22100000000096afd7c6aa2ac1d06f2bbb8d2f41cf280c67ea747e67da6b948a412beb6a4907a22cb9c80b2daa5433f87716e5af68b5de3585fa9df4841f3792746451c7a20737cd6f4a2bcdabcf161dbe73d3e59f83b6d906bc7d94c9e2251c180216d75b07054241424501017a12e390bdf61729f2743b8aaf9d0f28ed0024081bbdc54b598be1d208df6959d45141e438f56cc5dc0f6f31fa759236fa2bbddaafe5f3d635288c74145c1f8bc1a79a256a664f65d105342de055f2d5cdf6a085d7e2c92c232afb5085df2c5422ece80124ca137bbcf0981b753e2909f279858e7a8cf928da316754e627130b2c627d1758c2eddfdeb555d8d71edbf70aba4b2e1ef32032f72674df3e8b6e1b6cfa0e9d080642414245b501037a0100000fbe2210000000007a19d2f8756654c2b0edc3ddf2655e0a06d068093e16042be699a3f7c37f79122efba154d371cb1c6d772eb2c8143076b929d1ac172227def46b1c692fc17703e789507ccc4f326ea8284db8146406485d271aa8a6d6ce02d2bb768466b301090542414245010154908068772979827b996353d06bf00b0d489b969d43dc02d0a0e039618c6107901580f3001b42ca2e200933f1db913ea01d5426a93e60aa05a355f76c8a798da277a4784eb7d03a4b70da2855239619c50f8a2c3370ca5d4e91e0189133a55326ece8019a2dff0b823f4af2ba6fbab7d9d3888f972d25d3fa4c78d55756ebb75775a0d0a3d4d3cf71eb2c48c10e93b0ebded9bb3c0a97e23809d6fa32351a3aadd4171b080642414245b501039a00000010be2210000000005e2a52b7626ce767edf73ba2fa6d21a27560a13cc02d051e0560d78d8febc6075c4df2e94a0432be22689fa697aba6c88d762438687dcd885ecd8b230ed1b200eeb406fc1dc26c34b4351a98d82a55666dac04a1e5535c4620a2cfcc767bc40e0542414245010128dcb8ff37ed0df87f4fa695708e0c0d6b24fbe5e1f6d3e4ddeb549e8e42411b3781a05c62ea3bf7e6c5354b8bd155b1209c3edaab325fef570f22040246d2819f23b748a60b82a9c77c52c1d521d219d1189c1868ecb3bd68ec8d41a4756ba82aece8012c29b6cc7da6ce1a9a1f049f495d687cd399bec96a6148538550ef6935a9d082f8682f9b0175c2bfdb01fa4d9559b02c1a55f8ac4472ee23155567ef4beb2c20080642414245b501035500000011be22100000000090ae518c82f83247ad907bc47222bc516299c668920f6e5127cefcbb6417281a422af4d72ff23653db2be02c7bfadbad88f702349654a3289bad81c0b4b96c09027382043e926b8a5b7fd779428e55db35d75683dcd3c41ad0ca8d31c1396c09054241424501016ed20e88ceb782085af71086f5bf616b4f9e6e4331f2cb3a81389731961650573c395a9a56b9ff7d4b46dfa991c2c4e06bdece20fa71da0a7f873d08d3d2fa8989f3a1b85a98a8aa19d1a83a216ebac9aa52b37a9ea529687b6c97ce4b119ea92eece801adb90ba8528198b3005d88f55c15198caae5cc78e2d87e3395cd26c0d7b007ef6646aa3d4442888d24e7e98ba191234b1180282c1d81b6eacd742a2ef8a1a3a8080642414245b501011500000012be22100000000094a949588374527e69a8d521fcc5b3a9a828edba7891b9282e67245bd0c53f6a50ef3fb60c25939c1774d8f6864d45c05042b3f7236d9dbfe45e3f77b9e8dc06b96ab3868ccb868d8d2d4cad346a6a9da2a377d14582fa674ddbc5db2af5480a05424142450101589080323848f52d6dac131f6533c6d62957ecdff19df10256cd17bca2d28b38a8554ffbadefe20e33d4ed00943cb21429a8bcb7cbec90104f644213dd61c7856d6eac9f14777165da7f128ecad2324260145eafe8b424ccfc320ee1fab34d6732ece801e54047118b4f2d7b22cbab966de851f577db5801d0648052a4ef2dbd8984801b391af400b96f1cfc120a3e5f3a27acb9966b7f2f7bd263b4caeda4552b43ffb8080642414245b501037602000013be221000000000a0425af54fe681d1073e7bad63f23571cc439b1fa4b604ffaedaeafbb22fcf7b34e8bd30e52aa2dfbcaa6c6d70acf0a945603e009e007e8e597fb2b5369d9509f00c7ddcdca934057d3a46588fff79068b2600d976655d1377a9cef15339980a054241424501010c545c4f8859240935abbe576aa34c9112d0a347b30f251cc52e1ce247e37f6f29ed1debf41d2c22c07de8baf7aa48ef59915ae398416ad925e2e9c10a7d298989149673962fe4bdd66577776d12eec0e5675348cedebef7e61096ebdf55370336ece8012c427681e361ab3bd933ea2a2588ebd5fc7779c3ce19034ca42bce415fd6b6c739efc76477615f0b0cde2d9e9cfb98ec890322bde676ed1aaada15c6a9a1b835080642414245b501030402000014be2210000000001a08592130a4c5846d8e577d81b236d1fdc813a8f88d5edbd85ec0fbf3d0a11715bee29981845c93bbfb94c4ecef04ff3a151c59969d7427b592bead5f327f0ddb0c0d4aa04d771590a0616bd3a473f4145fa2c0e8061235c3f166280bd0c907054241424501013cca285e111c356771e3f17ee0acac80e20ddb4d9dad70f8b84456d7384d517c3c9a448d5bb5e4a7f2ce170825624f3a9dfdc43c96a62936505fbf167836658f40b2c4fd5a61f844285c94a2c1b874049a7be0039f72e2663d3cd1a0ede5763b3aece801d3dcb4ac803348df84e8fba71274464129caadf13b92a0f55c9b7a93d0c4dd4b7fe5733461ac984610721465edc3f24611db71646aaccfa1854f63f50622f914080642414245b501034b03000015be22100000000024f9fcdc1c6e9f37ba0536712df7c761f95291f2b0fbc89936f39e3430a02271d615d8c310123329f046a50f3c5589acca7e935f1a76e7e5677f74726091440f0eda71388af2e7b1ed23cc4b17e55bf36901c0c7fe7491a42377091ed83c8400054241424501018c06ec6bdf77114bb43a8c4bee9551b64b5c1e4383f2e4588388907bb8f5e65b592b816bf64a5517a8acc8b21317e3287ee7200595937ab4594f5a3195d1b286f76441d9f6e5dbfafb81ba76494218ad546787049f3736b3912443ce9905c3723eece801d5ce4dcb66a9cdf1058b014d566d710e7181d5f7684d82b4fbd3b3a4e7e07cf42d180a744269c3ea8c33bbe40ea81cd399db206c4daf4e843b9d49faa182cce7080642414245b501037b03000016be221000000000c63100e608d0e12444295028d84a4eab08684868228e348ae39276b1827d98110ccdfd5631251bf2c10d21f886f1d83e4f9ea31d0b5fbebad5215ad9ca3421049fe11e2218c24e48f161254775229f520d1cad2b6d882a920d116fc5df5cfb0305424142450101c4a302e5041b511bcee2074c3b771e9bc5220c7c2e696c4dbfec046453833d7fd8fb4fbeffda5ec1f437aa2b45da3cc92441e8769a1291c8a5d7ca9a3cc27f8023c324beea09f4cbd45f260e1378ba222cf73c7bc545dfe7c35b5b098ae726d442ece801378f9b9fdf15067a896d004a8abc3d71bbfabfd2191209c5a921788da00262e4ad9eccaf9085c79e7bc0bec56383d8879c88de0a01f25a3bc048c67820f40bdc080642414245b501036801000017be2210000000005264bfeb75753b3c26dc5dcc355ea134c231328d4d79554121e643bbc0d3957c222b55d04df3fe278cf19603ed936e6b3ef5d27db11e5a84739e803c0b562c0898f3bd2385d5f873af590d213908f04f78bc053ec8b3d449dc140262abc3ea000542414245010114fb05c8de5ab25d074245cc739b2768781a18869c1b53f7b91fb3ca7f40ad47e97c5ca543b65c94b06b7a31dc3ea0f97e97c7d7e02470575b2135d4841e108218ae6a8db250d2880304d8e45d2da6c460d662386d22c540697b740b2838606c46ece801a25025b8c9fe3bd1f40198141f04d1b53888012928eb6641f6241f2926d543cc5afbf747a24379984ca23ac7d3aebd53712efd274cff0d97c2e2327940df3188080642414245b50101c500000018be22100000000002d62b1fe188fc52b2e6f54ae0bd4855e8ffdc69a453a1114e0a365e10d8b71701927108b16b5b69856e6422011f9e8ae73c686f95d6f4e3c6c22262770eb70bd253e779c46b384ab1b4c4e7fa1e0ea6136f7fc40b24e22cbe8ebfdc62178305054241424501012076ebf92a2f24fd4bd18b3cbf9933edb7ea4a3fe71635d1bd5c6b3aa364fa49fd46eea531e169c92ba95f28d875cd745e04a93623e5eb79d8df0d230246a184c028fbd82c97005a77d2bf8bd8aea02f246f9e79b4eb8f914b5605bae2ea68ac4aece8016b9e2f0f5333f04c595d3229642bee59f748254e4eb39d7de79e3deb4e1fd0fa7a9890b796f9168aece02a4b1ecf00e3c50e8abe755ffef2c72fc9e83eb9df96080642414245b501015100000019be221000000000d86708148c62988f3390819fbc260ae7edfe567322adcdcf514e7f98b806c012d7933e26fb929d41b476271f6a59380eeadcbd4fb4afcc1bab1ff70ecec2240ddf2b0af3a2c8d8f0848e4eee08a7a8803bdb7dd158895de1dd7107c13b26d407054241424501010cd2f4d5e41f653ae2f50adcd30612b698a32b99e7e7365770135bc378e08d2f8854ce896c549d7a8143c0b73dc7ef471be37704e75eb44a63348222935e358034420a729b43cd56341fd41722dc526d1b97d6ee9100738f15b2474b030a3e214eece8010887c635b57345fda9407cb8c97fa1fc73d6c7130832724932885a1c5cc1683dc00ea5f0a6d20f9af3959f4d5406660ee82806e7577cea9fc419a37b5957df17080642414245b50103620000001abe221000000000c88ed8a9ad74c4871dff56985afc1da3404c4640974c7764b840e22d00760b75c3d1e3115849479b7e5eb479376b08354feaf307f71344bea8e5590bbf9bb10e2479dac68f193103ae6256037c9880bc198ae9e4bc68226af03d45085ad62407054241424501019cfe45216736c3a9a593c8c79eca9dbd45fb51a2c54fb9247d1006cb39ca2d36040021d938fdeb7e1d2ff3a58e56b47e1720d2f9de45d85b59c0a95594af218705b52a9797ab7c4e72c205fdcd4de925b9b1c410dde44f0443ec2e46b98eb9e452ece8014a00892bcb959b211ef5805e64936ce8880f63de6eb7550bbbbb481d5798d5562f17064b9feb6acb743c21c377a6cef1d8f30863badc9a68630448676a18200e080642414245b50103720200001bbe221000000000f406f0b031eadb877f750f4307a6ea5a99b58cbab417adbe0d71ba6c86c42308a089f828545cfe37acabf00305f554809ebff308f1e6105657efedd57c39ec0c51b3dbacfc31b2b1b9c62fbcdd53872dcfdc9bed62ec2197a3221e9a753ac60405424142450101e6d98809fcc87b0c31b39e5c277d78c737742ef938e26b47c56419a5c9586738a5a2e7088d60bb036817b879fff598cdfa25e530956739c7de84b39995d3cd86c9e826d45565ce3e9d12d1cd82d39fe20c6cad590701dbe252a8d0a0de31eeec56ece8013bf9687041d6b2dfe44670faea8b7e9d6867b07c817f3cd5a42a44e795166be0f4a0a20d9646a85d657df656666deeb6ce034743af638d9ca4ad6fe8c376d015080642414245b50101530200001cbe22100000000056ae52365f709dee401e5cbaeca606072721ed0e2f11096b21c780ba6ae577196539ac9c01ec7e4a439884ead3b943372c2c7646dab0ae4b5583005a1d9ba40612e10c69d3277ecbc01b71d9e665746b3099e199bbb4ad4f4131f99a9f7a8f0c05424142450101e628b49a58a78db0bfb7a15627dc896cc8ca9ea358dab2d672e02e26dd093b213d0fa918cca59a41459ccdb12d2240e3cde76afecbddb4e30ce2d35c5816fb87b6bd5442508ebb0a0d0bfb278f225ce4123c5a20ebbb6a15cd3e37996732e9955aece80184ada37001a3a92cf43b2bd939b9ad64e872b740aa40a18f1300cad83aee2d349c77384ff9036e641aff0e49e37b8f900c40d259340478a1357ef5bf8b27577b080642414245b501031f0000001dbe221000000000fc5d2a38224a45dfe079d4e12ae46ae977adb5e4d5d002bb657d4477c54646592308f1186464e890d591e55faa66b0f3d65bb99d595d7fcdad3169d8ec21b50e58ca213192625a1c9e1747eb28c73a7e944a5ee586b0ba195af65389d6f5e306054241424501014a58bbb03a93557c951ac7ab343040beb22abcc1ff4351a015359ae07dec592b28cc538c89ab35e95f975940eb9aa003a920087fb950442139cb5079ebcea4831d088349a69a8012033c81feccbf31a9c9951da83f9b3d4bf2f81e5993fc59da5eece8017a680f1bc0d7547a80347123cc33e1099b8296b2bca496e912ec6943e0b1a44ba197a7e1577c6c49a66ef2840bc6c58b820a71f1dc41350d75bf563febc34969080642414245b50103dd0100001ebe221000000000c0c8e89245bbacb05d37f9701ae48b3017c3b0c9c36eb40eabc2a5c2e18bff15a7db5259c2013b8049768aeb18c919228fd9aebe2c19cb6b634ce6a25a96f40754dc9e4329918727582e81e3e0ec0f7ee907582089cb0fece54174d852c6f00805424142450101128d1032f92113bb90a6383dd8cb89ff10e3da742098d904f49f23d32525094ceae3a62eacfdad15dc03686336106a045531cb3234a81c0f6968709a0e1b26877d512843ddbbf7ccb54a2ade43ab02467160af11a622390d646d2c0a4bad9cdf62ece801cd56f1b164b01faee9d827fd836e2683324f116c84bc0af9e72ca0b595ea5eff7693c08369bd095db6526e80f165c4b3069154e96894bfb0528f4bfef14824c8080642414245b50103ad0100001fbe221000000000f69cd214b42ffb984fc0afcd433b7b137e52df0c905a34bf15953a408e92e829f8cbff49625f8105de65f58033da1d08a627a2b1d83478cc861ae0fffa4b0905d481799ed275df2e867213ce050026986c2e45bc04a425d356ffb4ba2c64e205054241424501018e0b597ad0053d468294006356992b5e71c6f97c8dad72c3537f5b0d0c4c33581587b2a87d9f66aea4eaa9154a9c3eceb309daa9d8cb8d6afc6a7091b27b558041c8b2e9f7dc0a7e022e1f668f7644f808888b67236b07a702afb29b9055466966ece801632209012cbbc84b835426ab32a8d07eb82eebf43005ca0d268dee1bfcd846746e5fed8df3e38bf3836e8009f48e40b286ca82ab0ac3aaedb6f41bf5ddf4e865080642414245b501035100000020be221000000000be75d774520a4576c04e832f618134d4c6fcafc2d3d5baefb839391fd86b261339bc8dc457acbaefecfefd96a817666ba95e08356ebdd8bedab34c4c8f4ab90ba8311ab688d1a3edc61cd603bdc0d10c5d919baf6450282333cd755ac97b340e05424142450101b6f8eb6961790b6ce4da1e5cd1cdb607a3530dd11aa8fcd828de3b6763198d4a6300977007d8a18bbb0baf98cbfd63a74ee490e6a0e0932a767c69e213521e8c8ba4c7758d64b79a9d02de3e0c769e71e94f9696a11f3715b1add51b51117f346aece801fce0e1f317617189fde141c8687034d5346510c0be8da3e996f276c4698cf6e5393e68b07da218c3ca7dbc4db843f564ddd66c7c537976842640b4970e179d45080642414245b501032b00000021be2210000000004495ae0dbcef21d1e942258112c603e2c2469ce8ca1f031975f3922a309dff0cd521f488decb60dac6668b0984bc3797426d5248cb38b44b3d276fd80efc4b092df6db65ef17fdb72329126c57550de5b4c94bfffb35b1362ddb81d897c9520705424142450101684e8258cefacc78d27168c42a9558edfc4bac12d94d48412f7e61971d640243868a6e0abd04b8e8d30cdd8321f4ea4d95c64d61f28e67b01bb63a1596b8a680d9ea0bd4a3408b0c8989625b84ec92163455380b36d53fce012e5c9ca062e5b36eece8017f410032ae38fca89acbbc68cf7530382aab84cd89c62f5c05e43e55a9f7ff8cbbf0fcf7134d250797a7b87519d4b4eff9223702e868665e8c83358f435d244b080642414245b501010703000022be221000000000aecd15520f37a8ae30e791a8e4841f3c06d5ed1935cd72813543c48339be9e706d003ab7c0445961133c8ceb2dcab584b5108f0a9ab9641af563298750e3050993e157d4baab93e99cac91994e449a5392de058496842cab537a42fe24523604054241424501015c73137eafaa8272fe04a29f5ef8f681c0766c11f38374526297fe843eea803d8a91e384f47959d33f41cdc5b5e648a3902d45e3f4349519cb755eadd6a12183cebbead69a29bb9f63a5d631f77279b0c92c33572b42eac3b655db2cae54ea7872ece8016e88c46864b21060f2e8dc41a7abb4662785cbb9bdc65e817f52a2d4b81445ba4b006bb70f943cda2565227ae67c23e217f6b9e94b2d9ab5faf8205e69c32d1e080642414245b501032e03000023be2210000000005a11f83b577935736a7e37d0000306f56b5f3e0cc91f92301ddaacf91ad15f1821313ba05de5a236d56390ecb9130d04e69bd16a23fd43f767eead6d50822400690b902b59811b196a1df804935e72b61e420a968b49f2b7fa0cdc513990fa0105424142450101464dee8491e4bcd3fac2b6a45c7779fca22a78f6427e1ad3c53e724364b3657b6eba8a2264b7ffef2e1de0b73a7e1e75ee0b472fc8d1a140acb426983a5b4281012bd9b3953cefdb0a960da9bc3e70cd41bc938cdff74740d4361470307df69976ece801c3dbf37fe77d6081b98b587066d1850e0018896d0bb01cc3da36d0b97f8bd621c9db349b03f34acea3ddd1c22627b773e6a5192b33679e43b62c86e29496e05f080642414245b501010502000024be221000000000b49997e9a42d9faa48ea008df3591c45b5231b4ddd907c78283bf8e15ca23e29fd57b24a7693ce6dd3575e6613d10e4e4287aefa3a8167cbf1a85f220a73140033933bd45f5e63a42106374e02d41a87f47867f7db76bc25b10cdecc74f42d07054241424501018a82a7811c64c2e5f6d190330c77d9d776318e771c8fa3385af539c42f83735cae4546ed319d6500b9b385c416a8b79c3f329fae2278bde6db4b76bc85a31b87fc4eef66bf63e7b62e668f5929a40a071d0323c06b771f75e68fca5968cb04357aece8019d666ed19778e7f69576f8fac9d4eff4c0fc2e5e1a49e008ae4ecd6125faffebb3196166ea271fcd1ef39f1b76f1ba72e693f1e9e694a468cee9ffb1f8e337a0080642414245b501035d01000025be221000000000f07e1da4f842e7e9043a3b79303a05d77af4cfbc25c1d4d174761f97c4e64c1c4be8a6d1f2444adb5671b64f2830d517f534f3fd0553cf1dd46a9a9a88c26103ca13183399cb42fb408dddc8e98cf57b33ef42aea2bca93486fe68d3fa50440305424142450101eeba0913ae07e9bb2702034fc0efabbc5e4a8150ff9301089fc67b4dc8aff24faf56ac066d73a5282a383ce08079005157428781bb1cd0c9679cf274516be38a78fade409ab2aeb06d6e6fc42fd6676b95be8546a4ab4beffa7abd3b8e73cc257eece80100bbb503efc53e33faf5ca5c2979f25abc84f3752102793d5a9f924158a31c63d68a7ad4fc947936772b8d157ab838f3b2716e4de548908b25f604f6c3c99a68080642414245b501012203000026be221000000000c410cfd018438a3ae9ab19ef70549392d63ba1df036eb3c8ba75d7979615ab33a0030a71b7350c9e1ca9c0cf8017676b71619240c2c2023a03b80b48df77380e7d34a77277d5807afd5c3b66523c7a6efb712b17760b958115e1ced077d2030105424142450101f062121c0b4a6b8ca967cbfe305e955f7ccf82168561564992599adaa40fe97f31c93ddafec94658433345f5387ec0fe45b9662d2780978b5c4624bb34965883d36413bf3a1f3375f587a3a7c9549b589d70646afe7b1aa99b315b0ea406918382ece801f078d07e1493644cb6c7115eac6dc7aaaef93da83791cbdb5f9d28d1b875cd0f01d6fba0a60d8408920ac8a6b3ca0373a1121fcbb41a968f69567bdbd7836c02080642414245b50101d801000027be221000000000da61da54d340178f4a03e64a7d0b0b63cd39e69fee08ec0c3065ddb80d6aaf15208b22297b924e28ac28fc9036018c5c6fa0a9de7899fc3f4eccd4d018d4900d93c682a142311b987c35556f0a0d6ce9eb4eb03b072b68e9cb4a00dab97dbd06054241424501015c620e82c550cec8ceaa2c09ebf6b6b0f7008b7bfccaeba9ba2555c0c853722fc81bd85032e7960a8f4124e9960aedc59ab478aef8adc6802ca44823e8519580ebe72b8e5629eb9a073738f4f14d545c3fd5f197f1a2bbc564aa65fac348071b86ece8017f814f180a5cd69cf1be582a5674427c5ffe13b51848bf63072fe275f6d6f0e0957899388816aa79843b66b78886eed318f45dc8952391c51897d5797656341b080642414245b50103f900000028be22100000000022f3873ea91c18b353bec7e4975d116ebbdc92c32bdb268e89f0daab51b49f7d7beca647459e09f9a4b61d0252dfa44b35f3d24e473d2a8b927846b493389300834aa4d91955daab015d2ebcaeaab6b790ace663fb7434aa21744065c462330a054241424501018cf5d8e7526e2df8a6b132d1055bbcbfab171618de7ae04ed8b3d333d6729125be4a6830291736f2a5b43a6782ef6f13d71bbdb8ae66acb1e1e38d7922af1685f5b6f899acd440b161bfdeda68aa5992dfc2a1ee0bfd9b0846af6beb17d4e3a48aece801d7b13c221da46e350138f5d57b53a62357d684d268710e396e7b9394baada6e92d43cd221ad7b13a0991fcbde7632fa309888fa494987a5589fbea41183b3ecf080642414245b501015101000029be2210000000009cd3a0d50e2b0ee0d6c9ad60e4c5dc4db76eb30782a84671096b6b8ccd8d80552b1f8016d2fdef33bf31a6305a85fafdfcbf4216997320fba84225a8d78f200a4159ce1ed96b5eef9f813ff7d054af7ae0d774bbfaaed3dc2617757109debf0f054241424501011886a66c36a01ef67df2ea2e2cae481264e83aee69d7dc839923cb5d7d3df74b511b2ae8648ce9052b893ee189f33a24ce21e583ebd2e62900a1ccbf470e1c8bc309a5c0f0e6c7820350b73788b370f447d347517e34af261cfd856a2135bf6d8eece8014e8261109b3c1207b08de4eade622ab9e8e2415330f9c0199fdf0218f6139325d6f7252a98a61ffad90a5607e1f8d6ad7beba699abea7f41d225325fc44acef9080642414245b50101970100002abe2210000000009685eb1b021557728751db265b80a62b7bc57fe1af83520d94b11461f70e5558c9723fc27aea2b315ae8e5e0786f8861b7e88fcf2282affed244b0d33d1fee0a7507a620a3f9a4e05b24aafa86a2af42ba6e41695ac74aec7159ddd2208ab304054241424501017a29f26adf4f97f558927ce6a28a519f59f8e974cc3f117475006d9196910509dc6d3d7b24222d2844b6c9be429bb4a6f1a454945895b54c17b3c8015e268a8bdd566d64b392926a2951c5c0257adadb24c31c15a188cce6fd8a84586a0f5a6f92ece80194460fa431a59339ba6b320143ac2718231fa002ed50bff43ed775b36ce09fddce557b8cea2585751e571ebb7ea7ae902a377447b82754e7e4836af06b4986ca080642414245b50103790300002bbe2210000000003c16aaf8b6c1e1f0850ba1fa164c9f283e39fe5d16ba7781e548ee417f471d5af5745aad06e530211cdc89bc27694f7108a0bd02f096312e4fcde5c667347a016e401f3f4a363caaec83ec8ef7fd9958a1921e9fc7023f2eb4f1f8606800d004054241424501015ebd8df978ed471c139c689fcb26b674532ed4d385934a547bddfb9de0f9cf2b3e4903d3e25f22cdcee0bb1fbbee6fdabcc6816676d1669ccdd4cfeacc78678bb17c23247e36a35e60f9b1a6f95b910b37d524ab2c9c040c0a2a62b9c7c70ef196ece801cd8f1da9564b7f78ac975e3369da1e870a8c3903c5a5d8e89d8e6147f2fef59596e8cd132ec02cfbd48bc0ec65b64bf8a10d9d47bd4d0fcd0f18c248b902ed11080642414245b50103bf0000002cbe22100000000034f51ca342bf67b0bfd9297ff8e3d1d5db195d38e52c191019f7bda90b69003c683187fed64ff130aa9687e2c3bb861f9006748ee2b03f34085a9378f8ac44015055bec259af3240b2fd8969d4f45d373bda7c2abf531ad82d63b30072d3a90305424142450101cc5a5a0c8a225637303ca9b0ebdd453d4d05a3b597ecdd140c55cd0963cf27481eee17e4a5afc3796b9e1800301921172eb18af2c74aa5cb5db50232e1dbce8f7716bdd7b8fa6b8e4e34bcb9620944e1fbedee713bc6113ca95b6268712b55199aece801b7f80796d4b9b987aea32fecd29d406d2a38a823276f0ac17ee530ccdd63f5a5a5c9403d4953933a0807d7a591c300400a4940d5fe52053f0098aa0fdbdbe84f080642414245b50103fd0100002dbe22100000000022d4bcf3b961994377d669d3f257b0ae0f455d52b75473939998a6e1147cb412d0118f8de44024e86badb15595efe15a820b4aa0ca4ade4a10d95a3a2b41a90bb95ca9875941ab367783d5e76d23c83aceaa747f9881aa541501af372533720b054241424501010282125dd843a4232b2070f2dccdd28199186c48361e0408af37921371e71c6ef94de978b8912fbea6aa7dedd0ac144b1ed227e1c24a52f5bee73ebadde7dc83ef55c8264008ccb55b221eca125f3f52964850e3ff374d78c18a9ff9eb7ab6039eece8014acab885a5fb3df94cac48e897d2b306d827c3c47a671c1770fc5abb25d00450004398e5326b7ed3887e5368348f8ef4c58460892ad23958b0e3eb961834e709080642414245b501032b0000002ebe221000000000fa98ace3c11231611882bfa1c7a51a03e8db8803802da76924b531dde59ab9560556c02c253c8c54a9df1f6e8c3d89206721b44bb79064baf04b85e8b7ab050ecc97ead1e0116f96d918ec1b7b5f656815e381729d5eddc35c633b74e0c0190605424142450101f6e1f6ec38af4d2f12b87ce46a1973a6871317e498bf16c6870b8e94c392b56b0e350b7d6f07e0e9c8a3520c8e1d71cde4a92b5c7a7edcfc56a26c18f2317686c259781471b68d66e6eeb8ed29d421420adece1df9060a8e2b299bdb41005fd4a2ece8016d383ffaed42b3eba49d8b8e9fed64bcf1b917d2a1dcac1de157964bfc5404609c40d7fdd48c5a25d4d6852ef86409f2f916aea8a2db8d4aaca72a391c806280080642414245b50103170100002fbe2210000000003ce53a64e3bb44f2f44724c040ddbbef9df9a9ab1931d374b8ec84794014c2373bbc7e72fc42c6688230e0dd0ac8cbfc648e914592d40c2e324f71fa22c0cf04c099acf3c1c40bb2821884ac3aecb224f9ab094aef53429c67e2dd255d83d30d05424142450101b4b136e521184b08e9898b821cdbefa6219ee5c5a0a1a7e8e76b5a59fe2a210de1ac358a1f3f2dc1437c5792124097231202b097b0c807ba7525177a81f51f8c684c6e0043772d1e7a66c992fd988c37366bd44e038015a67d1a7141bf120078a6ece801bb4f93d61ac7707a3cf0031d7a007cf137bff0f16611496423ddde571343c7b95ef6b828dd91e33f41d95f2da93752a72c1615cfbe22faf1b065fa324b18a186080642414245b501033501000030be221000000000b0916b1c4bd6334e9f6b61aecbf8fc3fd50e450f0be86d23e59d3754747f3e1960afe8157cde046226357c1ed9566f40eaba90e2385241ed3c4f9921426686003494a13d8754d3e91c3ea79af6ab684e68aae8c5629f50ab3458d79a59910e0105424142450101c239a2da7b403f441dea8d4d65c8b22551702c89722813335ba5b4814fa5f0672bfb4b08c0d2c471e30b923d679a94b752790c2d0b93f647016a422a0901978ef92eb73a6ba62cf51cd753a4b225cf5f5727e8a420e49e5586977a7cfd2fa290aaece801dce02c361aa618976a0775e9f882d5953732f76e758cd996e9505c00bd0151bdf81407dba7dae32d00e72fca5de379722f3d54ea776e0d47ef52903f92ce9ca9080642414245b50103b001000031be221000000000c21154650ae36233f8dcc44224e58ffefc816d90266423f02f266bc4a63ed64fd8878a84f0db4b953ff0ac8db4227febcf5210f4fa0190883d134f0460e7a30fe19900a809e879b68c0ca8c4b7b3c2ecdd9484401dc4a37dcf931eda2e4b67060542414245010166f491c4e157e1d27a0a1f35e6d6b0e979244d5384aec542c78d0abddad2335dfff36aee43f11bfd77b6b7e885f513aea7ae1ad24575807032ac708c31dfc08950f992f73482542eb487d5e1375458ee6e0fb36051041798f6f33b976110f70faeece8012709b3ceb7725f4b0ae99a49caf8740c2bc560d903377489b323c56b7dad8ed292f7b9ac40c79cf3c6d2a0b465650ff6e77bfaf0f98f84117500a7a3108ed0ff080642414245b50103f202000032be221000000000cc2753909e72cf2e47c6b2317e434804f32333f914b38f217d997b11a3d94560854849f12f8bc376f6dc9ac968ad10b57f4690d52d5c87e52939514be9d1f90dbddd051c8c593ff959e91f3d24ed07e0a5cd93aa2fe2db22f97bc3cdcac6b50d054241424501014c400a7c86203d38b7874840e26f688ce27dba4da909aed29201a1b70fe4ab1a8ea4a3126119c00100ae8a5674bd6bae340b0b8309abc968431a9979d3347885852da9ec3f12eda783baa87e4b6d7a780b01cc8e46f3681dc582240494136617b2ece801189c42ba318687c3d228391b37787fa1c5c73e1f312032eadc1fa98dabb14e192948492864d6415ab65eb2db97faa1806567e452085f672ec10e702dd1b6a43f080642414245b501035b00000033be22100000000058adef0a341edb539145a1aaa19312c10a4d9b9628af1839cedca2ff92fbd70a8303a019af0f6db30d5645166bdedd9eddb37f4206d103a5804545b33d60fc0f7a0b1fb5b6a1ea01d2586689a234c34dfd8e92373be5b80e63e819cb480e4e02054241424501015eaba5cd65db9c8dd61b90f58744dec2363af995aa11b83140e4c1453fd00f61211a132b78b93c7f2977992e0fc3f4a29353a665947d262c5644a1f731ab858db53cdb20ad15e0e88b4e94393698c31dad82258d348f9d05697a87285b6582beb6ece80120ed41a72205e8977689349d3e07e5db91c34df1fe5ae5cfeae30c42295a4eb4046166a385a663dccf6c394edb64a87b11070d68c637d9b16fa27cbb84e015b4080642414245b501032500000034be2210000000006653c06084879e77106a9bfc9d6f861e40380dd1b725d32dab7fa3d0cd1a05246fa7e0a9aeb9354103de14de70bb53465c0b4de721d66e82f3183fa747a9ed02b1b009c231917d33796491a13d4bbd2e45ce1e5bb9d11d4bbbe2ac1aaefb1f000542414245010142ac28e8d2018b1e9958de525c662d0aa574c2691467ef37b73a3f9cb0e63b0e94260aa805cf0c4d4eb3168176b5c4fc665a23f18c263650ee4f4e32a4a62a8aebed742e5e1872c5595ed99529e59bcd049d995b51c7ded316ed7a1e39525d67baece801792c2f9df82e1624f427f3c74c6049346107f8f19da44feabadf468ec409bdf17e9c22ae31c5c1be64737ec0e5dbe1705b76bca56851e8fe0030ab403a0a97d2080642414245b501032c01000035be22100000000040c2bbf06dab99feb72f4a6294240bb15f44d57a7c3adf92aca6c41e5eaf141c23a767b9f2da547597c24bd0e34d37d9b945b42dfe3128bd98e43cc6961fd6008c0e85662b1e0d15ca173bdc3f61b77e1168cadd15eebccfc8ab687eff36180a054241424501010e819bca97dee2f4aec8320c89ccaf71bf61d1c531d9c997fcc6b8131947f0699134ae7fec556046ff13871974c2f0f019653f9cf38e71825fe03b3b17384880ec0ea6a3b8d9c81982c87ede8c72f577f3e6f3f9f511b681a5e5771425549647beece801d1f9dc2da1c4e6bdb16e30db2db886bd6cf3e336c2e0db706671aa710ba9b3953d057523d11e9d5a154fe1af13d5c0b0df84c86db6dd3b42dfb410859ad9d694080642414245b501034002000036be2210000000001e8783535f694e250f5223644ae4760f2211c9821b369b9954a717cb4aadfe4d5af1233a4424f554db72852145e9ed72a524e004cad7aebaf162374592711b0e11f39306b07f4ecad6f8e337b9c1fcd79bcf7098a105f86eb3842ef96be3b50a054241424501013cf14e98f3279f4999489417823b084c24c4b67a05ef73d10f06717e2e3a32015ac479ded54453a88dd82a0d2ed574fa4a4b688fff34aed60e756e155827bd89e6c5d939037892bbebb79e1712634006d7c975c96fb69eef033f1f0b4adfef40c2ece801f5c9cbbf3b89cff3dc0dc310510aca6871812bda81c6ae84d29f7d6b2b70fa1710b1701b187a73313042a2440d60af7394f487514edd8859c67021fb5fe8ac50080642414245b501033a01000037be2210000000007a3f1e1ad9f01a973f53c45758c0cdd7e2ca72bbf9a008a6f57ee8a11e16712004deb79cd6816d866aeea8f4ef100bef2a7c2153302069bfdbcbee6fa9efc40758ae0cd699cb716accc2985a58f9d3f14b0eb28226108ce91b99f1e788c4c30205424142450101042279e513f8ea833d38340207d5da4cf8b480d75450a0ae4e7bbcccc2c4ff0c27f8c1ab707bbf8d12ea63d29559fb6b567488764a8b24d27b4cbe014d2a768ec855052094b5cf4496fae235ece624f0d63ed18653a662143b3fbaab0eed959dc6ece801aaae59aae325e76c7721e473aa269fad8a3ef5cc09c36860cfc30562366c70e0f20a85f6626cd4c4554822405334e591f7a04f4b06e610e3ebd860c715b3b271080642414245b50101c702000038be221000000000d63b7b50e1b5f756c966e1f9672af6d20148f8ea367c5b43a80dfb15040b4653c850af6dfc064e44b82040b764c46404a93088e55065e6a7a8ff997f6cc1ca0de6449eee8a11274452daebbdae88d023219889c5d72b4e727db6ee47c3271b0c05424142450101dc382ea1ef4fbc492f5d2d35caf59273cfd76907b2202f0b3a5543fc706e491f3693c57fd18a39a6eaf3c874ea284cd715224b80ca06559d71a4307e25579d8edcb450c265be611179ca5a78fcf8447086dd7fc6afbea5234ed8c643436cb4b7caece801c31b9fcff206d0d08d69eef1c6633e252e1bfe111ed742c2109c152fbfed1f9a9e302bc9790690ea535002da3a55809bc137ebaddea5a6c7936f506cdbd278e4080642414245b501036b03000039be2210000000006a41474fcca86c47b8f685bdd2f1f057116dabaab33f9ab645e6bc8ac213291e62449c6c974bcff3bc863e6b215a785eb5e5de547761c2d37a2fbe4c7d00d4036822a14db1cd0d4ad9e269c635d1116d7db11037cca1e6836944352f237bff0c054241424501016cf707e6d844b04f4f583b8ab44782a65d71e4a60c52cc54719bbde735b7c54478ba83ff7a62026caf13daefbe0c6c423d65da160e540874eb6ad111815d178557b177044f5384f4f569acb9d5b732040a106061053578ac63bea59a3adb84e5ceece801f91cb4490061d8086181872d3cc30a7a4ce4d3d8a4f0f67c4873c3a088e1b8924ded737c90b2cb9b5dc7fedecc6b1fa14df11c1a6d777bdd873d8f6ddcddc989080642414245b501011a0000003abe221000000000daefd743f4f7c30b3ff6260b3044d828cb010136cfa2cbf86be08014fffcf55d409f4dd77b3221e4da4b1b262fb1d8b0667cfc69b6e90acac7e53441dac6010c397c23038b7491491242a8632e47cc30dbd7d4038594b4b3b08f0bb5b8f1dc0a054241424501018c288b8722885edb29e5e7c161358b022ea297df71a279bbe04ecde1a5cedc2d97ffadefe840c60ae4f9d7633f4b1c7880c0f7ca3ba20eb748e88926e6d010849a6700ac475110c683126f781da3d6a51985831d475f7d4ed4171932160ff6f7d2ece801164112101f0e26ce00f6dc776dddaaf5417334516a300f8daf5ac9710947ae5ee36505ef3dc55d9381058b43c4de8ddc2095d6d87adc61a32ea0106ca9666eab080642414245b50103570000003bbe221000000000ecd1059d7fcf03d20ad68df9dcf2d2ee89962caa954df48cfb9f24e32b31160c0da4f826b3c3ec31a37d6e5efd80d018f5c282f378a000d662de1884d78ee104e8fa8c6964f2a86e06d6218338c66af79ce60d4f507da1471a56459fece243070542414245010166f0f90f71bc21f9e286180343f47ae3230ba84d373fc3522886511759355857687f6232ef02ee1605d726a3b11b5f4ac22d648678909c154e8885d559ada88714ca90e3c7713f88246111f1daede81dd417501b6f7c45c4c7f85a026bd2069bd6ece8011c0cd4c62a0b5fd24eabc6786621e34aa30dacdc6ce6e0efe970d2625bbfe14d14889f4f96655f7c217b420ea65a6b57d4fe1ec0955b8b4fe747753a43d91cda080642414245b50103760200003cbe221000000000fa8c1c0b1dba2d9aae7e42c8bd4d091c8efc4bd12e323cab248a1a4daa9ebc2a1b8f1863408f993ae22558f18a9fd5e8d1d5b03db2f17c6d0ec28fbb9fc2590dedb78ba6d62007cb7edc89cd487236e03ada6e7c1e1c5b46ba818253c092e50b05424142450101beb7def00ddb522d8fcbf9ab3430f4aa333608bf732705af59bd3e503695fb6dc02f91e3ca8f1d0626a885ccfa1c3c05a233b0ab83df2803cbeccaf01fb22b898d139da86173ac4d9dec73d4e11871167766b53269e88f7a15e0fe1c12e9b8bbdaece801cc0b8bef5d455875cbb63efad5f2f240bda94d2c6b23c4d047316c5c35dc45c08291e7a35030d0c3b59c828da9c9ca0d1244b2a4763b46cf496bbe917b8c0911080642414245b50101810200003dbe221000000000c472769d4a501d196a528298aa390d07ff2113a3ee8ed5772455d3115c110769f3c6994452f6f9b4c4b46f83d373444e52d2300f31b51604351e91a1d52e330a796a6b9a2c5de6434bbce9ef985ba43faf118db5b37d0c836266d30efb1f78020542414245010106799899dfbff860ef989d37d47fe5e75b332e4d6cde7f7d2f69fecc8a3de5505bdd7191d7302ab409dd99e20120bda7ec7d01998f3328f691922b5aa871398a10269b0c506a77e59da6094fa69afbbe96f49bdb2186f1d93370c27993324234deece80170abab7557d8cd615e48dcac8f4405200ae559f71610ad1979608b8a233fe673dc3975bc7c8a8c1dbb54c5e527ae0e04f9b6dfbbf9e7b9cf1ab0db7db430c422080642414245b501038c0000003ebe2210000000008ac36e411783f91e534f9ffda3b55d6b8f4348f532da4c40b9ef8258629f000288f051cf47f230c136b301fc66433bc585d2549c8746f696e70e7710cc6efe0c34df4a032779b0b89544471d74ed8b397b7f05b1607d820051944f4ffd8a3f0c05424142450101dc427a9bf148511e56b1d68be1fc533d02e0bc2aff0599e75ed36540a99a9124189575607dbcc5bfe626c708637ab2619c94050142113cfc35da56063807778797810ef7be6c8685b5509f81036be2764aa0ba4a66d2e61416f6bb5b129ebb67e2ece8013ecbe0e7bb9cdcdf01a3d53d9b88b6a029f1449ebc42a84166688a94794cafabca28f2185931e3ad9eafec505e3337e0a94c108e24772db36e521e858032c099080642414245b50103900100003fbe221000000000867903ce39c6b5326270c4ceb7863ff5d1ab043740124f38177a2b7a3c19fe045f66724c077b9c0246dffa74e156a0888aa29b34261c6ac9a3d3660ed05ec60f2256aa42c88e69a1dbe64143dda9ecb0c7883e208da318c085872324b70f830b05424142450101cef5486933c0ea184eef8264b4e2e5a8483490f8cc5841d1c48df8a34d440f3b45d82f9a1524c7d02fe936b66ffc74fc4f0006d7f89c7f28d0f5b2c62f830f822d4f6b7d6efb7521737589af212e69c12a28bcc78682b1d56825886536d7692ae6ece801e5726821e1c527b209a1162b8583504dafccff6e2ecb64f04fcd57071e7112283c4ccc202fa54917f3f6f158cefb288e1f2cc32e1b47695a6abce30187f19e57080642414245b501037100000040be221000000000a4bce58fd8c5a46bffb51f34bbee78d52c444ba971ea59a5fb6d6961cf3752029ca0b61dda7db8c7e0af0ac5530edefc98b6355d5324b7517252e436b810df08d9ec2ca9e51fac900817ec4ee433860908e3cc2bf0b27a262c8667e48bd12d07054241424501013ceb85fb5cec5a6b7d42f554dd06db9095e876705655f1cc294383da839a5050cfc082fda31a05fa89f0836f30e1120419ba15831a2dfdb81a0bfe6da9927b878f4a90917ef5c98d74860f2e6767b55776767a5d63ab407e04c24bdede08368deaece801b1f92e2324a647af45c8c28526fdb376f3c9dfe881c99bfa4d707ee6749821e7e61b552f6debd6b00e185a00a6238851b9152ae1f0c4423c04e6da3b9df47ae9080642414245b50101fd01000041be22100000000054e69badd296d8eb95b97c55005dd05d6d6e7560e65419be6913f56391fc1f7ea74bb4cef4bdfb1f3c06b1063ba28539c101c61d88361a5ee1145908df86cf09fb18ea8a8d5409d5707604b470a585984ff23208af3a9ab4c42c00aad9033a0d05424142450101126d4325b589a34b60f6c174c057da36086e93250eca1047c4997c2b2959df19d9b0bb687351390aa0c99543235041d24a2b2e0cc8eb14e34228469cd6754286e04c9625cccfd74a7cec0cbfff1b993a9bc256d14b83de6522f5abd9a54610e9eeece8010f00148aa802fe1fbd42b896e75e55ef4f369507e5686dd5606d30b63228aa9f64dc2fd8f580aa21dbeb789f9fd03b9a1570efc6d6f1c08e91c3099a6fe48d5b080642414245b501011a01000042be22100000000002feef92b43fea5e81a6331a1189fdbe2bda32bd955913b259db03b96311ee4b23d5fd391ad6ac784bf36adbc74d95700b6db1261eda371b0e02cd2d8a36db0c0beed4fb87b510c11901c1098f2547d31cedfaadcd60372b7138390f056ecc0305424142450101e6490772e6c13ac7a47f3a70b829270e3f500f59d933c9d812da87f85fb3f04ac254cbb22575333c19d726f023137e3c53f470e41797ab48be88eb3a542fa8831b2f8ca6642bff44c59029d9e1323283946d5a770e25209b9d3699d97d6502bff2ece801523b8cee8edf4a4a1ace3959c4de3815ed43a5c51574b5e5564d4a01c9d2361d570e34e252bf1bf15f3c16fb86eb089b67563a1f63f8cc30b2f6c8857c8d313d080642414245b501032100000043be22100000000010f687515e18f337d9ebb78542ca035612b2c2334e386a7420d2148e8700da5034b2394e3ee54020b4323f5986b50d6cd67db022635d0cbcdcca949c7d022b04f630d509a74f45a05a2d1ff2d8d623d2a7bb9127deced666b8c69bc14f18c10205424142450101fa288de4f14220b1ab264157b2e2b0d62dec1efe8eed4b2bf78496bc42f7c805b19e600cbb01bc3e6aecec8f7987850dd4a8f27e878b8df17de46d87e4b0778277813ac5a7e61afcb7a0d2af8ab5c96a56970f5c7dc554ebaeb0e95f7af5dd48f6ece8018a81aad3e9fd260fbe042543f086b798cda3d75ca244417aeca7054693176b5a3bc9e43ef8d426e0f9034daa9c9f9d51f37dc1171b14054e7e3e23122f54dc5e080642414245b50103a600000044be221000000000782bb8ea9ac05637e1dc89e4ac74d260a02b16a406e5a8923dba0d2ca64a4f2233daec3344ffb153c8432e667bf4051f338a293420b9d49205540b7bffbb7c0b039e64f2e59d5a55280938e4f272ef61e56a2f0ac618b726e7f765281acd600f0542414245010140cea7b8be524608f37620629af00c5b8c4c2d7d384ff4de0a471002d2de373afbebd19402b05564b30cd4082bb52eaa650603a4c8b4702010880be1ee0c888ff7c18ac8ca86cea457ea689166ca4ac8bad156b950941c580d0311fb582fe4d2faece8018fb85c02bd8ae549bf5944bf4d3217082e68738d0879dc0ec8e41edca6cf84361b1d69c49c9d35c29373345b484ec615567e8552766e5359807e2351e384d6c7080642414245b501016100000045be221000000000ca230a26fc4511c0e0b28d92fd39b5434ba1dc54b767d9022be64206af848b6f4539c7e1c633abd5ac4248da016b5c515a9de7cdd65e700d1ac3c92e32a78108e146ba277997b981cb65306f7ae933f8d933fcdb4903f106f815575e8aac640c054241424501010ad0167cd22bce94a4f0e09ff6692d522f4ac202392a5889a84041c46c63fc603f8a2805a15ef2a18b7c12e7d317930eae65cdcb537257144195a91faccba68cc20ecee7e0db638fd4e0b11128847149635024b64d4c621f059e3738178c122efeece801e199fb57adc5801c3d1bfaf9c9432fd55da777ecbebcb20302d9cd6303112ad9ed9c54dbc9a8de7f2d7a3eaf3abf9dab188f480fbd6dd2eca7647f37e34aef88080642414245b50103a002000046be2210000000009ae42e795120791222eb71674aec4f4627deef656c5d741c170ffcff24a5c26cf952778bb5ffb2f22b7d944da58efcf6eb5658da2b46d99e4db6cd51f452750204779014c30e0be26bc413221119ce55c81c92805a748c15e92bbbf558bd4d07054241424501017ed877bf26ebf01a83a3ab82dfde4a733dddd1c3a76fcbb2e10b6edfcb61995663ac514dcca528a1d5e393c2770dd6df93ae57de18e055b0512c5f7af3a1d589027430fdcc8693c34e632b5f818e1d934255c744664db41a521b004e971a66b202ede8010a1f3554d6dbf8acbcb7f77b86933b0637caa97483078187b64b8d30342cb72c8d9c6e8a842c2c08a8847368603b64af0cb19f2d8fcc07e884d5f44b04c89caa080642414245b501030503000047be221000000000084afd0110d6ab6a38eef1f3338388638bd734e58ca4268b07660c4bc746ac00ac11b2be0fcd61004bab2f7f3f0b887c5b167d227234f06e6d982c7fa9210608e6dfc1474a72f921c7942f4b2ec9f1e59be5ea7132b80f7574d653251947de0a0542414245010148bf89acb2c05e0afff78913130d1390d5d9966b72e6117c5e12036ee5f2452a9cd7ac60091e94bcc5143278b959fc1b4913a315170e60caed0febf433fd98868b675d8fcccd9d8cd24570a43586df462544f236ae78ddca884c6fafd21d58fb06ede8013913086b14e9ecb725eaef0c1470fc3e423dd7c64a63bda93e38ace10fa9c98468e829f2d070e6eb3191929100412ba03b73949ed6109ce5a16c02eeb8c376a6080642414245b50103e701000048be22100000000020e3bde1aeec5fd89a8a8cbb24ad570b892d8faee4be41715c160089343b8c1b03378e85d01583ece2739ddc2536f60bfc132cff56f629dd262cc8eec813740cd3c4aff817791d93e6940e5946f5dcd4e7c6517a230764430708abbd3db195010542414245010184c3322334b08f1b591ddd24010ef7c8a898045e24e7bfad3dde326df8133a163b69d1100b1926b513a88f6876fc44ca4eb39eecc52878b77c975d7fd04fd08cd3862fc541aba5794331dc4683b82065d6fdce6e9454971f21353e5da62222c90aede8014bf2a32fbd2e09e9f8ef0e5874c4c6676d57154bc9bc2fb0e9f2456e4eabbbd79a23e536b92723fe65369d99f364a77f962c4e56447a12ce00a6ae890da84581080642414245b50103da02000049be22100000000032abc2fb8c6daf54b0b2ae3c4358a85856cd50480ff8c8e2bdfb6b76599b85355dd9da33ca784847644b8a1792957dc8f4a46f4df4ea3c43d6798ea4a1a477061da79dfcef021e8ed310a56e1b0a5aee34476db707494ecbbc5f9f60cfb15e020542414245010198ffb7dfb497b6665988b64ff2629dd6c976969ebac7af6a3edd9e7cd3a26163065d04b718c813952151e7998004041929ac177d75674c510e3225a0446e7d8058819154a8f72797960f57439f3d0d2b3287e33939e4a016c32652c4400068f90eede8017c91a374d7b3f57d21540689f14a43eb0c864c04706e4f11fea2834265f689f548dc5b7a553f576f38250cbd43f46da2969007bc9e0a938203e41a7a4725d199080642414245b50101040200004abe2210000000004622ae1d30c71b3cb514645c837271727e22fb5360d48036717f75e39e587f163a141c1c95735633d0b40ea8676aac0918cb15a68b596328cde729e67bb6f80dd7e726e4b615132954c179cd7747e65455ab00b05f54088a916bece239a7ed000542414245010120f08f415006052a8a91f0b05e4cf91206da727e3a323cb236ba27a1222c027a4211453364457b59bf8bc3a22c47fdaf9402901b4f2d723f6809f420e6442a8d7cd1c3e43a7cedc9014af0b6d851dda0c6c8b4a9415e7849c4ab38edd9a864a712ede80185c91eced15daedce6be2ae3925c1623368c63f2a910e01614bb770c04dd031c08ff651b9cf76efcf22ab2e69348e4e89b81d5664149bd73e99e1f42640aa3d5080642414245b501037d0200004bbe221000000000dafd40f805b150f64f067262bfb0fe604a7343e8da4fd3bd8e27bd17a6f8633963a1f74b740add72e370c10b3ac135d737a92637225c52975bddf419c6099b0cd448ce8da0cc26243acfdb0eac71333ceeeeb7390165b906cdd4ef9da3a12505054241424501010a2bc87da1c8efe7aaabd7f15bdc6f406938c39379f7e43ddc408de70bc56b4ad65075f0dabaddc173746bb6fc81a44d5f72e38c2e90417c6dfdd57c665ebc80a295ee63def50fa51a543543767fad570ac4ebb5dab9d1bd56957cd16ad326df16ede801873c5e5cf01ec5105ace651a434173a7d9b8ba0f33f63d40109f9d2aef9c67386b43357b705f3843e9a9593557e0ea3edbf35801662b83d4929b42803e677731080642414245b50103b90100004cbe221000000000c6330bfe442e11caae3389c8e299a9af4c7558659932da47545c7f51baf7e672f23531ff21ae25404aa1409978120a499d2e93a95bb9f34dd02d63558dd9510b1d9038c82a65d31c9d40ee7568459784de493d864a484b237796b8635c8b260a054241424501015494bba45c8f6ef751ed1239f6428cc60c7e2146ff9990e40cec86a680cab804de7a1d7acb3d58253c85d6318fcf5dfc06c6c1a285c9ad52f972fd8d59a67d8d50da028148e8c663c4cda1c1b2f04e5dfd59727e35d6215cf7da2437962717931aede8010ab92c7d9b56effcb6f25bc296e52b2a889e554d1f81e181ba171e6bdb85fb97e762d1c5184bc33b6b33f518ea6a11e8374e46f974484cf44d14aa50fea49706080642414245b50103980100004dbe221000000000482381cd7c3f3285bb4872d55740224b85c374b46f1366dee7a2f40fe01ca666dfd11818b3f5f8f6db9132f2ba4793d22a9448c85865465259cb4727404fae0eb3bdddce76fee5ea8d6496703407002c9eb81aa5a6cf47a29e8d3d27c13d970105424142450101a07d0f1e6ddeeb205f5135ecd73250b877fd113ec9eab8f681b0a06d2751bc03bdd7d473b5976b5d16976b0b208d6399a2dbfa48b4a84f1e3fdb28caf9b57384b5ce08bd8c06df884d47c50a018e36cfed6d4b910e49df6043981a9ec87990361eede801a5fd7a26cd6aa060fe3c30e4bd9def69292dc5d6a13d1f54ecb84c94af933894c00f54a35d6cce6951b50f557208793169f9e8bdf7f5ada337cc771f1d7ec1b4080642414245b50101070200004ebe221000000000801ea4defc0cac1f1c22879d996b7f436bbe8acff592df509b414360d1a09f7db07af4af2d1b52668a6c8e498bbf2178eb625559a17ea65494aa395409288606f250d2fd553f525e512fc0be301d58600ef11babb83042679d10db1ff380b2020542414245010192f31ee773d3275b1752f955dbfa7118916428a2fa6941c15a5f96f3572c6e208abe64558a17a36b9761a825fc4a2ec5e333eb01c5751a40d5939c9d3175b5828dbcba8d2d9a2e15ab1be2ef0ac6350449caf3af164fd17ea6fc9d3d5a95b56d22ede8019d7eb6b3cb8d9a904c33f16abefa2f060ff3c56c6c12dd357773af47148bc79b9f74c248081b7de5f372ab9a04a1cc090c3aca692b7f2ddb91d50ed4a4395efd080642414245b50101bf0200004fbe221000000000c69610b0b893c760265a19bddb14f3ebfbd0771bcb339656b97c148f7b392e3c6975ee8ff8a5ba5a510dc7b648dd080b2b1e6b540fb00e48f4088672fc1a730d7dd2303ed93548d649e293af5234dc66a4305c9734ff408d016c18bb21719c0505424142450101788ec6bba945dd7e904286030f3bc1269531a7d37db13f4c3605495a1b6a3254704177a678b68d10edea192dcd9e9f5edf88f2b49924d7eabd3cbcdeb589a784a9f0a6898638b70e5fd46c04f9d9dfc6117ba34df50885867c982eda8cbf376d26ede801b1eb635326aaf064a283a7fcb2dd49e2a4f39f9007aa72cbd08d0ff063d74b877b082c8e28e2b6a7b8f7e74be7ef60500f0aa4697e3812922a28b663f4ad0f59080642414245b50103d701000050be221000000000d6da128ba483541a249c0524a74c0c9389cc36a42c664f5c8a76df7fcb488a29372a9d853cf10c1ca0b435c26704fb1cb19169a43464fae704a464c94d5dda01a9c776a368df5fc2958c9dbc68530b9513fa363d0f5268380a837a667506890d0542414245010158bd71979a61fd87b4f67af92af6955c4ad360c091e7b93ed5057c571ae61421043aa60119ddbd201cbbb0e2c0d568787ff118d3cfb51e6125c36b2f62c85a840faf0539af86d49358bbe1ff7d8ed5403b1bc021b774ba1c747c1e9e7d8e3c802aede8018ddbd4bfb9b441f48c336d04d421dd2d34e5df3e3add3993f10101615ff751ce8e49ea9ff1fa255226703e92180138f801e84ab28c2ead4d74b71cfa714c333d080642414245b50103e300000051be2210000000005c491d243a78e602503d7db7c103c4aed929fa9d5178a018d23bf5cb6fe1074a9e2e522c05cebc51f6cfc8d12958dc6a01d43e79e18ac3b055a79e18c177ec097b12c06876c76df1fc211854370d68f1265ef22e6fbaa0237912209b08e85d0505424142450101e2c84cf40ecd99f0b14f311bbba866d93d4fc0a779d6cdee0206f709d260f93de289bea62369efc0d2e41241012a282096ef410ed43d8b9b6f47fc8a2b30458d471c07ce9371f8aa0e9705b4df8867d6c8885761d7deefe97a98480bb7bb5e4d2eede801a6f8f83c052b3a952b65f9c439d88b4aad2483a02011ddd10109a0a76c39d5d24cdb3c3c03d28bae9020a4846978ea9ba9affa93d272386ab276aad39b7f2dc4080642414245b50103b900000052be2210000000002eab13edcf0a4faecba622bfaa3fc50c211b5b5ed4bc9e91b6a3ba1be8aa33095faede45a605c8dab20e4c25f6e687a6581cc5677cd66c7572926d08e513140ef5ff74db5f744630ac9bb6ea65e40bdbc0a5158336f0169df6a0cb6670bf450505424142450101b6f3274b6c4832105cb801967c11c9aff96b24fdb38667fa385bbeb5c55a5d066f26e40e9f3203763966f8a77987005727eb987cd4d00084c329e1d3cfca9389eb44e472385a8740d55cbfc791e5ca2e9303663d9ab6199fa9f577cf7c455b4c32ede8013cc330e96c58ee39d9f3b09522ce3acb737f4869bca274f7eff5834ba53d7fddb1d7863e5dd5f56b16b9fac1f36111d0496829e4743c641463fbb3c8cf4d1b3f080642414245b50101ab00000053be221000000000987fb69831804481460c2c503fb53a76bef84a190b400b07020fd26633b5257cc515b7fd80443498b5eef1055d0e4df779dfa1623c983bba933fa37f9abd70016fc2fe89ffe7d778f28ff3ae21dec315fecdd7e2d278ca6341238bb041b2310705424142450101cafca59b850507bd299a20830334b9925d156a5b08a462d2434981f9c22cfa09c14f1b7dce3e391a71ad907e729d751713cb4468638a83c07440082e1874db867c653e57b2e33cfe41611f5e461e3ae835b6a01a90ade5eb78d7cc594726a25436ede801381768a963a6fea01a955a2cf298481ad21160fa12c49099b18ea4eca1385ee7cd7eef9594e2b9182c7b6ee8a064cd4793d0abe9a7c483fd2af31012f6138657080642414245b50103e100000054be2210000000000080823bbf9070fee0d41fa058d8081c0d1ee08e722903a8ad449fe15ec10732bd6ae9998f6d2a1883955bc402113d741dc83959d7b845ea31f920592b02b305d36ba20141aac03ae06ff82fea8ecb39b72979801c65a18a25a4ab4330f0a702054241424501013e0d319e8db87695f9757d8b853ff7ddfdf5fed899cd03ffc407010808cfed568b9733a0755ca4f1610f7ba8efcd87b60ca8de5fb2c1614ece6eb338a0f089872c6cf520b23148ce8c5520147749e8df33bed3326cf106740a37732c783e2dd23aede8012f9964fad0367e3c2bae38e218431ecd122dd3d3b912c894ee78b55e329fe1412c65356e522340a006553a3fe5da4bf93dc4f111eef962ba5fb4658fd38d680b080642414245b501033101000055be22100000000074fdc396df427baa0d4119a6ee2c258f25e3277c8ccde71ce9d3bd71225b0a11831c922db86a00a26f65c08871cfae947a03a20a7e17eec88acc758a68b825010c0a12214e2425029db154b5d6a8335ee87dcc33fc914620b51307a6b647830b05424142450101406eb8c632c415153b1113573ad953d369bc18167e058c05e2dff29552a2284c617b41a657d4eb18dd5b0ab9832d04e0cee5731a6a0c9ee03dc7ba081c747d8be2d83049cc514e205fdb4aaebcf4c99a98ac953b58cae5a3974dee2e662a9a573eede80175b46792ade88c2ac1aff4d653e45adf93d6f9ded020d8a680237b1c983dc8c0fe9d73eb6f15f0ecc4754d02051559338b7f5c447654a18bee4ddbea09dc7f8a080642414245b50103da01000056be22100000000054dd6575055c6b6db13482dc82f87aab6908c069ce5cf20533b2a6a576cb7c124559e2185581971f7a9054aec1fc0814e202fdb1c9e7ea83fcf4e698bb019d092d2e4866ba743d32df69624f0e8b9903edb1e035f78ec8b36f87762b96c45c0005424142450101d4ee5d7ee2074ee8c7a7179ff14b2ddaec3caa8cd701e97d9d0cb03c07c1140ec7523236eb85259bfbe34e29d280506fc8b3099c37892c7c5c8246c7157e3586a2ae493f5ddc0ad0b0b7c065605f9af635523bc19136c48ee7bfebfb11526f0442ede8013cba254cca550a2738dd24f97dfe2960e1764c1fb8a14cbc2a7bba853cd9b76d8a1c245ec083718cb95a55a019a1c2224a896e029146500ffbcc73b3b7486511080642414245b501036903000057be22100000000012fe46396e168483a3471a7fe163416c480df1c43ea6363de3978ec17e5c0476488f078da325f2b8bad7dad9bfe78763d3c2e59de13538a169265ecfacb6d107ca57e17644aac5338451ef2eb37a7b90e4c38d4f0bcbb4fdc0e35d4760ab9e0705424142450101da24257290a47e8bab5b21f14a5cfce8f4b5dca0b34a53b458409543323081353436ae7a59287bef8952ded7793af93d768503a1b37d9573d8833d1a0db72d877b1946017cdc7dad7cdba2a5fbb3a12cd8125a0b0574f4d2bb5b12b52c03232546ede8011c33e512b31f743de1681b2a10fbcf77c51b991ab23e0fbcdb62fa53f3f98385ac5512e1a026fda5d1c7c2d23f8e4cc9bdc42683fb439c409f6186c04053c2ff080642414245b501030e02000058be221000000000327d266dc6eeb2588ce897e000c18609aa7288dadee2fd9dad9130bacdf6844424f22c8edea399ac9eb2d0917d093f45fbc862b715557599b1621e5a160ed90158930bcfecb6d41f397051b14a14d9e33be17b5ebab678f1c0736bde9cfff50f054241424501015e73109373eae9369142a29ca4f38091b44dbdf6cb40433b9df227506d83db1ee4c5c4ec5f33a43871867e902699e97693cc1e3748545681d654de8973c38e8c601253f3059d579983432c9fa8fd63f4f09fc69d00b7db84e439358eed9667874aede801bdb092d95d9ada458f96f53aa6eeb59b2d4232cdd08cf986cb76580f2e99967e6b7e3eacfa4c10da0df6d05c6747b1e6ea66ecacf40316da5dc37b3a8872fe1b080642414245b50103cc01000059be2210000000006613d0cc948a81d9f32bf75a933016bf954e2ba6033f390c8b518720d633be760e1070e6b5f6a1a2b239a530c8f5e7dd323979bb9f388ff19b0679b7fe1a0502ee626bbcbc9d8152c6e150b0c331d4e5c4fd8e90c7aa76a800fbc11ce19ab30b05424142450101e88a64be5d29b2ccf285d31bef787d624d13060d55fec5bed6886a9e1a21c90999df5485cde4bf82db97d177264568fd6398c9c84c1637203b5af2e0a011438cb546816642d42febaec8b40a528ee03dd637dc32f3e8ca680bdc7f9087a1e8c74eede80172c9f714942be3885765f86310427f126e955410a4b44bbbef529175db9d118397dd6d697a8b1d5de61c64f361ff2a82097a827d159bb5e56b3bd16064069d26080642414245b50103200200005abe221000000000487982f0186e386c132946d3f2c4733c37a49232eddee9328983328677753b1d814b297e224380577214993232e87584c373c1ae694d9d6f2607aea23c41ea0ac3e1d3ca2e8421885a1819879e217279cb2d3f4a6d1bc801dc06fc40779ad80a05424142450101e8910d7ee048393734dd88e69455a893338e89ccc259f69d98d8984326d1545b322807e3bf22f3ca9a025e6726379fcef04792c3be5771c99288dc5b6bcb4584e78b3f09a381142fde23828aff7f61ea373cde1cb81ca4b12a0ba1e527d3b96852ede801830a3dca89971b44b595d7cf72e08d7e14557e0f5b8cdf9d6ba3b3a89f040ac388151faab005b1bf19b9c58727b9d167ccf1f4da7e6a09200f599f73d69a2d3e080642414245b501031c0200005bbe2210000000008823ad0874d66d46d67e3d53e842c2b3e5d13d0aa641f36d10bcef6532f47e6897f5b604f80f00e209e1de768b3345cf60320cb90a97fe403fabf65c0e0e2e00fce53a6c4febefb8c0ff46f617453669cc7aafd236c5a4ca3216f8280b0a2b0005424142450101e2dbd9952c28faabc649d895a54ef35bc3d6eaf80bc15d6bcabcc199b8a537637b9e185bf57802fc713b3e88455b4966b51b07ead87b4f8d226448c2fb3d3d862a47b516d00fe10fccadef8df6e524ab0f0c0168669989b1e0a5e931508ee1a556ede801b67189b64597eb7915b12462099689f422e8b66df7f52c95dcfe211049e54f1199353c461442029d2baad44726450f48613c3da7179b9fe13b4d83e8476f889f080642414245b50103810000005cbe221000000000c29a8e52393fdccc4a8e7e0249dddf49021e2c23abf2cc5f8c349b7dc0035c1669d24cde14c3b61e07e5e33083349e267ad9c593c348168f49c5df94164d410b5d444fa1a462f9b64efb653156654afbc7afd910ead76012f3a5374169ca3900054241424501012e77830517378adf4977bed712225f9dba8b3145ddad1658ab074e4f6073cb28d127a6b40fe7c2a3975fa0e6a49e11cceb5c345933ade2ac01fe203d6178418d2166ef1a9d5203189ff609939dcffe1590b0b6605889f3be710cd64f305410445aede8012c0740927959a813c6e4a436740632126f924907456c65fa426d88a64fe819779d4f5d4cdcd962db54758c0764ea73e102841c5ef4cca2f3d7b139966badee64080642414245b50103590100005dbe2210000000007afbbe5c8161040c3f76a679f60108d75dbf432ed0d4f231d9cb68fccdf4c24d181ce4454b6b15c026d380a057b68fcebedc70a0e6da8353f06bdc51f46771003c8d174f5add8cf85cf6b4226bb0d6cb89e78ab4ed721ffbb07d3150e552980105424142450101b2c934342d10a4a8cb17b30ff1f3400a95ead89024761929810a555f13444c3891ae65cc76c075067b70b7f58228ca08e31cebac5a0d45ca7827b86d91d06187cf5eeaa09a206ad7c0e51a77e1e2ae4336ef3a8bab7f47dfca14a28d57974fd85eede801d1c058cbcfcfc5b1fbaa030376f5b5b246331d636c69dc396f60703ba864023ab945ff58489117073d2de32b114e08280f0ce3b6bcf6b8fa056a154f14b708b9080642414245b501033b0000005ebe2210000000004cbf4f2c2ab8475c90cdbfb8cd77d43eb7153d5530630f1ab2155cee4e1a8858658eaebc31fbdde26f5a5b1fb0b5e0464cbfe23d7572fbb510c4d5cff933fd01ed74c5e6e881efef33aa7af2c57cf0d088dadd3048e2160750ea438a71718e0c0542414245010140ca5ed470b9dedf6a716e6a132b17a43c0c8109dc6f4088f180a7868cf69272b8a2a59c2d90dcf865b31894a890c5305990db0c66aa98ba97317a9ce83d918e8d60fe87d1ea72b045d0d74e2072b91dcd65a6475dffb2d3a0ba45c6de1e158a62ede80153e1ca38b40e1ed8f4daa3284d7c4cd9b7113a010a7fd2fdf3e0ec6455b05338ddc59fb655c935414a6f6d8337e3fa403dee8a4e4bfcb0cd62e7a6af3a4aed25080642414245b50103e60100005fbe22100000000030e788dc699cbb906702c887940fb23fb2f84fa0e039a615fd3224395e2c4b6f302b6806bf6210ca21e39108559f73de2714b715815289e89253dbc6dfef5a0cb75bb5850b681a708e26bb4417cd6c5a57315d1ff478e8eeefba4f2248aa22050542414245010180eb0d6f0e6e4c41284cf12980eae6cd78744c27867e5df047748b6443f17217da500c30268993a41ef0cd705af86d10f05dc84e87d4fb4f793a8a4a5cef7d8f8a6cf62f274bab85d54d753b432cc004b94f1af6d65a64ce025e5d8ab47f8c7366ede801d9216895f5a71160d56b32b3d9b57f5772af0afab1f55c30780f5af0fdc7548108da8041310ee362630cb7b973bd5cb9b26417e9166c707a23fd5b579f666443080642414245b501038802000060be2210000000009e54cd91ca15baae2b1c66a1e31b64f44a0e36f06f73c17ff35c3ced461f592808a8794570fe1315660c5769fba449a6115d556329a60801bf386fbd713cc30917079e283b947c734f9517952074bcc92b689e73efc9ee92ec8d1abfc3e58e0705424142450101ccd6908464f92556a9ba8cab1c95137751454e20fa114fa18b36a2f6fa3c8009a93d6d3d0b13d498f6f68c06b8e7dd0c98856822acb1c6606092f6d027fafa88b0d0fb0abca1b3fbd287256702cf51c0679c84d3e365d2242fa6e8de1f2c43576aede80151ace759ea33df03dc4aaf3ca701b450082dbd2f702f777e16f7566e8dc0980af0e132112f104bcd3aa32848dc263b188cc0c5404a255f18422a07a488c09968080642414245b50101aa02000061be2210000000008024945ba806c533bb1fb9fdfae2e4c4522bdf4a33fa546a4877965eae5bdf30cf29988a39c699f7e43cfcbc9eb1a4a55526da48f543d97dad59e68e21a9540742adf0df30a0f60f9cc56ba8e2ff852511f610b7524c0a7cfd008c13a546530f054241424501013a5a1b46aef65b7f4a75b9a3c76ed127ecb7fd94fcbdf9aaa9537a7c76869a41f558f6ce6d8e3aa24b346e6c7e9905cc4d19b9651f6dd7a06076f51f137cff8b7d891091d5ac58d6fc9a627b9a95c7b4ef2f475352d5ecae8986605c8a8c09476eede801af7e4082910ff84b3d246316552548675b86700ed0d16ea22ffa0e1c012e5cd299ec511f45b96ea9616c41ba7b612a118c858ce6d233e478d9dd1cc8b7ed8791080642414245b501032701000062be221000000000faf6f44ed0c35589adaf30a5f0255d7fa87853562254e28024bc44e62355d23158c7a241c07db03f007916c4deebd08a83c8bcb59dd1ce7703637f5f6c48040eb3686175d5291e1406c0eba4cc6c7e4aaa2a6e4dd90f1a80e20ea0d814232c010542414245010126099078bbbbc52a8e0123ed221e7dbfaa903510d879dcb13dddf2a76259bf740c80cf5fc85688d2229e709b916e6a320d37b51963975d461d95be2b03f7e6882bba4b4d0c7cb3fdaf649f7d4f27127346c753a71ddb5984c2630497daa8578472ede8010dfe0ebe9963ff7730a1e0c75a9c85974ba17d821ee4f9beb29ab19d0bd620eccd675780384f5f0824965c829266856f9545e33cc120d705f964ac91ad7d68da080642414245b50103b102000063be22100000000070c6669e9c869866800ec9eef1ee6e7b406df21adfb937ec83614518030d8a0db3574637d411248cb5d69277ee5144773dc3465f22ccd609c9ad6a2e1162f70d8e85a489b2467b38e4ac846b933c757852f2b7a68b9ec1dd68cc06a42f3f7e0f0542414245010174bdc2c97a6ee7e2fe7388486781d7598f1279b72461b6bd361f54c754613320f0a3bbb265b6d2bcb8b83fb494b9cfdcda5981f4f19c0a5466c5d39d3c7c518ce28ec16b35519279dac115ebb9c26ec80d6f0af04742b64aab8457a22278e29076ede8013846eeb31564d1a125823d4ea450d4ef9d85c4c2b74a85b6d7b8bee023bf53795eb7e0de09c1cda0c76bfcc0264f1992ffdde26b7d03020e6238f6592efc61ea080642414245b501037101000064be2210000000000a4229b34c053a2a44de7a7e518416ce0c5c074624d0d5b2f0e2f9be6d42a71a45e263c18c51b174c96e0020e184c2b5aef022e533f8b2b8c53f0faeff0ee205703b9403daf5c2def3096fe498a6b436b1b2822f563c216fce5e7589dc3fe30705424142450101420026d9df561a1597e6e65c82197d5c2c421c90f6601db44fc3c4c0aef5560d5dad3459880c2dced107ed6d7c818e2e020ae587f1df99598771a39729d2ac8878bfcabfb2fd93242f301cded58d55098cb238d9ada9c93d4cae076815be875f7aede8019d2535191229ed07fe64dc67a4187ce23f405e4c7f9f2bfea6a00874a0ccd7cda410c8499e7ffc752bcb1ec4f47a943d214823b5fce51629e93786407b1d2fb9080642414245b501018300000065be2210000000007a126774bded3ad831aef7a12cfb0258969bc82554374039639f2557f418b874df19980696fc4efcfd0036751a46f14b4a35d4f21099d5c1739b06f81fa08906aa973721d3b9367645af8ced66ec12594a44318c52132d8875c84eeb01034901054241424501017422668f0438efcd4702ce82ff841587167b6d9d61855ddacaea07850f22281e666c56c1b2cd200344608c1931943024254e06efdc8bf79b2f5136f370ddb78add463790fa2aed7d14c2a16f0c3074ff783eebd6decc37fcfc8ab5c1a3d37ba67eede801521e7c9aa47a09abcf20524e7624a6c22ec450338ab8a13ad9d6489cc27772deb2b956534fa9661747d477a992255f0ca2aaba0da9f9e20759c2cb7d7d9ca3fa080642414245b501032a02000066be221000000000f274aee2f7b7518477ff0ccaa66241f1a90ce66e39323fed456ce1f59bdfa933523229e47d89ea19dffb2aae0ec29ce7b2e9783fb103a6a84b080bfb5ad44f006385481ed4a673792b4883f284fa4663b0351eb36b9b189d5dd8929ceff1fe0a0542414245010130749aea6d42f9041503d66c8cb3279a046879cd8f492495ae8404e8ed0c31169f4f9bfe4ecf8f75fd372898f2074b0ab172bef8e61b60aff9461d417c9a75807f269c7fe9de9ef758f4d30174f0eb13ceab44a4146159cd6c227ea65bf686d482ede8013bfdacbde0009eddca1659e51a171a48ef4c199a1c2c99b348beea806dcbf59d7f2665b7039d4e32d99bfed9676cf871dad35457e823452c31eac7e708885396080642414245b501034903000067be221000000000c066c8b46288c99756d206cf81b6cb106c183aef6e465080a6ccfb4b0725a80b00c123d103d0238f88ad4de15f4aeb8159a1e81b8c30bc19e281d2025c33af0117e101407671223d36eea22767f5d472e561e670479801a7befcf2ae0be67a07054241424501014c65c74ca8238074cb0b97052acbba17473a2f3805b87a30e9cf88762a6a636d198cc8608f20779aa86398554118c448a839795b2fa6f122385aa3a8ad51748534841022fd481a5ee5d0df60e576080428b750f230cbfe1677944137182c0c5786ede8013f13248aaa056e7c662230ef7d5900fab3dda5f0c871bf16b7136c57ef82cb03987da53f026fd50723294263b9782d2d8a4a01b604e5fc96b10fa4185effacc4080642414245b501036103000068be221000000000d81809b7015f03e3f5e5e6da671f2c03bd33ead0ee50fe06146a1842cc5bbc204026965c60c8fab280dfa19cd68cdea8b5ad947fabad4905f8f192582c62b20a68b48a326e112fe52866373538072ec88e9b6fa3559b88112094ce7ca5d1240705424142450101e8f1a51a081b66f862de521b9e54864501cf38c2862c08699716332374ff2452ed2d0aa1b85c359aedcf80234453d9f935c3a0636f7bed28314696a5f958478238cbcabbafa4abb3a85b4f56b94539badfc746e41785e1fd3bf204da6549548f8aede801098c534ec725e73a58183c5c1b1e68fdc9fdff332521725a265acd9f46c5f2f34fc80c99ffd2c2db6f12a19ed555d42190f374497a7a35b406e0fadd31436d2e080642414245b501034100000069be2210000000006e156f64aaa413926383ccacf1947ed88142a4fc8312cd6e0e00fc6c8342de430361b84479ded33de9edef126c703a7c62bcbd5aa0c96c344f5e0f78f16fd006dc9e16dcdbc49eb4bee278896ee3db939a6f06ded375468bdd82159be107c10b05424142450101d4c29d9a1a4d56b726126b8b47b2dfaad5f324c66a57979ca67e0340da960277cfa02a452e81127b41eb02171205065058af690dec854ae17f0f085dd17579818e3224e0bf5d7a19972900614b566efceca99c0088add0c85185ebea256637998eede801a8954c20c5ef3aa3848be3672fa1fa50126d3f9beb7f30bf3540c5bc114602136a44bb213467b7db7ae070d01419e1df6a1966337d8a979c7dfd14668f5fcabc080642414245b50101210300006abe2210000000000c56ff1777385ae70406ecec691e9cae97bc02779b1ee08ecbfa88d12ea2e64d296aa1e69c134fbdfa2ab3b27c2328d794275544cfb341fd253a767a9d03b20eeef8fb25ef2b262f3feeac88bccd295fff664e5901a33a156ea962598f2bc10b05424142450101ac2b8e0d18b792941fce3609cba3859f8d9bcd01af599cd17a4cc4cf7c59046458d134adcc5e68496215a0d25e8eaa193778a047d9544d48d1693a95f56e8d84e944dbc5ee350522d96a35e5bee3b8ac993fcb5e136110496cd9e0a44d78d41092ede801c14cafa647e23c2e06d8d4956c65b26dcc5ac59c4ff8a199a33ffe354d0096fb59fd597b6efd3ddad0f1577d66433e931e3043e9e3c63d03e196f81b52e999c2080642414245b501031e0000006bbe221000000000e6ac664c0799c5d1240292a22b9af3e6b73517685aec98eb93a349fb75be9e64d364b9ce3799eabc66c7227f34d7253eddb62eaf8da640d502bf56709b52c402cefb6ec4ad771f08629211716ac5746870d92a5a8c952b821deb1da3d6c4570405424142450101e4a78bee557e83d6da66010d23eef0d84840ca7fb112dab5072fa85b82ec4f2ed064039ad911ee7c463337c069f65d7855958179672b8df52b86388467600188aba4e821a20f5cb972c0b37eb468a83be8ca7d36d1d6ec9acd7f5765b2fba65396ede8014633a5b223843400f79675a43687ff041ee95bc8bfe97f235e3133a319dfa73f886de983de6dc75eaf6479999478a29be2e59d03693588d960630308fb82fc54080642414245b50103520100006cbe22100000000068743f86f685226422a99541e63bbb334bc94429a42191db27a0f0bca30c447880af08f2dd06e99dfecc19c9139e5e10552482d896b615d330b6ef3e46b59d060d15f9a05175c7887cb3ebcd6bb0387552babc04b55007e56bdc8dba1ee9670b05424142450101247348710aa03dc87ec8ed75f24ddea78a7c5e0580ee3e0134015c9d1f8501003066bedfd1b2da9295b7ed7e4eb63ac5f88c9b07992530c1417d461951ff2989694586a4a1ce5188f0e3ad41ee6fb42fd6f3e4f6a1e2418b33559ed9c4a7b8b99aede80179788643c9059f1f2d057e56e3db3b710ab8a8f70fd1a99c8474027f8d672bcb031dd7a1127fd0d267e3b6e8d3520df9c343c7c76c7b9284334e43f2c39d9054080642414245b501039d0000006dbe221000000000a2a2c157e6cec651c8acf2fb3360efbbba0acb4c7b8eec11c52e3d0cdd825323ca86cc4ac032ae39ff6aefb6ae627a6df5d3847a8aa721e8172e152dccc09501d18f46fd622042c3b2be6006979515bace7c93b77c5cf4461bd24b685d7f9905054241424501018c6422e537d6d981e92dbfa06e657b794288cf5612e77ba912c5c7ae6e9cf1504314744006b6032db5a395aa00e19a8a4a1563f0238461f56fad87518204228a9365c6828a1102188be8d34921e5d908c4fb748f0ed6450818bad655c7be15ee9eede801b82250c5cc32a5c5ec34330e204a3690ee12cc9f83650f9ac6ff1ae634ebd9862ab2bea9aea62e7fddcb6d1920a007e6ed3c13adb4681ca01d82f47b4fcccb9b080642414245b50101f70200006ebe2210000000003239923c206657f89ebb286ff2c559f98c413203b64281248df66af0d314994de0db72af54fe51e1f0b442e211b014723ea1484d704f5c5c16f90e37a9dd270a880aea6e329849a38f0c62d64445922aa8b8e9db311db0d037d596c0ab0a730305424142450101209078cecb8e2d7a27b655ab1bb8341e20edc025a191e765122d70d2113fdf69997724ffa8099e6ea955127281736bec0828a3173622be3f72a26619c2d1c08bc6cc090595f45fa52d4e7360b0b6f57e765eb158f6a174e47c3a3b81940b2665a2ede80143e0ffd475140b09153ea2920f40302f4c2ed28046d626365765aadc5c0e4c28a64623a4234b1cf80bac62eddfe2bbd5af46b03536914b7872e0d8a08238a3af080642414245b50103c50200006fbe22100000000044ecb53a32833c5dbb80af3537feac34f5795df3a1a0da9612615b3dce8907673bd375a804a6a94d630a95930cc8f4a6a62ca46dcb124edd1f65cf232bab49084c4ab4044b5a4ec2d6ad00f2f9eff1ba08776f50ba5747caa08e96b6301add0005424142450101ac1a813d75b84c54d6910872c93afd7ca7dfa4574a14ddeab0619b018689203f99c7051dc41259144b4c26a3e06b5a9a0d8570b109e9f20af8c44d6b046d1a886020d0da8c7193b81a70b2d5a150e0b64bd0663906400ca879116f15b8877b92a6ede8013620631f458ad08e05ff0261bb50b639ed26fdac049363a12e01004471901850c9e3fab077a9e670a7d75bda1970240e1124bfc520c0dc00e88b0a213d808cca080642414245b501031d01000070be2210000000004ee7def07be7cb2e1356cf03f256b241244cb531376c2002e0f059751722d23758a7dd9e8b768dea04333f9071ffcc31988499fa22649981bcd19bc5d07066089bb463bf748dc331f29d7e81aa7d0ae83f9201a1e97100bc399bb0dde165260305424142450101d6b98743763ba47b69d54364d2a1e456b4ff6bd882884b740734884113d0406293ec8836de99bf10ee2567b36634fd1a44525f59a2c1e66229a80087b9567285b85a774d6916bc4039e4347cda996edd8b70cc33eb449c83f6ed6b760ff8afd6aaede801f27812868198a01eac0636766ef2f856dd3e571132acceaa39de9ac91fec61ee8513e6fb42cb490243b6560cafb1e2c924245158871b9030116e91f103e830af080642414245b501012102000071be221000000000b4b5e3d13e94aaab22edde354a701a3208d5aa47763f9c03e41892f01118a90258169da70a80f6e1e890c3688f6aa40cb0d86f0ff7078795c99817ed6876b30d439fabadd96ec8c0c6029736f4e6caf58b8ecde2e303471d70b97f410d4ebb020542414245010172cf63dce64127a241bb9c61371eed5b38dac313ba0fece6475be4a76f27b600905487671daf91d8657b26cc67f023558d048c7ed6973488849661555dd49b86ee385315f3e371d9128caae20c46f1d426174c66f250bebc3eed3553c955f6ffaeede801de3ed3d83cb39a3b9f1e6d6bf2a047d83d0f2ea25619e8674516baa8679d67e5debae995f927b39e85da739c0aa87c9f811eda5c9f8dbabb3b18a7fed956e9a5080642414245b501031202000072be2210000000006c24420792f162be4e81dc2fb356b75d0775edb384aa0857e72aeabc736e2f2942afa9b737fa64967110396cceaad2fefab0146ce4183719ac9cae3e24ee1a06e1519dc42ff8799d1fe67221fcbee2bd34f0c7aa820cb94df834ae4d7215a20a05424142450101d45f98b03173b939ceea7eac5a9c679911e852ca558f3f80e5223b1963ae6f70db2df4d67e99c0a580a30807a6df7010f2f7901cc934672403313d0d5f08d4844225560ec5f82d7a5f3d6aa6af3ad2bc645f0bbd5a6fbd8f30e3f9207f554116b2ede801cd8d5095c4033f603e32c005c323d2cf271be775637bdd34b5df21b4947f3540597a5c46f05f17818d39f7d9df156ad896b4f90cf47bf619330c66224e2b669b080642414245b501036102000073be221000000000c493e7d57dd02dc69b4de587f111d2572eae35c10b84e2aa452c96f034d87a5009deaa189811d40bf5bb467c87cb289c0ef65394c83d17d5758f87f7a611f4018109f850b23fd8a42f137a382c6691e2170fd90af868fdae1ce5443216a64d090542414245010158781782de5be61442bd60f369ed9758c748bde50d8b53447a54cf9bf874793893540802e181500792533053fef269e5bad00f199686d809ab8ddab560a6cb8a0f82e12cbb8af722fcb045a020e52acff256f2ff210e9e3400a4c09af365fb57b6ede8019af71a3ba55ea85e1697bc7f9b66fd0092007fa433fa139a4a05ef84fb6e883ba8110f9bbce27146e75d26dd21088ad1b0c364acf9269b35a01d50639e520e55080642414245b501010f02000074be221000000000a0eb22c9768ee0bec2aba6d8d498a7523b0bfea28d9d5da4d01f7f62cb9c4f4b18e8b5854138a4c0241aa91d0285cf2d3d37ae6fab498744d40692b83fe98e03e6dca4f9df16280a9e34314403007b59499f1ecda56d08f2e51417f088c9730405424142450101e6879336a2780a971c69784c5335ba0b0dcd5b0dc6ec8c25bb067ea5cc349c294e6a73906f511e69415d1d849dcdae2f937ad143afbde92b0ac1305420fc9481afa5e786fb67cfc324ecee8c187ef52f42516c60c6b13e338b2f804486624bf9baede8014853192ca6169ab45b6fbc4e9a2cfecf4bdea8bb565b31dd7f8bf78fdaf6f5fd0fae7d1e3efd3c15acbaabef7a0534bf8cc706eb71079032d58eb836af298e0d080642414245b50103d502000075be221000000000f88401159c8482635a829269888e38f45e88cfaca7ec1e5c9068b62f3ef51c3148ecb172900f6d0a46d9fa944845d3167fe8a084b3dca51c561a0bb8deadba0ed28da4681c25af86e0d4d9ea587d15e39bc9238b8bc9e10070e7291142f3ab06054241424501016c6767dd6038888e19d80f30d338f145d54727866db548d3f14c65b3d3d0cf0df206dd8316025692a029082bd6eed4a2ae2cfa9fcc9dbf207f0790e4b71d2a85c5ad260a133b5493ca95459bdb2ad3554b40ec41f4047c978f2f72c9e9189b24beede801647810e3f0916800e061cdf119d8348f3175ce09edfbdb398b88d6422e688326397cafc65e062b7d465cdddeab3ece79bac8e6a9365bd94f00886f0645b99b71080642414245b50103bd02000076be221000000000dc3fd364a3c38b37c382cb2fda47899638de1be9114b234085eba578e42aaf41c7c55a6648926c3495fcc0b9da304ad5d69ccde5805f948af9836295497d010997f4131acb9d9f248b300b62f0747b2fd2112bb0636ae9d5cbb980ed90b4a50a054241424501015aa0b544066f5d88e4d541c0121b198cabd4f8ff8569ec74515e05c76f4bf35290963a93cf0243d27f3d8a080800d65061f73c20b21d94d7ab257fe537860a87c271f4b13bbfc6a56c2e716dc79bc4ff82199591ae398916b0be8da29116fc8ac2ede801c59b8b2dacc4d2a81156b904e3510610bc92930908ee6003a0df9a672e1d6fa306d5f2d6b56b38152c27656ce5dde18c8d177ba96fa8ad40174b54263bdedfb1080642414245b501034e01000077be22100000000060a613b826bb256cd3722ce3dd2811fc7804cccb3896e36d78971dac035d6b35854a41b5a3393d5b9ee1f39c33edf3df6bbf3ec17fb9bac839efd5a1b90424031858bae0921747052ac96fede5d9b8ab5691e584e2b860e3ba15febb2e3e920b0542414245010174d101d36b36fac0d5114ad39b6927664e3281b32d92f6a9e6b90d6e85a9d1414431d01e62ff9ddd2db05b9755b3f5b65c83af54a949d0bde91e9e25e1729680a7b621c94f4c2980a48b9fea2af3e4367f31176315162e18ad7f5afb15490577c6ede8018bd6e8b691ff806e0ca8ecb2c5a983f06c16bcb2e24dc92a1091c9d1032cc0bad6389ee438367e6105d2c75c63d50aa7b069cc93c8811e755c11aadf41c6d728080642414245b50103c502000078be2210000000006480f113be88d0159c49ce65dddd1fe0c14c8973014f91b0c96fcc10c3cea637322bc8f715984a315ed8578c6cc6896eabbca96f0be6271fa65233b494fe5805c254275a23fcb1bcc0e4d2f7e4038895eb881195f373690d77b2cd355acd2c0605424142450101901d3503c42690567b50be7d3045df2a566018fcb1814eed5acc647f145e2c0fa16fe05580cd9152370b5930d151d9707b6c58653bb364c7e0f8d069fc1b1487998b15576eef4769d5c2b398fbb1ed21825e1186b1babdb8fb09bb0f4f987674caede8015f58c466bfc294d1d951406482ef5e62bdebb40a99d348a3f09f0a3247ef7a6c496bb0b18028d1e391f5a374a8647bb994350e8f5e1f45b11069e4706e7e86be080642414245b501031a02000079be221000000000943be4808d08e6353a62d823c6f774599319847048ab993f93a449bac0facd4013c309ff4cda8643613e162d26fb908af76c22c22a7f103c8703408271f2a104331f4988910c0c3a31f1f85d2f2fcf0e6ac0e1018bf6df1f9588f7f4b2c8500105424142450101c69ee106f2fb4c36e3f30234d3532a512fd0d10414599d81a4844553a9b36f35f4352616fee5bc6aec69fb53eac2368f4ca2c01b11e9d2076d02c0de04ff138fd37759539dcbe60201771bf399f773c896f50bcc507b9d9045cb5efb7171a82dceede8012efb036e0b375e290149f6a74829fbdbd7a860c53646e8b866ad35febb64bf031d73ae4ec2585d08eaf26692b32468fa9160c24d227e65744f90d3205110c1ec080642414245b501033a0200007abe221000000000fc274578f1f8360fe612ade4588a807c0cafaf427df1062c6b3411e01c20b54b7e8de78a1fd41cbcd2271e00584169c3177ce95bb7b198b5d48918f297eb1c011305a18d9aefa9473a1844969db81934cbb43ca3c54554ead9a91402580d200905424142450101360c57d3d1e82859f1c68fbbba1b6638d410fa0e423fd566ea0c9263688860379c6a89f00c78a2cf43fe1a292c00b2f79eae25c1a3042201fbce63db7fdbad85682690f973ffa81cf45b0534af0a0b9bb6aed8bd8f27db26a94057bb0ba1ffd1d2ede801907290c9ef3937657b2db4f92520d18d46fbbc04831d09a7b05ac881c2b4987a96d06a781da8b1c25481a550669686d5b0371d4c662ca1e2086f57047fa74644080642414245b50101d70000007bbe221000000000964a4b37295d2a77d337aaceec111e643368db74c2b0e3d34ce054f0e9a6d512f8a1b13ba2d6b241daa6f5ecf250b20c7e9f32ca78335e1ded6cdee7856d2600a88dea7ce7f89c78a048c42d1182877e17d141281109ab76660d7cc56794a90f054241424501013a0c06cbbbe9b287aa5f224ef0a550c0782485ac041163fa7b270454cda487001614c58267f165d25d71b11ccdedcf3d303b13abb56c860f25a15ce8c0ee0787cc698942526ed124a833e1ebb3c910c04695634a1905edc58e57d2fa64f6c417d6ede80199c3132d5d69a472a7f0ce2bd8cfbc9d336b0f9407811153fd61894bb291a4a93784e90220e521d50a986d348c12553f5e23b572889ab62cdebebff666752394080642414245b50103e10100007cbe221000000000ba2c68ae34010926286255d3ec4754776ff071fdd5dc721ba38a39323a377e4792066c7e56974c5506b8fbd99baf2023629acb51d2a2e6e21dd3c6f67348a801fca10c71c4f5306e7bc366e84b0284160a03f0de8f7f4d71d3c6d33ddb7c080e05424142450101a8eb7626cebb04c0b2ad8cf350459f4d2bad3540afa63d8ad0313436eeef2d7778fd6ed44c64123cb5db1f5963f570a72439ec9aa9b97edecc05c1849168db88883ae026f27efef78de1560d6954a95e313ec3c69ad371a7558f9efb123848e6daede801eb8e39dc8ecd290183db91c2ee11c2937174472a7be900953fe293272eaa021704e2f3dfba18078eff3b1f3891a78f0c73f8c63c226b6543eae975287b26fc21080642414245b50103320200007dbe221000000000fe45a2e60438b690569e9a3f7c37e44d3de9d09c489d8b87c5b081f3e710294661af7606b02f09b3a28347a9a2c4503464a392ff3dd1d6c45807022f2856de0ed645616047872c3d0f665cfda582401b61cd4e365a454c6ceed88393005c7e0205424142450101ae3b6f1e37097cfc3b895f2a09e2fcf0b39da5263b059857424ce05e4decca6305403ffdb03fde63439553e53cc971c9c785f3eb79d1c5f61053c823255b3a8e4ea97e6c6f21d85ca1f0ec161186cc52e1620f7088c1f04335ade0e5fec4b66ddeede8018dc32786163d098928d8b62535c2475e690ac02bc28554cb3125d30039be4ae372d4a430eaa6a7173d8c192c7c2afc6b0a80a3d3caa3c3a21fc1ee835f19e71e080642414245b50101a80200007ebe221000000000a2146047937ba0e13fcb60de1390f90f46ddd9317d28c619a1a79232eab11a0fc2b2dcc833ec1d0cf2f1d29e37d6a26dd2f739d80e1a32fe69a7af349eb4080eb0e74712f9e9dbcf681eec13e69f262b33bc0e2cf5af149efd82b3df4e2daa0f05424142450101bceccf8e8fae4fd52202a948658df6c1c0200e0d61a3198fff7a2d1b04a5950400411ed90d3fc5b742bf3cbef6919bd2beb4cadc7e741598d28ae5846ed91d87f59bd90e723820c185c1ca90bd8b7e646f79d033ded4bc5fa321fb3f1f419afce2ede801b5780baaa951f419420078276c6be959f2df651ae885529045337d513f2feb78ada80fe98d3f4fa14fdf120765eaacc23ae3affb1e8bc0e22138b8870d44ed9f080642414245b50103090300007fbe2210000000004c1924fa9144faf80d75f7ab8e5909b08e04e31907b261c86528d0b1b931cd0839c55d2770432af567ec13fe5b2dcfa74f341067d3a026bfd98a7eedb0e88c0faf561302e6ada01b7ee664ddac028e8c0a2a1e9bf47cdc749b7114329115f60605424142450101f8c816a5d05bd52f9bbdb25371f0ed4f5c79483f9827c395831d0a020a261f5a79c017975b73c3724f985404a52e6155b9677454aaefad76ad84a5d142170188125a86c72fe279dfb60c9eb1a87eee5ed24854fe9413e418c63187c25413eb6fe6ede80167529241d51d025562e10fa3abadb14521fb76fb54f8d4b7715966ee0605ad270d04c58219c1cc04b5573bb5079ab91d6389ca87078f534ac800ee7e9b6e4d0e080642414245b50103d801000080be221000000000ae9b73cf099e0fb0f94766620da873c4997c322c3a41133bbaf03237a0f5aa07545d5951f0fad3832210301d7721084f5092b1ff257b3d808b02d1d759cc4604d2111fe9cf62686b2944d5dc1522154e2cfa7ca8f752de5a1e2a91e63c8ba30e0542414245010184f448cf83fad0af4737d358ef7cb0b1a95b772184ef0b2f07ee6b760a9d7e0f9edc121f174aadaf4dc4adb40a668981c444d0d2bef6c5b5b2ab6c1aa4cb618612f461c83e52af3adfc82fa98c27da7c4855dc6dc07a784a50aa3f8cc53a4af4eaede80104541cb748c76f5882145338d80a69dcc173f20387fb3c34860b105e466eed310fe371dd31cfa73515efbdaa3668af5f03db0c04434eafa7ceb7264f3321bf7c080642414245b50103c100000081be221000000000dadea64013e37c6f2da4325bb61480390ef99707f0d3d73958baae6af1f40a31fd8a15139ed608010fee6868241cd9eb55e6710d1e2a4fc5490149a9b0fe420c1c8f89c521a7b0b3494caf0a6b4b5443ea4c47d85774d77e32a0ec7db53d320e05424142450101ccbf315c49ee8830112fae7029e1baed9e59791b4f6850c584f25431eb4b261930c6abbd0c4d1481f98ab0c4c269e0d5785f306fe97a18f6f282d5b00174238ec2fb9ec76c328b5f21fed4e0e3fd365eed88367dfd61f812605102fbd18b4d9feeede80158bd88fbab67867d7bb844341dcc154783203af82686afd3b6bc5620c135c20faf014e601b9ca0fccb1c8887d83906c11b6b1e110ccac0090b1d5a34247a93bb080642414245b501030f03000082be22100000000080a919f9ec2baf96b953b42e6382d44a3f62f3c4225e5a8e6ef43b440ca2436a0bd0f21f1fb3a85d9aeac947b81d3815aa0f668e565b11f869f7c54d6f837605af2e86b77d24b911f9083faeb210a6a1ebfefe8c8444487a7ed785a6a1c00b0505424142450101d83739525d13e67d97f307ae848168cddd2a13ef6f1d0e1a9935d7eb3f81272629b604d5beb30418ff85de9eff09c1efbc23b27737e2363d9969876e0b400d845b65630dc44da3058191a7763334152d11d86d1b6505847b0345b38c6f8d2caef2ede801cf3fbaecc3d15571d381d93979a2a24606a5ff719738786ca385eb9b0d4d5352e3eb7a72cf7b85964428066cd0a47e20e7107167f5351fabfaaff3c61236634e080642414245b501017103000083be221000000000ba2bbe5a28c1ffa6523b311cd2a4216818608b7068afb8c7c23e7ba900732c0ed8da66bf8761a0293e83c4a98bf1c246275fd14129ed33662d807721d549e9018072ad8e9cec512d17698064f4160e35912650160a884812be3f9e562a45df0c0542414245010154463c009556c75dffcc584284bcb19e88c30603c2bb13458bb848c1c471145310caf321dc0991260308bab4175189cf4f2f468ce85f2e71bf35e4817e5f208295f31084ba5012adfe5322925b287c429899b4adf0a6922d1eccdc7ac8695c59f6ede8017d496f181ffd63fbf6143ebe801d575e6142dcd726d486ae3bbb19d695b04b4377861530874685ccd90447749da1175228c451d6c4516b5c6fa1dab428bef3e7080642414245b501036303000084be221000000000eaa165b5fb4195a560be8fbdd054f9b1043882baebd838e50bd1557712131e18313915ed98a6236599f5140e5584dd4c74982df746b4e9ba0578513a8bed510d039a4101666233e834625a33b916d21b6edbbf94c64b08fceaba385e74211a07054241424501013ecdfccb72792cbbb0361cc0159a638bddcd554cb76d640b6d3a08fceb854b5b90efe03f7f0e472a7a74860054105124cd35f022fedeee12acf141a9a061fb8278a099cf71a74e6d506e4b28d7b3fff35a442298bcf168c934e6314e1407d28efaede80117708bf523a8b57ceb5aa0d9185ee6ed8893b40bb8c35e54cd7d59113e56e0a1d3ac861fa4c53cde44039be89a63fa68beef2249ca6be3822e44440e323d202e080642414245b501010802000085be221000000000a4a0fdf3f720d1e3fdb109dda8f710974440f72d3e1f3201b9e508fabaf59c0f40c13281f0689546876e5151559d49851f7d797af9cee38336eeabdddb3b990d73e255fa08a0187782a55ae7dae97029ab56b08295cb726b173caa66bffbdd02054241424501012ab87373faabbb396f4bf5ae5ef22f94d037222f6eb3c63b8d12f485642934320fabbd88b6d9a5367f2cf243aba7e9d3847b835e8c4d1305dd823dc4f7dc17800a956e0c52c1c0c3ee64567f50157bfaa4d7744078024e47fce6a741ed883d6efeede801793736c8a911ad96b4359499ed64d9ed128115513fd623760afd8a5b34f6c1ac5413d6ae25ed52c2c92afc592f3a64481255b2e1a1a47a6fb1e32648e2ccb06b080642414245b501015001000086be221000000000c24e47e2de2c38dac5e22e6658ae99d7d3e17d588218fb84e71e77ada1f13409015ebd957b9726d46c154fc708b321cd59a6da1976fa761bcdf52a374a0dbb080cbaec5d2589785aa2e2eaa683b90cf43eff7ea5dcef0eb622a790861ee6d30d05424142450101fe962111a3e257a01c94031a8b6c37056c5a1ea818a47e50f0139a5c055f3d19679eb73140badcfab8cf31804b2e9b9666154f1220677e37be039fc17147648cb03779b0400f8550ae29a290423effc2eccb1297731dd7447747cd94fb18316f02eee801a103b379a219abdc823d718dcb417b7d7ca07cbb5f5f3de8289d2276430173970cc19adeb401e65aaf2c7d030960375b8a1d20c919e474b2a8af2b33494d6204080642414245b501033a03000087be2210000000007e89a8319f0e03ef8548f89e2c7613e4cfc77f38e46c3791452a04fbfa790b624a130ab070c3832dde48e9088a38a5a01096d39963f828abda90e1fa8025cd0b9e8354b45a88adbd4a3030f5ced6d8d5246e61744a7fcb9c5c0ae8136c93a60005424142450101182b0c5c6824e8b9df412c1ee1f9d9e66eeb35ac1ecb2095379b2df67c474e57f4c1558fd31f036e1b45e84ddd0339afc2dd218c554233881ab848a54f5c508c3336249140ffaab2c34073b786666be42f1944f7e34119289dfcb6565e2dadab06eee8011b5b8a2ede73bf16c8390bd671cc6f1996cf937f11ac58b8dc7c0a80b612f3cb1ce0c7dff310ccf70ddca0482cf807d5bf3b6b96c969a220e04fc7fef05c633b080642414245b501030700000088be2210000000005a4e6f4acff8fd238d1308595a460adbfb94d85d850e289dea3093715dad1958a56a7d4136a387ea90007cc2705642607af84c1fd13eca50df737e5cc86f140269461c71480a889cb07264f411712f85535c6207f6e8328802f0b72e73ac3c07054241424501017230fcfbe8158efccd72a54c664ae4771ff06b98c0cd8dbb644879755714ae2ac9ce96a5575b7201ab4d3012ab8786625c8af96c38f385f98e69bcb2734c63876fa51f0af6f9a23b039eb4a7c3cf0403355cf417442df17909fad6f061aa871d0aeee801a2cd196ca2730dbfa3f1d21f905e2976a0ac0d562e8a6e46773d78ae8360c9927c7906560e1c9428d01aa8dd9399788f469af6f01c89975d97f5fd34119ac318080642414245b501031503000089be22100000000096ef4d6c0ce737682525558f1997722f406e68d71de57f5b2706befa29d7d40eb85a2b67f23440a34251303cc30df99b4219c32d58ba7687221f417569a8d703775700452d6d2172f7210cb3eb5bcc20797a7b4b87be112622d2c909d4919902054241424501019a310beacd524dd0d2fa2313f094f530a8f8d61ec3a4d0c98c82dda7c1de6056e1fb70ad79f6870335d75a592dad010ba53f54134e24def369145cfe5a567e8cd4a16e643d784a6321381389e556ec1b815ec5e5dd738928312cf8a907dc16850eeee8017acb1d6bf57dee8cc778a303f74763e2526cdc99f98c80ad935881c4d07408990db15500faa85def1ff7e940f5c6806039a5819caa22ce60ed06c198bb2a8046080642414245b50103da0000008abe221000000000346b838694d431309b0b2589d807d9f815b247527da9101a2440899e5570961e80e74cf1085c263f30d32c5b29b0366f46ad80b9e2c0bacd9edfd12e4ff0c50f33bb5d7a697f830c02a8cd5863a5e21841c5364fcb71213cb03d654c91e4730805424142450101529572196f100e14a7abb6dfb5c91e13ae2d86b70a17a22a2f4de40893afe4726f20803a2bcf2fbed7d3c4e6bce9497d6d5891a39c48bb3f0db51f9cd0909486ad3e5883721694a9c05ca636ff9dc1b0fca62b62d311e8d692a3f51f8c533f5d12eee8013dc88ca494fcacdcf60934350e34bac959023b88b85eb764458d7e4c9d8a9d26b4be8cb60e83406cf8cc6677103cef32ccb7666d3c542d2b58262d4e6334a8b8080642414245b50103bd0200008bbe22100000000092f54b7f6bfa9be148fc6b45a1a6cd62b73ace40219ffbc52dc623eebcddb535fd335e634c45363110ebb00cd57cac4d0c26857c6a11e9d9e3079d30a8ef5100c97686600c421e443b9bc262e7244ba957d913646efb2b73893d8335d1883f07054241424501015cb778b0440b620eecba66315469aa88e49679f16d79d6bc101c94f0c3b21f4a9e6ec8fded619cf3e6c2a9d47aaf3a15933852ae5ed6f00bf33a28d40457e68800c90b990a2bfee9dcaa510e34b130ff7c2fb4857937b9f4a1572c1ab6ab0c0d16eee8013b0c8a7c4b26aa1c75e3dc8ca2826df8c6964f3dc95dfd65e8c1056c97e86d71cad39a5a8e7d030097e0723e0dafb87ce5263c45eb1c38ea0a0cba04fc52ad37080642414245b501034d0300008cbe221000000000e42b4fcc90fbdb679d79a4e54ceedbccd9c4975d0f1f464b759183a8fe24ec721d81daa35843c8918c1f81ebd4bf0cb2787a35bbf5389dca9b2bc6a2a245820f5fb116226e2149ff659daa72b5880f3f17b67c04026689167825a77ee54517060542414245010122a0df8715ff638a269a0d33007a11bf029dce3b4ae24b33c12fa41595dfee419270614367384bd70f0cb34b0dee5b65f8eee958332b3185d9336a7d04c42b8c4901a7879b6191b4cba59dff09ce328db64b8819f90183ffb2a7e521929e63c31aeee801c51824794310c8e43ed03eff52e2e41d258e906c27fc50b5a298e269fa33eb591cf1de29713f69189a68405de4b9b0889ca916d746e714ea7f500bb358deddd5080642414245b50101dc0100008dbe2210000000002afc24e98e268a1e414c37636cc6d8aada89fddbc288dae88d1e9c7216185209d76672fd0a5922dc0db310a7af41785aa32514fbc1e64ddec6d8848b097204003a9df4d16d4c5cfe894458aaf76d1a087000c1ec7af5f06406d845681f89080405424142450101f67edecec85299c307450b82f03f8a964eeed6922484a024d1a80c8ff583712c57a164a1f72ea9aa0ff707af93a7f98096c5fbe5aee10f069a843385b66f148e166e4e10b6c9034d833c9e5c9496e425af52b798ccf6dbb5ac911193cf083c2b1eeee8012a42a867f429e02f19e64e4fe4e78dce395d42e27af7f731da12a8c4c83353e885f4376fcfcf777e66312234caf5b254613d1682969459dc91a3d4c98959b240080642414245b50103390000008ebe22100000000090a8f2ca36c4fe0af918d94adaf93934841f69a5730248e1c8587d31a9729d1ebd209eefcc96586c702f7fe42f6e19931c3662b1a8ce988187c1b901d0ce2704a0e807dfe44931ef505f5225267159dd61dfd7c562b473cbdf75d18793c947030542414245010180eee5b5a79f680dc9f7224a26227d8c6ef78ea90a615e05a24fbc3430ce5f22d8c0a044c9e1c0a63419048601c685e4708abe4051ee9af3bcf08ad659cb5c83069363337cf07d11d07fd275719dd2ce75c540f99273ed07e136ad5b8d740f6822eee801c564f3a010b0fbb0da74ad4e1114a84e8ac9ab53472eedfa425176ccf61d1458945437e3f3b3c328eb4354bf490c763ad9047ac88bab81f30ad8d047494e984e080642414245b50103350300008fbe221000000000825e3323156d0e8cdd926c589a23ef6bd4a6ec3bda5974583a793dda4e6a8251a8e998f29e8e8d9254ae008658d4119174d90edbae0e550f007c4b51af49090d51f0d7f06cf601e64e59f4f9b31f4b58b58bbcfa6c9b10a952b76959b892ff04054241424501019ec7388e71de1616d94f67c39e85b16e81c1881fe1f299abcc34fe21318855061bd444c0b199bdf24b697a555b102bc592c6022ed831741b8634b0f7774f0b8bcf4cd1e790da3db077ab983a233b0143daa82efd2a39ff7a0054d82eb8543f5a26eee801e9151114010b1cfefce4be7ea38176b0aa146bccd4e0b0d405bdc67802c85b8a3c24fe487d8817ac26ccdb836fbb5d6b3e307a71111f9dd39882b2960eeb016f080642414245b501039a01000090be221000000000f8d6ba976fdad37f8a0a84a7280ad0c1c99280bfef3b6389ca983fd270158762c8180486f220bacd63daf8156fe3c2cc7bf6879d1e0c09348f4bc21068323c0cf12aeefb5e4b5ff2abf7934fa3d629d251aada4a14d1e51b577b5b074e6e510d05424142450101720f106df6281a1e6d98ec40fff28aec580b8b9b4e15091f5ef09de241e82d512c65dcb0f939c09f735cfa3809d8bc983b0cef55308d1b85802db8a2d4d8c28249bf8abe261afdcc9b5a9ec5f44bad932d57d2269c29cf02866c36e43c1179e22aeee801a3945e857d771c3e4ef8439ccc1672387c39084092e20f039f8a2885ad482ae27f17473129b4dbeb90f8cacb5e5042887ae9b6f586909d2927d475b1d741c125080642414245b50103d201000091be221000000000f84c36d98b6ecc8804b6717f6395f745672999ed42dffb4c5f87607ff5c4306081513d2825f3cf01a03f9176ba0131ab5d452e36d67e8aa9848ef2f2640e650108d9df0d505b0fb6431a9573b2c573e642c1ea61cf5d2135480b55eb8f5d590e0542414245010142ee96cb64c9ad7aacf74a2753495ed7f396b157a0e18b056969763ef6511f73b910f535d92723565ce0b1cca6ed5a761e9e342d8ca4468f8ef818447db91e8d6087a43954756f2c4d15d7ae940ec6ddde98460fa0be23a9693310a5a612307a2eeee801d422b75b8c9afc9880519df15035e216f3d04dcdbbec3b8abf2eb1ec2358e37c51a4ef8b2e85eb4f046670f8f07a19ceaf3a90841855353b45dc5a5dd981a2aa080642414245b501037800000092be221000000000ba2c1ad599b70e290e9469c3c8c6e607f970f5c7acc88eea3f59a5cea358b9236ac3e866ab93113591e8fce4fe5efce0a7ab60c0e7d99a9f8a1961a622b5280f0f40fdde235fd7fe7cbbd23aa10eec4bc453e8b870a2ef488457aec698a2610b054241424501011a49a7d2dcefc7f8564a5d67843b899ddc16856e29b119809845bda8a8545470984bce6111150ed88790813dfcd82d127f3820fd8d25588a576297018c4e4d809db955be3b257ca2248898aba3b0f8c112a4fef5d05a67ce0d79bea7cbf3dc4232eee8014f9d7811c5c52de7ff1b9f52e01a1ef334544f8373111061d8ad18c0242250a870c03a637159356fe14ed1b28d124208fc2353cc729cfbf4467e49be070d7ce9080642414245b501031f01000093be221000000000dc9bb16a5c42e7ee4869fe7299a570ff250afbc42f5c5663c136f0b06a6fe872a38df813697434a0653af98db13db44e7b8573a51a38bc45e5faebb963b9720972e8d05f007b5d4d02eb109cdf3eef463721a7d0377433fce0137e2ad090d30f05424142450101689339e6b2d517f10f8f94fa968816527edbdbfdbbbaebea1ecdb192c61f7d12752f134a1bf4c10b27cf2f9975db1840f1785e490d3417ab85e8bfd534e57e8051bc797fdd13a429113a4fa24c77e5d30c92b197dd0f4fa99eb22ab369e692d636eee801d783eaaf73c6ba74d43ecac60cd64f95d5d167aaf451b28c25ec257f4e2fd304e71fbe44652842091be757ff3d5dcfcb4dfd9afab97372d42288268c914d751b080642414245b501036003000094be22100000000064eacdd3d8107aecc9d16f5fc9a4a16a402349cbd74d5ab23d3bca020c41d236ed0eb62e88ac3f5b7f36ac36fe40822b68f0e6538b418a5cee2f1382c722c60007af3557ba8337e60b7ad8b78a7bda0a1d3dd4f050019b981651d097b74b8d020542414245010166c5b92e188e297bb804541e91e7f2d46278d47f560e59d62f3d17d0a9d96022c8807fb396a83d7011e37fae5ed3ecc63c6dffdef69bc6d8042ef1e4489bb184b436a2e72771b3b6be92f05fe177f6ca48138a78574160ce6c1c92b1484af4013aeee80156d2023bfe97f6c1555703e5f32a7f1e0b314ce6697ab875381970ddc0583ac86ab8bc0cffb2c029f32eeddd86a1b7b114d9a1354e19d237352729ad16b8d4fe080642414245b501039b01000095be22100000000090d43ec1e8eca7a7ad4cf6f041d3df6da37fe9e94c44b7a746d9ddd098e43a72ebb197926263ca00413798cd92333227293082a67359dfa356186fd066e22b0c04066f3a9b7e7d5b0dcb9fbde1108db3f7d1e146ca5923a6a6841bed6b29260d05424142450101eafd25d90b90294330ba5f6f0d5e49789fa4f17842c0cb5c8a0fa402f0cc73301106dd6810d9c27c3025f5a8ffd5a42f0f1cafd842dd79e6eb9978d3d8fdea852d06a2e792875e7a7c07b72d4a00215799489917fffef7471238a4516502b53f3eeee8014b28b1718ce6e72f3b2103da2dd59194c04cd87f6209d77a535bbe3de8eba04b73668c447109fecd8813427a9e8a41697a3cf97ba0be0e7e609c68198674118d080642414245b501015202000096be22100000000024ed33f4a5b6109bf94e5ed3d8dbaa8db80a881ba06df3e1c9938393369b5234f0d8eaa167431a67c2412633697bcce2cca52617253f1bc652d2e18b8385e00758c448e800deefcfbaa12da9c1f2b501f3a436a1ac622be709d1a81fbcb69c0b054241424501013e66dc5143856d6e2d2e1ba43ac21151fa184561cfa9ff11595beda7ceefec0c5d39e36a144244c27db49ac85b111d2b13d37a781e2f4914346602f6a08ffb82f57dc6cb185b621c963946e7e0e6db62b3f64c39732f61d7824b9d703fcb205e42eee801783a05d72f4070b5b6f7169c70613a932128e95de192b6758efaf40a970d9e0af081109f582836d587855884efae75315a2eed8e7555f6cb0bba5148562d33e1080642414245b50103cf00000097be2210000000008ea78cb555f16ba0e595595381698c64c3d82df1a7fdc87502b0c74d37e7a5761975596c80da72e68f6af9bf0d84fcf3248eb765369933ea9f36949179fd530859ff33b53e907be05e17f1b639f8f0ac806125848451588471070ac9071c830705424142450101787ecb975364d0dc9d18f05e6da9ad91232a9f1a2256806dc4c728a17bc5e827b90d02f38b138c487c0673f3bc520be6e9c0713a7e65f9e70078639c3d7068824d06e02facd44422b1d30dbb3c40a441b54830ebcd9c9c50ad95c732d8de45e546eee801736482a35ae486d2079575bcb9ab9fcd0986d67707a4ee6cb288ea5a96644849339f8d1d07add2f10023329730974bba2ff9c35dd17ca120652b93505396cebe080642414245b50101fd01000098be22100000000048db09b5871de841a6b58ac2ea13d9ba4fdb7e0b2443414333e394e01b1fa74e7119e0fb6ee0554e41762608ca887ee250ff13218e2a328220889996d8afcc0115c3ccab95ab5d88b94cc1684f47cb1c7f4e031451fe26697761e82f981aab0f054241424501018eaa8c26d9e133b730154d9aa4536bef9e95dd2370ac4ce08dfe1e1e3271c9557edc9182d496c186999dbc2863acca7de6fad6d6b16531aebd23a1450b17598e22002433bd99a577926b96b09280630852a489651e471305ab1aa085190cabb64aeee80141932ff8175866f8bc23180d55c3a0b4d1714d79814ff329bf1cc3b2c9ceb637fcf13a4bfda62be15e324331ac006238c6519e2b0a4feccbc0dc6966522c3bee080642414245b50103a901000099be221000000000a21f499ad501f45d93ee631dc3527b6aac0f0b3a98bc2db4024f51e89a647f638880648564b2ff31fa5dbee8af9df7c2ab2ea7e5e19d96d57c391e0a9fe3340671e7b6b7b28bd8489b1def838d1b6e19634a91e51d9760da1c9595e8651f5f0c0542414245010180f217d2cb7152060820eba7d46e76628c5e32f76f81239116993714714f814d67791b0b5c168a4de9cf7d60da29ba37e583968e1b2988e412bc6730ca1e8282dc3014daf24301049b05812cd96f6239000a4ec5c86667338844a8add6fe094b4eeee801c319cca8d2aff41dc712627fe879014a408d9f55754867b57d2fa8cdbc862924bc551793cf766eb61dff52773e8e886e37a5555a9d2e515a3c32a7917367cf9d080642414245b501039f0000009abe221000000000be146dff85d716c947ab652477a4d35cbba943671f91f0998ae9aeae9847fa5c206fad7442d8366342cc05b54677042327473fbbaa09cdb247a2527ad9bc99005180014002c882621006953d4c1f75382388d2aece084752681b3ab9984a250105424142450101a4fa3ead9818cc852b4f85624874f7a2a99761f1f0c22d255422bab22f99504253bf37a988975b07951227ecb742a253cc8a67af38c43292d827ca0a3666d6832a9b27d00ab2ee70b70c9c3c3dc421b6cf9c8aa496619f28fb77391736a0d94252eee801e7f3497f205cb5d37133de24c5d589f7a9d8aeb9394a5b3baa8b3644a690e7d8deae95e883b8cd422e6ffa42be906ac308d9fd3744596bb2ef2224db0c97803f080642414245b50103e70200009bbe221000000000b41bc33259bdc97b2b521ba7848e3deed62e24f5eaa921cdbf92701fa6867a10018c924ef7248d8f1c30454d0a79b59467401dc71a646e8cee2d54ea049b0e0d15c58acd48610829ac006974ee28680e7dd0df5cf675324170436ee5b31a8603054241424501017a2eef7718a5c4c28115c9f3e57ba993a059fcaa8eade3eaf412e89738fd591ca7615ce69d81644b170e5c08a2328e020a8dc6ad6cacacef3126617a8c1fec8ae436f414480976ebd95891e92d47ab0a841bff8f67ace11ce9148efa95f072ed56eee801973bb242429209b2aa4a149bb875bba20fb1a2f97fc5871a10756982cf076d61a989f5b48a860c755e1097b374132cdd6cf1b9e3367df3f218987e01b36ca8d5080642414245b50103190300009cbe2210000000003a24ba778e5d96836550817f8c59594b487f5665b2f6144dbbb8d75263e4596610f4241de781ef818c02e409f6d381455ca71971928ee2a39556a196c4358d0854e60cbd5e5634f5db647589ac16cafd09cbc3de69e9019637ae81d9cf4ee8040542414245010196dfa520cac9629454afb74d802d7f96995935fc095399c649d936dd78b70a451e3590a466d53745f7761c87b587d23216b7e8307ea1e67a7b704e07c1014884d8a740b5da26c768cf85b49d5558b807e3b06d1ec71336d69af4d95818ef6cad5aeee8011ccda569584c14e9a874b956d88b67db7d185d7080b24c74642f06d6ac797027c390d82dd55faec9166be74a87fc9312fe6a0b0a00c3bb6e98c2939d7cdb3ae8080642414245b50103fa0000009dbe221000000000de3e19b111dff53db929ee3a2b48d73b692422833557a1bf43d8c2f5ba25192dddadee05325a08cf7ebee334b4934e7294d789e5743e1cdc0ecc7ddffbefb80a45686236f652fdc0eab0652e719906b9377043826e0f9b75a22c21a11dd2bf0f054241424501012869a4740e82ed8df484711c14e674654161e413aa4370bfeddcf75578ce294dd2cec0905c378490782a32ccc2ce8f8fbb4e437cdb373ba98955d9cb87093b8f4208f5ceabc7d7526f758e2e23d7833150d68e90928c3c021bc1925611e687835eeee80163cb8888f56c442b6f43c074634651fc5199ad3f17caa52d94a34f011840e07e05c64c0e7b9f38a05e80e3f214962e4347786b021433f3746e699f943eb7e4f2080642414245b50103a20100009ebe221000000000ba831ba15ca0aebcdf9ea04f82323e5f6a80d60a40cead4640660f09a720f712af100b2c39908c348cc3d6c983838834da97165c0fa89bdc5c99afec9b72ae0a067b5874a448285d4c19eba95667e22973f93a0cbf8c8f74b0d5755af6bec30805424142450101f01d9dfe98e6bb1f2ebd68a2e149e962f2a99821d080cbb18ec836fa9be843186f9fee7943f6bad4a20dd0ca433d038d529800273e2dbc6af835468fefe33a8556649ff3e0062049d4afb7b9261e0ab60cafe632a4a73a40c99b293ef086ee7162eee8016e3f4fdde10a535546e26740b20620ee4795db299228ac2f7ec2167e54150f1c55b98f6a49a13fa477cb28a2d27615938113e7107461422a0b73a7febc6e5211100642414245b50103ea0000009fbe221000000000964d4ba3c5428c3db572693c94b5c893a9a0315a51f8a24407fcbb1567dddc1979467d7f9b751757582c55cc32808c4e594c7509a95b5c6434234c565999bd0df084743b096ebb180ec411a6ee15d0da9496ab1d2c96018999f831dcd00f4e0004424142450e33020001110ec434cc142de3e83f6c5d32526fdb84efeb826c7441298ae84621479a717ddd2601000000000000009a4419c9fa76e2a85014f2604f3acbe29d052d7f8ebe31373f213cfeb3633f3901000000000000005004ada038dbeeb636a58c61857a2b2f64a467b929128d118889a390a81739400100000000000000b681933791fbbedbb24bbcb2eaa48411c7c27711875b942537fa2d247c51122e0100000000000000240a8579b9c6cfce8e9ffa06e49a0df2767744abed4a3b3458b01fe92d44b4540100000000000000ae8ef962e1fb879eeeae9f78e8d57fbe6c0fc2a5b404c93a65f5dd8c982b51230100000000000000863a39f3310812dcfd9725d1ab3cece6dc763e7a43eb5ca7c54f7ed5ba1ec3190100000000000000f44df689a3d5ace5b6a109e35ac63d1e62b52e3a1a582ce0fa0e220fc8b671570100000000000000124f6cb9120882340f4289c1e4ee09fcad3ed8930ec634feda536628bcb4fd55010000000000000080e41ac5dc4ac06088d9c560d193b6f7b7ac4d86e0e9bb517c21df9453efcf4f0100000000000000487888f977bf72f61ddfb9ca54897c22fdd73acea696eb009b5401dd7101130f01000000000000002ccd992d7a9f1650e3fcd531b307d9651c104df6d5facd2cbab688d3fc334c440100000000000000d043cceb4e7a4daaff5be2f8fa11cd425af051654571a146f254afc04ffde42a0100000000000000ca204674c4aa968ae4cbe115db05e478b6ee03533038eaa9cfb95ba1b265653f0100000000000000361fab81129e1e3ac7e7c9b4aea10a218991b824c6dddc55414a8770e84f7e6101000000000000003652dc7e781bf25e8bb4b1d25de1bd9e9a7fd67a8ec75b479ae370ed9bfb2c5a01000000000000001873d87e3d21ad568f5cdb255f9870015002606921c6a3a905ce146abd77a2340100000000000000fe585a6b3636068a9009b18083cd6242c78da6f5f57dc0a7571b65a36d354c2901000000000000004ca1067f6f4f8fc7c9432818d0225242e98796be516fcb04ee272799a66dd23a0100000000000000c441ccc085cca761a998a310a1f074495af3e839f8c71de12615f22a7ea1073501000000000000008e747ea3ea05bfccecc9595e9cefe7fdeab4a39fc7e847f56b04135fc4a012710100000000000000fa5557d061b27db6b1bce99a95c830029fc60ac3430d55af30e94edb3a77a25101000000000000001ad6ddc62c61923290e127bfc7708c944c8d6a012805b14c70156109d83986400100000000000000285c8ef3c7445f4ff0ce912cffb2e521044aebb62620963df624201c5e4dc7700100000000000000947ff774965431ec727966c6dd539cd70d15e9181b192ad47b74b1c85745b8480100000000000000e479230ab2921c9ede58fe6211d8fc8e8a3ed3ac8065a7dc6ac50ed63763607a01000000000000000ed017fc562bf08519533de0279f2f7588ec4ea382d48a63edf1df2c807fbd1f0100000000000000d8d8296101fc3a99c56bd85c4078c2e389c071f731e18cf6be7c00aa9f4e2e6b0100000000000000845f63a15c8afdf9737eae1b91666268894eaa6996079ac7297490544137e80101000000000000007c3c62b725f20653adaa44243e4b3d8485c66c310e57178a3d3940f8f780043a010000000000000070e65e0e42d42db5509f35fd338047df8c7a6879ddaf62465c3947b929eda6210100000000000000e415160504f666dadd30265cfcf02b9d10136ddcd69958d9174da69329f5f8490100000000000000da8f660f8f4b90fff45ff6fbaa4df167a0f94baa1daac048346c50698fd285690100000000000000de6317d82c9feea35a44cc73086d6e73d0c7287728bd16c63933f913b7f27a11010000000000000028db15599a7fec3c93c26f62014fd30c214358c4443dc6bd515ef09a113070760100000000000000141f7c5f1baab0cdc3e2b10fa7380084c25f20252d96578d81700414f9eb7b7f010000000000000020b32e50928e8148a1b3d137e6cacc80f2a1ee5a3730e8880bdb0cd5d6b07f630100000000000000a406fa3390513c131099c9ea276472163db611174ca44e3b5c3e52aa3b47f94e010000000000000094c8b55eb88a7e3764116702ef768950c3e59336623aca8260ff44291ff9f66101000000000000003e7c3c81fac56951ff05ff47ffb49285750f6f55619ef8d0e11a388d92020f550100000000000000f840d40d3f38ea72cc1598f7e2012808584c0b90ce7546cdb82250f6b049972a0100000000000000962c3b7413c205f80b072ba3d8b9a828b10e42be559fbc07e9c597ea72cbe56d0100000000000000f2a70fe0286602a06633c7aed149fa7a7bd542c97bd6ae90cb1a941e684518760100000000000000b6cc496a16c32cca3993d385b995f38a6ba1df00baa71632dbe8f750ecb9b2300100000000000000d625a53dae5c0f3fa7af221c68c96fdaa1a498f941e10a1751a93378c4f4d04a0100000000000000d236b78d518725dc6332642273c5f6375be4494ae1e8e6e8b42bd1a19af14c4501000000000000006464efe71ac4e34c699a088b19ea08801d9e394ec50d56c7cddcc3dcec103d4101000000000000009c6f977636e4989f241a12728b5d2635effc4fcacddb1d2cbb45a739504c827601000000000000000652cc6a94406c8d2bd9c9f9f09768125cad75244c0f495373af649b0c82295301000000000000009455588e090f0f98a3a77dbe74d336e08abe7a1b6e30c1ea6ba7d73687ce721201000000000000004ad8792f5556e979d62743cb45b3098c23c003000131eff8b41de0dceb8e053a0100000000000000e0888da4a5a14218fc2c38b8d18d21a4acbd0a912f39cbe8276be83305f1797a01000000000000000678705af1f738381114b82ee5b8a14757515185f69afa15b02f40ba9adfe4530100000000000000ee4343f61d67fe213365a0495087acbea7b000429f2fbb80af14d32d3244bd4201000000000000006844faca3ef4bb5a7ba212d2a2914ab99eba84f93bcf37abf62db3a9f19fe0050100000000000000145ec81be8208098bcefaafbd43e887c772685225a803c19730d25580814bf640100000000000000dcd6c04b237634b509ba798f7a5da05f9dfcb67ab5e920c7c2c9b9eeff83ec3b0100000000000000d0146ca7b1e2653d0127dbc6b46398f6421cedbfe73fcc796af22317c6aca71d0100000000000000aabd30681e440dd836c894b44d5af8e890cd99599899bc0195125e742fbe184b0100000000000000ee768be474ba6200704b6bb75d6a0526263047da6fbdcb9344ccb35268361603010000000000000082f72bb1cc00b5f96c95de38d2698ba9c64a7ccb4d58a077538ce1ccb55efb570100000000000000a0674ddb6f130eae680532c6ff0b05a317cb3840c8906c3a326f26ce8544f82e0100000000000000ac2bf96630b117699e7fd6c6c717890510e00fb6db3f8a0a5c461a9309ccb26801000000000000005afb98d27d68e67f6e8eb81c6d3375425a5dc323bfac122963eeb580cbde5f430100000000000000f614bac5b188d32a7041ae91c9decb77f68d21c8ca76e06ad9205eb838056714010000000000000068bf63365fd8ca42dde5e1adbfb98817f88f205e17c1a38e07f8620fa19eb909010000000000000098dda42a2ae6079331599ec904ddc18cf94fee85ff96e1fd3e7b6953db8fc70901000000000000005869c7315c16568345efb6241e1c0e9ca9d279c8c7b398d32ae7c7323a93092a01000000000000007a043f552a7cdc9de8641b15b0369b677badda49d8082fb71c7eea3344159e390100000000000000a2b759bd4c7dea8909c86f2f570eff3a3b56e71d50ec8893eb66abc945a7212b01000000000000004e9fb9675233d8de1b78adc3f27ec87e2cb1bb838354172d1fd9c93b318e582301000000000000004a7a434d9bc1105ae7df912b7d64c32ff70ed5e531f464447b7d3188c06736700100000000000000b04c669e6deec9b1f47db449905e1bfdfea3bbbff5d4b4fb9c85dbfcc5a0546d0100000000000000baa5986e9b5b50de5febb4ac70500028e71046fd84c8083c0f62ff6fe31d1e0e01000000000000009ccba303b66ee95ad28150a987a672273809107589fe2f7d29665fc943ca62660100000000000000c28524b5af98c5c4f13ea78f14ea81e91df1afdd4500333f9423a80ff331de0a0100000000000000e83c8269c658801c3ac9014fc5a3c8309d1769a8dfdfa272ebedecb1a2a1e4290100000000000000ae05ab8b2f6db5296f2766087549efc6169b3499dde3d6374f7c772085f65d2e01000000000000002083ee72d69233ce625e5f1c3629c331bd6c83385c73722e846752018c64e4580100000000000000586418c4ad73c32f19381e4c32d8f2d082671af513b0a2b528dcb24a4230281d0100000000000000863f88d41e8128af4882262a1d55cfac41539436f280b567385da2cfdda040260100000000000000e077c9be8e48279abeb236ba07e011b082f2bd09528aae59180c7eab0619e60e01000000000000002ad69fcc5342b7b7e11595eba5a00d20ba35d6f081cfe395bfa1dcce4a290a0a0100000000000000900733f798d51d2dc54005ba53eeb9c3d63909d4f7e7ed32dce66b7259552f6e01000000000000002c605db889f693e3f6f832512bf4d6ddd8570ad3c71db7a9b57bd5689b44a73601000000000000009aa7c8266d57e70714e5757cd2e187e287c63afe88ca3a4dd42b3f403737f6760100000000000000705af57ca737e78669fc625f5fc98b16d120ba871b488ec79a9d12a1ed72f2390100000000000000183be67e3f80e946e21d9a53db35eb2d9d53b227de1d371e61ecf0f7b5e6b87201000000000000000e2162fc65a7a09a88b48781078143933638f7f40c98048b0a9b87694e34d7130100000000000000ac72ebbb90dcc166bfba1257e42b8b58574704ceae439f2c19ac085b61d40a6201000000000000003a04567e101ebb28329de2600817729f6f3c17c5e5789f036a52ad49b99ddb23010000000000000088dc2749fd6af775f8d9f3a1dcfe548d60037cdb8ec1db422165cacf9c96970501000000000000004af471b98c45055a8487a042c44567060000e421e7908d4ee86169d4f36d4e0601000000000000004221e0d01a623d31f3f7afb312e0ae6158db5edf68319a8b6ff98d623b0b1b03010000000000000068bc9e681587d349c5ac904a296ab9a4481c7de5d6b585621c5ed2670bfebb5f010000000000000016d1f436ee6064b05ea10ff906a7b6baf9cae49431489a8afbefae088bdfca1d01000000000000009231b7f5d1f17d7dd8791204288f265060d3469292edee45533267a7a3976b780100000000000000aabeff206862500ce4d4d54491712448e3823ab909919cd759937759a086a10801000000000000007e4ea4d47bf58ce719af34f30ac79ad8515ff9cfd2574abe5309a21ef0b17d5c01000000000000004c230bfd3b60ba5597597b154fd95829385acf1bd740607b9ee07262c994106a0100000000000000f6b74d3139ee7543b6821c342b061af75b609c2728c9eef724066fd35abdfe5f010000000000000018aa6738aae287470270a2213f8644d31150682adff4cf3b7b2da19c473ac8390100000000000000fc81c16f4d0dc4b27d1e0dc381ef15781f8e0ee41fbe0586bd9afe15ed13173c0100000000000000249f2c086a0c22a7cd3189282875e2398b6dc28a2b9a8a0ac9eb3a53de73b43b0100000000000000b266ee40cfa61107e9ec12f162cd72c6518af1df74002c2004c5a98b476947130100000000000000286caf403379f1cb584e8875fa8487e3ec404c540b21bfd7ce4e85c3ba31a31c0100000000000000aa42949770f022aa18616f18f02917b25ea0182b6aa062ab117f9268183d9d3d0100000000000000886edfab02c7ab5f4f62f26edbf5dda9868c8a413a5c379b1b63d72a2121cd36010000000000000076cc698d58e5e4938fde51916c8c320e0c3f71822b4664254fbfce6cd4dfc03c010000000000000006d80e54ace9eadf3a6200256aa73882a94de54135dee1ee917f79774e5132670100000000000000ee4b50b0921a9e1f4e5f22cefe6bb00d6a4357c8570636a39b26e7344b3f66240100000000000000e844261de8b691e753a7ac7132c821469d83f25ea8e323ec1cec4dd478a7e6530100000000000000925864a0fb6058ba04b0d1425df158e708ba37cfd877f126796e2bd7970c1d4f0100000000000000ceb4d80222ff1340bbf953176e38bf2a8d40a4c6bca020beacbcac1f1ebe7702010000000000000060606066af08f9fb9f9afa87fb07ad0baab01233516511c5e6947ab8f3524c7a0100000000000000661e5553fb5d358cbddb07dbe15fcf7826a27e2c30847b9548d0d8f8f8048d7d0100000000000000248769c8acc11f3a3dede5b15035433729ded2ad8af740f2e7d3340d9d40ef0d01000000000000001e97263763dc6b55363fa51306709f7e0243af1910630253381a8592de97f00401000000000000003a1a122ffc962253073ab844867308704f6d92ab8f6e318e734f3b18b7e3ba3301000000000000006040a8414acddd0b30dcb1fbe083cd72889055353bb9f64c6a4875b861a4295e0100000000000000e06380c0e44c490844857db7ce6269e52272daf3db8c9c98f38b24c79c1eaf560100000000000000667ae5fddf7c7a58681fd75da15d319dda30921d6c659db6d0bf982830cfa06c010000000000000036b1387f3674856f0fb22a876ef12bdf2ec21fdcc849f200acc609e5f33279470100000000000000ec2f04aa8bd2ce4717e5747c506be32db5db1cda05e3875fba658505ad2e373501000000000000008e9fbb58a96118053914ff5c36348f8bc82d7e0051e8183315a4746d77d5af000100000000000000f419d56e18e14258c7eb351d1951a0c091194aae5c55ab1ac4cbaf795ecff17f010000000000000092dcfaa7fc0a16bb4a2e197db3179d207663971a4ec18c5a78594d579d02fd7301000000000000002ab8ea26eb951dfa831de87f69aa6608b4de76df2914cbcddb163beb5ce21b7b01000000000000002c2a0dd67d2550d4231d79efee8891dcc1cc103c007935aa49e6a2157c11fd2a0100000000000000f8106e64f3900bdcefc9dee82dcb471571bc421ca0b0b60ae2d9fc0f482328630100000000000000d88f551dbc4416e3ee46cd89c683eb51054fc9242037ff19a52af87b30402d2c010000000000000056091588f3ee5b9fe15d20fc336d68541a08e823e20d691a41613ce2c154196201000000000000006e2fc806b2626507a907566aebc2a6fb1039a533df8e7a8c9d9a287f3e7bfc3d0100000000000000e45c8c0d6a5aded1d938d8efdc41e5ab6b289c605cc6c19c3d7a12b384ffc81a0100000000000000a0fe04881e8cb6865fda23d69a35ce7f28574bdad4fe3b85a9925f7aebc6620e01000000000000002a11c0724e4fb460dc5513aa71488c58d8cbe2b5af25a62a4363e51d23fbb60701000000000000004e263edd08343be113e4234765496cf31e3bb0068d66e4782209171df67b2c3f0100000000000000265680af1a1392be7d96ba0fd2e32b5fcc11f2c394bae83dd49a29e4bba585000100000000000000c284219802cb0e56397b42e6434eae1cc49b1f67b0d05b2f13ef175c8e0aee5a0100000000000000cec30d319a93c00b7044b2f11165f3a5679d82ff4db6cb2783fefa42b236b8220100000000000000a88f2dfe51d3fa0370ca543711ee20e76ac172768abc40759ee95d0f99bf364c0100000000000000a88a466b1f2b4b7b6c497f85d1d1c3e8c13517217f2f2cd00c8ce300406d311a010000000000000064326ea393fc2fb9fd9102b08971b19ddbc878d77d0f53978be7f3ab87ed826c0100000000000000b4e7de600ce05dff305c9e1ead59c7dcd2b27f05b71c71387583e886504fc453010000000000000086899c0ce665034c2d701b5429944f115e3c74ae858ff9e7355dbddf27a4e70e01000000000000000e71bed03fe04edd6d1e28e7bcf8cbbff881734408b49f7c3e87e0aec6489f4401000000000000009a64638ddd657bedde38872091a93d700c6a29facdabec86f916c51266375f570100000000000000187d6e92ae950ca3e83ea4b5def1553313c9f732e865113c24a5dd1075ce2f7e01000000000000006432baca82539fea5029b23f67c912ccd9a652fe58c95bde1646d21e2b02fc620100000000000000a4cf6a4ed9ce95da9df37128939a4bd909771b726e5901be83669fd30ceb9f1c0100000000000000c6003d828b9b0c61a99e86053843c22cbd9fdd734219f449e6200f3602c04a7701000000000000009ee91399c765031a73adf234280853022bdafa455fc539ca3920838855b11659010000000000000070f27022b5b79b964b7084d664d1fb56899b3c00295962bd8e7f330b55ecbd3901000000000000000062420d348b1b6663b61c0078995d5c0ce88659271b202b9d15c6597cbf760d0100000000000000f61ff7a1acb727894f81bb4e328a7f64ddbca4fe5477b60a72fe491536f97b550100000000000000341699a2305af27ee4e032aae91b4779218a2be3173500502d6644dddd1e653801000000000000001c0927ab80ed3e549a848e77c86fd30b448d4d188e0de7bd66370c05569d19790100000000000000444488d1b3e45c7aa8e35cf71dc80b41639412dcaed88f71743811c40abadd61010000000000000040639e72e9c4806d47f4bb7519ee7ff613ac108b17d3be775d72a173f0fc691601000000000000004c8ae5be1fefb1169156bdad8365d33f6bbd91b77a3e3897cbe0d5c4d7f7e3620100000000000000284b3a6ef1ead33f5fac01a077dddc179190daec59fe99586b99c0e35340ca72010000000000000092ef51762ec663f4b099fe6a72a74107d3844913df78f0b80d626fc4e20b1e0c010000000000000008ff6a7d89b6e1936b595c141ffbda304e6d40e36ac264d411615faf8049ee110100000000000000ecab81c08817d3365ffb7e7f3a7d847a701947a1db288bd2177868251da5a56b010000000000000038ddd57c995be34d71d938d2d1cb188a0e8b827da6d23ccd08ecd936850e9e520100000000000000149dd20740e21748698d0d48fcfe3e97e3ec1de3fef8730d5ce5f2fc110590220100000000000000542b657db2973a25f1f076f7b15ad6cdccff6e9687e7a4b5c0129bc77c847c26010000000000000046659edf4ece89d3ba4e4539a2a4466334f4107f099c1a9fbd8bfba22de09d7a0100000000000000ec9afce67bf970d2eec61a3f309f52c860732b69b156205775af8eef041872720100000000000000045fe14e3b9bfef6b174bb0624f9ca0f897e4cf6168ea5952124c15b5b2597500100000000000000a8c43807924e0b4efdf713a557ac34056545de254fa3745fa6cab97df913c46e01000000000000001afa773e53d051ca10aaa2e7d76b01beaa90c8c2d6d4a9c36d6dace0fcbb88230100000000000000f40475fa28c0b67b56058df22178c5baab25a28d153dc3277e0dbac6c094b25401000000000000002ef10a06d765808d2d44b2034edee5654fb27218398f0e79731b318678ac07110100000000000000500b779c0cf43579b8927d8ab50ea09f3d540ef36911b7a7ae99e2fbf4e137570100000000000000ca1f7eb42dabd6595813c3b7404cab648a2467fa9e31752949e07090b286a36b01000000000000007e80e157841d2bd8452224800a02e3c4b561a27f1731f0125f1291c359b4d4130100000000000000b0d74c7b304195803ba0b25627cba2c79293acff064d41b010a32b6d3ce1060501000000000000008cce5eb3755f181c945b8de1c6a3bcb7454557ab4f2d8d3cb9f7d137c2e50b570100000000000000fc320d8d5e575097cfffdab4641c3feba3bc195d7ec01121e2729ade54fbdd0801000000000000002a8cb7211931f3477b5731b23c2d973280b64b509ff16096da574f0648c9275201000000000000001a5d6fa531d4845ce8e8b33fe5e37f7f6c25593f92edba3ed34689d15bd8db0d0100000000000000ec4b66140f558efc29047fa4cadc629c72756a4d70727bc00b7461b74baad96d0100000000000000c4a4b433dd005332b2ff4af2534cae3ebc0a696340d8add1a7399e95bb0ffd7301000000000000009c1233cf95166c9255e9a6c5e0e927b8663b1b8a5a179b6bca90aaa1608705520100000000000000100f8e7d113d41416cd80214599a75f85b3b19e96cd2da7039c471de2006a64101000000000000009c1b9343afd827544ee40e6f45af9f75d1d4488dd9f479510ee8cf47c188f92e01000000000000006cdaa7dad71fe0ac92388b99fe1dbdc5425b39eeaf06a022ea8d8976720d914c0100000000000000c870afa85f2324fb5d56a923fb5a481e606bf27a3660a674ecac23d81be6cd2a0100000000000000e293330fcbd554eee59e0561b517231bde9f1bd1c64cf8530525f997eae2690901000000000000008a40b5186b72111670500b8cc2794555e4c67408a703f89984e957e9dd9d532b0100000000000000e2898e69209f2ba56c07e0c3f037fb20a97e5980ab3bf5f19c840b8f5d14641c0100000000000000a44074d35bfb2169d9f8bdae3caf80bcbe66f8bbb20dac30c29393f2eaf4967d0100000000000000062b28feb8046eff2268a58ade1d5c30db94ac566c33ff5a99a60541703431570100000000000000a62c0a8c58b06086dbd00a76e81ce32688c54f510a13dc5a287bff4303f41b4001000000000000002aefd1ba05d4f7c13ead2b20b4d868e8d787b8fda35b182040a2aa9a8a163c2a0100000000000000beaacb072ff2347efd4169fc7993ae758ceb56ea6f6897cf9a25ff71cd41cb02010000000000000012541487fb284062dc65881d5a326f3c963924b68ff36efb87efa1520c816d060100000000000000a82f7ab0942f27c701a45054359b3658662c80c0c8b1ccafe8271c5d3489ee0d010000000000000034a21abcc93598500f0d1304dbb4df783cd4faa9ab063e4caae4dfdc09899c570100000000000000a47830f3a4a794fba6e4ab624dada5b5630a82f1d61eb6dc406e01319c1a2c2501000000000000001840550329e05c67e05813a375fbba9a2ec82d1934388ad0292bfac2b45bc1030100000000000000ae2f6e59604ff1c1492face76604bddc0c48209485eca50f8d82cf67c727e37201000000000000005af885b0d311bd032ff14efbbf42adfae2a8ff2088f1e2259394bb03409dc4330100000000000000d6d45addae162f1bb235d16821fcf1136568b644721367bd3dad6a9ba9dba6660100000000000000c248107abc3ab176f7952a645a89b3cbe1303b3af53b184a32360cdaca6c400701000000000000009e51e504c5eb7a27f36c064c1eb1373d66a4e11397ed215dc8909db7fe523d5e0100000000000000da5f327512e71306ffe1905a70e12a259d0f0ef611dc7f56be8f0ca30940033e01000000000000000e6bdcaea0c05306fe727b5593d281bfa2eb7ff3aeb4bbd965af01396807d6100100000000000000f69d3157e535952e2155d65b90f2059bcadeb8a7b898e0443dc2f719e5f0841a0100000000000000d678254fe840948c178fb1f90f58fe093d1aaeb45b4919089f52c7faf222f5000100000000000000eede1bef0c43d1266c31905705140c0aaf136887ae4446bbc94cd5440d1970660100000000000000868dcd842b9e0304ac20a855f4374fedddad8781be75eabdf4f1f3d638593d1b010000000000000066c9290ae33d9153a0635c8b00685f11f9a89dbb3e9e907f8bf56c7b1d797e670100000000000000bc7567b86703c8aaf553fbb6add4de6c65ace7cf1816e31db07c6b4056ee7a580100000000000000e0c2d0e86ba6e4484856ec5a68dcce77175b15ed87d436bfc5abb5a01f38c01801000000000000008e7d9341619a28dd6a2733adef62b13f8cee0733de46494549fc38ae52f02d6d0100000000000000c43d0c516a2377bff6712585e03422e226f99014c9565da7d61bfddcc60ba2230100000000000000c6b1190ae760998a1286b3e6b9dda502a335c56f868f442b971fb891f361f75501000000000000003c611bcf0d259f8b57865f3250a1a09ddf562089bc322eee54be703ad422fb340100000000000000dc0e6a184863bce89bfee06c3c046a7cccf040d0d235865c6f0c56a75790244e0100000000000000500f6ef080819e095e751b2d13e5980446df95cc3997438f3b5ffdfa6ea0ec5a0100000000000000ba94bfa261dfe0b32d2697c88f97b9b1a3e603d69f916023dd3afa9d8f3715260100000000000000d4d43a66ffc7bb3c0a543a75fa1ba8c1b25eb338c2165bd0ad9aab3ef367fc170100000000000000be495e4b7ce4540071ad9d1c723a5a12a7f90801b10f20b426bacfb2df0a7b2b010000000000000060a92cd57e6ccdb5667307b0a0c9daa782758ece051899c00a7cf0578c1f131d0100000000000000d064fcab2c3f2b380af1b6ad48f31c17df5ef9ff46ffb7bdbc1908c7af5ad26a01000000000000000244515309e58b54210661c190fc86d5b4568eb4e85c578318729b50cbdbbe190100000000000000c4d7221426175dcdca951d8e377fe9c314972a9667550d2980d184dc7713244601000000000000002e2473679e557a5ab4b5f53f3775458dca1ecb9780a69a12d491e06e7b9c4d7d0100000000000000666758dc7096f360265acd0446e0e68d4d2c6787ac5d2a5d5ca76ded97805f2f0100000000000000ea4a297a0d3ee9491ac96d774dff2da3c675b3d7e4dbb9fa4e76c047b093880101000000000000005aecc7d9d96f48094caee4d395c8891fdc90529b74d835dae0d52123e5d2386b01000000000000005e092b5458ab949391f3323be3b09b90f02c9f2cf57c99e61bf5111a293c245f0100000000000000285f4d091e0564aded8101aa98e417e47008261ff729ef856003c798fe62725201000000000000000abdcf937b0f368b81ed8e41a7060a9d7917e6339c48b6abc6d8517b00620f7e0100000000000000f25c49e92258e740a9a79f938d3ba8249438b07884ee48a4973bd15e6fc52e1901000000000000006233f94f49a8fde5ca1b30460fabf5f1537bf4eb800caef619fe1c5a0572353401000000000000001845051fe44100fab50a4621b65b7c189083c3701b8366e0d853e139f60a45470100000000000000a41bbd69c636b6c12fc0512d2b016808851770a22c97337acc5fc8240bb82c0b01000000000000006ac55a703d9f7b20d49bd2387ee9fd6f1c6447a3401f793ac7be2064f3b6f464010000000000000092061d3be62cbed8f429d95dc1eac9d9bea8e23c12499426704ca64ecc28675201000000000000002c44ca2a77214e408a8ebbc6ea0e31cc4c01f1e9477afc11d9903a47777b345801000000000000000c34bc418f0ce9002c060ddf2f2b4b6942b1163f4dacd5608c0bb6d2159af135010000000000000092a908b3b5f108df9401ff0c6213ebdf5eac412295be6fbcae76a9320f12231401000000000000001cbf8a564c0beda112e06ce28226c1775b350a506fcc9133f1c87594a655e02e010000000000000030ad59b9fc56fe3d392dfcab57fc9b874707c1fb3d59ee78463f8d74042ac3040100000000000000361601767f088b9ffd46588d7049d46d33f1acaa4ea4561219c4709510c1cf10010000000000000074111e0c15602d7fe0c6eeb12065b07c83d890b94029c8815db2443a01b0eb2701000000000000005ceb546da5983b4c431d196e5bd32aa4803dfb513b792066f83546f63dc7534d01000000000000004a16b874d8591d53c896cff1d280d5c4e5197ebd894804efd727e4bb37bde7060100000000000000141bd9a074f86b6b6851e89bab648df0e45972c23acc7ccd9d5bec4adbea3a190100000000000000365850afeaa8058c89017ceea5cc3dd218f4955d2a4567f8a964ef389078c54d0100000000000000608e3ec03a52c67c8b0f637cfac7345fa1c2b4b61d09e50bc7b79a2644ad053001000000000000005a7d2d2e52a9a7f01b1ef3b252202b4c96af120e867141675c0820fbb67738700100000000000000baaf82c13c70e24dd09199d2fabe38d3712470ad7c8235bb9c47df20870d301c01000000000000002c64e7353c81c2b4ee434edc4cb8ee6d7a484371540875c546f6c9b42f6dc2300100000000000000c2a1109372991310a96d78b8da314163f9385399aeea368ed7e7c37f201893340100000000000000a4db5602ea76d64a281129f46538f5c0384c1bb9e3a7c5f8b96c4d893f963a6e0100000000000000040b8138245dea143b0e2cb70875ea60ee74ad3df50b916db931db5e583d5b0f0100000000000000f6d9021798d518b38d24a74035499871973f7e0e67a3bc5ba37c5d4893e1d9480100000000000000ce87603414deb87155f1df0a5bce17ab2fcf11302407b96b7b73ea33169a69670100000000000000f045461d722b94b6c0928af47a0d8ed3b78d646b2f4f754b1c90196465d7f92b01000000000000005ea61dfeccf577b54216c24d8580984b5a948fc2e0cb15cf75d3ec89ef00470f01000000000000004eefad32d3c3dd9b713031d2d3412b110a5997283e12e1573f9523bc479e77220100000000000000949ab1dab46139ac9df94d74a3a91b263a98da335b9127a2fc7369476bca3e41010000000000000080cf2f4b8c6f71647d087c97a83df3f3eb230bbaa5bf86299a2d3c8ff2f2664701000000000000004a85979f5a5af886d6808c63ca7f266f7a55bbf652e039b72f1fdaa9c90deb4501000000000000005878e7553def1229480d89260e485b7f3a5280d16b9c1f1163151dfe472e247c0100000000000000d876abe03feb4a8bcfdefda30228ed6820b1a1fcc5954148b0ab36c88fd1ef2f0100000000000000167fcf03f83b30dbddcdfb8e4854b03645d77a3be1268d674e65ff2085615e0701000000000000004e3ab53a4117874a7fc193a4e51a4b50efb808cc0f26a3c3616cecf4974079660100000000000000502537a2d009d0bc18752420e46bcf3ca17091bc04458ab50188240361ef633a01000000000000004e3ab9c976dd36a1fde5ba6c85e91f24b0e0f1a4101b2abd92f8eb6953593243010000000000000072febf0d8454db0a0c9287d7b22f739ba0154b6319111cb5104130557b6fc3670100000000000000e036c3ff765103769a69a0015caee3b83fdccadce2a269a3df376d0dd4001d4e0100000000000000e64945c884e8e1f126c36899bd7540a6866ddc58aae678cd0415e240dd3610220100000000000000744488e84dbbc73174838a2fe49f231ee82448487e5e52c5268f9e659be8e95f0100000000000000e6dfae0c3ed3754930c79a74e58d09583dae0bec35363be7c1bab1c3f25bcd480100000000000000408fc6ba61cad770bfd6555973b079db6ad49880fe3335c4561eba5ee649372a01000000000000001031987c29702b10623bcc65912a080416d2101ac6e204a2075d9a66dea5752601000000000000001c85862b279bcf4a725b66e8fdbf4150e253a52affacd77f2b52400a2209fb430100000000000000c648a1f7aef735b686df2634b86d91c94428cab340393aea3a5494d02cb57a4f0100000000000000181ad1bad2d424abe7f915a584f39a6953a87fafc1ac0b9942ea2c64406d4d510100000000000000a0668c4c43e71d99ad863e6f10e887b2a1f2bf32778d1d4d6eab57f28ba5425d01000000000000006a3f450101fe066e048ef87e288d88e0635b3677ea222f17ebbe87d4fb92575301000000000000003847ea56b040d92b53331bd3db1cd353b1001bb6678bf29cb12414eb5ed03178010000000000000092049218a50b3a59059599fa127bc3719546531c80ecb68d8ca8f5a0671c915a01000000000000002e1c9f1141ac38310b771a3b1477ec4456a02000abf09c4d0fe505cbd8cc0e2f0100000000000000acb79942c9051fe2047ba6a2f0c73ac71bfa033ff1b76d84332361573dbe8a1a0100000000000000848667087ecc48b5e6ce7a424eb24090c6b597cf121591e3d648bbde53393e79010000000000000048f1908f2c135d1c47c7dc79f63d6c4154a56ebbb46b039db014d8b444cfc1380100000000000000f01fba66537030d59b1e78a3246fed5a25c5322734ff422a9609e78eb0b6bb230100000000000000000ea8eddba18d4da471cc88fa5ebf59aa7e0cdbbcc7ab13643e2f9d2a1e120701000000000000004ce0e117bccbd3903add7cfecf3546010867b1a5fe6ba3ebd189e4cf9586a224010000000000000042f67b46c75b06dbebb389f8cf054f7c82187a85ed234f3920b919a161046e2401000000000000000a5aa5bb5d80bbf7ffad004fe82a73d66db8edad93ff6ce0d8df78d3e0e6fe1c0100000000000000acef4f49f13788a7356755512b74656cdf11fa80d7fe7223e250a7478d2a8d3a0100000000000000b4acebdb8857e0261b2229a1c6848cbf75001ab181e0504826d6ef04ca04e92101000000000000008cf03fcba2760c34855ce798becfa293b37a78b79820144da5ecb526f633cb60010000000000000082c55569a2c6861ffa4f116b34d3c6e12b5fb075f048e7d6ce35ceacfa40a93c0100000000000000da124054c707782430358583f3fd98aa1d4dd90968e8b4f3d8683ea3f27aeb1f0100000000000000f2bafbef8d77c9facf5a04d9c079d847bf793bbb1b659b4ea4c7679557007d7d010000000000000092ba571f52063897a993dc9818bf50cf039213409b550794ec32151fa089d31b0100000000000000da088ee3658a5e9587f0e2e7327bce5f20d5cc98cd986a5bb817d9b9a587567f0100000000000000dea6ca7823dcb0837f8533d8da27fd1a1411b1b48df4a8706b2f1201baf23b330100000000000000366e377084ded70d1065bcfa6e0629d9df8b01def4ff4620432dbd1ce70a6a55010000000000000098755d5f5fe724fa2f0116ddb92c0d9cfac4da3eeeb51e3ecf0356cb2966a71401000000000000007642f0c1c4d20c54338f0e3fce9b66576b392cf5ad165368c9924e1773c8f0280100000000000000ac198b700e0e627e2642654d791a9102e19bded7085332a1b76f543f0d94a1700100000000000000380ecd43a50457d037678d57a799cd411c1f56265ef38e0f777790e7ade53e7301000000000000000a42164a9749a911c34ff4a8ebdfc5d004de3fd7f06891fe5c882d2b56c2e6340100000000000000c2ebf281e96ead69e96276de04407f5d89a6626b5cf25bc97eab4d35b1b50a580100000000000000b64acae010e2e85df0d36ce79d919a14c43f0dbaba232ee96ed1e5708ceda94b01000000000000005ab857faaed9e6c7aed2d200aa81404e546160140fd0e5f314df885f6cb2c31c01000000000000000c7f753c4216d22f5e9a56f145f2dce74230a875c77c15ebb57bbc672a84bc5a010000000000000076f35f08d6050caca1bc42f1bd55d6d300580caf671f4a3b3bfa14b23b8019400100000000000000e8a3d82b64794602d009b8aa44a922a6c4aeb9c19c36720026b9b010e4880a420100000000000000442b8aea3303213b7f5e5b592ee0ad9a2043c34b62b8bbeb61958eddfb70912e0100000000000000c6daea6e872ae2760484c874d425703ce31f238748821e5fa7689e529d8931380100000000000000a2d5d4f254b43ef338ae46aeb6c96293c9e80b6508a4f9e3534ebc3786c3137b0100000000000000d00d343b5b85e47a6494a75fe67e3524841029026739ff98f7614b269c7e10440100000000000000e434239ced7867561ec1c186d12742bcacc9e69a19f6677540ab902f0e450a66010000000000000050a9d209313d30fc0decd461fffd3f46248364ab3a7e34932ec3b1e4ed720334010000000000000058ef7a28606a9d21571e962f3ef8b8adbf9d3acc41517bc9313985759ff1933b010000000000000016e5bb9cd97e2054f662f70325ed8440637c299e77736d3c31795e1225746c5e0100000000000000e03b6d54dd7017cd4ebcc6642ab9c23c62e2d846e610c7ee2e2d3cd71337b55e0100000000000000e6801825bed5f4cce30bde6abb75623a845bb633ff2e14a6d696a85e92690e490100000000000000f6c65ba1b18a790cc704f8e3a1bf3177ff51e7eb0081df2cb69173bf44a4f71c0100000000000000fe84d61179f6f25c0f3b84b4867b3dd0989aa22a2d9bb709ef67a8095008414201000000000000003231e6a8c5c5e47509eeb593347ec53cad6f05a0b30938af3861075488f2b03f01000000000000000e78888c098cf42dfe016c9ee1cfeab499eb765ad64cac48b28892a0a41ac20f0100000000000000d896c042b016100c440efe8a5ada9d0fe30f4eec18ba67992bbd1d5e513d147f01000000000000005a2e0cace9c852e709ab431b7a2ce9bfab90fddb0676af571452b8373756f63e0100000000000000de32e1c268482c89301b141116a85c438b6ce208513c71a12fd81777f144033701000000000000006211919c6d1b11584fcd6f3f3fef54d0d72f23ea821685ba235675bce10504290100000000000000e21d26c4a79725351bdc30e3825afaf4ec037fc82890c9eff8eab6c5dbe42f0b01000000000000009e25cf7dc9db334d01aa6215a0a9e27468a5bd09ad742bfe68b302c3975c31510100000000000000eeae3f5142c5fb5b16632f97c3885fce76a7969df551a3daa26c0690c610716701000000000000005adb6c8cced26392d5bf6151b5118a54c8c83d4a6b3bc26c2a623eeb3b2ad3190100000000000000ac91d2d8a78bb7d759742bd920741ed92c02115c7218d39642dea17086de3d3a0100000000000000d263e15b1310b82d6ab02f9f77b307fa234ea4a139b1135739602ec3c97e3a6f0100000000000000c20ac319e9cc300bed0ac95df0fd200751ce194400a66775ba8b68523b3f895e01000000000000008890f167e5966fb28d63bf02377a8b064b2466930fd947e54adaf5bbe6019c540100000000000000f6e92ca7e76a3ead20727d91199658a84ac784f496f078a71da0ab09e16a6c1a0100000000000000f65d2e46fa483be5f4d052b2f03563fdf7621ddcdedec753a4ef9508782e106f01000000000000004ca7ddc4814c4452f6d1c98371c6d2ba26ccf94841249edb7fd120fed311262b0100000000000000380a3f04ad89f508b389352994ac011d1845187c479281320ff77f4352ec0f200100000000000000a2c51949aef85bd6d86dc1d699c453fd89f19e171c580a43244b37ac32db775801000000000000008cfe7abfb01580f1d3a9633b11b9229b37f979e440073d885ecccdb32eb64e4d01000000000000001e2d439fee3ab0966bcfe0569144e399d8c1e431528b04a9a103a0d5d76bcd5b01000000000000008cef7f6ca0d96d48c03771e33900c28541bc7d5ee15689e697cd160aff14797001000000000000008c63ab0ca01ed8b358bb5eaa2582a0866fbbb68d170a03f2ec98e9eeffccf85a0100000000000000dc8fc9b4d3b7bb540e9f12c606e4b0d784dbf8542db625f56e686a2d2fcfcc25010000000000000040fbfdbe5c13e3a46a731b8191f92cf01734f42cf7293fd56d664a732c14ec5601000000000000000ef24b61e18983aa285f70ff8e361397b82a15a41c6742541ce7b92da9f8931101000000000000007854ee17d89a8b19b9756fbb60653d788f6d261fad13a56933e0640c674a3b3e0100000000000000e4a3a09d9e9b63295de7b3cb2284d87af269ee20b02de0b66fcddaddededd0070100000000000000e2b114eb3572e899237c85d50df9e64d4f29901220e9de1d7a8e04d96d2ef24b0100000000000000a42e70c4530076b877035b43c2e41672dbb02b95bf1755e1f808e3cbe23369630100000000000000d8df9dac3ccd81b28470ca51de56a4ce7c637a0ffe4b73a826cf7fd33b7820040100000000000000fcff2cb776b86e8a3aeea98f908c3195e4ce5db676d36bc3a7af3608698ef0690100000000000000b684476641cb59f5820a10f788f088080a953b907f990df22cc7943dc3a46d3f0100000000000000c0a1d3aa25d5150e5c38220dde8f6049268270e41242d623989357654078362c01000000000000001431ee16d3a18ea78d6d22801c6a25fe304172dc5febbe550026a1c0929864500100000000000000fafbb4dff5111e01f4ec36533c57a92f691edc68cf3e86a2a51ff16113728b290100000000000000a01d73a031bb8f4aba71d271be16a28896c85cfb9e2dda4fa3b44936207a2b290100000000000000eab3d83726de7218ba112c82a45c033830114b31870c16d0a49d9070621e012a01000000000000006c1eed8b9700d423e00bf57ddb3af32d21d8f0a104dc650e871ff044fd754708010000000000000004ad9d2a4b586292d19f935a31d89b90df9c99c74592adf2d29e64a5e79ab0120100000000000000e6dd828a602eb81c7522b98e53ceee9e22d0cb5a6f7cb0e3b00a7006f0e0770201000000000000006c7642774bd973c6114e6f0c3558c8f166cc9c8e980ec1d7ec3cc0276426c77b01000000000000001c3d79dc60c89a9873f2c5379ee71f4f298165e07109d4d47bef6d993a084e1a01000000000000001287be7a45554529c73b9243a51a201100a37a2c2509a9fa3acaa4dc04b5093c0100000000000000325f3840c5a9642addc7b6648fdcc21495999e5c00b368420b23e8173f5b9d570100000000000000820409942da35bb5ab23f20f62db3f47c5f87492b13f030734b7e7fbcb05312f010000000000000088ae601d5eca42f99686b6d19b3889fa79e32551d065f1cd650badb2fdc9450a01000000000000002eafead8f602b2ebe286db17142e60a41962e071346b4f1064fcc70f2e19552e0100000000000000de53a86b00a4690bb292c096b97dd222fa82e28a833bb5969aa9ad4a273d31630100000000000000f0efe1c1c20dcc2667571aa3375be67362e7f28a49bbd9195f4b5d71e99fc1650100000000000000545b0afc9dd9b56d8da46d35f40723548fc4c13033912da4fa1da2e41265047c0100000000000000904d79ac4481c639afe07fa078bacba2d3a890b3f8e133cbbf744e7fc5297558010000000000000036e5fcdf6e878fee49aedc20f21dd56bb41ffc148d32c34ade94e6fe40a947250100000000000000bc615a1317f7bcb567f078dcf0fb173bc02efaaaab4751c1d50c45b9d1db891f01000000000000001c808827ef3bf3e9de1e02128c8dfad7c13bca0a657b416dfb2352b48b02db3b0100000000000000ccdf3a11292657bec36bca11a4d5be787cefd82cac4440d0682b5cbe52af6c5e0100000000000000904c675598403fcaa16f8f3da9ffd6fa995046782c87590a7c715f2cd590543101000000000000008cb90a65a771000542944e389890ad0abbd2e2abae26dff9c389dbdb20f525440100000000000000b6ebe42548778f3fbb2fb24dab7aef418391da6fb9a07dbf8b889e338ca825010100000000000000fab692e1a2e6397aaaff6fc06302935eb2dedc68917fab400c049df1ce790e2d010000000000000022aff5aa2b5c2f1b1ae4ae3855f3ead541c2f0c9093a946178e127dbec92a04801000000000000008056a44b805cb78adca43e77724ea222b5cf6c25d6b314d38ba7c169576b0e1b010000000000000096169e83fd02bc9ed28c6c832a3240e0e01bffd7f8e5db4f2b85521077212a620100000000000000c848b06bb3577ba4fbf042fde0779bb9a7eeb2978fcde090dc5733ed36043e2c0100000000000000faa30132a968c6ad5db9938a74cfc8f2a878349fdce5776577bdeb3db7a96a13010000000000000026b3a3fb30b16dd2a07b9b6887998c789f2e0b2b50a4e26b792d16d9410215790100000000000000e68f1824cfe5dcc175d3796ab3ab85442bdb76f9210ecc09e01db47291f7e5110100000000000000b84d61ad8579f5930af27feb2c48e63831bd2929ef86c3145e1bb065da6b1c4f0100000000000000f4ca3b31c69d7a198e30c876e1bbb904ca8c5b038e5a811667d13e391e13207f0100000000000000fe922a6bcbb95646bd14a97d96c31cf7a4bc599b1ca7a53f39eb603db40d5e6901000000000000008ec82c3594fa26941692a378722e80fe77a45b83bd1c6adaf06ad48bff228423010000000000000032143326782c6df4957093bbd84260679d1373bb0a62bf12db89e2a7c6969d710100000000000000a0cd6c7a7119abdf54904eb287334be70f7d9ed0b14eb82e4b453efdb7899519010000000000000092ea0b49900eb565e46e7fe6c88d93858a5908a48e13c5e073183df24054bf44010000000000000044479354ecf5fd25d70f527143ac61b7824743dd51426d1eb89964e05489af02010000000000000016a58215c2c1c4a19db6a79ed12df45d1bd036ea02ca7fb81798fc28e80d054501000000000000007a9b8439f7407ffcb77f97f7bd878edefcef5c6c780bf4a49a5b4ecd9fe850190100000000000000e238e399e8aa6247525fe6e10f77ffe34e93465f6e2febe695ea74f90e3ec32d0100000000000000b45bbe08cc25b9b0e98a490833440b7932e4086201c82cc950376eb77a6ad94e010000000000000050b8e2a374c29f6a14cd27f72c3467663915b006599f220c40672bd080a0217401000000000000008ab52131785bc4db73448c72fd6bb6f55f6ef370e9293489e124d0eaa3215230010000000000000092aa82fc527b300d0365a62edab188ed0816aec9a68071f88d7d9fd78efbfd52010000000000000028bcc25f41f0efe2db27838a212f5f7abad9542345b3502ceb91226ef95d0e080100000000000000b2370f9da2faebcebb91db0f4acf2171e4667e1e88f6a011c1248df5e19d85310100000000000000e25536d123bb812892da8dca4cbe099a8b1c86ba9aa702cb5b95743935ab9d1601000000000000005467c16e538a310001c285235bf0ac5fe3a9caf9d82738451a9dc188c892cd5d0100000000000000f6b6e74ef97f7d14f4493ff14c9f0f6a76cec7a4f713d48fec5d008aaa830d4c010000000000000004e09dba366325cea9b0b8df63e0a00a917b53ce34aff64a8d1c3e76a15fe34b01000000000000005ec8bc4e1b32dd9e65c029e27494ea67fde94ff891b068f6f01a0ae33b17312c0100000000000000569605f7c34797a11605cec3b4950f8f5c690db51b24bbe366a1259105fbd1120100000000000000e80c0983f6e5a56461e1312af164e23f0bce9da93e1339df16b99e45a335434a01000000000000002afa459c5fb5a2e43b8c6162674b968cfede7f551acbffd9de8ce753643290400100000000000000c6ba3592ef9d1e79b36a3cb707372fd1e0c0994887600205f2265d370341c71b0100000000000000501a9f8a14e0f544bfa2dfa5793cd7f1549d45d213ac99a09f1dafd821cee6330100000000000000a4af5fed768cd4433ee90c534f86cc4a458795fa7bcc54b0f3ed8cab36cda46f0100000000000000048f25ba12ff8795ec8dc6a7ebda399cc18453accfdba1e58b111bd3c3db322701000000000000001628bdd8cc64a774215982d34698cc24cbf79a71c526e1253259c75c3ab8273e0100000000000000f86a955e179e71bf054104adf25dcfe05d6b4844c14a70b71818c6e0b1dfd81c010000000000000012a54ebefa1cbff0e621b59449bc24d94881d988c2439178c91ce7ffa374a2430100000000000000b2d01a3feedc72f78e575c8e77306f2f337f33a7d8a5a7d28d27dc58849a631e0100000000000000466e7c8220cbdbfbd1acf470b839b2c41f428ae619698a8bb640f10f0d9ebb6f010000000000000008427554054a0e5a7ce541a69a3b63c4734661eb39e303e3fed502435e5a73730100000000000000fc6000725023c95d259e5e405737c39a2965d6289843d3448fbe06a5165ac7390100000000000000789c23ffdcaa9fead87226e570e0ccacd11ef3659a0c7b5d960517d86797bf0b01000000000000005cdc38b8b8fcecdd85ed9c6ae897c0c6212d8fc55fc06ed61f503c157688831601000000000000000a790d1f76d6fafe4cf76f5086e16b4c0102e1f05bd92844034b342a69c2303801000000000000005404349feb5559d19fab0a2f924c4cc70dfb9cb28fc954322f184bee5026e10e010000000000000064ef00a9076e78d72baaa85d81238c5c02efafbfd43a0c07d8e57c19ef10b34401000000000000003e39bd391328e498ddc44d51f5ec01017790c2a0334210fce8239ee47487dc1a01000000000000004cc73eb33d62c0ec3e7f886195bcded7857aedb9ac161226bef22b0317582e63010000000000000004b263ecddcc9db4186f90e8b65bdceedcc5f786fb72698779f4d523a7d28b430100000000000000fe8fe762d7b321d00ca277b8c4aa433bbc8001c7d1d7a2846929c42e0dd6483b01000000000000003e6f47144b40eed6ebcca435d33d00d4609bdf3e7af96270c65e858094b309150100000000000000a0fdd556e5d6c1f8ced14627bdf01fdb4234118e85703d24274a265c45f92b4b0100000000000000522263467c287a18428c4a5a92f37d273699c9ebb0bdc21f5e9a49eee434dd360100000000000000c8073eef80dc2bbcea972a83302442249e5d2a5213e65297c60d56f4e9be86330100000000000000fc04c8e12ae94b0e3889ca36e2b4a09c32f9c6b7bfac999f56e5f2dc72badd1d010000000000000096c09ca3191f61bad04d1bfe86d042857d3df82ad457ea844caec645758c4d220100000000000000a04f6b14597d61c7600128326a1d75205fd239ade0c3b25cf359ee731c80c85201000000000000004e0a418c4c371c6e99b76781644eb38a63789b5d7c5263f949751e9714fa7c350100000000000000f4071d8ef295841a0bc13deb8799e23dea815e376eaa23c86f65a5083d9adb650100000000000000fa4086b525cf61d35736e736110859250992c443420778a90cadd8e8c88d8205010000000000000016dd18d2e8f3a507321ce87ca52145f9526c0fe35f7c868d95fba2b986f84169010000000000000040eec19a099c9ee6ff24e3eabd0a7bcf36e0e0130b4718d50106344044c0d6270100000000000000de220179c5aae1e95b3ccf3a989ca36ac76061bde0cf710670a6910d26564902010000000000000090c3500722dd6123a02fc74476a10b866118e9930e9c73d6e1d48cdb6d57e43201000000000000004246cd1eddf8b9255734a8503025d3d0a30995d71fb5645b1b16be777c78e901010000000000000070c83248401cd7b8780895793b6a0e568802f297a3bce0af7d51eb653d23304f01000000000000000854a35b9b536c8f18af98c90d6e8e639f855eb29bd76cf5105414c21a1ceb6f0100000000000000ae7c51f4c49c174b2400d057d61a1f43a8e13a6850abcfb079a909d136fe374d0100000000000000ca5b9515daf1925b3e200d73957dbc4aafe71160d33b5635ea57641eef8bfd7901000000000000001a43d2bff1c184ae4e83348c8b373df9dba72d90741f3e3c664ba6a2cc96e9460100000000000000225ddc65b656709973da9546280bb4b9921bda577344d3078f29a2a8d463131101000000000000001645c1277cd42f9d98b76f5b54b9c8e33550d31727f24e5c5c50acb1089f68600100000000000000b6e834b1c53a32fb3f8713bbc96bf812c6e208232c2d6f094805e8fdeb0c876d0100000000000000b60af9ed41b27a8e5667ed9c45cbe12bdccece1dbdcc096c0763c0f5046b7e1a0100000000000000f2118bc805ab00459fbbbf94668a8793c8fc142956a91a538b55797633728b4e0100000000000000e2d0e3cf521829f443aae687d4533647fbace9e641f5fe677a2c4bc3d7d3b142010000000000000034dcf88929109b300a7d0cb74fcf0ab261b1a273e09f3d9cde025e8a2b10d62001000000000000005eae7ec92e82f6fd8bf2b1f720a696d0256b35724406cc1c16fe4c071448311701000000000000000e28b34ec63d7e1e4dcf7a5136b63bced930bc9e02d416fd5aee66d248eaa2020100000000000000ce49383c47b78ad330a3e5899850092ed6c99012b59d57307de5168d0d75b423010000000000000098280d11d9c4bcdf35884c994da018af07701df1a3ac3da0fb8bfcc95b7aa05701000000000000005eab887073ebf35904cfc25021f4f15442d6f334488dee6a02b9117698f0d538010000000000000078dcc07cc7a2f8cce0384b4e14f9518d3f771960abea998292bb6a52f6bb88560100000000000000c8387a2ccdb4ec7443fd3cee75e8e559990acf2db6c54a88cd99f45fac1a66660100000000000000327e66f794b3379ce2ac64024813e987661fd4567c9f067d32438be273911f620100000000000000d25555083cf20fedd9b5aca0f26447b57bbc2788ccdcbf0af2efb0b010f0ef26010000000000000082693a174b02d3e8b8d6cd1574b5c782dc98edd92134854adc083ecb36c829250100000000000000fc18fad14af9aa7bdbc62e1fff9ed7078d63d1137a3c1e670bd3a3cf5a93d3720100000000000000b4765627822d1702513ff5648aa320cda597f54505757d630e96d66aa1f3c73401000000000000008a2c2b3a835311965eae34810e418fbd1bb858fb8840a850bf036fb7eeafdf7b01000000000000007abd57972d56585451d75240342fe7195758d6022e0120fb2cdbc9792bd2ec270100000000000000a2d8db3a4f3fb0687052b930bcefffc672c01a65538ee7b33ce9dab822bca1780100000000000000ba73d9161f8469269ece11dcfb9081eedb02df28643417622e258bf2a424a6460100000000000000084b56e7c7fbdd6b45d2db9e3e922a969b28318b35ff26692d88beca3a749b260100000000000000ecbd30a0be3dc9d3f8f4b29af83968bd76d0c62bea36505a114cff593e0b8b4d0100000000000000bec516da47d1cb28351ecaf868365c664aab9076816f1c536a24be5892426a150100000000000000688a95e2be5dd66a92c6b401a64dead0b35f90f517fab9673dcec192e1fa207001000000000000007cb3bd528db15070308159636ad9ca12c1378fa7f8adb5206ebee98c0986065b0100000000000000d8da588746cdbf28af1a52046bad4d5374c656a7111c4969094929548945df71010000000000000074c38c9e1867a292166ced2f98282cde342789a3b5d13c5f76f15998eba3e8510100000000000000485f591ac71f970ef4610ab2fd5f034fcddd9584d0213e46f3161611d141512f01000000000000006a33e9371b308fdcce6c5a7c6bcaa7f5678ae01db799290539167d61b2d1102d01000000000000003020c45aa46e4f75fd5fc42d966f1b99bdf1104a667722d91568f063bb2fd511010000000000000028c912c4d4ec34a97f6416c64424bf80494332248e6a7bb65784a6221f3fdc2b01000000000000000a3c03895ec10aa735b1e2eccb283242afe8ea6f17a675815112ce842ebfba0c0100000000000000e45e549d0f5e96b9c5b320e37828c80629fe7130e5ce695dccd3438dea5ae650010000000000000072f45f59c55d6206a31a1bc2361e096731b34a8745d0882ca2e77694b650134a01000000000000005405d5e7f3457fff778bad5558ace408e563e388edd901f3af12990afa7e8a0c01000000000000004e717a4d2c84dd7b159a91bfa677ff9dc57bc7fd3cd661523c8140a7680a27290100000000000000b280b7f37e7cc9a50d4a52e0b7fedcbcd3cae22ec16ed4b4b75ad460efc227160100000000000000282509d0ebf9a77532c60bbf28ba1509de4a0ccee5ee2f58ff267963b4e11c600100000000000000b85015643739ee03dba7b523f32db23cd192333ca1c2dc134be5b67c306f104d01000000000000004e8cf6fd4c64d23457b08b38b3a72a24279593b2aec4aec3294c67c92f4d801a01000000000000005c3a3673e42f0560adb44ee5b7be7948f8964eae967f80ec506c0147cc11ab020100000000000000d6451fc5d7809e480c3dd53853789b86678b890f4bea62024194b14a745370090100000000000000c0a1f13309be3124c2357c00f6a5545b843c70749fa8ece832dc77bbb7fb630601000000000000004c3b4d7a82e669e759b0cd7a6d6dfef7b94a00283be725f25b263d1b4d9a7f5301000000000000000c714d39ea91dc8f07a7b925a03895a148022b8f117d19fa2f29479189fe423a0100000000000000f697d527f96e9f3839e3502704ed69b500b765f6a64ce6dde88af24db12f14120100000000000000de63bd0d9626943917c561a484e93462de4c05d233bc4c1fd7c8b6833d29353a01000000000000001a4e01ffeb9f145ee8a17dcff9bef42b02b5c9b047e05742ac85de33639cb06c01000000000000002ad227e1e2b705e448702ad1d8744ba85d7d5756952b19976d3abe60d60fc70e0100000000000000907b44da2b726af53076263cde755c2723774fdc25ea52f46769d8f357ca9f01010000000000000078d57cef73882a2d361a28b7fd2ca671b3f726ddb22627d4b915217834b7f2390100000000000000082f296935a2a606bec1f195e6b8456f07cbf7b27e1e557b3887e1c46e4bc37d0100000000000000f8549151ec9c6dabfb6d844ba74728b04a8ff079d00e55f5ee55bc3b70604e0401000000000000001a12a4f27227ba6a04b5804f339b0ac61510b6c15abc85e3bd479d6536fffa7c0100000000000000baede105d1ba83fd42159a80f1c0969bd4cd6ee77700ebfa1aa3283a9d274a47010000000000000032e2a6ae50a3524184557db89a681b1de81e490b4066b4fd67364984da49931f01000000000000008ea9246c435f67a71e9708e1481478b762011e0965cb3ecc405155b4d2543078010000000000000048baca83f5efb426424d78dde70340fd86117f01108c84f4595deaddb22593420100000000000000d0d353277195e01a4236de8fdfe59b442f17af9028dd3f44967dcb17c2e01c5d010000000000000006978ce2bdeb4b02ac7d76a3086ef1bd09e3b9857e950083b50389a48871f3690100000000000000a6d6c761b15a3aa636f5254f6a838ab668421ba56e31430282b33a118ac9ff1e0100000000000000745f2d27e596483e2ebe6ee5750b0479f3c0d355af3498c570549a29b8ac730601000000000000007260ab80632d40fc1d7eeafeeeb99dd747a0f4800de34c2e82578786008c5248010000000000000064ba39c577fc90f2d2976ae979a7205a4918f4c8db532081f06bda711ffc3721010000000000000080ae71b506da12f8a73992372864dc19b64df51f1437fcb247c215a47a759220010000000000000032df009616a2b4d4f6bc9111f2ac419abf4890c99d6def4ce575f82fe7dd163501000000000000005a378e104d9df02d1dce1f9a344f3d4b6f9f088bd8904d0eefe08734a98b42710100000000000000b64c6d920385362820b9f7f9abe817a4bf64224c857fb65825376471d8c95e62010000000000000082b8b2c123aaccd04d02db43724f51bdb437265e59832714a2c2748ac83f2049010000000000000084ec16701285c3c5edc5ee9ae8172215873ec71af3abd53ed9d407c2bc029b3f01000000000000009c2328407d05c9c294c6f207f7c8829f1de10c13a77ae0707dba0dd9f40f8f6001000000000000005c450084e0a0ae90f805bb2e856bb5b46a43cbeda05a7c1e2cc09265e1791a500100000000000000e21ecb8f6b42f003d0d9d3496bb5dc1d57dc74e496acf2c4a3735940cd54231b01000000000000006aa3cfb637cdd3b960d2578823da2b6dac1083495e421c6c32c9e17ec0c905250100000000000000a25f7e57318e03aa832486e574815234404b22d1548f35cf77ffe26573da1f0d0100000000000000227bd022f3a23819559cefc5a323fe8347a3b75f74de5c7930db82cb7fecc8400100000000000000b4af11fd00750c08b33c22eb7445aa0d0fdd877bf701f0755c436d7907c1c27401000000000000002aee8f940e7da1699a6deb601858ec4aca5d597c50ecad1c3402dc97da339a0e0100000000000000d4738db14d68af6357fe1d33fe05635287c20b35a55a6df8018141ae26af9308010000000000000026ab3e54f192d9f8ddf020571780d10d92a0460284fd7b9ae94e74c926c2112b01000000000000008eb2e37eee463c14fe7855c6133aec6244ea3457ffb9b8e0f3a6ef5784548036010000000000000094d6f27dde80e42a7f95295a0f62492eec7be0b70478fe5f30f4c29e280cf92d0100000000000000d27d11dd2c3ab8a3e15a00507904b9fa81c0182176208f622543309788c68f050100000000000000f64491339e6b78127580800529b6ce624217adc82ce10f56d5c6911962e3135f01000000000000002808e5a217f5aa78b344d154002fefdfaf686ebfdee6b7deddfd1411b6a06d3d0100000000000000602f60398fba25dc09db2b4d8843ef9d96e0c60a01ab42ad976fceaca42195700100000000000000a0fc4378807ecdc482fe8d93dceebf78cc5d627c03f9412ce1c1d87d7b44090b01000000000000007008909e788793c04e57158251d1485884f3bb98741d889ebffca8ed9644141b010000000000000072dbf42dd0d9ec7a221912ba34b585d7fc6a0fa240d719332bc372180b3b636d01000000000000008a5716f1c004e49a930e1f0de54439f0a32145cf2565727dfafc11a024503659010000000000000030139a5482c8907486506234bbe2eed7359b6b7c0630468d46dbf2118fc5bd340100000000000000165d26b408a69bf67c63a945a49058f53250882485046f71375f9e3128aa9e52010000000000000060cf7d800df60f707be3b735805e13cd4a7eede0ac2ecbd7033a872b46403f2c01000000000000001872a4fa55daefa13b0b4e4f218cad38d08d7d39d42e47f7df5eaaaae02f0a370100000000000000bc800344f0bf1a71721fca1cbd7ccc47dc009987bd3524a883ebc1e925071f610100000000000000741b61fb1574f9989b1fcbfe4bdf72134ed5699983228a2cf27ca11d76c990160100000000000000ca2e3f3b6f3cf94b81b3bd4c55c730e41d8df97e97bd8084cf1cd24e68719401010000000000000060b1145a4451816b6d3ccc173fbc25b6c7b287fd270758152b92b30ae62eb7520100000000000000483e62e86514eee828c7144e945b7364598a01df7867f7f637cca09e702eb02201000000000000006aee8ec61df9edad0c5903feaf88f6297ea64efd82a52c692c5e937e744faa010100000000000000040d3137e8d771c19de798bd681da04262e8185db315bfe393cdb5cfaa79fe170100000000000000864b7adae776289119fd4da00567efe67846ede7f576955aa4ae889981276a680100000000000000ec52884a19e99c0df416f81fb211ef85aff4b0cbc220d9ff6cd34afe35bfc8790100000000000000b42d7f267a4e469af84c38da01808f135bd6853a7bd92f6cb44e7fcb952cba3d0100000000000000ac220a7798ad88df760c16cf8d18a42683c98528e854daefc7ee32bae85d646101000000000000003404a58d5e89fc3bbe715dd5921f5f0180059e74d9ae6196fabead7746cbc55a0100000000000000380702aac808412a0b886294725efbf4fc033b7bbb85d3b24c2876cc9e05d212010000000000000082af68c2e6079cea3474b2e9d76796b70b2acb3a0ce9fcc98503080f25e629230100000000000000345d63333cd7a9a17653f94374476eecf5eba6b1e4cc1c6e9ed215ba55e5cd000100000000000000960789a11e8dc46636aa94789c0985f9b8fe7fcea34abd0e56f92180c0ec773e0100000000000000a65039da8396aa3e559367c50a309f12e8747e4d16245ec6000d19a0af26f9470100000000000000680e8993ada64e3423fe61be3e70aa728ee73725fae78fa5bf8c57e8e1c54e44010000000000000052019b0e030fac4f642c31526c6308bf9501a297c9b6193a82d6915fb708ff7c010000000000000036b72e06ad058a7708948b66346a0350f8e61fa8c58b0e925600aa9bf1476025010000000000000068b06ac165f434922b8bcf48deb490e43b0d5497a2caa64654a1c673da36b83f01000000000000002654b04f0eeeb6c03a28fb4647305162a4e1112f631b66293ba16b563b971d42010000000000000052283740793ef867c77327902030e4f6c0deec494b5f4db2d680452880ba11040100000000000000785545f9357cc0a5e100f2ee283dbf8f5f9d4967b07aad4bca6ee866312430390100000000000000d60eeac98c4a07a3f08e3ed451c61174b367a787f720a6b7478178209e28334e0100000000000000843d6ad78e2665ef2b86f659fc0f12af29b11bc7f138e45f013e9afdc728624d01000000000000002e9d04dc6cc5425a6ba5e0f6d8b88623849a669542d42435c7c843aae0bdb7250100000000000000e6a422c3c7eb6cd9165fee529afd2209fab7f39b1ae491dc3d5c1320ccae1c680100000000000000c00470ddb02e84a7bc7500d90418dd5d133f4f8184ede2e15cfb43b881cb0434010000000000000092ef07386f9bdee89cc4cf8dcd1aa942134b21ace1bc2f121c261fb57388d278010000000000000026126b727ca3d359ad9aa85e71508ca3fb6a6fbe1416cec946bac3819fba9a590100000000000000d61549c459c94ab938a68b3d96ce644c8672effe9916ad3435324727eb18967c0100000000000000d6df798a261d38a33360d44a4d53e7a686562dec34fa12b7e5db309b736ca642010000000000000076f3274425ab752e21ddc0714e28e9651d4206841b736aac6db37aa0d0d4c937010000000000000098ffae7440754e3de87fca995a95250a59ea23cf595dfa1e53b1367ce20b455401000000000000001a089db458c31ac827e683803163d520ee1b08ed721dcd45b02c3d85855c8c2c0100000000000000267812fa98f9e8e92421e2cf5fc5de6a9423cb81f3b9242d57eac782b3cd09700100000000000000e4fae18a7a4c4f5196539556b85a450a0c1ecae4ef28bc52d1ff9cf3374c946f01000000000000001283692388ad47b5a1ed8b0d791c4f86a36e4422ef3857c912d68f5d425f34730100000000000000eae8a9a4316fe7ce6cb8f624a6615ea281f02082a5be9d40593db1048b234a1601000000000000008eaf59711472c3ef85e130855a4e34e0e3f2b8852af88ab561e9a49f206156280100000000000000dc6956dd35ff772cec5f85ce4aee9e6e6a4b9242495077d77df3e3c48bb26e50010000000000000080d7f21b01bbe9c1c6d89a7275a660af2c01570e22e9ca538b212a9577421d11010000000000000062c124e506bb86a27ceec1d2dc59e15802f14312b81e84f8998735ca13ee9a730100000000000000667ab5e18370d52b66a8c57cf9104d5732b22de108fd38eb14f8b7da2a29ec0c010000000000000032483a2e28748ea5b9d6b87256ae783ecbbdc3bab669542fc642d2c8a6e15d380100000000000000727f2cc64c33ac6bf5b433538f35548a996e80ca8fb8b1de84626882c7b6e1660100000000000000be210d84cead5ae1c0ebbefbfc0b35f33310d6aeecf4308529b86c2cf82c0f5f010000000000000058070bcbd7f8f06c03f9b7aaadc8678f182aa4b3672f3eab877525324f35774401000000000000008ced7755d2a8b17b2d9d157c1b9ab5b0ed63b25b9377d21230d346c7066c39520100000000000000caa319312d929a03892d06e2a524621cd9fbaef1f86d325501b4f1108e65a96e01000000000000004cc8f3fdf153ac8200029d319b8fea21e9b483bea1ada7cb9bd9265da69cb9350100000000000000349352bae005c140e9dfc1bb094e3c75fcd206a832d8bbd16cec98cf9f53423f0100000000000000346e685f148b3adfaed81980e47a61844871dfd86ce1d4f84a7b36998c66712b0100000000000000c497e8cc7d05d62aa5f59e9699962c395433b9ad38bbb84748f90c7d9f1f373301000000000000000ae2ca012bc830761e5afc8e1d81d62c6d160962524fdc3cff021ec49f34e123010000000000000018f128a7e5736dea14b3cde003c82a6ac8c922d2ef21b7e02e1aedc841033e3f0100000000000000a875111a42b538e568016c84c89f4107e37e5643d9a307aad904a7bad7fee76e010000000000000012082ef825e367087ea396f92c97868c24aea7ac2435c8acace02be51068b9760100000000000000a62c3f742cf861feabffd3f949f64703932d3f49aff5214e7ce5f7bb6966374501000000000000006a0e1ac35bef24f8e229f76ecf852ce3fcc87a884e8770c38e2677447e3c8773010000000000000004b06530357d34a7c37df9bee937d879dfe32d5506d1eac3fec50de7bb44dc5a01000000000000007afb0c23bd2d29c347a9b435a6db5b56e035a3e021f3fc9dcfcc47e52f3f0e1701000000000000009eac82acfe866c0d456b51e909c6b83dda9ea53d89b71945c3af1539f7b367740100000000000000dc514d2b1231ab63823ae49ff21fe0c250eed9b4d2e3e83d9cd5072b5f994719010000000000000078ab99c3d9ea7f380ebfe7db64dbc72ec4ec0516b143c2f66f8b06943cb49175010000000000000060bf5700590c273827ccd0dd56915f377fa2ff366ba72bd81942908b7f2a742d0100000000000000a013268290f12c0417675510df18855b1f1a923f0b513f7e7ddc1e763e27cf0a0100000000000000fe0446048559fda5d5d172dbe2ec70e54a5c3080394d055b0b15badf80b6af1501000000000000006ce1cc45c84f9394951284b950417355ff4a093946649a1ed9935cc650d3457e0100000000000000a253dd11fc5c488265b2a938adeff374961efab3789e0d9e637b566a317d71390100000000000000b0d22d2a52036e2e23ccf11b48ee7636021e88a63160754c8e161276e2bd9d520100000000000000744a5456dc956cb654fa881f1ae233b3fa4b245d357826acb8874669726a9a250100000000000000d8971835a1c90022cd05c0f259fc6407970d8fb59fc3ef0c515e07be6d4892390100000000000000aa22f189a8994b03920c2bf1d15d0699bb548c81c52809751cee4ccc794f5e0a010000000000000098f80bca2432755fc52885f1d8023db386ac20b33fe47dd516df4d9318f8ba6f0100000000000000a434f5d00ed6fdcebe637e8fdeefb417f9fa17c37970d1cb13e0ea4ac82e2d4d01000000000000009ee276b61fbded6deab6125d7b348e03c4b3aaffa7840190ed1cbcc4a07ee93101000000000000006ec37117872c92130592d0d0a6929d21e3411790f02e20cd313f09ff2cc5866b01000000000000004e4fe940a0fade721c4c3841d8d88788b100bc0964b65c53f1fc9de3f55d416801000000000000007e0ce7ca3586ea27861b81e7334e5dde29293682fa860c367b5a47e538e3760f0100000000000000549467ec60e011b6bc97752245e56aa4abb389ad87f13b914746bf88c3c93f0f010000000000000084a19d04d5544e82fb8f9f928af37029d73c82da314f437ca62339890b878e4a01000000000000002eff374cd044c1a795a453673fd050ac67510592d7e051ac432f9f1dc1080f6c0100000000000000d05a7baf5e5fe42f96c92c6243a6fe99cf7d5a2f314ea6c4672e5f28e23cd3220100000000000000b857af823b2fd4d88db51d82bbaf4d69b624e1edd3f4fc87afc632c34353971901000000000000001013f3ffe121ff83fd6c2c1f10175b3ad011d9e57c41e85926b1033277ecad5801000000000000003c68c21b23ad1e95d92b93146ddfae139e2ee2bc2c7a8b437c41e20b8a11d9280100000000000000ea8e718c93e3fcb04bbe362c8ed92207599394e6e06aac3145a3dbd6e068f04c0100000000000000e094c83c28dfb858b2cfda0bf6ac10b079e0c35b875365158201a60800d5ae6101000000000000009a14a73f6d4b1c9896ad4a10169452a5dfff45aebb946ea701a5ed94d23c470e01000000000000009e69adce8d1e72ba462dd7e226f1b34b91b038d63573cbfd2963948673045b7e0100000000000000045cebbcdc5e50d0a344d6e01b0a7d180c7fc14d8d007de35db199b31c026f2301000000000000006c1895ede65311cf2f00b5c57378fcc48a95a614d4aee03ea5fe0abf27d8d5580100000000000000003c355f55fe52ddd78f410db36294273478aa852a75c8eebffd3efa0ab2ca5001000000000000003ef792db76744a93838feb7da8b7f665689e6db4dc8a510578804a49320bfd6c01000000000000008a24331a5b0a512762ead3fdba639afeaf649b9d7ba5a939b502bae8d709640b0100000000000000b0951f5af1811973fa55ff157a38b0995e5700eea61ccff01c33096543b1b053010000000000000042436e76e3ac0e2b82971e4e7a063b51405500ed19c6a0c041ece7f9dbd92860010000000000000086e37259e697af7b4028a1cb533d906758ccbe3397c121010e5a094cbd10231201000000000000000ce0da1b140a5d9983ea7245ddc70a90a004d4aef4c5c056cd5fe3f271099a7501000000000000004017c942a56376173f9ab39d8ea985e21b703c3350280cdfb9e495795bbb995b0100000000000000c4fcf817d2c8c0c0c7adadfadb4ea60b6da2913587f69641ca43325516d864520100000000000000183a6a2a9ea985d9096c322b2ff3db841cbb158e4737965f5a98d3a53d0c551e01000000000000009e5d3be1837102ef421cf29a2c0ddbae40b42ad3e090307443f8d4af4da603750100000000000000eea459214eef62024f3fd784c5bdeb42e844e39bd272a92917d12fa66b62394801000000000000009a8f1dc9de51f2a09dada8a66c489135472c9d514cc9c9d17e8dbcd89a307c3a01000000000000000e4c162c806a25c08db000ed62e044cc8ca078f61352f0cc5c83f70cb514c15e0100000000000000100d6b2fc74287c9d030678b8ef8576c642d8ce76b9b6ce366c7ea5ad1bc4d7501000000000000006cc18e610d5c76a068185aa7f300215e2fa46ff5f1bb58307dc8f0df0b49112901000000000000003c26e68e3c41050e6603b62482ac1e6852f1076795ab0a9ec9d87bb4e76f1e210100000000000000568b52d575a8fec7299159b9d534b3268e50a59d3052fcbea17956d8c3e53f2d010000000000000008fac1a954a0e7a7702a7c90de2b1089ff57865e3181fb6e9ff7a1c34fd0f2650100000000000000b63e767df6a931fa235e41cc7bc47f939fb11dfa4e25698e443023bd150a437b0100000000000000d85c008dba8dcf0f67cae0e58ccd7e09aeda4d16e78ebfc94c0de3364bcb9e4201000000000000004e7f5c7b96db92ac862298a99bd047b0ca3f8356459c7e42bda56a2d80146f0b0100000000000000a6cfb4d5fb89fc4d3cf2ff522b7fd4267ccbe8ab89546bae641d1a583923b05e01000000000000000ed511c927dfe3a5d2ea0d858f16a008db97532b7a311fa82885fbcdd96e51160100000000000000ca1fd3a7d39bdfe8d47a70d6b1c82d19ccf792482181aade0e8d2903ee83a5130100000000000000cea92756fea9002431fe31980ac9fdd87f4139f22bf7436b182a0dd03e762c4401000000000000003c1a2bf3bc6b6c09a130633141343b91e3d84a9ee3572a00f5ce2a1bad23af7201000000000000003e7f4634849980d7f3ac6804336df6758ad393816afafa7c4932371c1d8797260100000000000000225b2615b66b0581bd26b9d2aa025900e83dde43819b4d94050de7c90d5aeb710100000000000000ee80790df6b6c2d1c29a053a364a5bab2536c77ec7c07080d5d6d1755a16e171010000000000000014fbfe3cfef628559b53f5cef7a3439af56553151254d5d9e8d07e85f10bef42010000000000000020a5bb4207395b9f205e128f899c3b37d265ff90531eedbf42170121029c392e01000000000000006e7b420de20353a979ff79c79672b543e70d70081eac397b039dc39fddb81e500100000000000000262510163ecdc49a4c74ec24e9a330d36f6ce31747090a05c4a264fe72ac3b220100000000000000bc45808b65d5f14bed3ffc08df2c647507893528e24e9470fac50962b60a3e5301000000000000006cc53152825109cab7a5e657c955cadfc846a338285027affe175e6e89d5c7560100000000000000e2ea16b3bfb7647e0bb6f5a8c01c0462662485630e4ceb1193ba0e17c52d5f2f010000000000000058af2d747b43f08004665cee97001fbdac2d7f8f3a90ba67d57de144d1be3621010000000000000050e17a1e724d93c69103ab8656f7f38022a83394a1b817100992205e0534927301000000000000003ab369c42e4d12dcc2b478b838aed3c279bc7c0043f6398c315c9f2d3c47055e0100000000000000a204e47fab483977b269636c625a6d568e6066d6a1dfcf06e8b20864e342987f0100000000000000a2fde5e21a95cf7fb0a6d25e2d1909492aebea34a623572b46ca9262d44aac2101000000000000009acd340a31e012f94cc096eb81b69913480341aabb48cb7a2451ca9e5bce3c51010000000000000026e8b6bb0f086aaf10d543e45ecc77511b08c7a5f61ac760b6321bc961cbb86f01000000000000005c17a2edbe07f27251213b7f4db4f4e4ccceb01b5f957a296ea89b83cd6b6d460100000000000000f2441030ec7780e75e43490ffe6a2918beb750128114c2a54f7af73eb015852f010000000000000024b9dcf73084abbacbffd70baf520f1edfe5e08877dc6dec17670be166d2820d01000000000000000c2ca9ea7ad31c8c17d4ad472d7520dc29b38fec6806b95d6f13c5f431a2fd0e010000000000000000b9289b629edbcb7691131095127580893d36bf86391a3497fa0642c58eff2e0100000000000000dabe3557e532052159d0eb6aed80464eff5ea60a7d4d11ea6f3bbd93a406fd7401000000000000005ce86e8ab8446889e7794b716d39f72772671300c4f286247c9b94b3971f3a6e01000000000000004cb6d487b23b0ff1c1dfe94c87adaf45ef8cc0b60b3506bf72284b8504d7ad7f010000000000000008e78bcca3d3e2ca7f2813f465a76cf048f628556415860d98c8997d10c4ab5801000000000000008658f3d380c23e829c50082e5393448d014928bb3c7432b1dcae95566f32fa0b01000000000000008ea53537ec9986bf2df5bcbb81af619d3301489bb12953949cc4d62d44b77a080100000000000000fe20cbd8720f46d2c16756fb7cf5942981e933366885167c3905d3213dbcdf6f01000000000000006c15f28efaac1e24c96bbbbece09d237532c1436ed316744249aa55700d5e4370100000000000000242be63827b1ed32640e6465378afbc28a6c83b54013b0b79cf671c7830b36510100000000000000e0dd1b9898a07d1e916784188bdf246ca60638be485f3d1a8407ae39355d644a0100000000000000642bac715fd971189538c56c5b2d81af2a5b86ca9512aae04490ef9a80c0736001000000000000009aec4750f312da2c844b78f64e6da58d6a682d4b7d3ab8ed0bdf3dec3efef06b0100000000000000765548435b4a773a2aad522351915ee05798f3a5873a52931df7caf8b615ea1c01000000000000001cdd4e72f7ce330b5f458d1988a481a6a518f81f98c6e2ee6e3cd14e247d19520100000000000000586b3b1fa70eb13fcaba8b11f82835084416ba431613f235af469cf144abcd1a0100000000000000503ec36c254ec34d47788344256e9feadf0336386b742992f38b21ed62a0677301000000000000002c1ee1c5edd89c3699d7e81bd67caab2351702c68a313ba1a1b1530025e14400010000000000000018bce81817ec3f23e2d5f45944dbf6c9e72569390fa8b002e560291bba469f7b0100000000000000e2e55d6ad3fe7564b653e1687684978568da1df5c07f89d3ef6fef6bacc3b41c010000000000000024be8c31c6082f993e6091302892a1d5e678ae890daa1eeefb002737799d512a0100000000000000147e76962a08ed7feda65a5559464c39cd32f64a7f5d2879b22d06120e01634b0100000000000000526322af40924e6aa848968ffd4cd1f64b6932a6af827e09d6087ea2a23b8e4401000000000000004e5103b8b919c61b36c3162a9c8d47c9a78a2f1bb849a9eb518bc2338a3e0f51010000000000000020494dfaa45734759c4aa32982246b29ea6e49f22e0ab07e8760372dec30fe7b01000000000000007a2d4c9c30b5ebcbe2ef461947979e4a70a2d94c3988e16670b1b51598af7f190100000000000000d8aacc82937ac337a930403e3dc610bd4ff7d241a0a4140044812e2c0b9ba16c010000000000000006924daf9bf6f8ba2317e0fd39d4cfc568409c67b2f0b584db8ce27e8e0bfa340100000000000000c23b317e2ac29311d399211cdbbbf116012f9577212cf68ead5301176574f467010000000000000060728068ea6f3d0f3622d47ca6ba9e54e49742d83c728c6c1f3c3db925c12d350100000000000000b0a42ec890cd378d66733e45c739319aa6817fb717f9c882f6646116c67c113e010000000000000066e62a41d32cf015685e4316f2c5d22026c358abbd815b590af151fc8f65256a01000000000000003cb5fcccbe5ac7fb6d86626c29fc5960a6ce0476d8cfb51e8338cc2b619c497d0100000000000000a098655547e92c7ac123facdd4b3cae305e330f764903eac5abe9af962c0ca050100000000000000a84e5174d0f7fa67088df644df104c96f66812907348626646e1d5de9ff8976d0100000000000000927bddfdb9c270ed770b442243ba58804de2a383b51198ac17afb48d4fca602e01000000000000005003dcea465f6654fc042d1d2e44e9b3491df630b39769e33df13977f081e175010000000000000062da836ff1c0f853f1e31c2cc30bcde8b0ea955eadfefad24fa6d2b4421447520100000000000000f0ed29463a12cca8b1bf44755b7fc4b271611be8322d125f6d6657acbf9866010100000000000000f672856259fab6a08e26157d63e00a8af36793f579358d2549fff83e845c607e0100000000000000ca6ce2b47137af4e77b2c050dd0fd5009dcd0da8ba2de762c20826d0a939307c01000000000000005861cdec6edbde92a2035ffa83f87b17acf09262ea38cf4729ba5a8071ebd24b0100000000000000fe9a0a40f1de6f902e0dc05b033a066eb3e82dbcd8b3542336e3119facf3332501000000000000000e43c4763bbd1595aa61e839d16f29b82bf65e942750eae7dda38e9546ff201e0100000000000000be223791af645d5d3ca38db90e6b59bf0632bbed9e3a3940a54bf80b112c4e1e01000000000000003458516691992e93fc2e1af1f445bb9f5a20883f2780ef854e77d2c1a94e57070100000000000000da6c5070748ea0655df06b4cd7b3670b182514cb2d1d0551225c3a7d5c8d62290100000000000000e2f27f21c4d6c72e3542c85e6acb1250107af219dcc1ec272f74e378ea5b6f280100000000000000fa11dea7f82cf0f0f478a16571cfff58242a1a766f510240a67dedd35a8ee82b010000000000000024e2216cc3180349a56347d56e164d38959b7e35cffd69eced79fc9b8caaf06e01000000000000005664b72ea779c079691d0e6135b1f2022e8c634c014b5b937b90f60e4e08042d01000000000000001e48cb51cbf5a72eb204d54a4689e849243adbff41d54d5f2dbe5d93a0e1cc350100000000000000ca6b5b26ee50944997b2061b81120b0ae368c5e32bc65cf8efdb47fb85bba3700100000000000000c01e3dcf36740aa070991e472d0d82d7f1904e76a049b957a6caf1564e46a32e01000000000000002e8e24430b51f6d5f8dac6024f87e451813ed4db1b3cb6b3e612ab4bdab8f267010000000000000086b1555953c5f9f44fcb3a2f0dfc8ca8331e02dee7116484884e0ac7ef2230770100000000000000009d14aa7f46f47424f9925f605599767e56f962960aa896008c9683cbc3401e010000000000000040506a20667b254d811877385dfe5663399f75dbe3e5a57f289b344b6fb3ef170100000000000000b02c947b1a2bc4d773ab0e28044e8293a875d7b8870e4d607500fcbfe649cf2d01000000000000002c52d329744a9d2458e129fc50094550facd4aeef04a717cd5c452774128cb2901000000000000008c35da616e607d118115aef27079b5702f9f496ca21f96d682d614928446b8440100000000000000daa83cfbc4d25a2336289f3261e6a4901030ed6b8dd063b824fa787a3f68126b0100000000000000627037405f779821f74ed2b514b22bb866d471052ad295340fdc38835abb520c01000000000000006e97825a19bcf21e0e299dad9a0594fac9d40bbc95c375eac3cd3824b3e7c62301000000000000001ebe00d3e01f16514c89161a99881d53753813cf680ddb3e3c6bc577829064110100000000000000d0c63d9905fefedf8cded1dc7c758793d257c74656fac1c2ea134d2c8306cb6f0100000000000000b0e486d3f6c244145d23032e222f807db5f3c553dba509dac3ab9ff4f37543760100000000000000e0dde9387f1c3fcae0bb2529e0cf3eeaeaa05e244be364a3bc316c9580d6e93f0100000000000000583e56856e4256b5b3cf4a254312d6dd09279a529d238ac9235d603ebcf0bf38010000000000000018ea5e9458304122e280ac00a656c32f68fe24f92fb51da1183949cc5045241c0100000000000000789b7f450bcbeaf7b6353015413c24a5feac5bfb72372b7085d2232146c9311301000000000000000c03563780cfbde0d9184f984939a14daa87754e9e5f74a23da0d015172ea35701000000000000003aebb093fe4e3d0bd38e9b040a6be249a10f680d4afee82111da0cce9596de05010000000000000062f15b56b585aca4194e0646c4f1003f1ec573c9563dd8409ee3b9e06de18a5201000000000000001284399e30f2a03778b05ab4c104570c30d929344d9785cbb412ea09668d7e4f0100000000000000eed0a44cce8ad788c707e7bc6454365652e7733133cf814e5d51b2ff95d553630100000000000000ea538b55ebf258d78fd7988d5a1c843c9f52ec8a519a977a9c835fd872346510010000000000000092b1ef5e4cf3ec9e8b011e3295349b53577943ffab41f1222b2091758b9cc15801000000000000008853f09259ccdb3d1210fceea0d14bcde576201f38f0fb7ae94930d4a65baa040100000000000000ca8079469e665cca6ac1dd47cdb0bdb56bacbd031813f710ea82e6e14a88b94801000000000000000061bd8f3b3ef9e9f391b0462ca53eb93fff4f3d58fff22e9f84061a53aa236d0100000000000000a086680178e3769f1d50fe93858b70fecf29743f7bbc87b11501ed64bb087b37010000000000000004f431a123a6d3061a18f9553340169faddbe7c5040bb490b4a064b1e0fa927401000000000000005a3d41d3610332f2f571541f984fe6fc38da4251f65a3d81276ed2452e4b1a100100000000000000b0f8da2a4b24053cd5999ad51d0240688269f87aa7ccba7714f46a027977f7080100000000000000bcb2b4974bcee94202bfa32a193ab42facfaf3d3e615c7d901e66d1db11ce6720100000000000000c66d20874fb3e00d049eb98daaf2aa549adea852f3adb625f905518c09c2ea1b0100000000000000705a9cb74d5adcac0f4fdd165d627e10d3aa2ff85fcafc33f2c5315454bd61090100000000000000ae3b8fe38c68695e5442fe037c7d32aa8466b10d500a5194a5708084647bf7380100000000000000de73d7b248f71d461a03af4cad901c83d5959f3e0b039dc987798cce072956590100000000000000a0b18475b63b6ac2080302eaeaf6284c78f69079a4246b9f15ad41691310d71401000000000000008621a861e5500d932d88ac38bbc44b3e5f674b0f97ff0691a9afa25a6863a32a0100000000000000c4089e51c06f4653ba21e1d7ef818b038c009df54cf37e0992d6584fa65fa92401000000000000006e94e70c8e78b8589c175e400a77f92544ded52fae33252482c902cdd20c9e0e0100000000000000e642c2865d5e506e56af1962d29ac53960c84d3bb39ea88a5c3664594313ea780100000000000000f2f4985003d63b08f627b236beb8e744d584ef3dbf5c82804b2473258bcf522701000000000000008a5d4c0de40939c7d94f99684174fab5e0a2ab8de50ff648a7a0b7b98749391e01000000000000005e480e3ab14cab90ac2d02aa586ebf827f06a4751c38b87e3e3860c06a16b80a0100000000000000a611a7e5eddc98ae160057f937968c8ac1f79a83aef44f680ef264c4f88c931601000000000000003a38fb488088adb8b7ea111068484073ce8a181cc6b76737080734cda341842d0100000000000000dac0f653da931dce514482b31b67bd3a7678aa3f8a5a173a87355aaacc4977700100000000000000fe3855e100f68bd31587a9565119777daa2b19e411fa16d9f41c86c55a947f7c0100000000000000e82aff6645589ca5801dcc40ef37a7ece665950cfd48cbd26723d7a2625ce67a0100000000000000801eeae66544febd9632341c6ef3aabed279740b6b28eeb1d1303db8ad0f566f0100000000000000f8f230459692d95fad1c21557d279d5d0bad1a3d43744f8312fd51b339f81b03010000000000000040e2c0bda3d579af7aa0c75a37cda7f8e6d580fc57df98c2e46c682b16ddb92a0100000000000000266916c0513f46eddd8b74fce853a00389345bd7f8e97783d83cbcc9d23fb266010000000000000060821cc864972a7f113473f9e0071b64c69584e77cc078cba367aba13efdfc580100000000000000f460f9d60c5456f75825a685b229aa6e887b5f19df3a7bb24073e09180b8b66f010000000000000044a2fd326ef1e0ca462550f91c247c613baa409868fc6592aa39ad478c85317f010000000000000036d0f298717ce466ec596541acbf95e460fb8c1b40d5e50980e047df4191786b01000000000000001c796f4302a0d1f0a32d1fb9162b6627ca7097d7a3a1b30adbf4ef21579ad36d0100000000000000b893761395022e6f6b8fdfa59f894bf51dac1cac6c4f52c6b8338a1dc19689300100000000000000a4f4f89481b6ee8d5ce2f50f9af9c49d1626966683a01f3e73c358576127381401000000000000005ae9062275202e049d0d4b8d7701ab0b3b70799c3f89d79fb25e79d10c33f92c0100000000000000dca27aec167e0fbb3b984e209d6a253fd1d0a9e4c19092bebed233c7452d530d0100000000000000eee9adef21f4cf375f086327411732b51607179bef18acabaf37d4f92f3dc00d0100000000000000a066fc4292f0ac7db890846fbc944d62f13b9edbc0e6c1eb92e8ee916b94f61b0100000000000000641147da6ce96c1cfb49abc2f3445ba4dd8e00e90721e19ee6ba99c1dfb1836b0100000000000000ca4a99507a5f4391876a1a0be7c26716a17e9f65a96679f2c8b2228bdc29661601000000000000003c09c5079e597b9b4abe38fc0f67677609cc936eb0dbfbf03061da4bac95b53901000000000000003cc3e4c20c232b2ab4f442d8bfae67a34730b69f01b0bd2033d20b5b7e670d6f0100000000000000d4db4b7b7ab59840347e5fcea8f1cf0524089fdd64a349fe5b8f5aff6ad99d7e01000000000000006c2b5d66b96f0c473fa442e5626842993528592791e62875260cfa928c96fd410100000000000000bef4f75529d960a44d2878fae1824d0903d5052fa53f75358d03b0cd2f550d60010000000000000092b4b6988ed74ccf189bd2e16868543b853d38e4ca82c63afcd3f3ad656d0e1301000000000000009a75a1a445f93eab93aa0f3f328ddd64d5c25c32788289c42301fdf1ff8aff4c010000000000000042be82ddfb8da6ee77760664bc9c0dfe6a5b7bfa58dbe148b965284514d0715601000000000000000e8d5114e90194b8e11db6414e52c5f9c3c99a886c478263ab17e040e1689f6b01000000000000006864c42f3cbb918799d7626c63725fd96cda1134944ffdf2925dd1c73aaece1b01000000000000008cdd8b5db78d01220b9b747813806f33d6b1d8c19d9fa3fb82098a3f8d53c80f0100000000000000765aa204bc165c6f9595b2cf359a1794f42ee6bd273118a38dd7a45dff33406d010000000000000050478b2d5c6272ba095f2d3316f19aaa06ff22f948d727999bb32feaa873017601000000000000007476496c52a5cc5cb0d1475412135699072772af64302a5a4cbc384ff2b2ad2201000000000000006c6a5941354b9419623fd51e0747f8f467f31b6f4b1e38b465107f02cecbbe3b0100000000000000de09c9e8d3e9c785c9f51c407a17e14fbd6c3c3187dbd85ae23c23e88dee413f0100000000000000d6501868e1cf90df4f8bd798e6940f4121f0e986c899f5539138faf8ff50fe0d0100000000000000e28b8124bfb689408bc656ed979b101069c42adb4329a14f972c39a2a3e9784201000000000000009459d084cafd40dea98075177523fd137a8e6f35ab1068dbb420e616ac176a2701000000000000001262742c54cd31a33891c1650618ea0163c1dee7fc38b716d4ab05f487369d78010000000000000074cfdb0c8499cfe04a51d0cd85ac6ba602436e8cb00b77fb2c1ca01efdee565e0100000000000000e88db1672f41855dc8fb8089b8506989ab40a1f8566f4d564e9f82138cd5b56001000000000000005c223c2fe106334557847ca756505e801f3f798f3537a116f2890577be420d4d010000000000000048839721a668d391498431b5747d7935d55104a2eba9489077abd8777763dd46010000000000000086cb3131c5406660f6adbb0125dfed1efcf8b348126898732fe9e89df5aaa90901000000000000009256c366f4a4070d2905aace2d57295c36d1082a179b17c483f42e944851cb60010000000000000024e8ec732707e61646a06a013d1657f6a35819bada07714f7b57462a03246b070100000000000000323bb7505e17594b4bdbbc7df6a205843f1f7145aa1c40810b2bdae58a95da0f0100000000000000cc179fd48870861bf5cac6ede454991d4f354f986bcf36a3aafcf9041d6b22660100000000000000268a98a0b0182f23a1f51e5adf1e56274d10a977744e8d0fd0b6e10910ce1c48010000000000000040a09307c5938cbd5f5838199f5da3d56052449b745ea13986b5292e290da33201000000000000008eeb878fe73763486242835e2d2b3c65a1a78bbccaee2ce658fed20b793c02350100000000000000344ef75e232ec97983125b4cfd8dfc4ddb568ed357332adf06f209d0183c86790100000000000000ac1fbbd6bffd92d23bc663ffc22f2d056f04534f6fbffe6fdcc2418d88c17800010000000000000054e139e51d28832bb556dac1f9cc6bf8cbb0c729ffc0a2d1d1724c72edd6e62201000000000000001edf51c6e6bc501a13351031b28451af2a98085f614ade4f2b27113aa4473c3b0100000000000000023518b184a5c21ab39941bbe6019b68ab6f2cccf27a4caf0099b8d4cdbb920e0100000000000000e2c23fa5e675899b0de4ca207be07b8d6a436101c5cf4c023db0624d56a46c7b0100000000000000d03a71fb9750afc4e9d30f612b70cbe2e617816dbfef42e5c1ccb946099be154010000000000000082a7bebeda9f9133fbd5c9af9b470e1362eed16a603c28a209df52743262015601000000000000007e3d9d762911807fe95f9a79e45faccb182e612c98cae93e0d419f8af768982a01000000000000005cc4dde005b2344a81d2b52f427c60a1b447fe5fb50f0cdf59e4aba4f281911e0100000000000000e420d5946c2d0d4d99cac1e224b2cea6295490ee3b6a9803cd28cb03b9aff57c0100000000000000b8af11e910e884a06701a7f157684ee2d78dc205bf3d330cbe361586db733346010000000000000036c4bf964f566aa45d6a4581e2b56c64923944dceb6f9ca332a5b9faa374a1790100000000000000205e0711fb2e06eb53f561854a722e0aff2a8e3c82616ecd2c6b78cc56327e580100000000000000daa0ca1a484715341fedc1f74ce6f2d62764eef5a95a5fbf3225db0ba79b076601000000000000007a8c92d925792e50eba4c4d4ac50b8302ceb21967cd44e94a3b0a8f1f6e837710100000000000000a2751b4ac6489a8985a70ed6b3437e0649c909f3d6effce47f0a20ecc33daa2601000000000000004c0395b7d2f32dd6162ca1e2b117e8833b26cb181cfc4b6c997a8b73455f1135010000000000000032e994c3f4e677a6d4df3d65ca980595264dac5140f52b1968748216c92c75550100000000000000c6e3275fb29aa69fdbd35aa9b3c3161875f3946016e575aa2a95c9cc6ad575710100000000000000aef5d56e0a88f892ebb22a48771a10c72eaa3886e4609310cf613c08d8b74c20010000000000000018e2ffa37c2b51670e849d1ba370badc5d5abe3678c59d46e0bffdaa4ad49c28010000000000000072cf291be9f6b9330459b406d50a21ef4d13e68a645f93b80422be6b9d875a080100000000000000c2ffc531b154837795bdd573a00b6b27e177a872f311353de1640060d6c3c8570100000000000000422517e03ab4255a0870a7fc588ab1655061c7322065a1fca053d82be3d1cd660100000000000000eaabe802ed0951368cb8379e36daa67e6571aa3c365c850c124fd9567c4c9106010000000000000002cfcc84716cbd61fe5b3b4011701326a7bb865f73c99c2736fbe41a264d30130100000000000000aea9dc5a53103f390c8584cf892bb854f64ad845b382abb3220c2be0a70e492401000000000000002c2a070ca5392bec7c3f56b2ad808d2a4fc3fd8e857f0ee2d6496aabcc970a5e0100000000000000f4d8f4014d1bd8343aff7f5f1d4366ae6985885032ec8d6cb198a054d085360001000000000000007acfbfe9d17dcc8bb5cc4ce3b62c4149d51ba5ff74293ad82ead4e3d5c278f21010000000000000092d47173f0a1a3d8db3ac255c2dcdb9df605501a7fe33d78d504c4cece2ad12f010000000000000036487c3f8eead755b47b123b845418d12e16b590aa24338a885185809b85a02001000000000000005458c0189a88a6f5807d6aabfe020ee80115394a1b67c33bad109aa647fe3a3a0100000000000000946b15252e2d51056c52ac8024b47513bb9d08e406f86a7b7d4778028c4fbf770100000000000000aa1f85bf11a5565f36a54f17dffbc62fddfea81f4209796c120aa5bcbbfee25a0100000000000000464e26e781ecaf929baa74714f82d03a533bbfc07c95037032a46459f07d5a2901000000000000003867a37432ab13fcd7f9c7bc872c23e0e0e9cc20bc01b6783e3202a396cab370010000000000000092cca5d3fdc514fcff6906eee877af979b006474a4eda8cbffdd861dbbd33b02010000000000000020aab3fd9115b4f4385883164cd41e282f9c9d547a46f9610aea615d99de2656010000000000000098378caf3d7a44614084d31da9ce1d96f2f7aaef3989ce583dddee4d04b8252d0100000000000000980b477be76f9319f3ef5bb6f0de390d75f362890dd6124a0685ab06f4158b14010000000000000098347031396336c66a994bcb21b8be5906714403cfde0dd94615c082cf42ec6d0100000000000000700fb979b0f33bbc8a9a4ed3fb3474ed2b89cfbb7fa7d1c6bacf6d2f1b76025901000000000000007e33ec70a5a0f0f5b9b1a0587deb97d390b5d6bd8315ec4c69ff8599d8821c3501000000000000004af566dc40a9f1b9698d4d28977db99590560e378444a9592c519c3c7e0f00350100000000000000e6d63bfbb03afa14d919f48fe3a3ee4255e8c30f32b6ea6281ce7152ad694c4301000000000000006711b6b6d09f4d60d60dede393cae47c8f23d3e3ddf9f7d5ef5540f7849274b10446524e4b9e32020001110eb0f81e9c4c670b51db5b990c7cf1ab617f4325f0e49ca87a3d3d0276aace80fd010000000000000015e7e82c953f9acf752945382700888dc9461d28737e2a292c83fd881cb6b8eb010000000000000091a5421d7b9317a93e0f04b9014f07b71ea4139b7cef7c73d8363e2fdbda0bef010000000000000077baf3d84563069557df8f4b85203873658a90b9ebf83e5b61900e911c4005c901000000000000001d030b5c75237b1819544aa58c8527f74442c032f6d454f2fe105728a3cf7fc501000000000000005c7830737b8750fe722d0005a35220d5c127b07060f8d0155d13f8795871f66e01000000000000000f4ae91d0c81c7bb1027f8b741c3162c33a14e67d258a02aea3615d41335285e0100000000000000ed3138e38b1454ab1ab9008f293627edcf445d87a3e1e2258565ecc61501494101000000000000003db091de7d340fa29193d647a5086084434764ba257ab1a1d52de901acb444d601000000000000005ae879fd6a7bf08c1d10f61a145b88664b71a636a2fb353da56ef2a43c8741f00100000000000000abe520e51cc414cfa7c66b0ecb74f8307014fe9bf73c88edde443eafbe4f5e94010000000000000066424c98f7f54757a2a27ee15d49a0921d7f3649c461c0f58b133923c3d43c770100000000000000983a8f34e06c4ef2b372d6af2e4dbecdb3eaf3e498e7aa5d0300b000cfcb5a4001000000000000001276bbbe6034c52980b03b3afe1bb1b50abb6fd0b1f1b8636c4d8ac82e42c9370100000000000000c25069b7d51e3077be57bc11db23800b79c972056e5589c54a0b1b0509713a5c010000000000000026be8d259a81badb0b0e53bec4cacb34d1d6853bc99394c02629766200946f840100000000000000c5bbfd947059a580002d769bd6c0b8c4095a11b9451710674753b25a88f3b5f5010000000000000049707ffaed4a6a2e08ce94824b14f940e85b647490d483e20cebaf9e3a1fbfe90100000000000000bab9bcb14cb509e46e3bfff35ebfcbe4411e0d99625a41fd43aa4ea4cf09b5c10100000000000000867dd8888743609b4894b0afbf40ce62a2415b54b4e7206458b3019435ea3c460100000000000000e06501dc67333d2f3c72c87dcf1cdff591f0e201d6367a5fe98b92006bf9a43d0100000000000000bc46e2d3020e96428a72cb7b7ce66ae398678332884156fe8f151eabac7fee36010000000000000060148b39a7a4d383a2a3072f84ac526e608f89210f50fa61f48d38c4bb0370970100000000000000be923527fdb4bcd5aadce8989c9d1e3a1e92afbf71242502f59852b51fcc0d970100000000000000a923d43be83b5ee5fc548cbfbed7a877347d52c02a7729e68da50eac379e801301000000000000006be86c38eebb8337032142d8e7cb9869084aa638da6217ce7675da65338e663b01000000000000002a53a045f66449948ceda879ddb1a3c7eb74d438963905407bc1068d5adad6300100000000000000010934937c0ecb5ca84b298a6be8a05aa2739db425fe03ce195744b9c751bebd01000000000000002c3e4f6da2743839a2d5bb56bd69325aa63b7bf075729180615f16cd13c5735501000000000000007441764ae201450364d2c89533ef302c3c4ec040888265d3bf0ef2b101de12510100000000000000f2a704f6004b9b59c5c5e37bcac8b1a7225c0d1d9c2f80c3811c8ccbfa86d6240100000000000000de918a1920a8559e3263eaee7087f111a7d36e2957e496716a4e57768327da4c010000000000000033bc56e557f9d68a0e80999c3bcbb0afca2eee221c611b88bd474daad0a159950100000000000000cd2f58068929fe22d2f9d9fb56a0a0a0ddd00156f06564f68fb8036ab402a545010000000000000034396d0ef461de6a067837b71e0aae27bd3d5fa63a1ce2b9169fdcf4b71c3fb40100000000000000a7524a19e867aa1174ebf5cf819b028c4082bd6681f6366e25f0028d46bf54f1010000000000000086476968265e08889345ac22c9ce3434e34a005186996273726a6ad0d3be4e170100000000000000003eeedc20be78dae5e250380c5ad897e24d5c1489a09134fd6e39f5c521129c0100000000000000c59cdf27a873894b90191eae14c9e973d8c6ed78add5ebabfe551e5136db292c0100000000000000a74539130fd91001439cde4016398c36dd19afc23e53672971ad47f633086c9101000000000000006c62907831c967bb4db42ae20d648db8fd0146264aad16e508ddf888e3a985350100000000000000cfbb0b2b4d251d7c785e4927ea0152ea6a586e1d01bed7f3e155144863cb666a0100000000000000f2b3f4738ef0e1db1719b15bf04788429a799f5ad08c8b6fe80e1a58561fa77901000000000000002f3c7a9a8f310a2d0425ba6a32270bd26f1cdffd989dbb730cd27ad3960d9a810100000000000000d592f6236034cfdc773acef60e1160c61c19f589c9a488a2bd91d19f411831ad01000000000000002a899deb423dc50ba3bdaac44f9d294d36bf3395ee0c1fec6ed9aadc88712a3f010000000000000046865091b6e11557f254c05b26de7f15eb851e387ea66b38f0ab148fb30aaa500100000000000000f52bf0c5e8dd35828dd1e19f69a3907dddc6bbe4d294cffa75930bb101a1e95c01000000000000004afb4ca2ba8aa9310978c675a5f3dd65600049cd46b6e800c4e56823969202030100000000000000e44893830b8e8cbccc43b2b5683754aa186a00787b865e4112eef42306b02e970100000000000000b608b4c0c2ff093ed88c9b5b24c6437fab8626f21f3d94ffe4da20e94f96ac6301000000000000006660785068475785ff105179854565924173c18214fd57ca8edc07390717289a01000000000000006897ca76e9854f6283a7c1c6d52ae68e7ff131a37c561430990c1cc2e21e4ee201000000000000000934f5f12034f9df3e95a2b4aaa009b81b9597a7b12d11503d38a21745d838df010000000000000039d2d8cf3d9f79fbc7d7fd3440e987c2914c462777105a1ca512e3d74ad54ff401000000000000004a565811312aab87dd8ffbfd01902f9596aff03dcee6659ffcc00296383e01f00100000000000000752686b3bfd01b9588e84cc8b786b1a42ddde41089d07604c47c2073216c4afc0100000000000000fd148f8f3a9bcfcc6b6965a6b5c46b9db1b95017e6cea99e6af0cebcc9fb9c4801000000000000007af05fd9d5a3f01efb73a814c5e0c1eb3864a1a992c292200a3ce2c5b825337701000000000000009395033dd901a0d13e0e6f71a6cb3b3c95acda28d89bb008f5acd7d12394294801000000000000006739149e1d0aff1af497ab1c9d8e78e2ca6d87ab761fa777c14ddd285d62168f01000000000000005ad93373cb94284e29c95d391db0931b46fba5e8c225b5d0ccfd1b1d3f77c55e0100000000000000c75548ac1fe3614242181e2640321950e9363c084f2185cfd0c25324edf5742c0100000000000000ad10229fde173b0120795502716fbab5bc44eea02707798ba1f4b95c1a2bf6b20100000000000000b4f1f98fb7fb551724f107664aa75fb9bc9ea5c94e6e0671843a3bc6a80869970100000000000000c4ea85b552adc87d10d1dd976a6aeec6e07a1f22127a846e8d448b2cc93d10e80100000000000000ede83a8f6e918963a245f096cfd2168b20b889af2a7af08b53d54039a2c323cc010000000000000040365e14834690d417b9f0cbefaedb88bb9fe7ab1d0332ba3a3851e5c30d9a180100000000000000d434cfc66ad9e8fe9f10d01fddb34bfe1323bd3bb19403787235034dcbecf1fc0100000000000000e8856888f6a3c4ae92f76adadc496e622705f517747f6a4219c53e12642981a5010000000000000031517073c751a66d10e0abd2f25f1b203802adb067145cf640590721f804370b0100000000000000cc5dc3ca58b74eebc0487a12fcefd0e75624bb2442fbe70bb87f5da230f305e8010000000000000092f21f9e78b08c7238f842364fac39b3ec2c47b9534fe1ce7003ab4c7594cb8501000000000000002db2358096567b7bdf89f7b55e92dd1137648685efaa69f9d207bb9ec829bec0010000000000000029139bc1e3cea954ca9ea3c4c19aae309ccfe8f6536ad6e6a7c81031906929cc0100000000000000673fa06be490959aea5df6bf2526b7dccacb4647470e12ece01b4f3f3b6675160100000000000000a74665ddba3dfd94ea6f01974c73bf680c222c87441ddacea5e99699933aec3901000000000000005e1c7747ff7e6b67cabb457df0ea82ca827e514033b5ed18c97bde71d00349a60100000000000000c69c025f13a202d8e5fff9b0b209eb185b6ded25593530eae893c83036341aae010000000000000024453bbc105fea95468b3cfa025ed820cf33192515d7c062b543da1d452d491601000000000000000794d2fbb772c0c4511c94a1880446e737c0093db10d731678317ba59763f7b10100000000000000d31934690035350da14454b96ef770913ea8d5fcdecea55b6c4b227dd483f38c0100000000000000640181d26d44b6340c22a4b31d41b7280c9e400741a8c66f4ef602f807814e9f0100000000000000866e80ad56a1f0b962210282480c097731ecaf0587efad21193181095fbf6b110100000000000000e4cf3427bd73002d54d30cfd09550e852c1c2869a31dd2d16b58aa92eef32de1010000000000000032de2810045e47cc724b89c4f4b32eba02248996e0d9161fdd656114dff53edc010000000000000021d7f3244f70f3a531a08bf6ac69c4c6329f1b85a09de02323de8773cceb30920100000000000000f0f3738fa7145e5b33256145f03b1ddcf53c8094a614e696ad68a71be27eb58901000000000000003c0c399e9977eadabcee0f5038b1746d0aeca846c8ec3a2c4489c38a5885eecf0100000000000000595386555e7b42a15d110a2498a1df6dd5b5b40e8d5114932d5276cf9f5ff6f401000000000000001bca3939a0da1e8fea6f673c96bf55a27ac431fef7b2e14376cd5c3e54a18567010000000000000040798d0dd692cfdb34b34bd0fb1b0aa81ba7babf4b4d0401dffe09097993882901000000000000002a7a66db2b4afd256fc2b5d5af596afc71a4fc0197d0e3e6d2b9294276bb4384010000000000000058ff18d09bad34a8291486faf9a9dbcfeb53bac98b84f532aeb0d93557df34ba0100000000000000bdca3c4373efb230f6588a2c52cd9725dcac94646293c68399722d6afd477ffd01000000000000005b590f195f17a2132bd5321a93ec9237022d8dd4b8932c5d3a584832832f8d2d0100000000000000e099e41ca65898f4d983843c0fe9bf50514522f8308cfeaea99a80a51613fc8d010000000000000024047267683441b0063ece5db265f934f472a3d5b38dfa8d80f7a25af45eb04d0100000000000000ab215a352a0297936881d90af86cb6b6c4d4d37d3ceeca39af64ff5385a2a2d601000000000000008f05fb27d7c61378f4096b53b7f14a35c70d1fc31aa1c14d911b5ab2d51e3f180100000000000000ea56a16bebbb85b6a5468c876c5b7040b00a18d7fcdf80d0663ae3ad305895380100000000000000ea6e33767f007f5e63cccf0daefa1b528c2ae000809fc2f42847e34ae81371c601000000000000008255f8f5a8eb12100e2e841c19ff4813ddb1a0ff19b8371c1fba1f9476ffe3270100000000000000f7b55c5f30444ad1ba0b9bb8161d8ea68ee658b0386eeb1053562512914347bf0100000000000000353c39bc2d6ffc87b4587ab34ca983b28c23076d5f9e22a33809be375d86739f0100000000000000d7549361600f9a8bc5c14823a4da0a87511f1f18d056d6a863446a948c8ac24f0100000000000000cb3acd4ff2910a6ce2edb1edf4091d205ec6d063ba8674e771d9ba50c4da3db90100000000000000e7211e57464135f6335e0065344882e90781867d8e8d1cd970bc103c46323bde01000000000000006e0cb7806a2eb0beb624c136d223f3c21f0a58faf5f15e3eda1dc74759294247010000000000000085b266c94c2a2d311d23ea660d14cd92ccd944a05058d1235967b8672f3ece9e0100000000000000037b9590bc74af17af8c223909e75b9fd1a078961708d479ad54a3bcb3ef59c201000000000000005087dd41a18af353a6f2c48d07a7491a1bf81c03a8e5a4bc9d81f0135c4699e10100000000000000af572a74dfbf8a41cd025961ba0b3544cae23c7e1c3ef4f27f06706e14ac115f0100000000000000a3e0d754d858a9ee8ec6a09741806d416f94816ef2a7ec1648d377707b64d83b0100000000000000ff48c3c4e4e22fe843e5016a8ea63c2d4bf7063261c0e9c994bfa5b487e6d50e0100000000000000b86cdfa102c7fc459ad459c1254bfdb8ee70799fb3dae474e78c5c58ba23901b010000000000000002c79e9966db0947a30acd331e753aa883e758ca3645dbdb19301c4eba95a6d40100000000000000877b90e86113d66b411f20994e93b9ee925403a8548a9d5e9ec45459b26d90fd0100000000000000d756b2052ece46006ae16a55532a41e020ca449e8d0794ade13481c09cc4aeda01000000000000006aafd971e86cc44a92aa26ccc667e6db300a2f8af4463b8d96d29deba9078a940100000000000000a29854844a6c9c635765d3026af7cc03c4c21a5e134ce94d62db7b466d05979201000000000000001a3bc06be979915760ec17e67d9c396f1af3f0fb47b5486c28dd0a558df9c2ef0100000000000000414d6e5e7521f8dbb8d24a08f8d73c641fd8d734829d808d93b0112403315d7e01000000000000003af509aa20017d20f865999ff56aa2a4c38f643014cb0f8b1038359d210f1106010000000000000093f6ae3518f728de063f15ca2dfaaae880f4298fbef865e2fa6f58b542fc2f2a010000000000000017ff09ed6ffb793ce313c000c8c684da1df508f3cb7f73471f89f8314ac9fd6f01000000000000008d62524b3d56c18c6e75dfef038edfcf7ccd08eaf4aa95b9262de28e58fece10010000000000000050b41f429d1d7febdf5c9124ac0e4163e85b0bcf28f30f2cb0466d31614050100100000000000000c53a156be1338611cf525ef963de05974f334a599e201380e80cacb9adbd0cad010000000000000038f4100d5495dfc9f0f1a5efc786446db941934631128fcc040fafb679e8138a0100000000000000218f73f89206b94b66229d663de9327f7c3a326d74cc609e1146aa18eb64c19e010000000000000029110f404b7a84bacd092b679a9acdb2976ac6915dba82066a55d464dd966fc80100000000000000c0a1faac2c3fce942577d07dd7113b58e146149f627f25c86a526ce64d9d002c01000000000000001f0bf1d32ffa3d521aa29e2c847864415ab738cbbd92faedba6044610502a0640100000000000000e8a08af0c91de30804a2b5adad71db6ec62846b8c523633e81ad14bdb07afa67010000000000000006592b586d345a0977cf689c9eb66b0d509e5d71c125c69032cafa5d86fd97d40100000000000000b8f49076d4297647ab09b559bd3de49dd7f1a89f364e1bdf715663baf77f0187010000000000000092f4bb5443e977b9b5c4ca9a40323c134edd90c3b6e9f52d6731f1c83e8cc6b901000000000000007c9ec961183de341c84ef209a388b1037be1183053858b97106873088eebe7c001000000000000002ed2b61c847f4d9d1de03f06887b94023470a9a26f55e71e18284bfcb8c881ba01000000000000006f48967c6e23a57d5a49050022d6934a9d8a8a0f2f3c349368c52791763911e00100000000000000838b4fc81fbce9e8c4f1733c8e42bd676f1c6e9fecf2d6d6e9d9e6d56fc3b6820100000000000000d7e39aa06109955ed5f814180d37907d6a630c9f1021449a1619fbd607dd7f7c0100000000000000448f1982ef4fe90e4e5605050d4f5489a9cf11f214afa13a907b62526c82855801000000000000007e09f2e050c4f868e62eb530f3e27bbe96c2aae037f979af89af5162b5f3a0560100000000000000f866cccb71c2fc1bfe37ef0749a6fb46092265bdf25a7e256299aaa617a0e82a0100000000000000756edbabc1c36767b191589ae4ce322bc3a5b851a67141cf9603ae5c1d41c01b01000000000000009121777694a833eab58ff374b5ac7679bc8d5443d74377e7f465c630c334d9c30100000000000000ffa272297109097a57894c93bc0be9983084cfe4c92934f4aad97ba080e94d680100000000000000bcbdc30eda2988dbc3fcf6aca2f92fe640864df1eb3fe89c1dfe7fc35aa417b501000000000000005b80c731c6bcc59b1748d88740eb0895c8d9a3bdf959894f615702676644d35c010000000000000077c2ba675411dc1b7f853a7fbb7d42bd16aca604f0d5160012941ad8b21bac200100000000000000279710868292e84993cae503bb00cce105cc761c61ed3f9428ffa78857c0d8a1010000000000000037ec7d8a26ef4b82476dbfefeb66c4ebabe6ad5d14d70d2277201e9e15f631130100000000000000dc5807c8459f4d66844532b036894fe6604540a850481a71404a6b2e0114df240100000000000000923aee9f8162634aa916363c1d12986e060c661ec02e7065b8646861078a8e4c01000000000000006ed9b3aea78843793d02b0c86baf16fa9a3690461f4f4e36dc54436832a2484d01000000000000000a8002450dca58abfab495af10fb5c63539c281321413956a50447565da2601f010000000000000054c3eac557155cd833ea69ba4a6ba752cd5168b351aae397d234b340a5ad03950100000000000000b0e0061804de004dff8b16421345738af191e4e5fd82ade83509cc0ad97ff5df010000000000000076da76100d843e94ae878d288b5692567d2185e93b817929f030cb696e3d95fa01000000000000008715251756dae1826b5f2768b59ebabd277d90cfa14d8b2915388709a383fcc10100000000000000a914a330fd3b7959d0e5779ef4f62f1e63720377e6bfbbdc46a7c2b29b7af7d6010000000000000043446bf453ba593fd178be36b2e995e69528bab0bdc44ab589c820f22c2f05390100000000000000cde31382e67335289f8f62f08e3ce2acc022abda0ebef80fdf7351028354d7ed0100000000000000613aad308f149d696255e29bf2ee326089fd3ac59212cb11f4b2473bc19801cc0100000000000000186da0f65b865b067ef9c0617558bc5f4cb76f1bf9115093d4fc688c799b26b20100000000000000fefb4a6f7cf7b3d2eb1f1482aef42869047a88c63306f5a623517068915c4aaa010000000000000047276a7e970f22028e7d0107b7e4d20165b2c7ffc536f38d55ab170e4bc4d6e70100000000000000fb478b1477649e273f268ee9903cbbcc3ec97397c3ef203a6698ec0254b2c53e0100000000000000edc02cfba111bacf59330e1065c4199ee5282a3ca003b7bc821b6307ba84c865010000000000000074d61698b1059634844c0bee9cf4d058dc0e21f30160fb627e90f462093c061801000000000000006fa3b572a833ce46fcdf8f5f4df7004a32960b83885f73ac417f38508033fac501000000000000003bfa274a31d381881971d92b6aec6b2b6d7475c8caecf66d9d0d3daab8223ff701000000000000007f1300c79e886668fc4711923b259a6dcbc42837d8a4d5d92f0c7912b2a903db0100000000000000f491df1216c383c87803eebb9ecd959882f187032226885385ef0ca6082c416b0100000000000000dd433203012445ca6e04d2b9eae7ae6d83b70a3371ea1883b5225072c52520b60100000000000000684ad0bf43ba729e3547bcab78afa53488ed288aabcc3b4d1cc4525e008fc0800100000000000000ac894a56d59cfc3f34ccd338efd2ca9566dc3b959f37234aa390801ccccbfa62010000000000000056d2787fa13875b10d1144dc0bb5bf596bc01cf68ef539ab28053581a6a9617d0100000000000000ace4d13893db4511557374af1882f8b8d859105e7ef8dd0121d4d5da9dd1587e010000000000000023270c415db05fd6d5c184e152b3d75a8d53fa9a43b025b58925caf748c9a23e010000000000000090ef50a1c931d0f1e40f7812d390a5639028298c4376cc323552e2714a779d9901000000000000003803107577b82e8f20cd20c228eb814193524ee92c9f260954f7f46fd54c874d01000000000000000edae107f4fb04b110afb1ecf53d4243a66f15c4c69876d6b751f5548f926b8f0100000000000000568280011edba62374ce5a2fdad01c1bff1b09f9c1961e42d7848505f1c0b4340100000000000000e6c11e5c80f99cba5744104d4756629a7974fc9db8c5941518dcdc963ae3fe7d01000000000000009f8d3b64d16c8f30defa53135d8a1e7a66af672256006f202dec3434a2d33f940100000000000000a5006ad37afc08fc34e3d343ea364fb7ca6396c51ff4dbbc27f6a7469ee93799010000000000000044b56ca3f1cae161add8284da598c670ceef03052f3d369608e2bb3d05f4f9ee0100000000000000633ff03d0221001ee36f12a30092f977b6926cb50b3ca2b36204beddb8fe2e620100000000000000f00723b7fccbd08677e37963ac30411f408b3dbb5e8c43179ec3b6df36a44aeb01000000000000005c435ae0e7c0649d19979b3d8daab7e5e355f895e9ba5f60b4bac3b14196425301000000000000006a3a54d4ce2550488de3867a7c7b453d2816bf6441cc237c60433c26aaa2e81d0100000000000000bfada9adeec1268accfa432231a6bb49699c038960fc5e75b2fef94759d6f0c5010000000000000023d2fd575dda24f66dfb33da885b714848d8bdedbee3702e7af08e4ed97dc0ba0100000000000000e6f40610dfa6a569e46eeef7c7cd768e412f13d4e4a6407f9a1cf049a352b68e0100000000000000b995396e80067ec48dac0597a14549f56c4ff0bafaee702a3254aa262c34ee8b0100000000000000a27550e2a03f99f1308f5abee215451d283b357d5576acd283e44487e7e6aed70100000000000000530acbf1b93c99d7a6970be2179809938dc8df68b1fac4109fa850b8ce9e26b30100000000000000e6d59d33c825486645c8dc4bb792b628853ffc1db37971055ae122a2513f22bf0100000000000000f3fd7dda76df1d4049f4a9c89b9f0123bd9a98167fb4e8e2d58af47b24c5db99010000000000000007256e10a14dea5216ac1594ca108a5da57c224f12aaff57ac139c95984adeb90100000000000000f2b26e68b59da4ddabac0caa2d57eb2a012f49cef9ca7f0dedb96fbb61b5d4d901000000000000005ad83188b1047fbb4284a4c93d3ae2b4583cd486ac02440b6f641a8adec340af0100000000000000ed9184c7e71882631b1f1114b2d86452231851671f7114290fe27c439f3f68b101000000000000003f4667ae05eec7353bf6623aae8faf05047db1aed2807d1d336c04c21d79b9c3010000000000000089d38a1e70eef588f846dc9a0bf6d665a9f1fc690addf61499929aa97dd387980100000000000000602b42500465f343be1b1856982605e2fdef92a45b3b019abce6c33ce3541c770100000000000000a5c1c12800b85f715bddc4185a30df728500854c339b7dde2dcd83384e88f7aa0100000000000000d1d5ea16074e9141839bb433b2559732caf4d3e93da77f9b99114be98611325e01000000000000000c585052f87854d6ae4308d3c24807d3ba34ff7c9bd348244a7606ff913fb2c401000000000000000ae977aa3ae83660eebed977a4b726dc0b13de9c769e35c24027a17eecfe275601000000000000000da51f531108ba182be4fce0f270913688935fa4dbd9ef99a01d12dd4ec4351d01000000000000001ccc1c725ee216e6a4f6aabc56c75a4afd1949709754c2d1c691a5168915a4d401000000000000004b26c3d7e0a1ef31fcc480d5d2d4b66562c1136b74fcea6e7cacb4d52326ce3801000000000000006a194c14f9909bccbc63892708f4ba815f2d50cf0a90fb61260c1ed358d59e1b01000000000000007b328e7c8fe7e945aed5e2d4a65a932fe59a8627aa62828699d5aa381ddf4d8c0100000000000000806676371ed3e5b4772a390f8c60c73c84ef84e7ce1d9596768883d2185af3b10100000000000000c277f631f7215a61fee7dc682c6962b40226369a9fa68d04fd1b5d0ffca668a401000000000000007751db19199eea92e7110b4e4c7bfa86eaf745c1c54741df23442231bb81cb0e01000000000000006b6a19f15c174611066de8720f768b6081a5d6886f152ce34fc77e21cdb59a730100000000000000b353b30799978fbcf275ab0af188be27355b9cedc94a7ec7bc76c0c32e07ac0401000000000000009129dc0162c2596f9c5f90df9f11d63bf9ddb6d714f32fd7d045e1a8a69a169e010000000000000083659e24b97e31dde145ec72ec6ab66cd32f7960d373cf622848e8938323637c010000000000000006703c13cfe10678220ca458ec175755134199137b6b73909c376cbe8330e2180100000000000000c0c8b899b2fb3daf9a7f6b71e887141a92da1f473a3d63790d28a25c30f9799c01000000000000009ce56193ec04e02678964990b508bed9f483cd1775b30261994e01b033237c290100000000000000c45b5870f33be25386a110b8178ddba4c073bbe1ae9ceb277515bb3837916a820100000000000000f40fbd75ea3999b45d0c8d1a82b505b33ec0d8e3a51a80810717044e1fbc035e01000000000000008079949be9bf709a0f8f80f2ede6970faa000172a89d7800284acc90c50180e3010000000000000017446a8e6b6adcf8c0a226df3e094a098baa475286a4c43228e373ac7ddef3480100000000000000787c542fbda4c4fddc72cf3506e0d6479e193a828a1bc2b2384861cb74a4010c0100000000000000a57f4ef5d8b2faa65d10005966600a5642308665a49e7dd3739a7d847577836e01000000000000008315fddc0cac4b20982abcea40ee93b5b524f1dae736a9fe29383bd0bdaecba701000000000000004c22782ee1bd4165a7a48f578ceabdf94af60bda2e1a5f5354edd480b7896fbf01000000000000008ab765bda6e24809df0d447f7657c96ea5562bc7cfbc848dc14c44b0c7f5d73c0100000000000000cc22b8bd4cc0e2969f46403911cedf858a729f5bb2375e3f07129d77eee9fe2301000000000000004bf80cb235d2be39045e6d83d95d073f9f22c806e39e81a4890923be707eefb00100000000000000078093bcae72b8d8b88207ea133882b7bb1144906934d4bee8623612f73e02da0100000000000000a678f8c4844e42b51f8cf7a5eedb46741e1c27df766e40ebeab9847283ef581201000000000000009f79e5cf8ae4b08e71b51481a361ac7e5cee5a64c42252f997d9aa102e3c37330100000000000000ed947414e09dacd79484396c98b2f4f64c6d4d99347cb4909d46c5e47f33f6a501000000000000000dcd8f189b05b1f230e78bb22b4f76236a731ea2e64857bfa802f1bb511e58ca0100000000000000b8479cde71991c00078532a159000e52525926e98520f98dd19f8e1bee5a211a010000000000000022debd07671469757bb62c902564bcc850f10cda1da8e78eb7caf79c9882fab501000000000000006c99bbbc4ffb0804ad8df786eb90a9d49535465f3715d649c5d278f1fa8def290100000000000000fc55323dff57839026d76b95c143d658009f3950cc1eec73cf7f4ad1aa885fee010000000000000014e441b3fb54537c345711bed623b2675669ffb90cda8ffc72c8845c84bfe329010000000000000057988cd8d0ef29afce154a6e0fb28137e971ed9cab06784b99c50e97e443be0b01000000000000009774f75ddda9f906fbf2d6f4c8479010a47d42e0362c62e0b41e0c20af7759ca010000000000000020fb5eaba86692e24c64e3c2f7feeec80a76ccef9a122e20c59ed0c459b4a7a30100000000000000d1388eec35655b3b51f0154bebef738858199df35486142fda47b2dd3b4c1cb3010000000000000066ba1bb1facc7bd81d2070da0ae5af06156a2f8176045ddfcb1110f6ff6e0630010000000000000098dec4d87976105e47917142f1ea793a9ddf937de57051b13a38b019097a32540100000000000000a66ed3182a2075e89ce53ab00189e041a7984debca51385efe6c3447e2f3c11b010000000000000010180e4023674a455295547a417d515d167c101fcd03110f5607e076dd6f90eb010000000000000049255d41d1f619c20b94c516d89791cb41475eebf5684abda7c8d4b07d7745b60100000000000000172775dc50b0848d9ec6ca9cddb064aad83934f67ec0afb690c00afc6caf06d5010000000000000095aa7514098a23487f9271a31200cccbaf2551fb7d840027aff0860910a903ca010000000000000071f29ee43bfc23ff7bc782ab91f17f061066374c37b6ff4972fe7bd6465fa2ba0100000000000000f644f6d08146a9f49982d98ea74e52fed08d21ff38a240cd3ab30e2e06a0e12601000000000000004b8bc8228499fa756a2d072e275c8546264f17f88a3df07ea786bcaa62c1034501000000000000008de6d0a0345e7556c0f97a7a7c30f26d6ce0ded318afd2f808e7fcbfbd9082e501000000000000008147eed34b4f35e6f68f10e5d452736be46b6f94ad328e37a7734250e143536f0100000000000000681d0cbdc65fb57da7ce62ab6c3184c1f32bb4f828895a36a249b91b7e720bc101000000000000003f4064978bf74c7b93d3890f97f4062c39837a83e0475465f2381b0ec34df3c20100000000000000870fa90ae37be1bf46fecb747a8732d5aafbc153854fc1d1f2a3f3488d186a900100000000000000e2ba59fd54c53fef7c9455a1e5d84eed99ad9dc3530995b7b661c3e25d2180e40100000000000000b592058072cb92ee319fca6997241375be2e27709e3ef93ae4ed65d689a54f8f0100000000000000ecc045a77071c96df85bc317d7a9425fd1baee1b03ef0692c760200416eb6c5901000000000000007432ac942b240cf07fea27da6c08386396b8e81d01bdf9b0dd7365ae45a9b08a01000000000000005e846cd84576a21b032fa321e73708a375691ff5fba6b09d937e1f623879face01000000000000004564e0bfbeff8c865ef5d48db991f6503303be12ecb56382128451a1d5bb5dfa010000000000000088c21cc835d5bcd7d7e864d6cdbdafad58b92090b56cbeeab40469879bd1fcfb0100000000000000e6781bfc3740ff35bdf624b8d8e63bfef370a88dd8090d73c9133d6b3be462c50100000000000000b77b0957235fc907b80136a3276f461401a837a3352a50be9a04300e8d95aaac0100000000000000ca347a9db580cbb32128af8a26fb5724a21ec7547cd004d2312332206584335001000000000000009f5ab655f25e2fc1202af6fa6fd8aaa23d90d58270344dbc4029d164e7114a4e010000000000000026f54492e909b90a4d83679717793dd90da6b690ea67f0bb50cbac8b9845de71010000000000000019f674198627ee586c93f538f4013ea3624d9ee78176a65ddb9b81bb2240769f01000000000000008cf07ddf0f6499ca7d8ba13a3d4802c144b57f1959198881cb1e79195c3430710100000000000000c4c6cf5e15dbcaf804ebb370b3f34d6c88317c199870d5793f84ff14198f6f640100000000000000a8926e96f23a2bed399246d8ddb5cfedfa56c03a0df12ccfec08034c5240638701000000000000001737a839e095aafab0ff8412e0bde45070b16c38bab99a8f14581625c87184e70100000000000000e172cef11edd7b5841173be146448fa7e8dc782ec8390393cc598e0ca727d6350100000000000000ca01f68ec2720c5d4aca9b9120ef2b3158b6435596a11adff45f4348aae1f7650100000000000000b0dcabff3c67fc93fcee2c59aa2c5543217fbb821bac73f0c48153914e49d0490100000000000000d83cb81f05b3983e56e7b46b6f0e7eabbd7fe261c24ff2547c210b7cc0c859fd01000000000000005a38c6253bf9873de6f9aa08f54d47bb15f78320d1b309cb0bb567ddc90341de0100000000000000f109dcb4456390725f2ad7a8f6829194a075b5d7fb7c715aca6bbebbbc25332d0100000000000000f73fed9c9c7f15783d2270a48c6c9d18d266ed5b8b11a93f151d7cd488fa388c01000000000000005fd8b483f7c9e962ef658f54ccc310d8a83e0760fca816b0052ad938e4120439010000000000000071bbcf98df63123620e1f99d2a6ab0a30e9a928d788698ba7d30b13756dc79fc0100000000000000f145c30be39bfa9e9d0bb9f006f0444130144b06d5573601771a568d9a61b82901000000000000001f0d090aae06e1216ff251f990289742fa042537db56176029296236873b2e2d0100000000000000a319f5954c8fb46a9a9d3f30b72b8b2413f75c8385393421fc55b8a83fc16e170100000000000000e7df92ee768270097ba6aa61a548b7a4460ac6d35c2601de292b7245c6310a750100000000000000f07dc64178894ad2415e43a17f8c097259c254e0460b142eee801a205ca30c6c01000000000000002cd4047d533ee477213ceeed6a1e0d92ffdb576b46fb19f88641116afe993ef70100000000000000fccce14ef0857d7dfb8fa6022f611493fe70f94db2ee8a706f9a46235f747b330100000000000000e71935c38e10af9d9986dc348d6b8601a533e339471c23b79f7e57bd941ae8e4010000000000000073a707c6b2e738a20f78d19b85ac4154200bd9211486ac6d35d22d1e8cd9c5e10100000000000000a21976f58656ae4e2e40809dabf9888a7f9503564ed0854d9d73c2690ebd68f201000000000000008cef064802f8f0b91f05c7955e0d7b8e55857a5a203e3a72bcc37b1f15c0582c010000000000000080df9f2c5fde79ace23ba8e7801af0d8eea9a17742f72e98e77da3efbc0b4b400100000000000000d94b0051ab8a10af3d0e6f3dc2cb2f77823f603a57284cb009cd6813db99a6850100000000000000afe9e12c915535d5c4bfa64f66417258be3a7c9496200060b1dc3eacc388c22f0100000000000000a72875685b414acae14e60d9e80a6995019b8795e628311c4315f22a8ab7d09701000000000000007c1f8d7af19a648d28a5431938926e579050bd0fb10f881623d03c0abddb0c150100000000000000b8a23d456c551a833b3e97a2e17a3e51714a9a66da92dac4dd0efe7b5e3910a40100000000000000dc73d3e546b98bee82a3cf1e72e3060978bc68d47e75436f042b9b17b3e2de0c0100000000000000d6b41e68e72639dccd875be9fdc7b5cf4dc3b5b5c455e35a20752b8b196897c50100000000000000a8dde508bcc0842474451ed9139f53995833988e9af34a38f8a8a85191a4f3770100000000000000e609103ca434abd43c1b17cb1253eb54e65364eb0a1ac7749433dbcb07f3d69f01000000000000004e2be3b94e292f4a0d2428fddc8829fe71fb0e31a83ab1560bf12bb249a3e5d00100000000000000685e13a5a3067b84cc9f2c11b88d46c300a17fcdefd2a817cdf67ff26211f0440100000000000000b21a8d93dd7edda31963073abed9656c394f328701300333c11a41fd3225d6730100000000000000b3d131d4391544a459854b4cc2fafc54184d49dfd1f48332edf97aa4b6d3e6e60100000000000000f7a35644f7d9a2d429d3e88d4b99442d1e1bde717bb9e12599cefc5b18a8569601000000000000006e613b022cea1b970987ba57e63b8f0e4d5d005258370cece607661418b228c10100000000000000da41b74281cf2c965b58ac92e9b1e4310063156fecdd6bb652efb6b8affe5b530100000000000000788aa019130c26218bef62d3717d66d28ded0a312c86e42c51162ef4feb9286501000000000000004e8341dbbd838cd2ffb287707b5257f7753e9ffc77bb157c235703fef6c885e4010000000000000062f015934a6bcd3fec141264a7e8497131cd2ffa2cf6522450d058c798c9eccb0100000000000000a6200c8d1eba0c02b69a4cf77db7cf425cef22b73d1ff2c421150017a0032eb4010000000000000084179f03924df0244fcc5bd33a8149bff5d52e6ac3f78d2538884f93c418fac201000000000000009d38599d913dcf75c0cf0ed05ea8391b154d850c176172d05e19fa5d458f87b601000000000000000db6bed3ee47ebec237e9c068fe22e0d06be4dd5d0144a54ce7cb4c19118e69f0100000000000000823e9ce884c8acf839d5aa6fa8a0f61bbf085595c545c34bb71767c2bb2e0b5701000000000000005db809d5f7bc2511156ef12602a10a56e20c942f2042d0a2e33d77f4b395f6dd0100000000000000785692c450d49390007b93bab9e629d4785cbed20583769ea1d470c0870ba6220100000000000000f1116e487683df2966279f0615bd917ed7a89f3b8d8ef1650e95f75f6b599531010000000000000041fc0d03aec399311e556614afee67f1e87ca997b072e61cfc60b22962be08d30100000000000000326a7dbdda2921799f6636335772a1fb9a4897c22b16721b0700e1bb9bce7213010000000000000012823b8619129358f5357eaf50699c085ea0b7dec5c9b69a9a2dcc51ff6e1c4e0100000000000000ec0d9939257652084199ba0e9a366a95562d38f391d34b4ae4fb3cb224d558db0100000000000000f2bf0d6cb2709317caaf754a6cd52231a3476c22bde651d7e658c9c5fb809c3b0100000000000000872f1f668e60d55dbec0088fb9afa0d1e4dd1383feba48cead619f2e4db2f3a50100000000000000d032999ac80ad859c086354dea8cf45b1ffb569225d5724cb08cd7fba990f5210100000000000000c511ee786680f69643a8219504d27d4b1db66d53a67403cf4591e2aab3b09982010000000000000004f7e112e3d649c05b132b4d7ab51823c057c3f2a1aa215c86357351f8c16ebb0100000000000000ac25cbd2ac6262f60f1a32bbd658b3f5974bf0b6eaf4cbd2f9e7e90c3d8cce7f01000000000000007fd4c2d54fa93d068a31b40f4cbe57a5b84a106c7a250d5b0584e954f3ee296b0100000000000000e7f2ffb7603629abda0fb5e5221c8e395f9894251dcf9ad235874b4a1e7d1e250100000000000000a4d694c5cfb5c99c22f210a8fc8005665f52541558c70e973d472cf0088d70b301000000000000007bf341e1c4d0deec119316a8849c8d4e912a8b5dadb38575db14e937f45b7e2701000000000000005833f9c4c9cfea2583f1e554f5ad92a24463d37cc10135c96bc170ee98e1b7f20100000000000000a34294f0d150a96de7fa3b11b3e9ff2de58f654df41c7041b8f24c17ee2b19d601000000000000001fa1812252a73b0c59a032d84a912a6969a1a66c7042eeb86e1db64baf3b022c0100000000000000582c42a4813d5a1e71d1cfab67a81315a6703800ce0c8d4a63dc67a47b75e83f01000000000000005b44ffa74798f014a7da2ddfca2433cce5a5835b4a1a2a658ffd810b86659cde01000000000000007cb9c4415a2544ab08649dac164edb5b274204f337dc44e5827af6ef3d8a50a6010000000000000068136432ab835f57315c65aecea2cad88c51e6f8eb69f0bfebb34ebdec7a914001000000000000008f7076f896d6ebd5eb0c82fc806824cd8212d8c48da67b1d3eeb0bb5952c105801000000000000002ab31b2ae41579b19899dc1a5cf88cbc064a13b9d4e5b8609504bb910073663801000000000000002309e8fa28e5378178652ff984995ddbcb301b73e416106ec69eceb3b448d7360100000000000000f72f70b173831d1af86a50284ef3677476f774b6fddb5eb6a36a2cf70aeebab90100000000000000000e441df11d2886c8134a93a68dbf26e82d5eb862e0b652dac36452d1de7ef50100000000000000779d20d9c5a7e1517e0496607398853356cf2ba54ec7e8606e98ac2733c97f4c010000000000000047b05c0a92b2977fc8af13303be9356102d5e9202ee6bffbc6883f1ddd92e5dc0100000000000000c8d5cfbca144fc4aac29dde930f9b372c61d3d1efbc1496dcd563e20c45a280d0100000000000000fab40a799be24088cf7303228fc0d4a4bdd8dcb499b620993382e4e015d27892010000000000000080121accd6dfbcee5f3ad7c3c0a200b9882fdadaac7263be9b17f40ceac11143010000000000000015db890d6ab6f3b3d7359364c6a93c19d122dbe593007619d72da3fac72542fe01000000000000007addda210278e1be717491eb99e0b77e1a4de88883eb7b0483d39d03fa16da190100000000000000cdbf776751fb784daa57ff05b33d50c9dbe34df9af08a71d228020956e7e1910010000000000000040960707fe94477ad1f764aff50e467b9fafab66cf713566268df50a5c5ac6c301000000000000009d5996b4acd2540ed963922a54f58ed8e6f4ba7e54fd99cf98e0d5e616cff9cd01000000000000005f3fa3e3eaf17017ee2175d257e4346d3da33d70bc93c45ad30bdf64cfbf5ca401000000000000009222bbf46c21869db9bbd87f1d6fad66f2e6c74c146ea2f8b33779b175f9d0ec0100000000000000f1ac1a9ea982933fd5189dcfefb7384457afdc2580ae7007c83537ed780b8a0f01000000000000006fdfba86a5dfb0a34a14a78181f8f5d035e670aaa804a9e05fca15a1d870f847010000000000000055863d94ef9c234b891606ae1c06840314ec1b6612d4f5b334447f53d2587d19010000000000000032940116b194fa6d290105599fd178fa7a28c293fb27640abd23c53b53b268010100000000000000e8dcf49e49e9883c217bad75b46ebf05186f46c65b50f89fcf4f991c3032389f01000000000000000fae5e80cfed04be08a8566ef8ca22a40bd429f23f44954a3df9c1c1603c339e01000000000000004f3f4e2f62f2246baa2cf1e8928ea617b2c4e2e2172b80bd633a1d336d3590a0010000000000000035d9427196d84b4a0d22f8a7a2de2164d6d1b771c28507427f48b338e1f6272e01000000000000005c5cc6edd22658d7f53fda685417f4a013a2af9ac83772bc7ae8c996f0aac3ef0100000000000000ba731644e4df752af2b1fcd86dee7f5925f69c450f1f829f7d745741e782fac9010000000000000072b06fef1fd8952b4f929b2f550cd3234d313fe4e1d6393aa01c092f34b11a8401000000000000003a0c5425d01569b13d8fe13fda74859ac4fb6d72f607fd461f25c4ebf7e98cd4010000000000000067ab19f97f412fb15d3be408a13d1bb550d1ed45d6b2946dcee71a7685b23b2c01000000000000008671c481d218aada6a99bb9afb0cdb443f7f38cc1d31ce2f1e1bd829ef65d62f0100000000000000bc253d7765dc282f082b0c969a58facc0313c9fccbe37b60aea969fbc8418c0e0100000000000000f28e2513bd27ad86d4c97b55ef586bdda7224c98ddbe9900ec2f0025c83fec7201000000000000005773734ceb9c15f844ccdb9c3c87b54b1a1c92da3252a84936d7f01f1a02265201000000000000002c3eac29c084cc7f9b3385186b09fcaf3199412d01a254e4e4cc092f41e6d894010000000000000040427b9c6d55df4a0d8db58eb80895eb08c2c682ae9a4c75100fa7393f0974e80100000000000000e64b3e77804b1e4cae39c8956c3c976f3886ee1ccb1e89a62fa129a9ffae0c3a01000000000000003b63170f06f8a97130a9de1ad18978b3de09aba1a4ec6516db2b5881f97d74f40100000000000000304ae76636739d9981396037b5cf68e06d203245dfd2e2621fcbcc6064bc37780100000000000000a7b6c6d65df9aee65c7a9324847a4217cfcdc24b57725d7c1d50a7054827498601000000000000005187dbf2c8f595ac73922fb5253513e0475e80c25b161ddfcd56895f4f7a48450100000000000000d481cef7cc85810cefc53981ba07c28dcfb89d10573681f9c421bbb9486549da01000000000000003b62a9d2df9885f9af0c15057e3d03969dca0690b91abe26ec70f400ee5f21eb01000000000000008dd7e1228f9cbcb02e6cc13744dc5b219d5353c74080f3332073d72bcaa100a601000000000000007a5847b9ba6b1f8ac40403172e4483a725c67ffc15bfcf81c9651eeaaeceeec40100000000000000f5e938b8bb48dd063df032c120fb5b4361836fac0ddd162c8ff33121a9b368f90100000000000000e2c55a6d7c2693ab9f4bfd9fe94856fee68ee8a62c7a8716f237a1cabdd76949010000000000000021c67cf8f405be15a1204690dc99b75867efd0e4965a76d0c5055e2cf93636f701000000000000005ec705b77e999b8edae1caed1ceaeeefcad946dd79d73ecedaa064c24d4292ee0100000000000000966b56f0ae20c78c0b332728c842dd5a14cffaf181d538c30e20cc4cc41c4e950100000000000000289782b73be66bce8c5e5d3e777e80a50b596f6c24181f7055a88d9026d5ca220100000000000000225280ca91339a751f61a5b8546aa0ee10e75201445f267b0e2c2566796d72a701000000000000003496dff90280cd98397e43ad0f42bb94a31bd613f0df1b33f708a99081d70155010000000000000007f00c1582571966d460bfb41c2b5f2e5144e01f0856c15e6bd3b43beab7dbc00100000000000000a2d7344bd60e18fabf7c81a3f24e5ca3fe7a281bd5ce42b11c2d7857b96d6e970100000000000000529bd04a39e4c1d786b97323f715a41fd53b47c1dfddf1e115642d145dcfdcc701000000000000008e24aef8ad77dc29a92fa0618c83ebe16aa3d1c8b8ac1e499059908aa384a8c60100000000000000b3a3ad0b9fc2bacde1f590f1e1079f81204d7983a5d0eac972c480b3f203b9fe01000000000000007ec79d621c3ef96464329b27439e633f43798b8da05729310ccfe5fd602aca2401000000000000007676907747b768a0836fd434db6ea0000912123f2f0564a31dbe289cb513da1e010000000000000004cbadf3d7638796f99321fc02006649e6faf7143d1c74bdca8c63ccb688bb340100000000000000a0a4282220624bca214def529c5ac40324bd4619f78eca11fb71570be253bcbb010000000000000044608cad21550591119de80028ce45dcf43d4e644b3a5f4522bbc9c8f1d689db01000000000000008d8022cb348ed8ed40a06fa45ca18ef5e91433464ff8a19c29b0d9f42b8b448101000000000000005ec09f6ef4baf679ef603f4a6aa61d40f176953e6b03fc7c42bb85958b51350d01000000000000004400c7b8264677b90a0bedd06b785db94aab6f49c044200146b2e6fa7897c00101000000000000000193d6defde31aca31b3d252d96aa0126e445af9a3be5694f7ad8a69a433025b01000000000000007c7f87fdca644e1e6ba1a3fc5cf27776ee3bae1c0280754a0b0d2bbe0e395e600100000000000000d047c90d446415dd16e5cc315b4c87c306f1db47ff592a5ac35ca4bea7dc9d470100000000000000f366cbd2f95beffe3daa55dd52f8cfc98f9dafdf66dd9a9bb50bfa5c45f7a6b00100000000000000a4ab152e8217389a7762161e2b3aa9c17297533bc6b63157f2b3038778d6183c0100000000000000ed0fb06e61b4c5c4a0cd1b113c4343bae101d5cf8326c5c42e6a841fb18006f20100000000000000fde36324cd8924512d22629873b6d60fb0868ec341f3d188d218c294e3f8a44c010000000000000090d05b5a62309d67e40a31cdb7fef61194b23809e105b481eb62096f6afc146f0100000000000000e9f58bce6f223b4d3ecab593c0c65d703bcde82c0651fdf9fe0a44be594d658b0100000000000000afae4682dc8021cfd88cd9a6ad21e94d59a341fba9ab9a576ee26c37450a0c210100000000000000bc565a620c9fa13a975f6b8b37a25c6eb1749776ed5b1c3308a935a83b4719f801000000000000002f759c1e5068bd51cae5928b7b35ec904c8e3648ece7531503ad08e69af8977e010000000000000007059c40884d31b801dd5733495a3c9a0102728ccfc4db5fa8eb9bcfbdf24c8901000000000000008b436b8f4ae43ffb64ae83136b91a22cd32029f06c21d70beb12437c1bd751010100000000000000bb29100974b8ca522740fffabbffa21d2b9ff3a02187e2cb21a08aab2ac14b2e0100000000000000a0204a4ec85cb752262952ee60e410ffd17f08cf1232d96e940ab4b33d0db1120100000000000000ebefd3256199b8001a82fb9456eba66a055843a29d9e9d449feff5a158c8c7cf010000000000000087a8cf45061d3b0092eeda15be8c40d0c04fd24cc57552c5f42e35b19300480f01000000000000003e286fd02bf1cfb10bdb52397245dc6446b48f9dae0a0919bd1e4e793d49bda00100000000000000c6c7d5c50023bc108ec8af8ec260715d19bc72448527ef4bbe7f11e3d877df540100000000000000f78f4e589d61fb39a0ea4fdc3f0026dba41644f1d3069be03f767a131e22de780100000000000000943a813369d3d3d464312c58e93c4bd5bc3d50c9694954c126e0269bf2a0a8db0100000000000000eb9b963cce29304ead152860667bc14ca1f331df9c71dd19d20d6685fe80e4850100000000000000283122aa6a6aeeb235b7a2385f75f015b9af4ece967695e419f63accd67037370100000000000000eb7e19dcf651d628d972b91b10ee8a95c3aad5fd0364febec3995df77292e03e0100000000000000440f7fd2202aff96fec10e0dccebd183995e4619136fe2b07d3b6796b67e36230100000000000000a2d6f27b74b8fbaaf666d67ddfc3c3617cbb7d9cc77a358329a2a5735646ad9201000000000000005eeabc5e404cfd4edef8cc92a75815b86778a129c50c13923aecb177a13a993a010000000000000038aa6a77dc828d66462074eec66882b37efb0691c879d1cd1364f7626f1ff1f00100000000000000f41127de00c7e87f09c2f4e5225f95eb79fe604350b8e5cd83a0bbc81d20f1a001000000000000001eb1c5685210bd42edee23514ed4e3184ef67341fb95fe901cca909368ad380f0100000000000000f7b08b19ea3d90fcf908bf88656b45a61686d2a9f746465c3035fb992017361a01000000000000005f608ad2370386cfe09163c4781a57b1106e1a4019a651204a1fcd300bc9376e0100000000000000985f05f92ec9034bb1936bcbdab54ed51b6d98ea0213862ec8678fcdf5265df70100000000000000b814f1a2c043f377d6221e57a551d0333d54c8f0b983a03611fb92c42c31bd540100000000000000a886cee748b106684a56592b311174ed1d30bd23676ac4e5486ae24fa2b06e9601000000000000002eb06d8f7cb3ffc56b708bbc553c6dc0799814c252442e5878be2830ab516d170100000000000000f28fadfe62859d32d868b2dcb7804d1617959877ba735a1c020362c4faaf8e1801000000000000004a8b3f021bd9f94473beb64359c0434f40b7a47f8e146e87067d01bdba51efd50100000000000000af65bf47b48d0fc3e920b3d99db62c93698759c5aa467a612cd1fd30e2a91bb60100000000000000dc314564963da474d71a68e0b8e78a1104bd42aa141a39b72139207e7a148feb0100000000000000d88de450d4adb9af19f1fb2bbcb7183fd94c148cbdd493838c26bbd166aeb546010000000000000014cf21c5d206353d0a3ec887c2a90dffd0f267e9ac78ef45fa99b153116ab0b501000000000000009f1e709208ea44927151589eeb60d681bf40411c58345dfcc53945f319c2cd440100000000000000b115837b3eae48d82466dc106412a70b6f893c7799849fb6c136bd5d831cdeea01000000000000001d0866f96bad61ad548632a2a38c62fbaf449fcf601edd37f66c8eae052f82630100000000000000ad055482f247e14bb474ccb7d99d8010c7b3c8d2b26a64a213f5621b8818e6a201000000000000008cdaf8531fbe2fb83946c24f4ef8c28bebae93b1cbcb02daf6a27ad17433bd010100000000000000d4152846e2064ec57aaa20576c0944f49caa910e9cbd3760285a13d1629291e101000000000000009d06e18d1e5f6cb00840c4d85bd8ae1ed5482f580bfd50b0726d725882cf96a3010000000000000035c2f0a6843fb1add0a7dcafc4d0c37da14a4a9f869de803ae7419fb09cad0e80100000000000000a2876c0d1376c2f71e52d61b21cb7753562dd57f5d5a3385da6b13ffbb1ae0af0100000000000000859cee8097a433d9d36faa96a3896d81dfc1efcdcd931123b1c5cf0f60d94a2f01000000000000005b29985b2aa1c7979f5a4b48f9749e68881665efb7a1e2213cd29a00fbba43370100000000000000be0a17bbe3dba128d996f6b057cf161fe47a899201f85ea14585406ff7f63a5e01000000000000001332b62bfb86742317bb10f17fa6b4225fcb31cb4f3429dc709c8120ac841ae801000000000000008f2e7b543a7d3a2231cf7d2dd51606452431eae3413c9cde087b2f32baa345080100000000000000931a85a2f74b6bd670fd7e4407388ab76fdd72991e45fa0f426c5f697540e2c10100000000000000be6c659a264fa4283e80deef8725369d2191fc0c44b0ee199caba1c6223b7db201000000000000003d1b36adbb3389ca1027b3b9d5e69a0edcb2a190bb7efd00720874a273e523d80100000000000000789181c18300a85f40aa7b8c2a1638ba99d71fb4b5c75b26a199111032403cdb0100000000000000c80b0c7d6df417f1fba2e532590cdd2a98a7bc0e418ccd5f326dfb0b89c3546401000000000000001025a031771bd94566c55bfc787f8184e93117898b2dabffc7ff91e10493996e0100000000000000dc9defb6659a54599c2ab0e88579c709a3d0d2bd9fb656f6f04e284c156336d40100000000000000b2d7b70cf3c888c7d97296dd813fe50083b95cb81d95260c94a2d629edb4e7a80100000000000000d1ba3ddacf3715fd62a2eae9c70273301caee7e6ca2606a9eb7ae102aaf76e960100000000000000a02de632725f5bc0acffc7234ef7ab4f5f157294b23fc57a1a4f00523319df750100000000000000759a91c755ceb0ddacfe6924c886039bc8d63f3ddd6ffdef5e267f18b53e2d6c0100000000000000af6562b87c42af34d78437887afea5c487ae1b99f78082e86b2ee68969b2815201000000000000001721b85adc12f3f21cac0d4e26ce580af00bc0482126538ee92905aedae6ce5f0100000000000000d0ee6f6c9eaf4d7d07bbc73f5174b9e1be17bb7acffd7e4e163b9bef36f89c98010000000000000049bc3aa9209291467d377d4b0ffd9de0fce6ef58ee5bdeaf03ce85d01e97b06301000000000000009bff271aa3c950df6d9f059376becaf5f2f98cbbdf610904406b78c4b9dd04780100000000000000157c3bf282cca2bb108688fdef72a422bca68c209e7da205adba9c44909c82c40100000000000000aabfed6d4287d95c52e66e7f6856260766b6200dda9e5d299d3aa66d8c4a36eb0100000000000000490bcf479a778226dfc7b823230ddc52873a69d077d52974250b778d382efbf101000000000000007b6600bd3f549724000813621a808b1d6224a871fe0e1dd460c3fe9ca946f0300100000000000000764cc4661982f89f1005fba4734c6b87aacc5e4cc26aa83ff8e1df88e75e113c0100000000000000c336cabf3e717846c2e15019defac700bef6647532651dd138859ec1620c454f010000000000000079438c21483e3383e22984f877bf50567f1a6504f255f1813050a05c8f7f3e7e010000000000000060e5146c0ca238b6f39d96c6012c1999189f871d7b31dfa09a3c4507d872704c010000000000000099484d2f6cc8d5fec2a2104be6df7350de77db980a292a2bc381b8ffec93adb501000000000000001941aae1e06e5554ec50f69f3ce29b1cfb102cf365499ae848a16e5a4ffef67e010000000000000001d89efbb6ca92ecb550e95d8274bce5d72e6bd428de9bcbc44cf60002c14bc80100000000000000673e90633b273b9f6766a2e21f1c665151d3646cf3a2cd3a4b5b036eac26adac010000000000000014205545ee3a0a8577ac313e8b1430cd79daf877ad9aafc59f43f6718bbb648e01000000000000007c9df999a1c4a9c1b5477489dc0c223a32a7f21eacffb6e937e7e1d7d1eee7040100000000000000961ce00c5b6f9edcb760e2a841debfc3883c624b9153fd2d12d7180107765c820100000000000000c34ca0d13cf4c73f15fadb90850ea18ec1bf591bee39b3834cceda6a500baec601000000000000000e8c4ff141978cf11b36895292001f6287f64b115722f19e823932093d31a90e0100000000000000bf2c5678423f2197fadcf2e7e1167e713aaa651f6d07edcd842d4c137328d2970100000000000000534826e9e480f6bc4f6625f450bc052be80fbac272a195241ae5c70c58de610f0100000000000000b2c0e57cfd006bc4a31e9d1ced78da3590d43ebcbd9179d6ae4ca50c2b84b4d6010000000000000043891eafd4434c9addebbc20ea4191b8828fb70562c01541bd33066f474a19f901000000000000009a0900e8790faf85d4a4b85270d9814ae4f4fe365ade06d11da2c0e0493711ca0100000000000000c73f6321d6adde5cfb1c92dacce07eab8472fb0b03a56cb4e6acec7e61077be90100000000000000f00649eb04b08b6a037b4bad50e74187945ca5a5eed9ad1ab15b7d4d1591656e01000000000000002f603e30047b969d85fc1ba978a3b64aa2401f39eb231392e4d0856d3f06317b010000000000000035448d960db09c5e93af6fd22ee46c4f5d6acb91f244fc183f66a053c9c47e5201000000000000003e5dc9790b2f45b92d44ea6e9a5f09369ef98706c36d7e4cee68be7b20c61410010000000000000008836ddb7c53a60022f25256719c70c16a42924c14db61787ac87e5d39037037010000000000000008e3d6d26c79f4711f4881590a4fe13cef4ee3dbdffe9358324e8d3139923a1601000000000000002ddae20d67a1fbf0c93c491d43f7f76e9328d0e7be04ed743521f357aead0cc60100000000000000863948b755f8a20c20cd94643a1c24adaa748e3d14d17e1ad9e5045ee89d5b9e0100000000000000ed848689945dd829a0e36d67f038e0a16286e531c90214d036dbab13d1c841fd01000000000000001964933057fa15f03b807dd35816b81d06be5407eeba37e2dbeea0d2758cb7bf01000000000000001d6576424cd258495f7df2adfb261a00b9652f0edcd8b26a6fd4de9f80a7fe0a01000000000000002fddf0ac69664b427a82afc4314acde474cb17f9f03ea675a2382db413892df701000000000000001629a134ad8e3c2b2d107d170ff8783a58af27f4387432a294ac7f16ef5c8b81010000000000000033a4f301590b0a75ea41a24f65ae64eedf9e9934677d5151d6282974623270c80100000000000000cc3b077b2c20246f3b5a658ca418502818cc833c7e07c8c0677caea580dd45960100000000000000db7dd8256c43c726b27dc6fc8feaa6618d1097ccf964350297b51f26dfa7898b0100000000000000935092b3e5066a82cf818a62536d88d842ee67102c1e1a6db0a57dc56f5b56410100000000000000129ee2b55cc5c9229bce5e08db0a98f44a4d88672a2a57e1399372c064beb59f010000000000000012c18ea3dd497b8169270241cbba8d023cf6eecf0de2cc17d69a7154414847a40100000000000000195fa27ddb6fe640961f688314eadcaac343a04bc2066dbb8abc65d981d3284d0100000000000000e6fdd70d2a5afd9d969ea9187de95fd9c401b8005bc5bda61abb67547722be560100000000000000b4f10eeca619090b27021b5fd40dca879e2b203d0131665822397a7d1c6e66e80100000000000000f950653edaa72fedb7b3f9eb0fd99785d80232c192ed0e0bf6789f107eacf6e3010000000000000021b1e00b9f893c9d6c0897811284b2b312f5bb85f566df2aacd736f62e66259e0100000000000000a0396d88aa19bd81ea72d0193fd22fb6d0478b9bd3756b3dd2a7977f0dbb978a01000000000000001a621619f82c1498d100c25a49f9e5a41ae1d549abafc8ad919082a5e39227730100000000000000d641f4aafecb1355799aae6ebe19caf7aa5a06bf399cef021c664f75736d87b40100000000000000df84d54a2a9b1ee1235cf8c9d2bcda4d9f58755edde9cc9f472efc7ca7ebc369010000000000000084892b5403e90b9886d9b28c59cca863b0c0309ade29f9c8ac97d464655f2b3101000000000000009b6190702bc935b6fed4edc04164692397c62f863f1c961afc5cb4ab38064575010000000000000011786c3c28485db63801ba00096fd41385239a7d98abd3c3a559149775bf364e01000000000000001431e7c61d0cfead281702e149818c670308d85099bfd6526a764806cdc82b52010000000000000026c264a070c0799212702ea08d9c30efff41d199302b7da89e976aed33892928010000000000000086c1ec938634b944f9bccb3326cb1f09621577230812e2771cfd95bbbc4d2a27010000000000000040fd1ef66fb78b57869f701781b06209662152e15196b39e96f64c14f1ea64040100000000000000e4932f43c1be02f87c1774b1fc5eb9b320a0d34c48e145d6e472714fcfcb0d500100000000000000f8b81dee8c43cdf35bdab296e4416fd6104dddee489a48b286dff12131bb196c0100000000000000429b0b9e4a5696e259c0a8857f1f8be9583f005b7bfea1373429c53a919dce040100000000000000d0eea5e4c798a264c2eb4d88a52f4ef92cd8ea328892f6f2ed4696759cbb4415010000000000000053e767bb54032aca54a343182ae3269578bce30ac9205a8d1d18dee301fd9d7701000000000000008e166084f313ef5fe71dc8aa0a082cff2a135983b2b9dc07bd14e451da3729a50100000000000000849cff700b439b6ab6fa13e3036bc1ea023e214a8b7cccef8cbddb08203df4c301000000000000003e89ba8a647dfef3ef8e54bb106a40db4b56d49818f673c5f71391b2ce822f6901000000000000009f6f97bbce670bd39e73f6c7302206b073c18f4b11c8ed28898019264e17b29b01000000000000002b9fbde3152ea1aa8ee4d2d98fd957dc1c7f37c1e4d63c15a5121ad72345cab501000000000000008960d274a7a417653acf4fa9a95eb7fe65a608dd5ebee96ef899ef44029a4be201000000000000002fb55309a83acc38a41da27befd22353f8088e123b5fc1507cfe3f646dcc188c01000000000000006847086e88970fb55aff7a180fc3d473a9b9e3f60e90b9c9759f154ac77e04440100000000000000c09ee215905d9b3796467164ba1c1d8d7a8df228f003d528b2c5b6da6940c7320100000000000000e759b907acca962ba6ef4d342117a10e66fbd9dcc479f175b3c03ba261e1f47b0100000000000000718ca158e1424e8e30f606dcaf65aecb409fdab5246a9c8ff91323ed4d4667fc0100000000000000cc45569e5ec31f0d258c9a323de87bafd9100d0c3deb05a203ad74ddf2690d90010000000000000078d57fbaaeec744e3409809d1225faa90275f603714ab8e2e732833fb33514c701000000000000001d6fe061b34f45f49939b2ecc1b36fabab2762f201a7fa8531357929e54b970b0100000000000000c84a5643fe7a66dfa5133d9fac89d58dfd37b4d3856cdce379bbb1a4f3cee5a301000000000000007e3605cfde64dc872269b9195570638283e3c7b45a10f8df97b92d6ddea82feb0100000000000000d651b76819aab8bf8abea29d57c713e1559ddde5d848f3864a7b52b9a0cadc73010000000000000005c401b4934e3f81c181da546eeac19c0bea5aa3e4f3c0322ab4c05513a789ba0100000000000000d961ca15183c5dac08e844378fb1ee30f0f33390af4e4807569dd1a9bf3043f801000000000000002b1dc16ba4794f65e3b9a80acc4c621cf3771f137ae66a10777c24e64dd93915010000000000000062bd11e1153322d94bc7037a4a6b2d40c0ee4338dca0b73c286d14d8336680be0100000000000000290d266fbdec67d8116a9d36531083a066455d97f3b7e7b98eb6e0162b6a7c2401000000000000008400bea0359f3e8a1285aeb1268e524985e7046ad330d486efed20753ca51f0701000000000000007de5eca494ed60b231920dc3ac991265ec72e4922e75c50b8a1dd8737e87a9c301000000000000007d09c7a996e2b21bf33cedc6394bcf47fc94ae83e5f027f121839de3903b39b301000000000000003412008d64e339eb4a3a763d7ba24af096616dde67dabbddc741f0b07833a87c010000000000000005d9fa10ff7ad73bc75f401cf2ba4c69271d0a6cfaf5ea98f2b81f1817c3e5d40100000000000000b14fcef1f5bba5a60792c8ea3d3ebde60942b79c1d34eca9f789da14cea27c270100000000000000dc04544011294afc730c2308eec8777836ea4d719e01488d010ff8a61fc09d5001000000000000008425aaa4a3a0eb35643fa678a90851f3b6daec24a5e8a63df2119ae53082d6370100000000000000cd9b00dadbfdb24477e9a9bac83a783861e262651ec28c3d027b8c399b2017bd0100000000000000dfe9183c4be429a5e5f880deda40f413efdfda4363275c6e675e519128ff67f801000000000000002ef37b093714af7a69405fd9f09038ccbb4544aa373d8bed3864428511c993cb01000000000000002058a2d5eebdc9be44941acbb9e2973384bb96c4974b08dc4d8b6b617d74666d010000000000000008575524a0cfb5d74dc0b9a8e16ae0d8a51a076732cdf87c248cb7b9f55633fc01000000000000003e3e7abf910f06e75aa5e8c3e3f04935ea0648fc64a241a85044e461c24afefb01000000000000006907563e0bf7299f501d1e13f128fa1b71714695e522abff5d47eb11bb61fce201000000000000000a293f665105de4dc0f73a4c05e23e85e99495cd626604c001848fb6ad28c23c01000000000000001b00bc4259bd38dba94ce98f0c7b8564645a929232ac09b0988e21549c070ad20100000000000000cb023376af50df3b6d8ad5b61ba232c815a91105e3f73e945678f248226a8c1e01000000000000003a2679ccd6a38a8a7ebf052844042c54f98d0984b755d0407f173874588a5f75010000000000000021a2c7244635d9a632b872b7f88bd407efed5932de44dc6bd8a88b6178ba7f4b0100000000000000f7b7f589570014fe1c10610a3687d292ec317f5c52b3b1ccb36f96ca3fc2052b01000000000000005e0181fdb2e9ee3cdb8f078445c10c734f873ffaa0738f19275df98d5be481d10100000000000000ef6adb58d1d7330ee4559ab7e850be6631ba5eb1c979845faa47d3957834088201000000000000006cec43a2e692f493c2a395ee052b0544f33ffa09ced6abb1cd2210f332db525f0100000000000000c2408e30d9a4867b4f45eedc81c5550db900086af0fb6501d6786e9082c93643010000000000000081a1b892765b2eded1751ca78ec80c1b1288e4e42c918bd7043e9a81ad9085bb0100000000000000a12af7f6211bfb76f1056d2b2399ee06b7fc78db2ddf6fe01be5a3ef0245104001000000000000008f7f55c6fcf428a34ab2c01f74401ef0306b8528f9c6fe4ecb44539599cf170c01000000000000006f557f690884ae725fa5824fec0752fad9c4e9f66dce337a458e0ec5bf80251e01000000000000001bf6b823669ed25fd30f1ddb1cd0fd215d231a66a0239c62fc55bb56d59696db010000000000000011186b5717e21360a51e6ba054de53ca7078693fb9e1c30cbbb166023d7b1e850100000000000000380f0c705a821343bcb98cb060bd7faf9b1d21b84eb7d9d29b48b9bde01f99730100000000000000359b54946f5dc8f4c6fcc54cd9b68bbd4b67e00d5f2687d7e1b0136a60caf72801000000000000004a392160f9dc5c4535272489c3c0b4faa22f239df65add0a2d9055a97e87861c0100000000000000a3288e5b2c4106b5715603889d69c032a3acdafb49ea688bf5a93a2cf34cfb56010000000000000006918fbeea920656f3fb0f71f22fd896a8e9d6549d3e60bd30413a457c4f9a6801000000000000008950079737cb03e1e5d1099cd7456d58e32aa7db09b899e85632e6af3ab1a59a01000000000000006167605de8d0aab848d2f36d779cae1ac6b893406ef3ac42e5b002870fd1466a010000000000000015fe3df6ecabb57bb90cb9b10b79569f1cdb2f7dbd6bc3e95cec8d9c62dd5efb0100000000000000cb092febdd11ff3b9077cf19177f9c8494898bd36f4ce900d21ecd2d73c9330e010000000000000024d36064edf815632b7d07e10886ad1dcabe78e923d1f14f7c73b21a0f3a95b101000000000000004a901eb6da8345dff7e59a4d0b84cb36218835ca2997de099870519026b5328601000000000000006c390d6645c1cdb92be1bc732520fe9c9eca06d614aaf11cfbabe41e13305ddc01000000000000003e713e430285f05f5f5b5aa810113969cc88dfad7e6a7198c4bca55c322e9554010000000000000016ab5563ff24c62a172247d0022fb6ec0fcfcc013996368c1ead68b29effc86801000000000000003125043521dfb44956b713b9156af865421035e32c5ae9caacbd079641e4dbfa010000000000000052341b743672553861b786e2504de82f5ef4063ff071cbba99b11da55e5b71800100000000000000cdef6778461a36475a54b64d3c68035a18dde6c13ad5c68b71a45dbccffc00f9010000000000000057aaface7bb681c831496495d5b6c08504381343fcbf8ec0f0a542c7b363480f01000000000000000936403a13c100197abac1628608f39d301b9837e241d8558eea96d1895ff1a501000000000000001f834777e70a38b7d68db63f5b3b876b6d059cd2871a2e060534f4c49686a3d0010000000000000024a4ac8a0a892b9ab59d1a4d9cce0b48c8587b452f95cd79e8f172daf65f9f5d010000000000000088fba241518b05e47dd6d063d6922c91354fd619c18f5f62efdb3b749eee6fe601000000000000003a1e7190ab0c48a10028ac7c9cdb0025126fe68dc6a06088ed6a03121d30573501000000000000006649ab2351acf8f89a09507e67a9ad9b23abe0b150cf61a0223d971040ae468e0100000000000000613344adc4bed6c29a8bffa0b797324e90ef7e297af8ffc6a7524c313d11d40501000000000000006a9b5f24c97c928f2694257b79807dc26ffe7a30d5c26b79db319ce339419aa201000000000000003e5e47776b2f943726df1a547403d4fa114d5c04ab3557e758bfcd4774020a8c0100000000000000dd9539b6b8cd6d77844d27953c7d61fa6b1640be55598cd7d2968fabd16a169c010000000000000085ece5a57667781a19be5002e07ec239462b0c6ff59e10d4e478017f109a55530100000000000000805047bfb02343aea902afd4de4fa008c445b3049a44b3cb0d29aabc0d8059a20100000000000000ea5884e0167645f54813fcc7747363594019151c4f52fcc3dea93dbbc9cd6fc50100000000000000a0c2fe8b44725caea506a24c4c453c4ef82de882acf0d1393285aee2767d51f2010000000000000030bdcfce24e42e80af515891400731958366af9fd180a4d578d1c15b9de174970100000000000000ad0d348cf35faa941d9d822df666bbd438ab220c1995e322adf106d3c5a1a4060100000000000000a9c90bcc71d596ac5329a84807fefa1e91cba5130aa4c33546dd47520418adef01000000000000006ac54b804895868888c585b58df3781011635c6f3cc2281780c225f781da10b40100000000000000b448549ae4c0f9b3ac8a7f2a04a646d867dccbda656b80ec62c84768a0f3390e0100000000000000ca77e7a167aa17939810ff2e6e5e4c66b997b214b8ad41ccbba8be7c202481e3010000000000000048e33f17267b389ac80e7b0b8d43aa720688ed4d523f502fc4734e400e53eab40100000000000000e646af54d43cfce08e0d2ab3a349b099ca3a0d0d249df91905c196ae43018fcf0100000000000000dd3967c11604d888e0bb692dd7962404ad17ad3bd586493afc05c0c434c52d7601000000000000009bddcfe82d3d0feff63ad7d8cd06f33197f56b2a4db91cc15248758ae2df1e8e010000000000000081fdda5d7cbbbc11b18bdffbd739755ddc3be332ad2b6b917c9f9f8b7f5cadb90100000000000000d04747076d8c7140d383e50d6d0eb20628e0ede74562189a934fe8fccb7a88d00100000000000000d3e23f291216fb4e48d17ba0c7ac0acdf1867e0a20196fb27edfdc8b6e03046201000000000000009681dfefe4599c287026535a56f591620d3e42159639eaeb8112c2eca760e41b0100000000000000b00eb77a5716be0feaa6fcd4c2ca1ee2f67d1f49284b6a6871746707b7269d1d01000000000000004856dab87d66f24db52d94e06d8d543d635f4aa6ad939ff5233939493b3387350100000000000000c6484f92fa1cc822f8157f1927fe251fb7faecb2ab83910237820da27cf3fbe80100000000000000361c5b336ba1098582674e32dbe2f0abe02a0c09ab40ae5e28d66f828b718ae70100000000000000d1d58d3f4495cdb501cfc2243ea73b8e4de1aafb36fa0cc486c07bb820c3f4a30100000000000000511eae6e782c850632e0a6b138e5bb4235e83e9cef23a76929815790bf7a06440100000000000000bb83f16032279139614340761befaf857f2ea2a198b1561790b5de31664ece510100000000000000e48e7ba1545973cf8bb9ab3ac21e850ff0b50e22d5cdf9cfd3b6f38721d4ca670100000000000000c13ec46e6e4c05622b2103c215c7814e48afbb789362765c709b9afa366e39aa010000000000000028a6ad0258633548f3111512f2bec89d9af385a28002a0dacaa95e40d681d4de0100000000000000da5b6360b587c48b3cd3ea07b46003c5db14a8742651170b9525e138d54332d0010000000000000065a52bfb05ff5b8bdcd3baf401901497443439c0807abb056c2e242b114d13be0100000000000000eae0b7d0e15435fb7b4f2de75aae3ec3badf4aa4c908a85af073776805f0676801000000000000002970c966cfa1d786631f4b50c4ca8bce2d61c3719f0cbe663f2c12d2aee4980001000000000000007dea6da787395c12aafcdf058a45ada678f6f369573b89ab7b834b327931676f0100000000000000cb29686d490f5cdffecd907fd6b189317ebaa1cf01b916e4eb86e340ee3da01c01000000000000004dc35a94f5f4454ff25518b4f6de1c86581a997634c240b5672b2b49104e55c40100000000000000bb08b1df805d94ce82d4db0e2554afc6dc5769f0d742222a981f440e70ba21f4010000000000000016e0aa37d33f049c260f21fc0dd3c5adcde5a2adb039e2d4fe04dc40ea38f484010000000000000082d494ca555e8c784a2c45b8fb5351d08f7818fc55116142eef288e1397c8cb80100000000000000f2158c754d7297bc9d65b6e85962e563aff44f2fbbdf0da76c64b35ad9ff7e48010000000000000016de42b0da1b44f8921af23c1adf65169e827e6ba63cb81930b77ac3c090f494010000000000000022041166308736f6a8327f85be60fbfa2625aeb3cf3908d2133ad51759b8d53f010000000000000066df0e122fd92a7726844ebec6c1c37eb2988d2a24920c56084d80519879b87e0100000000000000e9fc035b14e568bdedf2bcad9d7a78126880a580a8f853934a9b87e5cf8c2911010000000000000086d07882373aa6a5efc34515b70ae649ada234850f7886d7617e392dae03716501000000000000004a9a44bdfb838d1f9715e5430b6b9d36a184355cf43ce417bbffa67597652a9901000000000000001ef94e359e2b8bc0df722b4d3b83321ef968f22d848233611470e514dca6e23f010000000000000084d2d37e08262ade7a57eaef4d6691454bfb33678be52dabdb34f75f0a48e78b01000000000000006e34b9840761c7c6e6b1deb0ac67e204f6b59449c52c538ec018fcd3b1e1e72d01000000000000000ff5797efead90c9a065afefc72709e0a5ea2ea98cffb9059f995638b45ff77f01000000000000009e152894b66fd6c88e0a30931060d649a86f8298edac0257f05c74ab1f619107010000000000000082355a7ac8ff56b100f146a370566d6b6e3f82af39402e5d7df61393264fbb9801000000000000006e49c862bdea504631c48eef8a6a9c2a3d1b8e5e8a410bb4e033509840663f9e0100000000000000eff00fe3f9d187a2d3c7762c68cdc6529af7890b784a3d2898a46cf1b993f4d701000000000000001575952139d99bfc450335ca368d690de04d951e75473fa94a9d9e7a6e39808b010000000000000049fe66d63a2d7f060a87fa96d10fdfa890f69d40b172c107e97382931a708e800100000000000000737978a4671cbde483f2f603463bc1218b7c7fe5089172bf1fa83fc850f83e7c010000000000000076802d0c4d5db17e9a82d75695770f084be40023cdf4b172e75f4bc0bbdc35d401000000000000009d88b6ae540bd80ae0df6aec87bd0ab80925627107cfec6b5dbf68b93688060701000000000000004eee1b610420fcb8180d66f548a01f4bb3899fd9a41cf3c075fd17fc9c9c64ab010000000000000033f6e05d1d79b8534474c6bec3b3934c8235674b37131afbd7157d6c1b49787e01000000000000000842cf210a31a977d49e0cca95f903a410009663c40f3a57abfd32e3636191d2010000000000000010f6d511ddd2ce18be483709e72a906dda0bca20ba5609a564eb3cd8555215cc0100000000000000b74fb00fd2a352f99e02671c07f22de71298b3b5101f08a78ba2f438d77ffab901000000000000006686cd34d76787127d12746001022e302597f58c56e0678d904a7e83201be5460100000000000000a66e70a9149e82a31c6fde0a8c884215e4a79fb7335c00ea97f383195fb5c0fa010000000000000007197f87de414de14666153a601dbea8070f80964fc99ed58f0fcfda471e856001000000000000000356087c5a2b18b1389030ef4485fe829871c1ba9996a0f1be49afd3a17ac47a0100000000000000b5305b4dc8972fec6a4eafe2977f2b80b6601d49672ce48a6f6c5bafa53f070e0100000000000000204ecb11e2cbe2fdc760b77c2708163d99a7fa6b25c1032332fc2f792d50d9a80100000000000000aaf08be2eae68fa730030691eeba70ca0f8962bdf030677624edff892cdc71910100000000000000f65695d41b887fd3ba581ebb5f3cb5b33ff3d11b2c43f961f1643baaa7e7ea2a0100000000000000f07a7954f65339317f812d09046484a73ebb6ed518776df5c47d1b9553d39c0a0100000000000000e0845a51f4cb541635d90c6204f9740f9cc6373cefecc3f839f2c587cb314add0100000000000000d674a87c3248100ba574ce340304ddde06f724b9c79eb7cec69cb2535d15c8cf0100000000000000375d6fcf1c75bc35eeb4bcf41356e21d797ca7097af0f5293454817e6daca7b50100000000000000b78db05da773a4ec530bba4eaa9e40376547225f65ec87bd6dc793d9f6728fd80100000000000000202342f70f6f3667268a6e997526ac8a12688e4f7d487e044f7a651dd6af5fb301000000000000004c912ce07ea5eb582045b0ab2528e6a1b624f6035db2ee8e8b013edd59d9d8a30100000000000000a299ac12c664db7152ab0251c1276bf98ec7247e3275a6564f5459d950499c820100000000000000f2814ac72031eda8cd762458c1f67caf5b589f3fc5d2fd5506de46fc1002019401000000000000007030fd03e6db6c0eb39006156548e74d58f470b40bc6ffa96cdf7e6a4992418b01000000000000008746e1d07a12a67441f01cb8613ad5f517a716450482a60866cd9ce58d80078601000000000000009d26d4c59d4c0b6fd1e1ab3814c8d17c79ea0af849f2c605817011169af14d5801000000000000001fab7985c3e86ce8b0e7edc19b0118e97529929ffcd514258d29c3e445408c960100000000000000c00699fc0c1712b9b73f06e32f7adc6020a92bebb50268c14547704477319fb1010000000000000043f686a3895023c6fa1ceee1ec0a9a982e644611516c4ad6d617b99a859a9ba8010000000000000069c451bb4890484acc15321708c6eaa9ab7b2f09f8cc2261e71c1a43231c3567010000000000000085c5043967d3a7b2ba2b96039bcd05b91f19b94f4f1226e6ddfb0eab2d4132ca01000000000000006b4b0ac0ac5e69c7b1ed46470a658bb5da61335e8be7f38fb6ec97da66128c4a010000000000000019f3033cbdac4b7b650dcd0d22e9c6c8349e2561c48c904bafab1a9d630eda4a010000000000000068f18f66c890885232950ec2cbdb963c711575c9eaa7f2b62386d9ea8d31b7a6010000000000000065a6b76a8dce8df45f16d88ba9d75a54f60b00f525d86eef13dcf829ea4474bf010000000000000061f7b00f37f33041dbdaca9741186d8fa5e458f8a88cbf341cb3def36c8626e40100000000000000346ca1ed3d6c9a2844b90744013a0d65cfcb75b8552a4ea2a77ac72614be36310100000000000000011295d0cec16375dd645e533e11778d79760b1328239d9b8845a48de8e3daf80100000000000000aee90dc5562fb2d5a966aa0037f4e0676df1cb4a055389c4b7f025bf58f5c6950100000000000000b12e958ce27aaab1a5b7bea2bd10e0911cc181339d284ac1111c9a678a657ba90100000000000000d5183907d381837e1ff361e4ef60d90144e04829cf13e71a099aa29a5e913f020100000000000000b369e06fa9a8518d817273abb92adfa85cd0c3592f3bb68c53d32c51bc9571a90100000000000000032251cc887b1db49b021e033ab4438bf26f69dc78202cd9e035fbdc62b9782a0100000000000000e565d348643bb1e32ad984699ed67392a33486735759e5c61b274bf24e1f00e20100000000000000d377934593854d84cdced03f3115f754bf52bbc1729545258003dc9401dbf83601000000000000001a1453da679611351b7abda6a29d2305fa68392f48b2a9ce27117202e63fb0b20100000000000000dff8d1fad896452d619084d0a1629ee7e9c54c87b826fb4d8c7d4e52b88cdaf60100000000000000d129795011ae7ec4f692d23281f4dc5a9b77224404a2d7129ec17026b23825cf01000000000000008bc0ad9f851aa355db0565889d6e2b8f8ff1451374937ae2e8d6dd973b0671030100000000000000cc56abd7edc26d01d112872cdeb81689e518a91b16e77d4d16c4b7b866611dcc01000000000000003cbb3b5e54e92a209b7107a0dbb4eaf65fc7b529e56ad68d3e21c4014caf3c870100000000000000fe05d07f8467afdb18f5bfd1ce5135dc857ee839755afb0523175ba01757b4c301000000000000002f4fbf68c49b4a02c44a5eeb13f9f28917c7a7a3bd7faa4e9b1c6cecf53f0e4a0100000000000000113743e1317d516f7938153594e3ec05a58a30db8214e6665c2272432fb102590100000000000000c2df8f256eaeae6c643c9bcf059c39b1086da9860aa7b8b7e10e003eb7fe66a301000000000000004b24b54151cb10627a6533a9740790e3000104be718da94c1a836653276300760100000000000000ba0e04fc1845507fec83d480ba04d488b536cacb3ec02505d25a579db075433701000000000000009d2acce3ce7556c6302ebf30f0e4324349d724853d045fcd75d2dc03286b22fb01000000000000001b5274a1614c68cdec40868ae34336e6c8e2738b1a4974bcc2830645f24af79f010000000000000009141ea15edcad1bd5e4f9784983de02719e0f39b108500278ee318b20190ef00100000000000000fc32a83d2fe0f86266565086b16b98caba2327731e33e4091e4c6637dbd39ab70100000000000000adb55f11cc68686f2d6b83a002d68f1d820ce67445494b8aa7ec19ef89ca1b5f0100000000000000e8d4a2da4eb2c3c37f940b26c6e43cd8432b0ab3a088f593989098a4680bcff40100000000000000d5750412284dcb0b01f31c7dea726e73bcebf87829c68ee1b0fde883ee39f8af01000000000000003eb46abb27a83dd13ec79f9f06736f0992f39b3fd2cd14f12a635c09e507c15b0100000000000000eece43f1649c53f7b4ab57fbbf451458be4030ca9ce929e1c4beb3e4d011034d0100000000000000d27d8804ded5971b644f63672db1cfd92402425f8c1c986b64fe19a9a3ac196401000000000000003daf19d13486111c2f7ff397c97db75d7a0780f5ea3a29ff9e30bdb58ba91ef50100000000000000ba3e97cc40806feecc699cb928cf4a9860970d963c948df37310aa9da414079c0100000000000000f7e5a4b58a6573e35a6ce82092b34b2646dfa61f1006b6f13ba2d4f17b00c3120100000000000000370125c29d24a6f9f3fabd5019dd9048ca3ca3bc6aed1c5e5a4b0ff29658d8320100000000000000bf05ad6855fa0c2caeb606384e08af4b6885de5cbc5973937bf0ec9baabfe3c2010000000000000034146639a28193723ece5b8e672549630a7bac6e924e3fc8db7d6c3328233ecf01000000000000006119b8207b905c55ae6107b924a47939049fea78ce967da1f3ef3ef9ed1533e30100000000000000621dc2f9add2ac0165af497f1d0f873a7b61bd26de61bab0db6049cfc79ce4ea01000000000000002b791d085e7b3a272e011723b54d697e0cc9c08b9632d569d5f0ff627e9e16a70100000000000000700223b64491477f429ddc8ca14942b29aa4be210bbe40c1c56a3c833a540d23010000000000000055643edc287c8be779b2f36eee9921c5b205d4fb48d7e9de026802d742d09df301000000000000004b94be41082399214439d8b4ccd4b770982a777b7b4773422b87ae95a55454aa0100000000000000ad17b074ebd020bf2d1e83cc9a38149898bac35e1827f3e062352d9d599cec640100000000000000b1458643a53c0d1114dd6b2ff19758f0017de3a1736b2c7fa5763914c24a528a01000000000000009b5c8dd1f08b3106c1761cd0d1e1c7a914a77c290df2ecb132f1eaf601e46f46010000000000000021a6331abad55acaee26f5f1843fa6b34356c3952cf8441e3ae430e5594fe03c0100000000000000f53273db34beb2d8e8fd0456068d4f57b32217f7d97773f01d4de97cda044e4a01000000000000007c9415f4fd22e5aced5385d9ba56d21dbcdf10df8382dbbfd2cf5dc44480320201000000000000007456f2e766ffe9d5429ece21d788ce2d53bb4a14c394e89a946172f9a5e513d4010000000000000074f7e6a7c83ce4a749d54a7dadf2ddd05593359048520b6b4c62c41fbd27986d010000000000000037d604831863e4ff3f264e39b38a2a3ee32fe81b50cf02ae61ca1022df58f2b201000000000000004bbae13b490e57caf040dd66686b727b303c47411460bba1d2526e503c355fe20100000000000000fa5cda1c05f0e7069df58344fa2ed43133b4ef116acbb2b4f0faf280962a34b501000000000000009e3e51f98f85152018170765153e7c73051c224846f8f97ffb4784fe6cec971601000000000000007df6dfe9ccc8d2409024d7de6ebd8f25a1b906bb3c27498884cf90cfcd987f7d01000000000000004d98ce8f37fd944570179d42c8e1e7cfc38a40ce02875347d6c914281ed9fd3c0100000000000000d4d372bb2122dce38506e0dc3ad12189a4f4c0087315a74a9e75fea1e35b3b390100000000000000c6f0c0135aa15a67f14bd670ca877085c7f263f811cff7db248bb130d34a48700100000000000000834a62ff8daa95f63d81a7315907db602560ee717818c27497ba6b497af864810100000000000000e525e4998943379b51bb5c1a91943633f18b3ef600d2875c35216c15a4dcba080100000000000000865b352972db47341ecf0679c3e556f21f1acf96130bdc5fa5b8667651b0799d010000000000000011e96804dd8b6b436e12a1c9b7a4e9395a373cfd80280f99433ed42eb583e98301000000000000009136b5b2c6343ec492d3081dd7c6e81d26e32b7daaa478c1b9b0e7a16be6511b0100000000000000253622dd0e934fde3d1f8ab9308f027a6155152d0410210fd257a1e521e8f844010000000000000013351ca142d69f845826d5bbcd8e9ab79e4aa5e1ee294024cebb47a7e0fb8a130100000000000000305b275d503f1ec6fe226c84620f7f95e8669e4db5e9ef051cb2415b19a1d2910100000000000000b12913dae62f88092610e0b3a8714ea3ad2225878317fbf5ed310c9e18ebed260100000000000000f5f55e9d4aa34cf0ca68447789be38d3a393cf2f6b7fc9166bd8bf4133968ca0010000000000000036cd46821b34a5a2c8662c30ef331e4d6819acd8e492d34d21abe4de3c21480b01000000000000001ec3fe0ea67c80f6fa1c94f6401a67517beea40fe497c98fc594d8ae68a0023a01000000000000000880eca6a462d2d8daf7bb35af246df0da742d5d532562575ac203d965a678930100000000000000d08d7c4133b81aa7151ed03d636786af200d38ea0e8de42e5eb1196065a6b28b010000000000000096f44e342972128a595b6dfb3f8dbb35beb06488a13a4898b3fb4cf6070ae8110100000000000000a8abcc38af13e0d51873f5413f0955c06840c24ca0a77974a5caa183c9474b23010000000000000071e2b131ac8f13a224f388203efa9fd914a773e2758cfba5d5f3c3c6383596090100000000000000459d87618158ffa83934beb32225b6e3fa7f74224fb65c48abf2494de84270d50100000000000000b217911f8954bea667513e7fec2606df2d559e5cd2669bb366423658d5ee56c80100000000000000f183fc5dd4c28b39022847996ae945b780c1e7ae7e3803781dccc53ec1ed26eb01000000000000008b98f3d09929b8d8c1963b67000056e6b44c70fb0c04986c9e0dde0850e986c10100000000000000684f57a7265873c3dd8b6ec91a56aa244f24c303f1592709fd66be06fce75c550100000000000000a7235e7185a17e5105047a4e12bf29936add830cce1bb892422d9ece714653b4010000000000000084149cc202282b3605d589ff6c8737a2d94f4864ee56058536144a977a7974770100000000000000fcd998b754bdaa1c99701caa8281bd20871e77f5a8991d13a3004d35c93e9fc001000000000000006143de170564c11c33c7ed341d4f440ee0950e9a0f29b232a6813cdc4269bb5301000000000000006175208a6061d7fd1c9f0728d8a4a44445703045e6a2f97be321c84946af20350100000000000000d921dad425e35c9d872297eab9f97145ca8aedfd84ca2c6a89a8316319c25e1b010000000000000088b6c18aed539a125811322da1278f3695ef4484c59d8c33a9f5934bead2a83e01000000000000000b8ed69ae21d10eb1c0cf9286198589543ff073c5733cc090406ea021e4d715d0100000000000000940bd9d799f05c51b2dbc5cd4d9585f85c74658216ed405647e76db23aa792670100000000000000f3f6a5a1400d93f257cd8b0253d2cef31eac6f3049bfe3c7950191379dc2f1bb01000000000000006685deca424d2ac70550117ce316aea82fab1a1bcb9f88b984f56ada58c9e6260100000000000000e510f45cc2b04c8c3fde58a8d114cac325e6df00bd4ac5485f112f2c2c9f70ea0100000000000000dcae96c8251d140782fd4995a88c023b9112feac89add141b98e3838266d97170100000000000000035eb6fae7043b47cdd23d2faa0c65a67c4e5b6c5590f546da78ed240ece1c700100000000000000ab4c83cd71f230fbce8ecd34f2aa4a4a4fed057ca8bba20201872d6e580f42ba0100000000000000bfb6804ca72cb4e32dc8bf8ad30a5defebb90fb79871bcaddd266028694413b60100000000000000ab3302f4854cd90ab8f3155be1cb31985498a9a4a1d9426c6ffb4c86c56637c10100000000000000576cfd896ca75daadd83777a43741eca3ed5da2ff5b9ef11418cd5956f48b1df0100000000000000c6691c3cf5eebd250d01013b93d5d4dd6e406c38ea6a6966fc4d2a34956c4c4a0100000000000000638ef6fd4849c483db9bca499a75d7d96507a8e11db5ffad3a1a3ac8c11f4aee010000000000000041b5120a0018e3d9b6b51dcc0a06e438fb9f97750ad49430cc1c6f865ec7097c01000000000000009736f5660aed635893c1a3fd19e6c60eee450894205eea09d771b0b90364793b0100000000000000379cb36d9bc0ed5523df498018874190e839ee50092b1578e22d5afc891ad24c010000000000000007481b9b658a55221ca1dd90d7afa10278e12ef4d2aebbab1f2dfbc968a2cba40100000000000000a131370b8ac30d7c9d1ae404f06d100bb3399108e254abb60c7f9b996af2a7c50100000000000000ae37da60940e7919c60c3d3719aa0bad05f0116fe8f2e7ffdb24bf6ee22a76cc010000000000000045c17a93c45606ce47aa22651554ebc4bf492f3da68e8ad43f76c3f147fb8a3101000000000000001e59efb1ea0992ef40c7903d74e338db59a7e5cfbb46ced1971ffab848fc8c6e0100000000000000596ec665b0fa30467a2430e227b7a8c9638a8a9fc94eaa82fd39323c40255f2601000000000000004e6fd64100eca46bba08a38a453089ea7622a4f7f67d55c5df1280e2faea35300100000000000000eab2d9a94941fa2f28d4e8dfa2542ee87f6877a966ed4d08a614d2df66cecd01010000000000000018f4d754f5898ae24f2fc7bff6efb88a44c778d35fc1b2c5909858518f5d39df010000000000000007a6e225b63d830bcbe5d0fe9f2478cced8db0f1c01d40d0864ff4163d61db1e0100000000000000f8f5fb74e9007a15660e3c52ac47db525bf2757cec1ebdf68a762aa67fa5723001000000000000003c52628a9f4d7aad5eab967a259e609dd9d32b149c614e9d954cd9a17ca72fb2010000000000000055439c22ef4133af0dc7efbc594a05851925f608f6dda5b91ba3f2ac30e086ff01000000000000004f55c96297d0826f2c06a387cca6d20e5107d53674d5d0e70035f79cdbc3e4120100000000000000476fd3845541717d93b65b610b5eab568d3d9ddbde79c4ea11f79810381e78620100000000000000b11fc43bd5e2b238c0d0337be8285193b001101c9a8a2110bbe35eeafc30ab8d01000000000000006783db38a069fcec0260c6282648e68c76fd22e918322897c782ce87f425ffe3010000000000000005073dda3c462b94815e11fbbda3a021d792b7ef58e49be9c51413cacaae229201000000000000002c68c98f51b20754e40e08c3c4e3321a5707bc8bead3fbcfd8bad437aba46b7401000000000000000244364beceba2a73ddff1eca2f19e6c212b92e33796afa29ece15c309d0dc010100000000000000103e63d2855685e2c7082a5f2ad28ea8c72923a9e73ea7cb8e014213258a760d01000000000000009383177c2fe088e9ed2926caf07131be7bab178c3d923dcc1895c70eba69bf3301000000000000008b46bbd10e610544f112d385659948acaf7098bca31210b409c36a2f04797d77010000000000000042561211c18eba2f5c8a4beedbca78fdc72151e5f3e80ee249bf92af960076fd0100000000000000bc1cd6a00cbaddaf3dcb9b86db199416b1b58fa5ac5f372e4b3d4347f4fb2f260100000000000000961637e8a00c7c7bef9600c31f1a97ab99c284e25d1c5e58a729340c15b6a6150100000000000000b2598b2ce2241772e5a1c614c70da05d3363bfc7a86740eec5c5e951012fda670100000000000000c98dc6754b2cb297986bec40c51907a9a7fda0d2ac808a03e4cf36d2e7986e5601000000000000005c4b4f45f309ed0d578e691394a6bc32aa5d294bee9581d744ad8f7de891336001000000000000004af586cf1105ad073193a842f1d1b23143d050c54158df67bc272c0b83d2d0f1010000000000000059cf0b3e53a440bcc2079b3cf1224972215ac7f243a2c9032e7569a025988cf201000000000000005f8112d95fb18e1e390623c1ee286e601383106e6312f795fd018fbb321186b401000000000000007d694cae8e241c14d2a2d4adbc5337732a014acb9861702eefe7d94e861a650e010000000000000075ae5467a8b261be5fd72b0fa5b9b2553a39c5ccd8392c819c14e7596cc12fbe0100000000000000b80a9a0417f43555cc5465b348fd6c9ab95f7f253f33b69911ea88537fffeb3c01000000000000008aeae69850306b97024454ef4ebb7600adc211a479ea04c57d60dfde36a7a2ae010000000000000046074de41299025a6d2d07d73c39089142843ede2fb749f37b7387b9d00b0cf101000000000000003eeb4395b85a884c4194d60753931840c1c8dc7082c9cffeb216825cf09dbc2a01000000000000004ff2be6fc896ad5c5360330efdf46d26f881fb1cc3c589ea407db7c06b27fe9c01000000000000003fa92db013045648d46b650d9bb74fe06d94d799b60da3923e9040809edcbe8b0100000000000000b07108bc0c406bce6b6e3a6c511badbb94846970ea03c5a9571e6bba22105f2201000000000000007c601959f37a0f0bd567f8ba246a0af5a6e4e2514b3a7dc438c3dbbd098912660100000000000000bc9b4c9af1d5130035e8c3b3c7043d0a837af74472f9adb2afdbf602612bb59501000000000000003f70e4f56e2aba2477ead6d3eccb6a5c9be147381d12ee53e31f2c0e13dc6aa901000000000000007d3e5d4bb8356453c3430bc745863fd1ce138b9a9e586e647f34c4bea232e57b01000000000000002d7046462826854856153545ea81e9b22b133c4c3ad3c17828cf755a2b8769cb01000000000000007e5c6dfe75783f2dd0a78473662a4158f99a7883970171cfa97cb55afe8a0e2001000000000000008a476a441c41769f7c4f449822a24227cdc5b4b595a8380a34ed41a09e7847b9010000000000000092e0b7b5e98974739f250d8dd641559b01e6979f23829289b1b109d5ffae85b3010000000000000036f13ff9fdf422421bd426bc5c00d8b15d2b70225e5e842259ab1628f0abf1f801000000000000005dff3ba50c1a660bf944a055f4b26406b262c2bb03978f7cb00d073ab0a3182701000000000000009b7c9cbd4aab3141c38d8f70a25bee16a109d43748b86646ebcb327caaaf54b00100000000000000c6fd79be56e792e01b69e6d6130a2ccd40c7dc2b624744ac040e70e04cc8977801000000000000004e591d963f5069e8e72d966325fb37fbe1da71e2274b9366deefc8be9ffaca8e01000000000000006ef7601a2e00a112211174d742c0390343a9ed5bda859ce1435f4298a7a0ece301000000000000004a2a5bd3982f1e6c55659c399353769c6cb1663bccb1e3c109e1338fc5d0e6d6010000000000000059f9d3756cc564bc353571a07d364e3589bb15fefd6ff8a0f8e5fa319a937d71010000000000000011111324f09281f95ee698613ae536d8822a9dba574483d00c076a0b635175a901000000000000001c84ffe679c52b9be6fa56992429da5b57852ce168bdce7b69d8ef2be027f77b0100000000000000d8a9f28359c127e5f3768c6b2441ad65bd49871d9c3c10aabfc2413b54c705a30100000000000000574d311ee9b8f6412e94f33b280f8d1c9db75a6dae79bd98176155aa101cb99f01000000000000002f8a072de78b373b672c1690633eea75a33a06e45c482cfbfde704d94255100001000000000000008f94514e131d81e94358970430dbc2cfdca7605fe70848b6011611bf1709c6ae0100000000000000e918e954098f7c68207f7ad1c91f0c1264930aef310ddf0f24f37889e3ac08190100000000000000b8d4589f88fad4eade69539a5edb61b5666fc9fbde60e91660ee8b5fa2f828cc0100000000000000b0d09c25c58aba24de6fa8cc28468832b9c57ca423d37c214414590b9826c2af01000000000000000000000005424142450101da576e24929edde5e11d0b6a46ec3ecbea51af81a42c7a6d67c7215644532d52f8c14a4c5176cff587106ce0d3350042826d234d52a8fb31460de83bbdba1589 \ No newline at end of file diff --git a/chain/pra/substrate/client.go b/chain/pra/substrate/client.go new file mode 100644 index 00000000..05060b62 --- /dev/null +++ b/chain/pra/substrate/client.go @@ -0,0 +1,459 @@ +package substrate + +import ( + "fmt" + "reflect" + "runtime" + "sort" + "time" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + "github.com/gammazero/workerpool" + "github.com/icon-project/btp/common/go-ethereum/rpc" + "github.com/icon-project/btp/common/log" + scale "github.com/itering/scale.go" + "github.com/itering/scale.go/source" + scaletypes "github.com/itering/scale.go/types" + "github.com/itering/scale.go/utiles" +) + +type SubstrateAPI struct { + *rpc.Client + keepSubscribeFinalizeHead chan bool + blockInterval time.Duration + eventDecoder scale.EventsDecoder + scaleDecoderOption scaletypes.ScaleDecoderOption + log log.Logger +} + +func NewSubstrateClient(url string) (*SubstrateAPI, error) { + cl, err := rpc.Dial(url) + return &SubstrateAPI{ + Client: cl, + keepSubscribeFinalizeHead: make(chan bool), + blockInterval: time.Second * 3, + log: log.GlobalLogger(), + }, err +} + +func (c *SubstrateAPI) Init() { + metadataRaw := c.GetMetadataRawLatest() + m := scale.MetadataDecoder{} + m.Init(utiles.HexToBytes(metadataRaw)) + if err := m.Process(); err != nil { + log.Errorf("Init: metadaDecoderProcess fail %v", err) + } + + runtimeVersion, err := c.GetRuntimeVersionLatest() + if err != nil { + c.log.Panicf("Init: can't fetch RuntimeVersionLatest") + } + + if err != nil { + c.log.Panicf("Init: can't fetch scale module") + } + + c.eventDecoder = scale.EventsDecoder{} + c.scaleDecoderOption = scaletypes.ScaleDecoderOption{Metadata: &m.Metadata, Spec: int(runtimeVersion.SpecVersion)} + + if typesDefinitionMap[runtimeVersion.SpecName] == "" { + c.log.Panicf("Init: can't fetch scale type %s", runtimeVersion.SpecName) + } + + typesDefinition := []byte(typesDefinitionMap[runtimeVersion.SpecName]) + + scaletypes.RegCustomTypes(source.LoadTypeRegistry(typesDefinition)) +} + +func (c *SubstrateAPI) callWithBlockHash(target interface{}, method string, blockHash *SubstrateHash, args ...interface{}) error { + if blockHash == nil { + err := c.Call(target, method, args...) + if err != nil { + return err + } + return nil + } + hexHash, err := types.Hex(*blockHash) + if err != nil { + return err + } + hargs := append(args, hexHash) + err = c.Call(target, method, hargs...) + if err != nil { + return err + } + return nil +} + +func (c *SubstrateAPI) GetRuntimeVersionLatest() (*RuntimeVersion, error) { + return c.getRuntimeVersion(nil) +} + +func (c *SubstrateAPI) getRuntimeVersion(blockHash *SubstrateHash) (*RuntimeVersion, error) { + var runtimeVersion RuntimeVersion + err := c.callWithBlockHash(&runtimeVersion, "state_getRuntimeVersion", blockHash) + if err != nil { + return nil, err + } + return &runtimeVersion, err +} + +func (c *SubstrateAPI) GetMetadataRawLatest() string { + res, _ := c.getMetadataRaw(nil) + return res +} + +func (c *SubstrateAPI) getMetadataRaw(blockHash *SubstrateHash) (string, error) { + var res string + err := c.callWithBlockHash(&res, "state_getMetadata", blockHash) + if err != nil { + return "", err + } + + return res, err +} + +func (c *SubstrateAPI) GetFinalizedHead() (SubstrateHash, error) { + var res string + + err := c.Call(&res, "chain_getFinalizedHead") + if err != nil { + return SubstrateHash{}, err + } + + return types.NewHashFromHexString(res) +} + +func (c *SubstrateAPI) GetHeader(blockHash SubstrateHash) (*SubstrateHeader, error) { + return c.getHeader(&blockHash) +} + +func (c *SubstrateAPI) GetHeaderLatest() (*types.Header, error) { + return c.getHeader(nil) +} + +func (c *SubstrateAPI) getHeader(blockHash *SubstrateHash) (*types.Header, error) { + var Header types.Header + err := c.callWithBlockHash(&Header, "chain_getHeader", blockHash) + if err != nil { + return nil, err + } + return &Header, err +} + +func (c *SubstrateAPI) GetBlockHash(blockNumber uint64) (SubstrateHash, error) { + return c.getBlockHash(&blockNumber) +} + +func (c *SubstrateAPI) GetBlockHashLatest() (SubstrateHash, error) { + return c.getBlockHash(nil) +} + +func (c *SubstrateAPI) getBlockHash(blockNumber *uint64) (SubstrateHash, error) { + var res string + var err error + + if blockNumber == nil { + err = c.Call(&res, "chain_getBlockHash") + } else { + err = c.Call(&res, "chain_getBlockHash", *blockNumber) + } + + if err != nil { + return SubstrateHash{}, err + } + + return types.NewHashFromHexString(res) +} + +func (c *SubstrateAPI) GetStorageRaw(key SubstrateStorageKey, blockHash SubstrateHash) (*SubstrateStorageDataRaw, error) { + return c.getStorageRaw(key, &blockHash) +} + +func (c *SubstrateAPI) getStorageRaw(key types.StorageKey, blockHash *SubstrateHash) (*types.StorageDataRaw, error) { + var res string + err := c.callWithBlockHash(&res, "state_getStorage", blockHash, key.Hex()) + if err != nil { + return nil, err + } + + bz, err := types.HexDecodeString(res) + if err != nil { + return nil, err + } + + data := types.NewStorageDataRaw(bz) + return &data, nil +} + +func (c *SubstrateAPI) GetSpecName() string { + rtv, _ := c.GetRuntimeVersionLatest() + return rtv.SpecName +} + +func (c *SubstrateAPI) GetHeaderByBlockNumber(blockNumber SubstrateBlockNumber) (*SubstrateHeader, error) { + blockHash, err := c.GetBlockHash(uint64(blockNumber)) + if err != nil { + return nil, err + } + + return c.GetHeader(blockHash) +} + +func (c *SubstrateAPI) GetBlockHeaderByBlockNumbers(blockNumbers []SubstrateBlockNumber) ([]SubstrateHeader, error) { + headers := make([]SubstrateHeader, 0) + + wp := workerpool.New(runtime.NumCPU()) + rspChan := make(chan struct { + header *SubstrateHeader + err error + }, len(blockNumbers)) + + for _, blockNumber := range blockNumbers { + blockNumber := blockNumber + wp.Submit(func() { + blockHash, err := c.GetBlockHash(uint64(blockNumber)) + if err != nil { + rspChan <- struct { + header *SubstrateHeader + err error + }{ + header: nil, + err: err, + } + return + } + + header, err := c.GetHeader(blockHash) + c.log.Tracef("GetBlockHeaderByBlockNumbers: get header of %d", blockNumber) + + rspChan <- struct { + header *SubstrateHeader + err error + }{ + header: header, + err: err, + } + }) + } + + wp.StopWait() + close(rspChan) + for rsp := range rspChan { + if rsp.err != nil { + c.log.Panicf("GetBlockHeaderByBlockNumbers: fails err:%+v", rsp.err) + } + + headers = append(headers, *rsp.header) + } + + sort.Slice(headers, func(i, j int) bool { + return headers[i].Number < headers[j].Number + }) + + return headers, nil +} + +func (c *SubstrateAPI) GetBlockHashesByRange(start SubstrateBlockNumber, end SubstrateBlockNumber) ([]SubstrateHash, error) { + var blockNumbers []SubstrateBlockNumber + hashes := make([]SubstrateHash, 0) + + for i := start; i <= end; i++ { + blockNumbers = append(blockNumbers, i) + } + + blockHeaders, err := c.GetBlockHeaderByBlockNumbers(blockNumbers) + if err != nil { + return nil, err + } + + hash, err := c.GetBlockHash(uint64(blockNumbers[len(blockNumbers)-1])) + if err != nil { + return nil, err + } + + for i, blockHeader := range blockHeaders { + if i == 0 { + continue + } + + hashes = append(hashes, blockHeader.ParentHash) + } + + hashes = append(hashes, hash) + + return hashes, nil +} + +func (c *SubstrateAPI) GetReadProof(key SubstrateStorageKey, hash SubstrateHash) (SubstrateReadProof, error) { + var res SubstrateReadProof + err := c.Call(&res, "state_getReadProof", []string{key.Hex()}, hash.Hex()) + return res, err +} + +func (c *SubstrateAPI) GetSystemEventStorageKey(blockhash SubstrateHash) (SubstrateStorageKey, error) { + key := EncodeStorageKey(c.scaleDecoderOption.Metadata, "System", "Events") + + return NewStorageKey(key.EncodeKey), nil +} + +func (c *SubstrateAPI) GetGrandpaCurrentSetId(blockHash SubstrateHash) (types.U64, error) { + key := EncodeStorageKey(c.scaleDecoderOption.Metadata, "Grandpa", "CurrentSetId") + + storageRaw, err := c.GetStorageRaw(NewStorageKey(key.EncodeKey), blockHash) + if err != nil { + return 0, err + } + + var setId types.U64 + err = types.DecodeFromBytes(*storageRaw, &setId) + + return setId, err +} + +func (c *SubstrateAPI) GetFinalitiyProof(blockNumber types.BlockNumber) (*FinalityProof, error) { + var finalityProofHexstring string + err := c.Call(&finalityProofHexstring, "grandpa_proveFinality", types.NewU32(uint32(blockNumber))) + if err != nil { + return nil, err + } + + fp := &FinalityProof{} + err = types.DecodeFromHexString(finalityProofHexstring, fp) + if err != nil { + return nil, err + } + + return fp, err +} + +func (c *SubstrateAPI) GetJustificationsAndUnknownHeaders(blockNumber types.BlockNumber) (*GrandpaJustification, []SubstrateHeader, error) { + var finalityProofHexstring string + err := c.Call(&finalityProofHexstring, "grandpa_proveFinality", types.NewU32(uint32(blockNumber))) + if err != nil { + return nil, nil, err + } + + spec := c.GetSpecName() + + if spec == "kusama" || spec == "polkadot" { + fp := &FinalityProof{} + err = types.DecodeFromHexString(finalityProofHexstring, fp) + + if fp != nil { + return &fp.Justification.EncodedJustification, fp.UnknownHeaders, err + } + } + + if spec == "westend" { + fp := &WestendFinalityProof{} + err = types.DecodeFromHexString(finalityProofHexstring, fp) + + if fp != nil { + return &fp.Justification.EncodedJustification, fp.UnknownHeaders, err + } + } + + return nil, nil, fmt.Errorf("not supported chain spec %s", spec) +} + +func (c *SubstrateAPI) GetValidationData(blockHash SubstrateHash) (*PersistedValidationData, error) { + key := EncodeStorageKey(c.scaleDecoderOption.Metadata, "ParachainSystem", "ValidationData") + + storageRaw, err := c.GetStorageRaw(NewStorageKey(key.EncodeKey), blockHash) + if err != nil { + return nil, err + } + + pvd := &PersistedValidationData{} + err = types.DecodeFromBytes(*storageRaw, pvd) + + return pvd, err +} + +func (c *SubstrateAPI) GetParachainId() (*SubstrateParachainId, error) { + key := EncodeStorageKey(c.scaleDecoderOption.Metadata, "ParachainInfo", "ParachainId") + + blockHash, err := c.GetBlockHashLatest() + if err != nil { + return nil, err + } + + storageRaw, err := c.GetStorageRaw(NewStorageKey(key.EncodeKey), blockHash) + if err != nil { + return nil, err + } + + var pid SubstrateParachainId + err = types.DecodeFromBytes(*storageRaw, &pid) + + return &pid, err +} + +func (c *SubstrateAPI) GetSystemEvents(blockHash SubstrateHash, section string, method string) ([]map[string]interface{}, error) { + key := EncodeStorageKey(c.scaleDecoderOption.Metadata, "System", "Events") + systemEventsStorageRaw, _ := c.GetStorageRaw(NewStorageKey(key.EncodeKey), blockHash) + + c.eventDecoder.Init(scaletypes.ScaleBytes{Data: *systemEventsStorageRaw}, &c.scaleDecoderOption) + c.eventDecoder.Process() + eventsVal := reflect.ValueOf(c.eventDecoder.Value) + + returnEvents := make([]map[string]interface{}, 0) + for i := 0; i < eventsVal.Len(); i++ { + mapVal := reflect.ValueOf(eventsVal.Index(i).Interface()) + eventId := mapVal.MapIndex(reflect.ValueOf("event_id")).Interface().(string) + moduleId := mapVal.MapIndex(reflect.ValueOf("module_id")).Interface().(string) + + if eventId == method && moduleId == section { + eventMap, ok := mapVal.Interface().(map[string]interface{}) + if !ok { + c.log.Panicf("GetSystemEvents: event can't decodable") + } + returnEvents = append(returnEvents, eventMap) + } + } + + return returnEvents, nil +} + +func (c *SubstrateAPI) SubcribeFinalizedHeadAt(height uint64, cb func(*SubstrateHash)) error { + current := height + + for { + select { + case <-c.keepSubscribeFinalizeHead: + return nil + default: + finalizedHeadHash, err := c.GetFinalizedHead() + if err != nil { + return err + } + + finalizedHead, err := c.GetHeader(finalizedHeadHash) + if err != nil { + return err + } + + if current > uint64(finalizedHead.Number) { + c.log.Tracef("block not yet finalized target:%v latest:%v", current, finalizedHead.Number) + <-time.After(c.blockInterval * (time.Duration(current) - time.Duration(finalizedHead.Number))) + continue + } + + if current == uint64(finalizedHead.Number) { + cb(&finalizedHeadHash) + } + + if current < uint64(finalizedHead.Number) { + currentHash, err := c.GetBlockHash(current) + if err != nil { + return err + } + + cb(¤tHash) + } + + current++ + } + } +} diff --git a/chain/pra/substrate/client_test.go b/chain/pra/substrate/client_test.go new file mode 100644 index 00000000..49f4bb9b --- /dev/null +++ b/chain/pra/substrate/client_test.go @@ -0,0 +1,149 @@ +package substrate + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClient(t *testing.T) { + t.Run("should return ValidationData", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://wss.testnet.moonbeam.network") + require.NoError(t, err) + + /** + { + validationData: { + parentHead: 0xc3e2e26d7f56e8c0ae64e98d2cdb4376c509bf44a3f817e811167051a6c60eec4258170064e4934621abe9757b4bdcb30df44956df385633c4416e2a84355c2b59eecd9a20c2fc21304546193eaaf5c7473b45e23c58153f9f3977984366afb7d3053bfe0c046e6d627380c8a8aa2e9668d65435cc51d00685add26c6a303f3261a841f67972e54a18b70c0466726f6e090201b0a7e8f200bd7ccd579d4bc0f8f8618815d978ee70cc8bd723b593f4aafd656a0c59df4aa35ba633f91ead78caa8af4b1914b00017bac11675c37722972def0d88ad33a2fc871c6c055302bfdf7109415307eb463b4fec0f0d6772a8d7e4ad5db85aaeae4e4d879c836031b220847b01cf018f3a439d02490935302c92c624b7c1056e6d6273010116efc94625d3c4f16405f8c29dd0d6c0e629e04c3bd4a8984b5492d822a6244024a73faff07d28ae87bfcf47656887f68787e04b0778f5c587af8968e905d18a, + relayParentNumber: 816,424, + relayParentStorageRoot: 0x5f8a015c72d729af9680153a14c3f03994e29184fff2eb1f3db991da611a0a82, + maxPovSize: 52,428,800 + }, + } + **/ + c.GetValidationData(NewSubstrateHashFromHexString("0xfd7a2619ce12f375cd2c170090ea5f206ab69068affba91c75eda76925e13284")) + }) + + t.Run("should return decode FinalityProof on polkadot", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://rpc.polkadot.io") + require.NoError(t, err) + + gj, hds, err := c.GetJustificationsAndUnknownHeaders(6247439) + assert.NoError(t, err) + assert.NotEmpty(t, hds) + assert.NotNil(t, gj) + }) + + t.Run("should return decode FinalityProof on kusama", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://kusama-rpc.polkadot.io") + require.NoError(t, err) + + gj, hds, err := c.GetJustificationsAndUnknownHeaders(8007753) + assert.NoError(t, err) + assert.NotEmpty(t, hds) + assert.NotNil(t, gj) + }) + + t.Run("should return decode FinalityProof on westend", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://westend-rpc.polkadot.io") + require.NoError(t, err) + + gj, hds, err := c.GetJustificationsAndUnknownHeaders(6788687) + assert.NoError(t, err) + assert.NotEmpty(t, hds) + assert.NotNil(t, gj) + }) + + t.Run("should return decode FinalityProof on moonbase relay chain", func(t *testing.T) { + t.Skip("Manual run only") + // c, err := NewSubstrateClient("wss://wss-relay.testnet.moonbeam.network/") + c, err := NewSubstrateClient("https://rpc-relay.testnet.moonbeam.network/") + require.NoError(t, err) + + gj, hds, err := c.GetJustificationsAndUnknownHeaders(1016190) + assert.NoError(t, err) + assert.NotEmpty(t, hds) + assert.NotNil(t, gj) + }) + + t.Run("should return multiple block headers", func(t *testing.T) { + t.Skip("Manual run only") + // c, err := NewSubstrateClient("wss://wss-relay.testnet.moonbeam.network/") + c, err := NewSubstrateClient("https://rpc-relay.testnet.moonbeam.network/") + require.NoError(t, err) + + bh, err := c.GetBlockHeaderByBlockNumbers([]SubstrateBlockNumber{913571, 913572, 913573, 913574, 913575, 913576, 913577}) + assert.NoError(t, err) + assert.NotNil(t, bh) + }) + + t.Run("should return set id", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://kusama-rpc.polkadot.io") + require.NoError(t, err) + + blockHash, err := c.GetBlockHash(8654865) + require.NoError(t, err) + + setId, err := c.GetGrandpaCurrentSetId(blockHash) + assert.NoError(t, err) + assert.NotNil(t, setId) + }) + + t.Run("should return 3 ParaInclusion_CandidateIncluded events", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://kusama-rpc.polkadot.io") + require.NoError(t, err) + + blockHash, err := c.GetBlockHash(8654865) + require.NoError(t, err) + + events, err := c.GetSystemEvents(blockHash, "ParaInclusion", "CandidateIncluded") + assert.NoError(t, err) + assert.Len(t, events, 3) + }) + + t.Run("should return 55 EVM_Log in moonriver", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://wss.moonriver.moonbeam.network") + require.NoError(t, err) + c.Init() + blockHash, err := c.GetBlockHash(786623) + require.NoError(t, err) + + events, err := c.GetSystemEvents(blockHash, "EVM", "Log") + assert.NoError(t, err) + assert.Len(t, events, 55) + }) + + t.Run("should getEvmLogs in moonbase", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://wss.testnet.moonbeam.network") + require.NoError(t, err) + c.Init() + blockHash, err := c.GetBlockHash(814054) + require.NoError(t, err) + + events, err := c.GetSystemEvents(blockHash, "EVM", "Log") + assert.NoError(t, err) + assert.Len(t, events, 4) + }) + + t.Run("should get system storage key", func(t *testing.T) { + t.Skip("Manual run only") + c, err := NewSubstrateClient("wss://wss.moonriver.moonbeam.network") + require.NoError(t, err) + c.Init() + blockHash, err := c.GetBlockHash(786623) + require.NoError(t, err) + + key, err := c.GetSystemEventStorageKey(blockHash) + assert.NoError(t, err) + assert.NotNil(t, key) + }) +} diff --git a/chain/pra/substrate/mock_SubstrateClient.go b/chain/pra/substrate/mock_SubstrateClient.go new file mode 100644 index 00000000..58a5ff5f --- /dev/null +++ b/chain/pra/substrate/mock_SubstrateClient.go @@ -0,0 +1,413 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package substrate + +import ( + types "github.com/centrifuge/go-substrate-rpc-client/v3/types" + mock "github.com/stretchr/testify/mock" +) + +// MockSubstrateClient is an autogenerated mock type for the SubstrateClient type +type MockSubstrateClient struct { + mock.Mock +} + +// Call provides a mock function with given fields: result, method, args +func (_m *MockSubstrateClient) Call(result interface{}, method string, args ...interface{}) error { + var _ca []interface{} + _ca = append(_ca, result, method) + _ca = append(_ca, args...) + ret := _m.Called(_ca...) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}, string, ...interface{}) error); ok { + r0 = rf(result, method, args...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetBlockHash provides a mock function with given fields: blockNumber +func (_m *MockSubstrateClient) GetBlockHash(blockNumber uint64) (types.Hash, error) { + ret := _m.Called(blockNumber) + + var r0 types.Hash + if rf, ok := ret.Get(0).(func(uint64) types.Hash); ok { + r0 = rf(blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Hash) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(uint64) error); ok { + r1 = rf(blockNumber) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetBlockHashLatest provides a mock function with given fields: +func (_m *MockSubstrateClient) GetBlockHashLatest() (types.Hash, error) { + ret := _m.Called() + + var r0 types.Hash + if rf, ok := ret.Get(0).(func() types.Hash); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Hash) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetBlockHashesByRange provides a mock function with given fields: start, end +func (_m *MockSubstrateClient) GetBlockHashesByRange(start types.BlockNumber, end types.BlockNumber) ([]types.Hash, error) { + ret := _m.Called(start, end) + + var r0 []types.Hash + if rf, ok := ret.Get(0).(func(types.BlockNumber, types.BlockNumber) []types.Hash); ok { + r0 = rf(start, end) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Hash) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.BlockNumber, types.BlockNumber) error); ok { + r1 = rf(start, end) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetBlockHeaderByBlockNumbers provides a mock function with given fields: blockNumbers +func (_m *MockSubstrateClient) GetBlockHeaderByBlockNumbers(blockNumbers []types.BlockNumber) ([]types.Header, error) { + ret := _m.Called(blockNumbers) + + var r0 []types.Header + if rf, ok := ret.Get(0).(func([]types.BlockNumber) []types.Header); ok { + r0 = rf(blockNumbers) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Header) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func([]types.BlockNumber) error); ok { + r1 = rf(blockNumbers) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetFinalizedHead provides a mock function with given fields: +func (_m *MockSubstrateClient) GetFinalizedHead() (types.Hash, error) { + ret := _m.Called() + + var r0 types.Hash + if rf, ok := ret.Get(0).(func() types.Hash); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Hash) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetGrandpaCurrentSetId provides a mock function with given fields: blockHash +func (_m *MockSubstrateClient) GetGrandpaCurrentSetId(blockHash types.Hash) (types.U64, error) { + ret := _m.Called(blockHash) + + var r0 types.U64 + if rf, ok := ret.Get(0).(func(types.Hash) types.U64); ok { + r0 = rf(blockHash) + } else { + r0 = ret.Get(0).(types.U64) + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Hash) error); ok { + r1 = rf(blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetHeader provides a mock function with given fields: blockHash +func (_m *MockSubstrateClient) GetHeader(blockHash types.Hash) (*types.Header, error) { + ret := _m.Called(blockHash) + + var r0 *types.Header + if rf, ok := ret.Get(0).(func(types.Hash) *types.Header); ok { + r0 = rf(blockHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Hash) error); ok { + r1 = rf(blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetHeaderLatest provides a mock function with given fields: +func (_m *MockSubstrateClient) GetHeaderLatest() (*types.Header, error) { + ret := _m.Called() + + var r0 *types.Header + if rf, ok := ret.Get(0).(func() *types.Header); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.Header) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetJustificationsAndUnknownHeaders provides a mock function with given fields: blockNumber +func (_m *MockSubstrateClient) GetJustificationsAndUnknownHeaders(blockNumber types.BlockNumber) (*GrandpaJustification, []types.Header, error) { + ret := _m.Called(blockNumber) + + var r0 *GrandpaJustification + if rf, ok := ret.Get(0).(func(types.BlockNumber) *GrandpaJustification); ok { + r0 = rf(blockNumber) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*GrandpaJustification) + } + } + + var r1 []types.Header + if rf, ok := ret.Get(1).(func(types.BlockNumber) []types.Header); ok { + r1 = rf(blockNumber) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]types.Header) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(types.BlockNumber) error); ok { + r2 = rf(blockNumber) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// GetParachainId provides a mock function with given fields: +func (_m *MockSubstrateClient) GetParachainId() (*types.U32, error) { + ret := _m.Called() + + var r0 *types.U32 + if rf, ok := ret.Get(0).(func() *types.U32); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.U32) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetReadProof provides a mock function with given fields: key, blockHash +func (_m *MockSubstrateClient) GetReadProof(key types.StorageKey, blockHash types.Hash) (SubstrateReadProof, error) { + ret := _m.Called(key, blockHash) + + var r0 SubstrateReadProof + if rf, ok := ret.Get(0).(func(types.StorageKey, types.Hash) SubstrateReadProof); ok { + r0 = rf(key, blockHash) + } else { + r0 = ret.Get(0).(SubstrateReadProof) + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.StorageKey, types.Hash) error); ok { + r1 = rf(key, blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSpecName provides a mock function with given fields: +func (_m *MockSubstrateClient) GetSpecName() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetStorageRaw provides a mock function with given fields: key, blockHash +func (_m *MockSubstrateClient) GetStorageRaw(key types.StorageKey, blockHash types.Hash) (*types.StorageDataRaw, error) { + ret := _m.Called(key, blockHash) + + var r0 *types.StorageDataRaw + if rf, ok := ret.Get(0).(func(types.StorageKey, types.Hash) *types.StorageDataRaw); ok { + r0 = rf(key, blockHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.StorageDataRaw) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.StorageKey, types.Hash) error); ok { + r1 = rf(key, blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSystemEventStorageKey provides a mock function with given fields: blockhash +func (_m *MockSubstrateClient) GetSystemEventStorageKey(blockhash types.Hash) (types.StorageKey, error) { + ret := _m.Called(blockhash) + + var r0 types.StorageKey + if rf, ok := ret.Get(0).(func(types.Hash) types.StorageKey); ok { + r0 = rf(blockhash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.StorageKey) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Hash) error); ok { + r1 = rf(blockhash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSystemEvents provides a mock function with given fields: blockHash, section, method +func (_m *MockSubstrateClient) GetSystemEvents(blockHash types.Hash, section string, method string) ([]map[string]interface{}, error) { + ret := _m.Called(blockHash, section, method) + + var r0 []map[string]interface{} + if rf, ok := ret.Get(0).(func(types.Hash, string, string) []map[string]interface{}); ok { + r0 = rf(blockHash, section, method) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]map[string]interface{}) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Hash, string, string) error); ok { + r1 = rf(blockHash, section, method) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetValidationData provides a mock function with given fields: blockHash +func (_m *MockSubstrateClient) GetValidationData(blockHash types.Hash) (*PersistedValidationData, error) { + ret := _m.Called(blockHash) + + var r0 *PersistedValidationData + if rf, ok := ret.Get(0).(func(types.Hash) *PersistedValidationData); ok { + r0 = rf(blockHash) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*PersistedValidationData) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(types.Hash) error); ok { + r1 = rf(blockHash) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Init provides a mock function with given fields: +func (_m *MockSubstrateClient) Init() { + _m.Called() +} + +// SubcribeFinalizedHeadAt provides a mock function with given fields: height, cb +func (_m *MockSubstrateClient) SubcribeFinalizedHeadAt(height uint64, cb func(*types.Hash)) error { + ret := _m.Called(height, cb) + + var r0 error + if rf, ok := ret.Get(0).(func(uint64, func(*types.Hash)) error); ok { + r0 = rf(height, cb) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/chain/pra/substrate/serialize.go b/chain/pra/substrate/serialize.go new file mode 100644 index 00000000..b635d67e --- /dev/null +++ b/chain/pra/substrate/serialize.go @@ -0,0 +1,199 @@ +package substrate + +import ( + "encoding/json" + "reflect" + + "github.com/centrifuge/go-substrate-rpc-client/v3/scale" + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + scalecodec "github.com/itering/scale.go" +) + +func (gsp *GrandpaSignedPrecommit) Decode(decoder scale.Decoder) error { + err := decoder.Decode(&gsp.Precommit) + if err != nil { + return err + } + + err = decoder.Decode(&gsp.Signature) + if err != nil { + return err + } + + err = decoder.Decode(&gsp.Id) + return err +} + +func (gc *GrandpaCommit) Decode(decoder scale.Decoder) error { + err := decoder.Decode(&gc.TargetHash) + if err != nil { + return err + } + + err = decoder.Decode(&gc.TargetNumber) + if err != nil { + return err + } + + err = decoder.Decode(&gc.Precommits) + return err +} + +func (gj *GrandpaJustification) Decode(decoder scale.Decoder) error { + err := decoder.Decode(&gj.Round) + if err != nil { + return err + } + + err = decoder.Decode(&gj.Commit) + if err != nil { + return err + } + + err = decoder.Decode(&gj.VotesAncestries) + return err +} + +func (j *Justification) Decode(decoder scale.Decoder) error { + err := decoder.Decode(&j.ConsensusEngineId) + if err != nil { + return err + } + + err = decoder.Decode(&j.EncodedJustification) + return err +} + +func (fp *FinalityProof) Decode(decoder scale.Decoder) error { + err := decoder.Decode(&fp.Block) + if err != nil { + return err + } + + err = decoder.Decode(&fp.Justification) + if err != nil { + return err + } + + err = decoder.Decode(&fp.UnknownHeaders) + return err +} + +func (event SubstrateEventRecordsRaw) DecodeEventRecords(meta *SubstrateMetaData, records interface{}) error { + return types.EventRecordsRaw(event).DecodeEventRecords(meta, records) +} + +func (sme SignedMessageEnum) Encode(encoder scale.Encoder) error { + var err1, err2 error + if sme.IsPrevote { + err1 = encoder.PushByte(0) + err2 = encoder.Encode(sme.AsPrevote) + } else if sme.IsPrecommit { + err1 = encoder.PushByte(1) + err2 = encoder.Encode(sme.AsPrecommit) + } else if sme.IsPrimaryPropose { + err1 = encoder.PushByte(2) + err2 = encoder.Encode(sme.AsPrimaryPropose) + } + + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + + return nil +} + +func NewEventParaInclusionCandidateIncluded(decodedEvent map[string]interface{}) EventParasInclusionCandidateIncluded { + eventParamsVal := reflect.ValueOf(decodedEvent["params"]) + event := EventParasInclusionCandidateIncluded{} + if eventParamsVal.Kind() == reflect.Slice { + firstEventParam, ok := eventParamsVal.Index(0).Interface().(scalecodec.EventParam) + if !ok { + panic("NewEventParaInclusionCandidateIncluded: not an scalecodec processed event") + } + + b, err := json.Marshal(firstEventParam.Value) + if err != nil { + panic("NewEventParaInclusionCandidateIncluded: not an valid json") + } + + candidateReceipt := CandidateReceipt{} + json.Unmarshal(b, &candidateReceipt) + + event.CandidateReceipt = CandidateReceiptRaw{ + Descriptor: CandidateDescriptorRaw{ + ParaId: candidateReceipt.Descriptor.ParaId, + ParaHead: NewSubstrateHashFromHexString(candidateReceipt.Descriptor.ParaHead), + }, + } + } + + return event +} + +func NewEventEVMLog(decodedEvent map[string]interface{}) EventEVMLog { + eventParamsVal := reflect.ValueOf(decodedEvent["params"]) + event := EventEVMLog{} + if eventParamsVal.Kind() == reflect.Slice { + firstEventParam, ok := eventParamsVal.Index(0).Interface().(scalecodec.EventParam) + if !ok { + panic("NewEventEVMLog: not an scalecodec processed event") + } + + b, err := json.Marshal(firstEventParam.Value) + if err != nil { + panic("NewEventParaInclusionCandidateIncluded: not an valid json") + } + + evmLog := EthereumLog{} + json.Unmarshal(b, &evmLog) + + event.Log.Topics = append(event.Log.Topics, evmLog.Topics...) + event.Log.Address = evmLog.Address + event.Log.Data = evmLog.Data + } + + return event +} + +func NewSubstrateHashFromHexString(s string) SubstrateHash { + hash, err := types.NewHashFromHexString(s) + if err != nil { + panic(err) + } + return hash +} + +func NewVoteMessage(justification *GrandpaJustification, setId types.U64) VoteMessage { + return VoteMessage{ + Message: SignedMessageEnum{ + IsPrecommit: true, + AsPrecommit: SignedMessage{ + TargetHash: justification.Commit.TargetHash, + TargetNumber: justification.Commit.TargetNumber, + }, + }, + Round: justification.Round, + SetId: setId, + } +} + +func NewBlockNumber(blocknumber uint64) SubstrateBlockNumber { + return types.BlockNumber(blocknumber) +} + +func NewEncodedVoteMessage(vm VoteMessage) ([]byte, error) { + return types.EncodeToBytes(vm) +} + +func NewEncodedSubstrateHeader(header SubstrateHeader) ([]byte, error) { + return types.EncodeToBytes(header) +} + +func NewStorageKey(hex string) SubstrateStorageKey { + byte, _ := types.HexDecodeString(hex) + return types.NewStorageKey(byte) +} diff --git a/chain/pra/substrate/serialize_test.go b/chain/pra/substrate/serialize_test.go new file mode 100644 index 00000000..4cab7462 --- /dev/null +++ b/chain/pra/substrate/serialize_test.go @@ -0,0 +1,66 @@ +package substrate + +import ( + "testing" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + scalecodec "github.com/itering/scale.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewNewEncodedVoteMessage(t *testing.T) { + hash, err := types.NewHashFromHexString("0x6521df611a96c354e6937d8c88cc5d37ab8b0d4254b48e7dade360843d71343c") + require.NoError(t, err) + rawByte, err := NewEncodedVoteMessage(VoteMessage{ + Message: SignedMessageEnum{ + IsPrecommit: true, + AsPrecommit: SignedMessage{ + TargetHash: hash, + TargetNumber: types.NewU32(1045791), + }, + }, + Round: types.NewU64(123), + SetId: types.NewU64(123), + }) + + assert.NoError(t, err) + rawHex := types.HexEncodeToString(rawByte) + assert.Equal(t, rawHex, "0x016521df611a96c354e6937d8c88cc5d37ab8b0d4254b48e7dade360843d71343c1ff50f007b000000000000007b00000000000000") +} + +func TestNewEventEVMLog(t *testing.T) { + event := map[string]interface{}{ + "phase": 0, + "extrinsic_idx": 3, + "type": "0a00", + "module_id": "EVM", + "event_idx": 4, + "params": []scalecodec.EventParam{ + { + Type: "Log", + Value: map[string]interface{}{ + "address": "0x0d519eff262b8aa03bba721c9a77a037e17241eb", + "data": "0000000000000000000000000000000000000000000000000000000000000019000000000000000000000000dba3e7d48372b4f3e5cb9baf8cfbe974038a35d800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000003052a3c71ffbab18833722561ab831ad000014d401000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001900000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000", + "topics": []string{ + "0xf6a97944f31ea060dfde0566e4167c1a1082551e64b60ecb14d599a9d023d451", + "0x0000000000000000000000000000000000000000000000000000000000000233", + }, + }, + }, + }, + "topic": make([]string, 0), + } + + evmLogEvent := NewEventEVMLog(event) + assert.Equal(t, evmLogEvent, EventEVMLog{ + Log: EthereumLog{ + Address: "0x0d519eff262b8aa03bba721c9a77a037e17241eb", + Data: "0000000000000000000000000000000000000000000000000000000000000019000000000000000000000000dba3e7d48372b4f3e5cb9baf8cfbe974038a35d800000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000012000000000000000000000003052a3c71ffbab18833722561ab831ad000014d401000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000019000000000000000000000000000000000000000000000000000000000000001900000000000000000000000000000000000000000000000000000000000000030102030000000000000000000000000000000000000000000000000000000000", + Topics: []string{ + "0xf6a97944f31ea060dfde0566e4167c1a1082551e64b60ecb14d599a9d023d451", + "0x0000000000000000000000000000000000000000000000000000000000000233", + }, + }, + }) +} diff --git a/chain/pra/substrate/storage_key.go b/chain/pra/substrate/storage_key.go new file mode 100644 index 00000000..1dd4f0d0 --- /dev/null +++ b/chain/pra/substrate/storage_key.go @@ -0,0 +1,111 @@ +package substrate + +import ( + "strings" + + "github.com/itering/scale.go/types" + "github.com/itering/substrate-api-rpc/hasher" + "github.com/itering/substrate-api-rpc/util" +) + +type StorageKey struct { + EncodeKey string + ScaleType string +} + +type Storage struct { + Prefix string + Method string + Type types.StorageType +} + +func EncodeStorageKey(m *types.MetadataStruct, section, method string, args ...string) (storageKey StorageKey) { + if m == nil { + return + } + + method = upperCamel(method) + prefix, storageType := moduleStorageMapType(m, section, method) + if storageType == nil { + return + } + + mapType := checkoutHasherAndType(storageType) + if mapType == nil { + return + } + + storageKey.ScaleType = mapType.Value + + var hash []byte + sectionHash := hasher.HashByCryptoName([]byte(upperCamel(prefix)), "Twox128") + methodHash := hasher.HashByCryptoName([]byte(method), "Twox128") + + hash = append(sectionHash, methodHash[:]...) + + if len(args) > 0 { + var param []byte + param = append(param, hasher.HashByCryptoName(util.HexToBytes(args[0]), mapType.Hasher)...) + if len(args) == 2 { + param = append(param, hasher.HashByCryptoName(util.HexToBytes(args[1]), mapType.Hasher2)...) + } + hash = append(hash, param[:]...) + } + storageKey.EncodeKey = util.BytesToHex(hash) + return +} + +type storageOption struct { + Value string `json:"value"` + Hasher string `json:"hasher"` + Hasher2 string `json:"hasher_2"` + IsLinked bool `json:"is_linked"` +} + +func checkoutHasherAndType(t *types.StorageType) *storageOption { + option := storageOption{} + switch t.Origin { + case "MapType": + option.Value = t.MapType.Value + option.Hasher = t.MapType.Hasher + case "DoubleMapType": + option.Value = t.DoubleMapType.Value + option.Hasher = t.DoubleMapType.Hasher + option.Hasher2 = t.DoubleMapType.Key2Hasher + option.IsLinked = t.DoubleMapType.IsLinked + case "Map": + option.Value = t.NMapType.Value + if len(t.NMapType.Hashers) == 1 { + option.Hasher = t.NMapType.Hashers[0] + } + if len(t.NMapType.Hashers) == 2 { + option.Hasher2 = t.NMapType.Hashers[1] + } + default: + option.Value = *t.PlainType + option.Hasher = "Twox64Concat" + } + return &option +} + +func upperCamel(s string) string { + if len(s) == 0 { + return "" + } + s = strings.ToUpper(string(s[0])) + string(s[1:]) + return s +} + +func moduleStorageMapType(m *types.MetadataStruct, section, method string) (string, *types.StorageType) { + modules := m.Metadata.Modules + for _, value := range modules { + if strings.EqualFold(strings.ToLower(value.Name), strings.ToLower(section)) { + for _, storage := range value.Storage { + if strings.EqualFold(strings.ToLower(storage.Name), strings.ToLower(method)) { + return value.Prefix, &storage.Type + } + } + } + } + return "", nil +} diff --git a/chain/pra/substrate/type.go b/chain/pra/substrate/type.go new file mode 100644 index 00000000..cd5a1ced --- /dev/null +++ b/chain/pra/substrate/type.go @@ -0,0 +1,196 @@ +package substrate + +import ( + "strings" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" +) + +type SubstrateHash = types.Hash +type SubstrateHeader = types.Header +type SubstrateMetaData = types.Metadata +type SubstrateStorageKey = types.StorageKey +type SubstrateStorageDataRaw = types.StorageDataRaw +type SubstrateBlockNumber = types.BlockNumber +type SubstrateEventRecordsRaw types.EventRecordsRaw +type SubstrateParachainId = U32 +type RelaychainAccountId = types.AccountID +type Signature = types.Signature +type RelaychainSignature = types.Signature +type RuntimeVersion = types.RuntimeVersion +type SetId = types.U64 +type U8 = types.U8 +type U32 = types.U32 +type U64 = types.U64 +type HexString = string +type HeadData = types.Bytes +type CoreIndex = U32 +type GroupIndex = U32 +type Phase = types.Phase +type GrandpaBlockNumber = U32 +type Round = types.U64 + +type SubstrateReadProof struct { + At SubstrateHash `json:"at"` + Proof []HexString `json:"proof"` +} + +type GrandpaPrecommit struct { + TargetHash SubstrateHash + TargetNumber GrandpaBlockNumber +} + +type GrandpaSignedPrecommit struct { + Precommit GrandpaPrecommit + Signature Signature + Id types.AccountID +} + +type GrandpaCommit struct { + TargetHash SubstrateHash + TargetNumber GrandpaBlockNumber + Precommits []GrandpaSignedPrecommit +} + +type GrandpaJustification struct { + Round Round + Commit GrandpaCommit + VotesAncestries []SubstrateHeader +} + +type Justification struct { + ConsensusEngineId [4]U8 + EncodedJustification GrandpaJustification +} + +type WestendJustification struct { + ConsensusEngineId [2]U8 + EncodedJustification GrandpaJustification +} + +type WestendFinalityProof struct { + Block SubstrateHash + Justification WestendJustification + UnknownHeaders []SubstrateHeader +} + +type FinalityProof struct { + Block SubstrateHash + Justification Justification + UnknownHeaders []SubstrateHeader +} + +type PersistedValidationData struct { + ParentHead types.Bytes + RelayParentNumber U32 + RelayParentStorageRoots SubstrateHash + MaxPovSize U32 +} + +type SignedMessage struct { + TargetHash SubstrateHash + TargetNumber U32 +} + +type SignedMessageEnum struct { + IsPrevote bool + AsPrevote SignedMessage + IsPrecommit bool + AsPrecommit SignedMessage + IsPrimaryPropose bool + AsPrimaryPropose SignedMessage +} + +type VoteMessage struct { + Message SignedMessageEnum + Round U64 + SetId U64 +} + +type CandidateDescriptor struct { + ParaId U32 `json:"para_id"` + RelayParent HexString `json:"relay_parent"` + Collator HexString `json:"collator"` + PersistedValidationDataHash HexString `json:"persisted_validation_data_hash"` + PovHash HexString `json:"pov_hash"` + ErasureRoot HexString `json:"erasure_root"` + Signature HexString `json:"signature"` + ParaHead HexString `json:"para_head"` + ValidationCodeHash HexString `json:"validation_code_hash"` +} + +type CandidateReceipt struct { + Descriptor CandidateDescriptor `json:"descriptor"` + CommitmentsHash HexString `json:"commitments_hash"` +} + +type ParasInclusionCandidateIncludedParams struct { + CandidateReceipt CandidateReceipt `json:"polkadot_primitives:v1:CandidateReceipt@86"` + HeadData HeadData + CoreIndex CoreIndex + GroupIndex GroupIndex +} + +type CandidateDescriptorRaw struct { + ParaId types.U32 + RelayParent SubstrateHash + Collator types.AccountID + PersistedValidationDataHash SubstrateHash + PovHash SubstrateHash + ErasureRoot SubstrateHash + Signature Signature + ParaHead SubstrateHash + ValidationCodeHash SubstrateHash +} + +type CandidateReceiptRaw struct { + Descriptor CandidateDescriptorRaw + CommitmentsHash SubstrateHash +} + +type EventParasInclusionCandidateIncluded struct { + Phase Phase + CandidateReceipt CandidateReceiptRaw + HeadData HeadData + CoreIndex CoreIndex + GroupIndex GroupIndex + Topics []SubstrateHash +} + +type EthereumLog struct { + Address string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` +} + +type EventEVMLog struct { + Phase string + Log EthereumLog `json:"log"` + Topics []string +} + +func (eel *EventEVMLog) CompareAddressCaseInsensitive(address string) bool { + return strings.EqualFold(eel.Log.Address, address) +} + +type SubstrateClient interface { + Init() + Call(result interface{}, method string, args ...interface{}) error + GetFinalizedHead() (SubstrateHash, error) + GetHeader(blockHash SubstrateHash) (*SubstrateHeader, error) + GetHeaderLatest() (*SubstrateHeader, error) + GetBlockHash(blockNumber uint64) (SubstrateHash, error) + GetStorageRaw(key SubstrateStorageKey, blockHash SubstrateHash) (*SubstrateStorageDataRaw, error) + GetBlockHashLatest() (SubstrateHash, error) + GetSpecName() string + GetReadProof(key SubstrateStorageKey, blockHash SubstrateHash) (SubstrateReadProof, error) + GetJustificationsAndUnknownHeaders(blockNumber SubstrateBlockNumber) (*GrandpaJustification, []SubstrateHeader, error) + GetGrandpaCurrentSetId(blockHash SubstrateHash) (SetId, error) + GetValidationData(blockHash SubstrateHash) (*PersistedValidationData, error) + GetParachainId() (*SubstrateParachainId, error) + SubcribeFinalizedHeadAt(height uint64, cb func(*SubstrateHash)) error + GetBlockHeaderByBlockNumbers(blockNumbers []SubstrateBlockNumber) ([]SubstrateHeader, error) + GetBlockHashesByRange(start SubstrateBlockNumber, end SubstrateBlockNumber) ([]SubstrateHash, error) + GetSystemEventStorageKey(blockhash SubstrateHash) (SubstrateStorageKey, error) + GetSystemEvents(blockHash SubstrateHash, section string, method string) ([]map[string]interface{}, error) +} diff --git a/chain/pra/substrate/type_test.go b/chain/pra/substrate/type_test.go new file mode 100644 index 00000000..1bd322dc --- /dev/null +++ b/chain/pra/substrate/type_test.go @@ -0,0 +1,42 @@ +package substrate + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + "github.com/stretchr/testify/assert" +) + +func TestDecodeFinalityProof(t *testing.T) { + dir, err := os.Getwd() + assert.NoError(t, err) + // curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"grandpa_proveFinality", "params": [8007753], "id":50}' https://kusama-rpc.polkadot.io | jq -rj '.result' > chain/pra/assets/kusama_encoded_finalityproofs + buf, err := ioutil.ReadFile(filepath.Join(dir, "./assets/kusama_encoded_finalityproofs")) + assert.NoError(t, err) + hexStr := string(buf) + + /** + polkadot js type + [ + { + "Justification": "(ConsensusEngineId, GrandpaJustification)" + }, + { + "FinalityProof": { + "block": "Hash", + "justification": "Justification", + "unknown_headers": "Vec
" + } + } + ] + **/ + fp := &FinalityProof{} + err = types.DecodeFromHexString(hexStr, fp) + assert.NoError(t, err) + assert.Equal(t, fp.Justification.EncodedJustification.Round, types.NewU64(5152)) + assert.Equal(t, fp.Justification.EncodedJustification.Commit.TargetHash.Hex(), "0x7412c2d47c83266de081fbc0efc5c103a06ac6afbd93ec932df0aee80f0df856") + assert.Equal(t, fp.Justification.EncodedJustification.Commit.TargetNumber, types.NewU32(8010648)) +} diff --git a/chain/pra/substrate/types.go b/chain/pra/substrate/types.go new file mode 100644 index 00000000..57e1e546 --- /dev/null +++ b/chain/pra/substrate/types.go @@ -0,0 +1,12 @@ +package substrate + +// Because we're trying to build btp as executable, using json assets can't be embbeded, +// So, we put json as string I use them, the origins are from https://github.com/itering/scale.go/tree/master/network +var typesDefinitionMap = map[string]string{ + "moonbase": `{"Weight":"u64","CompactAssignments":"CompactAssignmentsLatest","RefCount":"u32","Box<::Call>":"Call","AccountInfo":"AccountInfoWithTripleRefCount","DispatchResult":{"type":"enum","type_mapping":[["Ok","Null"],["Error","DispatchError"]]},"TransactionRecoveryId":"U64","TransactionSignature":{"type":"struct","type_mapping":[["v","TransactionRecoveryId"],["r","H256"],["s","H256"]]},"RoundInfo":{"type_mapping":[["current","RoundIndex"],["first","BlockNumber"],["length","u32"]],"type":"struct"},"Candidate":{"type_mapping":[["id","AccountId"],["fee","Perbill"],["bond","Balance"],["nominators","Vec"],["total","Balance"],["state","CollatorStatus"]],"type":"struct"},"TxPoolResultStatus":{"type_mapping":[["pending","U256"],["queued","U256"]],"type":"struct"},"CollatorStatus":{"type_mapping":[["Active","NULL"],["Idle","Null"],["Leaving","RoundIndex"]],"type":"enum"},"PoolTransaction":{"type_mapping":[["hash","H256"],["nonce","U256"],["block_hash","Option"],["block_number","Option"],["from","H160"],["to","Option"],["value","U256"],["gas_price","U256"],["gas","U256"],["input","Bytes"]],"type":"struct"},"ExtrinsicSignature":"EthereumSignature","Collator":{"type_mapping":[["id","AccountId"],["bond","Balance"],["nominators","Vec"],["total","Balance"],["state","CollatorStatus"]],"type":"struct"},"CollatorSnapshot":{"type_mapping":[["bond","Balance"],["nominators","Vec"],["total","Balance"]],"type":"struct"},"Address":"AccountId","SystemInherentData":{"type_mapping":[["validation_data","PersistedValidationData"],["relay_chain_state","StorageProof"],["downward_messages","Vec"],["horizontal_messages","BTreeMap>"]],"type":"struct"},"OrderedSet":"Vec","AccountId":"EthereumAccountId","Account":{"type_mapping":[["nonce","U256"],["balance","u128"]],"type":"struct"},"LookupSource":"AccountId","InflationInfo":{"type_mapping":[["expect","RangeBalance"],["round","RangePerbill"]],"type":"struct"},"Summary":"Bytes","Range":"RangeBalance","TxPoolResultInspect":{"type_mapping":[["pending","HashMap>"],["queued","HashMap>"]],"type":"struct"},"RangeBalance":{"type_mapping":[["min","Balance"],["ideal","Balance"],["max","Balance"]],"type":"struct"},"RoundIndex":"u32","Nominator":{"type_mapping":[["nominations","Vec"],["total","Balance"]],"type":"struct"},"Balance":"u128","Bond":{"type_mapping":[["owner","AccountId"],["amount","Balance"]],"type":"struct"},"RangePerbill":{"type_mapping":[["min","Perbill"],["ideal","Perbill"],["max","Perbill"]],"type":"struct"},"TxPoolResultContent":{"type_mapping":[["pending","HashMap>"],["queued","HashMap>"]],"type":"struct"},"AuthorId":"AccountId32","RegistrationInfo":{"type_mapping":[["account","AccountId"],["deposit","Balance"]],"type":"struct"},"AssetRegistrarMetadata":{"type_mapping":[["name","Vec"],["symbol","Vec"],["decimals","u8"],["is_frozen","bool"]],"type":"struct"},"Collator2":{"type_mapping":[["id","AccountId"],["bond","Balance"],["nominators","Vec"],["top_nominators","Vec"],["bottom_nominators","Vec"],["total_counted","Balance"],["total_backing","Balance"],["state","CollatorStatus"]],"type":"struct"},"NominatorAdded":{"type":"enum","type_mapping":[["AddedToTop","Balance"],["AddedToBottom","Null"]]},"CurrencyId":{"type":"enum","type_mapping":[["SelfReserve","Null"],["OtherReserve","u128"]]},"AssetType":{"type":"enum","type_mapping":[["Xcm","MultiLocation"]]},"RelayChainAccountId":"AccountId32","AssetInstance":"AssetInstanceV0","MultiAsset":"MultiAssetV0","Xcm":"XcmV0","XcmOrder":"XcmOrderV0","MultiLocation":"MultiLocationV0","AssetId":"u128","TAssetBalance":"u128"}`, + "moonriver": `{"Weight":"u64","CompactAssignments":"CompactAssignmentsLatest","RefCount":"u32","RoundInfo":{"type_mapping":[["current","RoundIndex"],["first","BlockNumber"],["length","u32"]],"type":"struct"},"Candidate":{"type_mapping":[["id","AccountId"],["fee","Perbill"],["bond","Balance"],["nominators","Vec"],["total","Balance"],["state","CollatorStatus"]],"type":"struct"},"RewardInfo":{"type_mapping":[["total_reward","Balance"],["claimed_reward","Balance"]],"type":"struct"},"TxPoolResultStatus":{"type_mapping":[["pending","U256"],["queued","U256"]],"type":"struct"},"CollatorStatus":{"type_mapping":[["Active","NULL"],["Idle","Null"],["Leaving","RoundIndex"]],"type":"enum"},"PoolTransaction":{"type_mapping":[["hash","H256"],["nonce","U256"],["block_hash","Option"],["block_number","Option"],["from","H160"],["to","Option"],["value","U256"],["gas_price","U256"],["gas","U256"],["input","Bytes"]],"type":"struct"},"AccountInfo":"AccountInfoWithTripleRefCount","Collator2":{"type_mapping":[["id","AccountId"],["bond","Balance"],["nominators","Vec"],["top_nominators","Vec"],["bottom_nominators","Vec"],["total_counted","Balance"],["total_backing","Balance"],["state","CollatorStatus"]],"type":"struct"},"ExtrinsicSignature":"EthereumSignature","NominatorAdded":{"type":"enum","type_mapping":[["AddedToTop","Balance"],["AddedToBottom","Null"]]},"RegistrationInfo":{"type_mapping":[["account","AccountId"],["deposit","Balance"]],"type":"struct"},"Collator":{"type_mapping":[["id","AccountId"],["bond","Balance"],["nominators","Vec"],["total","Balance"],["state","CollatorStatus"]],"type":"struct"},"CollatorSnapshot":{"type_mapping":[["bond","Balance"],["nominators","Vec"],["total","Balance"]],"type":"struct"},"Address":"AccountId","SystemInherentData":{"type_mapping":[["validation_data","PersistedValidationData"],["relay_chain_state","StorageProof"],["downward_messages","Vec"],["horizontal_messages","BTreeMap>"]],"type":"struct"},"OrderedSet":"Vec","AccountId":"EthereumAccountId","Account":{"type_mapping":[["nonce","U256"],["balance","u128"]],"type":"struct"},"RelayChainAccountId":"AccountId32","LookupSource":"AccountId","InflationInfo":{"type_mapping":[["expect","RangeBalance"],["annual","RangePerbill"],["round","RangePerbill"]],"type":"struct"},"AccountId32":"H256","Summary":"Bytes","Range":"RangeBalance","TxPoolResultInspect":{"type_mapping":[["pending","HashMap>"],["queued","HashMap>"]],"type":"struct"},"RangeBalance":{"type_mapping":[["min","Balance"],["ideal","Balance"],["max","Balance"]],"type":"struct"},"RoundIndex":"u32","ParachainBondConfig":{"type_mapping":[["account","AccountId"],["percent","Percent"]],"type":"struct"},"Nominator":{"type_mapping":[["nominations","Vec"],["total","Balance"]],"type":"struct"},"Balance":"u128","Bond":{"type_mapping":[["owner","AccountId"],["amount","Balance"]],"type":"struct"},"RangePerbill":{"type_mapping":[["min","Perbill"],["ideal","Perbill"],["max","Perbill"]],"type":"struct"},"AuthorId":"AccountId32","TxPoolResultContent":{"type_mapping":[["pending","HashMap>"],["queued","HashMap>"]],"type":"struct"}}`, + "moonbeam": `{"Weight":"u64","CompactAssignments":"CompactAssignmentsLatest","RefCount":"u32","Box<::Call>":"Call","DispatchResult":{"type":"enum","type_mapping":[["Ok","Null"],["Error","DispatchError"]]},"TransactionRecoveryId":"U64","TransactionSignature":{"type":"struct","type_mapping":[["v","TransactionRecoveryId"],["r","H256"],["s","H256"]]},"AccountId":"EthereumAccountId","Address":"AccountId","Balance":"u128","LookupSource":"AccountId","Account":{"type":"struct","type_mapping":[["nonce","U256"],["balance","u128"]]},"RoundIndex":"u32","Candidate":{"type":"struct","type_mapping":[["id","AccountId"],["fee","Perbill"],["bond","Balance"],["nominators","Vec"],["total","Balance"],["state","ValidatorStatus"]]},"Bond":{"type":"struct","type_mapping":[["owner","AccountId"],["amount","Balance"]]},"ValidatorStatus":{"type":"enum","type_mapping":[["Active","NULL"],["Idle","NULL"],["Leaving","RoundIndex"]]}}`, + "westend": `{"Address":"AccountId","BlockNumber":"u32","LeasePeriod":"BlockNumber","Keys":"SessionKeysPolkadot","Weight":"u32","Weight#3-?":"u64","DispatchInfo":{"type":"struct","type_mapping":[["weight","Weight"],["class","DispatchClass"],["paysFee","Pays"]]},"DispatchResult":{"type":"enum","type_mapping":[["Ok","Null"],["Error","DispatchError"]]},"ProxyType":{"type":"enum","value_list":["Any","NonTransfer","Staking","SudoBalances","IdentityJudgement","CancelProxy"]},"CompactAssignments#43-?":"CompactAssignmentsLatest","RefCount#45-?":"u32","Box<::Call>":"Call","AccountInfo#48-49":"AccountInfoWithProviders","Address#48-?":"MultiAddress","LookupSource#48-?":"MultiAddress","ValidatorPrefs#48-?":"ValidatorPrefsWithBlocked","Keys#48-?":{"type":"struct","type_mapping":[["grandpa","AccountId"],["babe","AccountId"],["im_online","AccountId"],["para_validator","AccountId"],["para_assignment","AccountId"],["authority_discovery","AccountId"]]},"AccountInfo#50-?":"AccountInfoWithTripleRefCount","AssetInstance":"AssetInstanceV0","MultiAsset":"MultiAssetV0","Xcm":"XcmV0","XcmOrder":"XcmOrderV0","MultiLocation":"MultiLocationV0"}`, + "kusama": `{"Keys":"SessionKeysPolkadot","ValidatorPrefs":{"type":"struct","type_mapping":[["Commission","Compact"]]},"Timepoint":{"type":"struct","type_mapping":[["height","BlockNumber"],["index","u32"]]},"Multisig":{"type":"struct","type_mapping":[["when","Timepoint"],["deposit","Balance"],["depositor","AccountId"],["approvals","Vec"]]},"BalanceLock":{"type":"struct","type_mapping":[["id","LockIdentifier"],["amount","Balance"],["reasons","Reasons"]]},"ReferendumInfo":{"type":"enum","type_mapping":[["Ongoing","ReferendumStatus"],["Finished","ReferendumInfoFinished"]]},"DispatchResult":{"type":"enum","type_mapping":[["Ok","Null"],["Error","DispatchError"]]},"Heartbeat":{"type":"struct","type_mapping":[["blockNumber","BlockNumber"],["networkState","OpaqueNetworkState"],["sessionIndex","SessionIndex"],["authorityIndex","AuthIndex"]]},"Weight#1058-?":"u64","Heartbeat#1062-?":{"type":"struct","type_mapping":[["blockNumber","BlockNumber"],["networkState","OpaqueNetworkState"],["sessionIndex","SessionIndex"],["authorityIndex","AuthIndex"],["validatorsLen","u32"]]},"ReferendumInfo":{"type":"enum","type_mapping":[["Ongoing","ReferendumStatus"],["Finished","ReferendumInfoFinished"]]},"DispatchInfo#1019-1061":"DispatchInfo258","DispatchInfo#1062-?":{"type":"struct","type_mapping":[["weight","Weight"],["class","DispatchClass"],["paysFee","Pays"]]},"ReferendumInfo#1019-1054":{"type":"struct","type_mapping":[["end","BlockNumber"],["proposal","Proposal"],["threshold","VoteThreshold"],["delay","BlockNumber"]]},"DispatchError#1019-1031":{"type":"struct","type_mapping":[["module","Option"],["error","u8"]]},"ProxyType":{"type":"enum","value_list":["Any","NonTransfer","Governance","Staking","IdentityJudgement","CancelProxy","Auction"]},"Address#1050-2027":"AccountId","Box":"BoxProposal","CompactAssignments#2023-9000":"CompactAssignmentsLatest","RefCount":"u32","Box<::Call>":"Call","Box<>::Proposal>":"Proposal","AccountInfo":"AccountInfoWithTripleRefCount","Address#2028-?":"MultiAddress","LookupSource#2028-?":"MultiAddress","Keys#2028-2029":{"type":"struct","type_mapping":[["grandpa","AccountId"],["babe","AccountId"],["im_online","AccountId"],["para_validator","AccountId"],["para_assignment","AccountId"],["authority_discovery","AccountId"]]},"ValidatorPrefs#2028-?":"ValidatorPrefsWithBlocked","Keys#2030-?":"SessionKeys6","CompactAssignments#9010-?":"CompactAssignmentsWith24","AssetInstance#9010-9090":"AssetInstanceV0","MultiAsset#9010-9090":"MultiAssetV0","Xcm#9010-9090":"XcmV0","XcmOrder#9010-9090":"XcmOrderV0","MultiLocation#9010-9090":"MultiLocationV0","AssetInstance#9100-9100":"AssetInstanceV1","MultiAsset#9100-9100":"MultiAssetV1","Xcm#9100-9100":"XcmV1","XcmOrder#9100-9100":"XcmOrderV1","MultiLocation#9100-9100":"MultiLocationV1"}`, + "polkadot": `{"Address":"AccountId","BlockNumber":"U32","LeasePeriod":"BlockNumber","Weight":"u64","Keys":"SessionKeysPolkadot","DispatchInfo":{"type":"struct","type_mapping":[["weight","Weight"],["class","DispatchClass"],["paysFee","Pays"]]},"DispatchResult":{"type":"enum","type_mapping":[["Ok","Null"],["Error","DispatchError"]]},"Timepoint":{"type":"struct","type_mapping":[["height","BlockNumber"],["index","u32"]]},"Multisig":{"type":"struct","type_mapping":[["when","Timepoint"],["deposit","Balance"],["depositor","AccountId"],["approvals","Vec"]]},"BalanceLock":{"type":"struct","type_mapping":[["id","LockIdentifier"],["amount","Balance"],["reasons","Reasons"]]},"ProxyType":{"type":"enum","value_list":["Any","NonTransfer","Governance","Staking","DeprecatedSudoBalances","IdentityJudgement","CancelProxy"]},"ReferendumInfo":{"type":"enum","type_mapping":[["Ongoing","ReferendumStatus"],["Finished","ReferendumInfoFinished"]]},"CompactAssignments#23-?":"CompactAssignmentsLatest","RefCount":"u32","Box<::Call>":"Call","Box<>::Proposal>":"Proposal","AccountInfo":"AccountInfoWithProviders","Address#28-?":"MultiAddress","LookupSource#28-?":"MultiAddress","Keys#28-29":{"type":"struct","type_mapping":[["grandpa","AccountId"],["babe","AccountId"],["im_online","AccountId"],["para_validator","AccountId"],["para_assignment","AccountId"],["authority_discovery","AccountId"]]},"ValidatorPrefs#28-?":"ValidatorPrefsWithBlocked","Keys#30-?":"SessionKeys6","AccountInfo#30-?":"AccountInfoWithTripleRefCount","AssetInstance":"AssetInstanceV0","MultiAsset":"MultiAssetV0","Xcm":"XcmV0","XcmOrder":"XcmOrderV0","MultiLocation":"MultiLocationV0"}`, +} diff --git a/chain/pra/test_data.json b/chain/pra/test_data.json new file mode 100644 index 00000000..4041880a --- /dev/null +++ b/chain/pra/test_data.json @@ -0,0 +1,17 @@ +[ + { + "height": 273040, + "bu": "lM4ABCqQxCCz2gI1sRFBVqF9zYauVnA_pPya0ALroW2WoTiTnJhCZ8UBwPkBvQKDBCqQhwXF6XrsyV-VACHnVI28SYorCVUaTE58HuDSO9UyoLoPCgf44PW2ptG368Lia8jXQ9RjwPavVgz1JFCW6GI3oGowHKpyKrZ3NRTRs56OdynUkl2NWSa9ZpFS-ZEBtPcvoFe4WN7R18zditjOvuAdfGIV5wq5DxRGMITUh2bedtQh-ACgt5hsbyNv2QtQY-ooqFjKLRXjuwQp6ICVyLz7S05oAGqiAQAgcEgsGg8DBEEAMIhsOh8QAEKiMGicUi8YjMHIEagcBLjv-O2gRXQr9rO7BG9KRZORmp0JeGFE8UW4DF0dt3nOVZHo9If4AKCghs56w8GVDCA2GG-VEAJHbyDr21Ku7K74O97lvESo67in-KWgEPklE8Uu_Qnw-xfQKvvURspKTzKWVF20OIiEl54NWOagucdwRtb2iu3aAi5W6W-FGokBlemyozvizlIzXN0YVVOgTKlHUfMNPo2obyx5XTtkVtqC2oTmAXvbaA4DWpTdVFWgF8bVvSlZ3lq91NYTkAVZTCJfEINhG4xPR58iLJQtp12gQGVayuSjhE0mTRZutYsj47e6hCru1WgQV7jzg5EwTODFClT5ClG5AcD5Ab0CgwQqkIcFxel67MlflQAh51SNvEmKKwlVGkxOfB7g0jvVMqC6DwoH-OD1tqbRt-vC4mvI10PUY8D2r1YM9SRQluhiN6BqMByqciq2dzUU0bOejncp1JJdjVkmvWaRUvmRAbT3L6BXuFje0dfM3YrYzr7gHXxiFecKuQ8URjCE1Idm3nbUIfgAoLeYbG8jb9kLUGPqKKhYyi0V47sEKeiAlci8-0tOaABqogEAIHBILBoPAwRBADCIbDofEABCojBonFIvGIzByBGoHAS47_jtoEV0K_azuwRvSkWTkZqdCXhhRPFFuAxdHbd5zlWR6PSH-ACgoIbOesPBlQwgNhhvlRACR28g69tSruyu-Dve5bxEqOu4p_iloBD5JRPFLv0J8PsX0Cr71EbKSk8yllRdtDiIhJeeDVjmoLnHcEbW9ort2gIuVulvhRqJAZXpsqM74s5SM1zdGFVToEypR1HzDT6NqG8seV07ZFbagtqE5gF722gOA1qU3VRVoBfG1b0pWd5avdTWE5AFWUwiXxCDYRuMT0efIiyULaddoEBlWsrko4RNJk0WbrWLI-O3uoQq7tVoEFe484ORMEzguQbI-QbFAOIBoDEwfuaIoXoTC9yImew3WNNScSzWVsWvmO41rjA_8c6l-Qae-EuHBcXpewtdOLhBlXfOAiFzVyD1vTJrVMwidvtV3Ep4gsG0sJ0hg3n2IAVgcSTLLQ4OS4KHDp2m6ryYL_9Fj_UDJC0BCnBaCMIg5gH4S4cFxel7C1fYuEFNR2auF9DST1UOeawHc_ocs5nF_cvfCxmmOKU3EW6az1aiZKR4kAR5Mm0gJ-Y2WVjA98_VzOwLd6apbhel8TmpAPhLhwXF6XsLVgC4Qe43WwMU6aqPm5JJGxXh52ZBMNNju-CnvMveSdF8HxnAIwdQjQTijQuWLtS0qSJYd8jYWf-gqSVsFy-vgcNWq3wA-EuHBcXpewtkM7hBQkAa_yRi56Rhc42qDl5MFMu2NMLxcDJRKvbjQg7fCd1cXUxtrI9RvV3PWi6jxwdW98Yj3-SJHj3c6dyzpfx-pAD4S4cFxel7C1lRuEGQVkw0H1UJ0WrcteCywiln1jm9WBehiexIETpjeBCSpyzgrp1L14uiYPuF3oqOwADNoQFTjJ09GXRkpcr0A9NjAfhLhwXF6XsLWBu4QWzAT5hyEGaZUGmeTZOlnacl7bN_ZnhDY8Sg9b7i6AbGK2lxbOXs8loHqqLA1iQJMZ7TM9NVRgqrrPL9yE0meq4A-EuHBcXpewtcbbhBP16ZD3tmhW9ICnuiu3knTfHL4g3D33SMr-YVZIZ8HgV0syubqw-MJdzqhm0zEYdqdOjRRji5ZWxxe37o83vRFwH4S4cFxel7C1oNuEE_b_yqeUMEHYWGmwGmMEjsZCJygmLVxDYNUa447DDybkGoNScAkGEjKIG0SjlSzBE1J5ait8FwSRuxZO7QWLV0APhLhwXF6XsLYNi4QVbcr2yvzZzy01yoyHbNESjaC3TSuoxxctv7ziT6Ng7BH3Vu1HhV03leHtLgmKwBBm7dwbTiklcfNR8nlONFfR0B-EuHBcXpewtTSbhBUyp-QnAX3uQItZdgYW7zHAmGTk0Tsd-kQ7xEB61SWz5Mggpy7Q3Vjn2KsIkiQO_Olq2V-bmL45U8Mdkwx5faJAH4S4cFxel7C14EuEGOXstHNUydRNB8QCTn1W1hE0_Y7gsSBX2zNqdQCHk3106u_BW6bfLCJQ-nRp1NLjHiEgcUYOwZE6Soem1M5bBtAfhLhwXF6XsLWbq4QTvU0TQ4qBBYkXcyx0nmTBgRFJhkUgecZwQUc7MRTXSyJVeYhxgsrWE69ROAs6jWmcs-f1dnrhJIkoTuMU5JzN4B-EuHBcXpewtZRrhBdND5uiq_3mUjTkU66UVb9yBf-SoETq-DExk2HEVT34YgQmROcb81NVVozdHGWwCEKfwLB-s_glX1-dZIsITjKgH4S4cFxel7C2EkuEG8DkOdumLNsNnTmR1fiYoDNK_ETetKq8JSd-tt3q-oLUACVA6jHDztM8k0wk752HJ441dRMLaCE6xhBR5iVgwvAfhLhwXF6XsLWF24QYDy8t7Eo-tvV0UAgkyaxbvzY66TeA9IAqWattsZ7o8ZC3t9yn184y4VZXXLWjyU2-fB134miwcnTEGpf0G6PREB-EuHBcXpewtbArhBAk2HW7L9tPWkFYRAQ6yBHqL_6kDbjx4sn8uc6b-sx6Rc7meokEuFFOsFwf8FhXNCgmzqFggBcmYGAah7tx8vxwH4S4cFxel7C1p9uEFWyjBXLZ1sSXZTalaWLAz6hgwvJYVsRCuXinZEXbxpsUAxK7V97DNo3qB-0fLLrJshYNLT3it23N2u6r9SrIdVAfhLhwXF6XsLWc-4QRvC6kGS4iY-gKF50-XSx8d4GXlLGM6-g1oGK1IejJ-cd7DjllCLhxqLS8H7KadZl7rWAGuBAkyANNVpTBWZ2UgA-EuHBcXpewtWl7hBWuYSA-7zKpiLXg0V6Y3GFI-OJ4585KeoeDEPEfSDY8ZZpW6jaCEfTKWua12s68W64eQWYTOwb3olSI1Df0nTGQD4S4cFxel7C36NuEETLSVoBicFZ64u7uVX3IqBol9QJ-Wdv80HnBYYCBMf1gHgWxgQMkO76E9Wg-r7H3SVr-cfsiHUxD2FR5XFcklDAfhLhwXF6XsLVy24QXCd1c4RS4PDuNacJ9-tUGj436p9PaKeHHvvqRBFoagmKCq5S0DTP82OnzUHZxinY0o7aS685YV5LcX5lNvZWi0B-EuHBcXpewtiN7hBrbCYPV3q2a7Eev0bzogSOL4uJRkV9-oVpPpM2HEkg_cJ2b0c4c4t2hYqTg47vyw3mLifHwUVCQeLcmVjt3cabQC5AcD5Ab0CgwQqkIcFxel67MlflQAh51SNvEmKKwlVGkxOfB7g0jvVMqC6DwoH-OD1tqbRt-vC4mvI10PUY8D2r1YM9SRQluhiN6BqMByqciq2dzUU0bOejncp1JJdjVkmvWaRUvmRAbT3L6BXuFje0dfM3YrYzr7gHXxiFecKuQ8URjCE1Idm3nbUIfgAoLeYbG8jb9kLUGPqKKhYyi0V47sEKeiAlci8-0tOaABqogEAIHBILBoPAwRBADCIbDofEABCojBonFIvGIzByBGoHAS47_jtoEV0K_azuwRvSkWTkZqdCXhhRPFFuAxdHbd5zlWR6PSH-ACgoIbOesPBlQwgNhhvlRACR28g69tSruyu-Dve5bxEqOu4p_iloBD5JRPFLv0J8PsX0Cr71EbKSk8yllRdtDiIhJeeDVjmoLnHcEbW9ort2gIuVulvhRqJAZXpsqM74s5SM1zdGFVToEypR1HzDT6NqG8seV07ZFbagtqE5gF722gOA1qU3VRVoBfG1b0pWd5avdTWE5AFWUwiXxCDYRuMT0efIiyULaddoEBlWsrko4RNJk0WbrWLI-O3uoQq7tVoEFe484ORMEzg", + "rps": null + }, + { + "height": 273041, + "bu": "lM4ABCqRxCCynAE2hWx2T9-Q5fQXXP1SJFq4YJXM3zSGm5OH2MKBnMUBwPkBvQKDBCqRhwXF6XsLWe6VAAYDXnR1NVcDsR7ioe4p29q-dTiOoLPaAjWxEUFWoX3Nhq5WcD-k_JrQAuuhbZahOJOcmEJnoHC6yjhVyQKjD7dnifcQ4mCuMAHDK0z0ulBdtkiotvbUoFe4WN7R18zditjOvuAdfGIV5wq5DxRGMITUh2bedtQh-ACg-EawmyNByie8rGzVKwNx8GcGnhN91caJB7mzfKop3aSiAQAgcEgsGg8DBEEAMIhsOh8QAEKiMGicUi8YjMHIEagcBLjv-O2gRXQr9rO7BG9KRZORmp0JeGFE8UW4DF0dt3nOVZHo9If4AKCghs56w8GVDCA2GG-VEAJHbyDr21Ku7K74O97lvESo67in-KWgISWT8X2ZLLCC-ybsec_K3Bf3gE-H9pwJbWMgNsGSeEmgucdwRtb2iu3aAi5W6W-FGokBlemyozvizlIzXN0YVVOgTKlHUfMNPo2obyx5XTtkVtqC2oTmAXvbaA4DWpTdVFWgF8bVvSlZ3lq91NYTkAVZTCJfEINhG4xPR58iLJQtp12gQGVayuSjhE0mTRZutYsj47e6hCru1WgQV7jzg5EwTODFCgf5CgS5AcD5Ab0CgwQqkYcFxel7C1nulQAGA150dTVXA7Ee4qHuKdvavnU4jqCz2gI1sRFBVqF9zYauVnA_pPya0ALroW2WoTiTnJhCZ6Bwuso4VckCow-3Z4n3EOJgrjABwytM9LpQXbZIqLb21KBXuFje0dfM3YrYzr7gHXxiFecKuQ8URjCE1Idm3nbUIfgAoPhGsJsjQconvKxs1SsDcfBnBp4TfdXGiQe5s3yqKd2kogEAIHBILBoPAwRBADCIbDofEABCojBonFIvGIzByBGoHAS47_jtoEV0K_azuwRvSkWTkZqdCXhhRPFFuAxdHbd5zlWR6PSH-ACgoIbOesPBlQwgNhhvlRACR28g69tSruyu-Dve5bxEqOu4p_iloCElk_F9mSywgvsm7HnPytwX94BPh_acCW1jIDbBknhJoLnHcEbW9ort2gIuVulvhRqJAZXpsqM74s5SM1zdGFVToEypR1HzDT6NqG8seV07ZFbagtqE5gF722gOA1qU3VRVoBfG1b0pWd5avdTWE5AFWUwiXxCDYRuMT0efIiyULaddoEBlWsrko4RNJk0WbrWLI-O3uoQq7tVoEFe484ORMEzguQZ7-QZ4AOIBoLXVbQIW-11DexBzW3W8aNfeDTIwIZXV_DrtiLEONFu--QZR-EuHBcXpeyoKKrhBEnKwhqEEV-aeO0DRQU4bTMUsynCSo7dqSwspKmSVRm998Cu0zXGuqWX29eKz7eABZOhZ8JJZMaEtKvVlpmMnmQD4S4cFxel7Kf1MuEFFiPaP3ramX3tXcIUNgtgVKUrW7nNxm-yIQ0pNwlE_6TUkPS1VAIqQKyi5QI7pMlMLyCH3pSOFgtcoAGjhYRFRAPhLhwXF6XsqFY-4QbSjaH29r-q6SOa2T1C_ayOpP6FdUNdYgieHtfyponFRUOcLD-htnH9RwO04IM0eeRe_Y37e4AVbirnLNP8LlUEA-EuHBcXpeyoxE7hBmevwE-9uD4st36ZOCj0ZxYc5E0e_khsec3IsKYZ-mHN9A6NmrpBBO2pEnv9CcLt65qkgOdMuv-yECBUizYFCfwH4S4cFxel7KgNLuEG7bQw2KXr-9mLgyXW4jfn-ShLji6g5fzJmjPnnC6BbSkZuihqMiTrU2d7R1UFeXiYAQ7HnpgP-iKfAhQn9g5EAAfhLhwXF6XsqSaq4QQZC1X9efB__id7mpGMABQjEb2StfmavUKHbsczv_4LqVFMIIzWxKMzd-mvHjFscl6221SbP6Ud2vsDIYGzvB40B-EuHBcXpeyn_8rhBmZEJyv_dDM5Rc9sXyoLNl6f-Z2aJrYXPP3iXwfvc2fd9cGT2b0dj-OXGH_qkx5v9oFlaQJmRsndN412Ct4txQAD4S4cFxel7KgO4uEEj3wUADwh5mVEtCRRYKbHRtJoVoBH6O8OMiX7oZBlG53SOCeOZpG3LWeLQ-OkLfxO3qSknDn-mz9U8Bt6aMgzNAfhLhwXF6XsqBaO4QQ9tgAwJDMgL9JtaEOToYgXxLf_8W2wFDjUGuqtdJYkjOxVyZc5XYkjQFH-TXN8Riv40KSOGendKpQG7_6NJ6xgA-EuHBcXpeyoeS7hBWw3b6VCEY2CBqf-2XSD2L6aN6zeLQcE_S0pcc0dqtaBJuNGBoJ7gPLz7V90LUla3twuoCYsTLAoq-Cj-qcZXLwD4S4cFxel7KhiguEG1HWo82TFTDTVzvE43nGogeTQy9tSE15WPeILTHpQ0_mL1jckpAjSAutDTH6V02xplyU8gvkFuvIqrMWAiKkqlAfhLhwXF6XsqEU24QdwL7LTlaPIwRGScv7b11ICEJ2qau4Z9easjmds5ft8DJUlHrnI3bzLY0nvDEnkCQaVpoHqujF8wenOV4y8Wow0A-EuHBcXpeyoHebhBRsdayCsn5kPHDM5hAlEblCVUp46SwePmcXDlZflB04VmzETMR4fSiN0ZsGCZifgeOVEceoR49OUkJVyXHfJ11QH4S4cFxel7KjOWuEFvDSfZWCXExP42T_QixGOdRnxm8d97fa7ByWDgLquADwZkAC64kUXrwGIr2DF3eFY5CzbjX9GlD0G-iz2DZLuUAPhLhwXF6XsqBhW4QZJMQuVxPtW82q1NmcJvMceJ49fjEAvpB6fqGPLl1L7TOvmCpHUVzAKCcoFDIJvwQKAQhM4j9e7agBBKRRo0jyYB-EuHBcXpeyooK7hBQS7gfvEOzPQJ7nhlHlEvKGfpjP3siG2rkkfwfDM8XWowIY97-uTu1znTSCWd2CAEU4ghbDFMP0j5_aWCZh-mbQH4S4cFxel7Kh3buEGChN7GX3we4Gy56n1EoFoVUdGkORx2SPjDfu_Kqi6AkRfBXcWxfVYO-8KyfXxKJBaUL6hnUdrTMSBkAOyMpWo3APhLhwXF6XsqPei4QXSovB006pUOOn6plPJpXcl7NkCUlutjMw6kpALJOeZ_D0Q5XG26jGcEKxRVnGB8gVfz0-xzU0VlyXo6_FgeWjwB-EuHBcXpeyofDrhBTgIRV4or5m2CJTaD7CkX87XQnJNS1WTiMZbtnrMUjN1xbxPNSXDh8PoW4WOnHngvl19n41GLHq9lyNM4zj9zywH4S4cFxel7KgSYuEHtVmXQdUtVARIUZaa14yrP0zMKP_S-2YfkH0mnDFsaWEP3Po3Jz_0TW5P1CVEmFNM7o7yFxN-fm9JFVxWapMYOAfhLhwXF6XsqA6W4QQUmecuuFqFIQhLg4UTxEgE14vY_GKczNcbb4lgCpi7cN9VATk_E1IkOFav3PslEEUX1vuj81n8HVMEW7eJE4RYBuQHA-QG9AoMEKpGHBcXpewtZ7pUABgNedHU1VwOxHuKh7inb2r51OI6gs9oCNbERQVahfc2GrlZwP6T8mtAC66FtlqE4k5yYQmegcLrKOFXJAqMPt2eJ9xDiYK4wAcMrTPS6UF22SKi29tSgV7hY3tHXzN2K2M6-4B18YhXnCrkPFEYwhNSHZt521CH4AKD4RrCbI0HKJ7ysbNUrA3HwZwaeE33VxokHubN8qindpKIBACBwSCwaDwMEQQAwiGw6HxAAQqIwaJxSLxiMwcgRqBwEuO_47aBFdCv2s7sEb0pFk5GanQl4YUTxRbgMXR23ec5Vkej0h_gAoKCGznrDwZUMIDYYb5UQAkdvIOvbUq7srvg73uW8RKjruKf4paAhJZPxfZkssIL7Jux5z8rcF_eAT4f2nAltYyA2wZJ4SaC5x3BG1vaK7doCLlbpb4UaiQGV6bKjO-LOUjNc3RhVU6BMqUdR8w0-jahvLHldO2RW2oLahOYBe9toDgNalN1UVaAXxtW9KVneWr3U1hOQBVlMIl8Qg2EbjE9HnyIslC2nXaBAZVrK5KOETSZNFm61iyPjt7qEKu7VaBBXuPODkTBM4A==", + "rps": null + }, + { + "height": 273042, + "bu": "lM4ABCqSxCANtFqY7fcwhgH7nLW5Xe98MeN3oouRLbUW1JQvBZxIIMUB0_kB0AKDBCqShwXF6XsqEU2VAA9DKtV9HIUYYKMLExwIQ_tPgJZQoLKcATaFbHZP35Dl9Bdc_VIkWrhglczfNIabk4fYwoGcoOB_v6lft5LwYrNr7e99xRGABC-iA1u1FwvBCqGHK90doFe4WN7R18zditjOvuAdfGIV5wq5DxRGMITUh2bedtQh-ACgd9ZDub7dm7qhLNmjSYxcjFQvG8nt-rUEvlbPbRhOunC1CAAgcEAMEg8IgUEhQghEOh8QiIAAUOhsShAIgsXjccgcUh0Zj0dkMdkslAkmghBAEWhMRgK47_jtoC9qeixKuJHjCqJCfU86xe5ynVLcjJDsq1mfeuzRH2tp-ACg-VybAtlWkZGfPj9ij7uwnkKuYf_FJdpEsJrLYg89ezG4p_iloBcAKboHlYcivHAsgxQxRKeow2QriDNxdlkVC_VDbALsoLnHcEbW9ort2gIuVulvhRqJAZXpsqM74s5SM1zdGFVToEypR1HzDT6NqG8seV07ZFbagtqE5gF722gOA1qU3VRVoBfG1b0pWd5avdTWE5AFWUwiXxCDYRuMT0efIiyULaddoEBlWsrko4RNJk0WbrWLI-O3uoQq7tVoEFe484ORMEzgxQot-QoquQHT-QHQAoMEKpKHBcXpeyoRTZUAD0Mq1X0chRhgowsTHAhD-0-AllCgspwBNoVsdk_fkOX0F1z9UiRauGCVzN80hpuTh9jCgZyg4H-_qV-3kvBis2vt733FEYAEL6IDW7UXC8EKoYcr3R2gV7hY3tHXzN2K2M6-4B18YhXnCrkPFEYwhNSHZt521CH4AKB31kO5vt2buqEs2aNJjFyMVC8bye36tQS-Vs9tGE66cLUIACBwQAwSDwiBQSFCCEQ6HxCIgABQ6GxKEAiCxeNxyBxSHRmPR2Qx2SyUCSaCEEARaExGArjv-O2gL2p6LEq4keMKokJ9TzrF7nKdUtyMkOyrWZ967NEfa2n4AKD5XJsC2VaRkZ8-P2KPu7CeQq5h_8Ul2kSwmstiDz17Mbin-KWgFwApugeVhyK8cCyDFDFEp6jDZCuIM3F2WRUL9UNsAuygucdwRtb2iu3aAi5W6W-FGokBlemyozvizlIzXN0YVVOgTKlHUfMNPo2obyx5XTtkVtqC2oTmAXvbaA4DWpTdVFWgF8bVvSlZ3lq91NYTkAVZTCJfEINhG4xPR58iLJQtp12gQGVayuSjhE0mTRZutYsj47e6hCru1WgQV7jzg5EwTOC5Bnv5BngA4gGgSdJKXGQcqBYoSQa1BEismdvwC9XOy_IhjYKGUWeHul_5BlH4S4cFxel7SGCLuEG0LANqVNyixMdJkAPsyvpTEsJKt6DrMBBcGC_3bhfUkmJB7GUIcMCfw-gMlJPuRf6zol_YR0uFX9wxMmorNTNDAfhLhwXF6XtIZ4m4QRe1MsT0VqKpL39q4H6-aNFGayx4qqxe0Lvb5nNFhl7TQTA_2UoXQyoCgyztfiFS2-EEata3W1IA5JeLi3JS_P0B-EuHBcXpe0huHLhB_3dio-mhWzOR5JwNIiymrrdCTx3SXTUR6OtCbF9nT7Jvxnqsuu1xJCQSvEdyMPrMrDkQYekHVfzZQ3_7J9E4qQH4S4cFxel7SGMtuEFVGCLQtH7LWZsZsXmMQ_LVaXx3V1GE5YPCLWn8q8LqtGF0TS5TiKQMbJGWqM88qVfImGEB4u8OY5ZP_8wjNhsOAPhLhwXF6XtIc0a4QbRvLqomQGfzDERCRULkOj7Cmyei_LuF395VOFpdKCZIEMWgzxY9CP9H9Yyh69F04BF1jb_OOs132npDTxldoigB-EuHBcXpe0hrLLhBQHaHPIAeecaCqHO57LQOqWHOHjuF3D--jsVWG6HZFtR9spckbKtNneKaHc_5Z2FBUaPRitnZ7SvP334XMx04xwD4S4cFxel7SGM4uEE7qt1MkIJBnv3vr_N0GugYLvyrgtCSZYRMRLhhcT47KBSXOZ5paYRAa41Jr6_SH_5bnK6wzUxT2mmiDQSE9FPYAfhLhwXF6XtIYmi4Qbyrg6uAVlx14gaY44zepqqcjWWWv9YpVo-VIMCXjpFlaYoBOtFpNt1qZ0GCa_YrRBLoXa9NZuFKVE5EJo3mDgUA-EuHBcXpe0hiGbhBi98pFLc8w5eatulZnOHrBxIrUnU6Dclr7-RWc0HUAts4YDVQ2ZWHcJJafNGleOmKwWamAKBBl20j2gAjWKTCrwD4S4cFxel7SGA8uEH_JZGgb0UzSqJkzBSLtGoCqLzZ3y9P4JetTGtYz-oexnGwa2FEkrgjMfV1idDDOxjlC3DDAwLMTPgxV_NW6f-5AfhLhwXF6XtIY5S4QQ6nYtxB1DXo4U2_XR1-24iYR7g8GqmoH4uBr7z58Vi5PTKokPai8Yj8rtjx0bSIYkoCZyszKpjZ6432LiFHIa4B-EuHBcXpe0hgerhBR-Kf_JurBk8RqUivgabZCENdeO062JcHuMvagGGp8-N7JFkrRP748TOpy1P326zLPtRuRJYlfsVvzX7XZqm-NwH4S4cFxel7SGOPuEHGP7l6puKaKh6hQDxRjUQVh9ggBD9JM4NC2iULn60nPkCoRtJgvnyoh_QeXPAyLhArUlmWO6CRBl8Ng0w7ZowaAfhLhwXF6XtIaeO4QT5g9dxKzwURcNrqwyFrvKnDDEygujkp0z0zkjKEsVRTau0MHZz9gKM2rwrbTup7sjRcKS6Ak1vkg18cPs1i7u4B-EuHBcXpe0hl6LhBbAd_ZDc3cVASL4xkUuivBGbgcQH0S-lwMCUZ_yf19ug_q7qRFG_deJyPJ1AHDzfZmK6v-MlFMXRxwKxni8V4EwH4S4cFxel7SGnXuEEVzdAT6UuCellcDT95wM9Aod0Y1OPXoFxK0MULOGYjcSvbMjZIncYwFdeWMoz552EQfBkRNzSfLlfLYoUZ4B0mAPhLhwXF6XtIY_u4QS2Qn8DQ6nmQwvdvHts4dxjKdJnjyS_UZ_bO1mVSURwpFd6muGYaCQ8a60R505y97L9Hdetw3-NsMoDW_AUEgW4B-EuHBcXpe0hqUbhBaL7HD_zIEyVTP7xZ8uddWa_T7U96QLlOrosmkI8_FPcVBql0HSo6zslZJ8b9-KroE8x353ClX1ITcRim5lTjQAH4S4cFxel7SF-_uEG2a2jqpl7kiz-DUu_C3E0Rx3Qx-pPbIKzrKFPk3cYLeTZiJegyXyHZAl05VHty0Ey0_X29yfODcM9DRL3v86uKAfhLhwXF6XtIYqe4Qc0N-DZpeBhOs56uoxF60d8QGnoVDyJDFAnsISjXEHmGPwxWyWKxdZlaenj3ph9NthCuUGAqcEOFn_BsZGrro2YB-EuHBcXpe0iNArhBTlnwguqsBGvda5tNkl2k3btaMkgK0dUbaOc_UgoeWVpG3yGDpNYKibCqAGjpRSPyKvKGf-c_U-6ZKcLKcNB_JQC5AdP5AdACgwQqkocFxel7KhFNlQAPQyrVfRyFGGCjCxMcCEP7T4CWUKCynAE2hWx2T9-Q5fQXXP1SJFq4YJXM3zSGm5OH2MKBnKDgf7-pX7eS8GKza-3vfcURgAQvogNbtRcLwQqhhyvdHaBXuFje0dfM3YrYzr7gHXxiFecKuQ8URjCE1Idm3nbUIfgAoHfWQ7m-3Zu6oSzZo0mMXIxULxvJ7fq1BL5Wz20YTrpwtQgAIHBADBIPCIFBIUIIRDofEIiAAFDobEoQCILF43HIHFIdGY9HZDHZLJQJJoIQQBFoTEYCuO_47aAvanosSriR4wqiQn1POsXucp1S3IyQ7KtZn3rs0R9rafgAoPlcmwLZVpGRnz4_Yo-7sJ5CrmH_xSXaRLCay2IPPXsxuKf4paAXACm6B5WHIrxwLIMUMUSnqMNkK4gzcXZZFQv1Q2wC7KC5x3BG1vaK7doCLlbpb4UaiQGV6bKjO-LOUjNc3RhVU6BMqUdR8w0-jahvLHldO2RW2oLahOYBe9toDgNalN1UVaAXxtW9KVneWr3U1hOQBVlMIl8Qg2EbjE9HnyIslC2nXaBAZVrK5KOETSZNFm61iyPjt7qEKu7VaBBXuPODkTBM4A==", + "rps": "kZQBxQHL-QHIo-IQoLLSyap7APyhBMuMzpFSkYdfio71M-0bazQ4sO_Uj1k0uFP4UaAsDg_FMho_knWof9H-F3ViU65JrLuGHwTbZ0pxLRB4JaBEGd_1zx-0_k48yiOsRPEtSPdSG3JdKnxFWrcE2ZFMe4CAgICAgICAgICAgICAgLkBTPkBSSC5AUX5AUIAlQFi65cvjqLMajfR7xXQ8sHhuJJyB4MD5VCDA-VQhQLpDt0AuPYQAAAAAAAAAQAAAAAAAAAAAAAAABAAAAAAEAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAQAgAAAAAAAAABAAAAAAAAAAAAD4APgAoFMYMZCE0DN2qxIbaijkLOqzfrIk-n-VXpU9oJ2Geoe0kZIAxQFy-QFvuQFs-QFpgiAAuQFj-QFglQFi65cvjqLMajfR7xXQ8sHhuJJyB_hSlk1lc3NhZ2Uoc3RyLGludCxieXRlcym4OGJ0cDovLzB4OC5wcmEvMHgxMTVmMjc3ZThmY0U0MzdCMUY1MTNBMjkzMDU3RDJFMzk2QWMyRUMxAvj0uPL48Lg5YnRwOi8vMHgzLmljb24vY3g2MmViOTcyZjhlYTJjYzZhMzdkMWVmMTVkMGYyYzFlMWI4OTI3MjA3uDhidHA6Ly8weDgucHJhLzB4MTE1ZjI3N2U4ZmNFNDM3QjFGNTEzQTI5MzA1N0QyRTM5NkFjMkVDMZBDb2luL1dyYXBwZWRDb2luArhn-GUAuGL4YKpoeGI2YjU3OTFiZTBiNWVmNjcwNjNiM2MxMGI4NDBmYjgxNTE0ZGIyZmSqMHgyOTRjNjY0ZjZENjNiZDE1MjEyMzFhMkVlRkMyNmQ4MDVjZTAwYTA4yciESUNPToIA1JGT2ThidHA6Ly8weDgucHJhLzB4MTE1ZjI3N2U4ZmNFNDM3QjFGNTEzQTI5MzA1N0QyRTM5NkFjMkVDMQLE8vjwuDlidHA6Ly8weDMuaWNvbi9jeDYyZWI5NzJmOGVhMmNjNmEzN2QxZWYxNWQwZjJjMWUxYjg5MjcyMDe4OGJ0cDovLzB4OC5wcmEvMHgxMTVmMjc3ZThmY0U0MzdCMUY1MTNBMjkzMDU3RDJFMzk2QWMyRUMxkENvaW4vV3JhcHBlZENvaW4CuGf4ZQC4Yvhgqmh4YjZiNTc5MWJlMGI1ZWY2NzA2M2IzYzEwYjg0MGZiODE1MTRkYjJmZKoweDI5NGM2NjRmNkQ2M2JkMTUyMTIzMWEyRWVGQzI2ZDgwNWNlMDBhMDjJyIRJQ09OggDU" + } +] \ No newline at end of file diff --git a/chain/pra/type.go b/chain/pra/type.go new file mode 100644 index 00000000..943e9f05 --- /dev/null +++ b/chain/pra/type.go @@ -0,0 +1,123 @@ +package pra + +import ( + "github.com/centrifuge/go-substrate-rpc-client/v3/types" + "github.com/icon-project/btp/chain" + "github.com/icon-project/btp/chain/pra/substrate" +) + +const ( + DuplicateTransactionError = iota + 2000 + TransactionPoolOverflowError + ExpiredTransactionError + FutureTransactionError + TransitionInterruptedError + InvalidTransactionError + InvalidQueryError + InvalidResultError + NoActiveContractError + NotContractAddressError + InvalidPatchDataError + CommittedTransactionError +) + +type Wallet interface { + Sign(data []byte) ([]byte, error) + Address() string +} + +type RelayMessageParam struct { + Prev string `json:"_prev"` + Msg string `json:"_msg"` +} + +type StateProof struct { + Key []byte + Value [][]byte +} + +func NewStateProof(key substrate.SubstrateStorageKey, rp *substrate.SubstrateReadProof) *StateProof { + proofs := [][]byte{} + for _, p := range rp.Proof { + // TODO move this function to substrate package + if bp, err := types.HexDecodeString(p); err != nil { + return nil + } else { + proofs = append(proofs, bp) + } + } + + return &StateProof{ + Key: key, + Value: proofs, + } +} + +type RelayMessage struct { + BlockUpdates [][]byte + BlockProof []byte + ReceiptProofs [][]byte + // + height int64 + numberOfBlockUpdate int + eventSequence int64 + numberOfEvent int +} + +func (rm RelayMessage) Size() int { + size := len(rm.BlockProof) + for _, blockUpdate := range rm.BlockUpdates { + size += len(blockUpdate) + } + for _, receiptProof := range rm.ReceiptProofs { + size += len(receiptProof) + } + return size +} + +type BlockNotification struct { + Header *substrate.SubstrateHeader + Hash substrate.SubstrateHash + Height uint64 +} + +type TransactionHashParam struct { + From EvmAddress + Tx *EvmTransaction + Param *RelayMessageParam `json:"rm"` +} + +func (thp TransactionHashParam) Hash() string { + return thp.Tx.Hash().Hex() +} + +func (thp TransactionHashParam) String() string { + return thp.Tx.Hash().Hex() +} + +type ReceiptProof struct { + Index int + Proof []byte + EventProofs []*chain.EventProof +} + +type ValidatorSignature struct { + Signature []byte + Id []byte +} + +type Votes struct { + VoteMessage []byte + Signatures [][]byte +} + +type RelayBlockUpdate struct { + ScaleEncodedBlockHeader []byte + Votes []byte +} + +type ParachainFinalityProof struct { + RelayBlockUpdates [][]byte + RelayBlockProof []byte + RelayStateProofs [][]byte +} diff --git a/chain/pra/type_evm.go b/chain/pra/type_evm.go new file mode 100644 index 00000000..82eab360 --- /dev/null +++ b/chain/pra/type_evm.go @@ -0,0 +1,45 @@ +package pra + +import ( + "math/big" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + "github.com/icon-project/btp/chain/pra/substrate" +) + +type EvmReceipt = types.Receipt +type EvmTransaction = types.Transaction +type EvmCallMsg = ethereum.CallMsg +type EvmAddress = common.Address +type EvmDataError = rpc.DataError +type EvmHash = common.Hash +type EvmLog = types.Log + +func NewEvmLog(e substrate.EventEVMLog) EvmLog { + topics := []EvmHash{} + + for _, t := range e.Log.Topics { + topics = append(topics, EvmHexToHash(t)) + } + + return EvmLog{ + Address: EvmHexToAddress(e.Log.Address), + Topics: topics, + Data: common.Hex2Bytes(e.Log.Data), + } +} + +func EvmHexToHash(s string) common.Hash { + return common.HexToHash(s) +} + +func EvmHexToAddress(s string) common.Address { + return common.HexToAddress(s) +} + +func NewEvmNewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *EvmTransaction { + return types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, data) +} diff --git a/cmd/btpsimple/module/type.go b/chain/type.go similarity index 59% rename from cmd/btpsimple/module/type.go rename to chain/type.go index 426c9eff..f2eaa721 100644 --- a/cmd/btpsimple/module/type.go +++ b/chain/type.go @@ -1,25 +1,32 @@ -package module +package chain + +import "fmt" type BlockWitness struct { Height int64 Witness [][]byte } + type BlockUpdate struct { Height int64 BlockHash []byte Header []byte Proof []byte } + type BlockProof struct { Header []byte BlockWitness *BlockWitness } + type ReceiptProof struct { + Height int64 Index int Proof []byte EventProofs []*EventProof Events []*Event } + type EventProof struct { Index int Proof []byte @@ -42,6 +49,33 @@ type RelayMessage struct { Segments []*Segment } +func (rm RelayMessage) HasWait() bool { + if len(rm.BlockUpdates) == 0 && len(rm.ReceiptProofs) == 0 { + return true + } + + for _, segment := range rm.Segments { + if segment != nil && segment.GetResultParam != nil && segment.TransactionResult == nil { + return true + } + } + return false +} + +func (rm RelayMessage) BuRange() string { + return fmt.Sprintf("bu: %d ~ %d", rm.BlockUpdates[0].Height, rm.BlockUpdates[len(rm.BlockUpdates)-1].Height) +} + +func (rm *RelayMessage) RemoveSegment(index int) { + if l := len(rm.Segments); l-1 <= 0 { + rm.Segments = nil + } else if index == l-1 { + rm.Segments = rm.Segments[:index] + } else { + rm.Segments = append(rm.Segments[:index], rm.Segments[index+1:]...) + } +} + type Segment struct { TransactionParam TransactionParam GetResultParam GetResultParam @@ -86,12 +120,16 @@ type TransactionResult interface{} type MonitorCallback func(height int64) error type Sender interface { + // Segment split the give RelayMessage into small segments. One Segment is equal to one RelayMessage Segment(rm *RelayMessage, height int64) ([]*Segment, error) + // Decode segment and update BlockProof for the given segment UpdateSegment(bp *BlockProof, segment *Segment) error + // Send relay message to BMC.handleRelayMessage Relay(segment *Segment) (GetResultParam, error) + // Get result based on usually transaction hash GetResult(p GetResultParam) (TransactionResult, error) GetStatus() (*BMCLinkStatus, error) - // + // Monitor destination chain for update new BMCStatusLink with callback(height) MonitorLoop(height int64, cb MonitorCallback, scb func()) error StopMonitorLoop() FinalizeLatency() int @@ -100,6 +138,18 @@ type Sender interface { type ReceiveCallback func(bu *BlockUpdate, rps []*ReceiptProof) type Receiver interface { + // Monitor src chain with given transaction sequence to callback(BlockUpdate, ReceiptProof) ReceiveLoop(height int64, seq int64, cb ReceiveCallback, scb func()) error StopReceiveLoop() } + +// Suppose to be in pra package, however it occurs cycle import so place here +type ParaChainBlockUpdate struct { + ScaleEncodedBlockHeader []byte + FinalityProof []byte +} + +type ParaChainBlockUpdateExtra struct { + ScaleEncodedBlockHeader []byte + FinalityProofs [][]byte +} diff --git a/chain/type_test.go b/chain/type_test.go new file mode 100644 index 00000000..a76016e6 --- /dev/null +++ b/chain/type_test.go @@ -0,0 +1,61 @@ +package chain + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRelayMessageRemoveSegment(t *testing.T) { + + t.Run("should removed 1 segment ok", func(t *testing.T) { + rm := RelayMessage{ + Segments: []*Segment{{}}, + } + + rm.RemoveSegment(1) + assert.Nil(t, rm.Segments) + }) + + t.Run("should removed 0 segment ok", func(t *testing.T) { + rm := RelayMessage{ + Segments: []*Segment{{}}, + } + + rm.RemoveSegment(1) + assert.Nil(t, rm.Segments) + }) + + t.Run("should removed a segment from 3 segments ok", func(t *testing.T) { + rm1 := RelayMessage{ + Segments: []*Segment{{Height: 1}, {Height: 2}, {Height: 3}}, + } + sg1 := rm1.Segments[0] + rm1.RemoveSegment(0) + assert.Len(t, rm1.Segments, 2) + assert.NotContains(t, rm1.Segments, sg1) + + rm2 := RelayMessage{ + Segments: []*Segment{{Height: 1}, {Height: 2}, {Height: 3}}, + } + sg2 := rm2.Segments[1] + rm2.RemoveSegment(1) + assert.Len(t, rm2.Segments, 2) + assert.NotContains(t, rm2.Segments, sg2) + + rm3 := RelayMessage{ + Segments: []*Segment{{Height: 1}, {Height: 2}, {Height: 3}}, + } + sg3 := rm3.Segments[2] + rm3.RemoveSegment(2) + assert.Len(t, rm3.Segments, 2) + assert.NotContains(t, rm3.Segments, sg3) + + rm4 := RelayMessage{ + Segments: []*Segment{{Height: 1}, {Height: 2}, {Height: 3}}, + } + assert.Panics(t, func() { + rm4.RemoveSegment(4) + }) + }) +} diff --git a/cmd/btpsimple/chain/chain.go b/cmd/btpsimple/chain/chain.go deleted file mode 100644 index 25028cb1..00000000 --- a/cmd/btpsimple/chain/chain.go +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright 2020 ICON Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package chain - -import ( - "encoding/base64" - "encoding/hex" - "fmt" - "math" - "path/filepath" - "sync" - "sync/atomic" - "time" - - "github.com/icon-project/btp/cmd/btpsimple/module" - "github.com/icon-project/btp/cmd/btpsimple/module/icon" - "github.com/icon-project/btp/common/codec" - "github.com/icon-project/btp/common/db" - "github.com/icon-project/btp/common/errors" - "github.com/icon-project/btp/common/log" - "github.com/icon-project/btp/common/mta" - "github.com/icon-project/btp/common/wallet" -) - -const ( - DefaultDBDir = "db" - DefaultDBType = db.GoLevelDBBackend - //Base64 in:out = 6:8 - DefaultBufferScaleOfBlockProof = 0.5 - DefaultBufferNumberOfBlockProof = 100 - DefaultBufferInterval = 5 * time.Second - DefaultReconnectDelay = time.Second - DefaultRelayReSendInterval = time.Second -) - -type SimpleChain struct { - s module.Sender - r module.Receiver - w wallet.Wallet - src module.BtpAddress - acc *mta.ExtAccumulator - dst module.BtpAddress - bs *module.BMCLinkStatus //getstatus(dst.src) - relayCh chan *module.RelayMessage - l log.Logger - cfg *Config - - rms []*module.RelayMessage - rmsMtx sync.RWMutex - rmSeq uint64 - heightOfDst int64 - lastBlockUpdate *module.BlockUpdate - - bmrIndex int - relayble bool - relaybleIndex int - relaybleHeight int64 -} - -func (s *SimpleChain) _relayble(rm *module.RelayMessage) bool { - return s.relayble && (s._overMaxAggregation(rm) || s._lastRelaybleHeight()) -} -func (s *SimpleChain) _overMaxAggregation(rm *module.RelayMessage) bool { - return len(rm.BlockUpdates) >= s.bs.MaxAggregation -} -func (s *SimpleChain) _lastRelaybleHeight() bool { - return s.relaybleHeight == (s.monitorHeight() + 1) -} -func (s *SimpleChain) _hasWait(rm *module.RelayMessage) bool { - for _, segment := range rm.Segments { - if segment != nil && segment.GetResultParam != nil && segment.TransactionResult == nil { - return true - } - } - return false -} - -func (s *SimpleChain) _log(prefix string, rm *module.RelayMessage, segment *module.Segment, segmentIdx int) { - if segment == nil { - s.l.Debugf("%s rm:%d bu:%d ~ %d rps:%d", - prefix, - rm.Seq, - rm.BlockUpdates[0].Height, - rm.BlockUpdates[len(rm.BlockUpdates)-1].Height, - len(rm.ReceiptProofs)) - } else { - s.l.Debugf("%s rm:%d [i:%d,h:%d,bu:%d,seq:%d,evt:%d,txh:%v]", - prefix, - rm.Seq, - segmentIdx, - segment.Height, - segment.NumberOfBlockUpdate, - segment.EventSequence, - segment.NumberOfEvent, - segment.GetResultParam) - } -} - -func (s *SimpleChain) _relay() { - s.rmsMtx.RLock() - defer s.rmsMtx.RUnlock() - var err error - for _, rm := range s.rms { - if (len(rm.BlockUpdates) == 0 && len(rm.ReceiptProofs) == 0) || - s._hasWait(rm) || (!s._skippable(rm) && !s._relayble(rm)) { - break - } else { - if len(rm.Segments) == 0 { - if rm.Segments, err = s.s.Segment(rm, s.bs.Verifier.Height); err != nil { - s.l.Panicf("fail to segment err:%+v", err) - } - } - s._log("before relay", rm, nil, -1) - reSegment := true - for j, segment := range rm.Segments { - if segment == nil { - continue - } - reSegment = false - - if segment.GetResultParam == nil { - segment.TransactionResult = nil - if segment.GetResultParam, err = s.s.Relay(segment); err != nil { - s.l.Panicf("fail to Relay err:%+v", err) - } - s._log("after relay", rm, segment, j) - go s.result(rm, segment) - } - } - if reSegment { - rm.Segments = rm.Segments[:0] - } - } - } -} - -func (s *SimpleChain) result(rm *module.RelayMessage, segment *module.Segment) { - var err error - segment.TransactionResult, err = s.s.GetResult(segment.GetResultParam) - if err != nil { - if ec, ok := errors.CoderOf(err); ok { - s.l.Debugf("fail to GetResult GetResultParam:%v ErrorCoder:%+v", - segment.GetResultParam, ec) - switch ec.ErrorCode() { - case module.BMVRevertInvalidSequence, module.BMVRevertInvalidBlockUpdateLower: - for i := 0; i < len(rm.Segments); i++ { - if rm.Segments[i] == segment { - rm.Segments[i] = nil - break - } - } - case module.BMVRevertInvalidBlockWitnessOld: - rm.BlockProof, err = s.newBlockProof(rm.BlockProof.BlockWitness.Height, rm.BlockProof.Header) - s.s.UpdateSegment(rm.BlockProof, segment) - segment.GetResultParam = nil - case module.BMVRevertInvalidSequenceHigher, module.BMVRevertInvalidBlockUpdateHigher, module.BMVRevertInvalidBlockProofHigher: - segment.GetResultParam = nil - case module.BMCRevertUnauthorized: - segment.GetResultParam = nil - default: - s.l.Panicf("fail to GetResult GetResultParam:%v ErrorCoder:%+v", - segment.GetResultParam, ec) - } - } else { - s.l.Panicf("fail to GetResult GetResultParam:%v err:%+v", - segment.GetResultParam, err) - } - } -} - -func (s *SimpleChain) _rm() *module.RelayMessage { - rm := &module.RelayMessage{ - From: s.src, - BlockUpdates: make([]*module.BlockUpdate, 0), - Seq: s.rmSeq, - } - s.rms = append(s.rms, rm) - s.rmSeq += 1 - return rm -} - -func (s *SimpleChain) addRelayMessage(bu *module.BlockUpdate, rps []*module.ReceiptProof) { - s.rmsMtx.Lock() - defer s.rmsMtx.Unlock() - - if s.lastBlockUpdate != nil { - //TODO consider remained bu when reconnect - if s.lastBlockUpdate.Height+1 != bu.Height { - s.l.Panicf("invalid bu") - } - } - s.lastBlockUpdate = bu - rm := s.rms[len(s.rms)-1] - if len(rm.Segments) > 0 { - rm = s._rm() - } - if len(rps) > 0 { - rm.BlockUpdates = append(rm.BlockUpdates, bu) - rm.ReceiptProofs = rps - rm.HeightOfDst = s.monitorHeight() - if s.bs.BlockIntervalDst > 0 { - scale := float64(s.bs.BlockIntervalSrc) / float64(s.bs.BlockIntervalDst) - guessHeightOfDst := s.bs.RxHeight + int64(math.Ceil(float64(bu.Height-s.bs.RxHeightSrc)/scale)) - 1 - if guessHeightOfDst < rm.HeightOfDst { - rm.HeightOfDst = guessHeightOfDst - } - } - s.l.Debugf("addRelayMessage rms:%d bu:%d rps:%d HeightOfDst:%d", len(s.rms), bu.Height, len(rps), rm.HeightOfDst) - rm = s._rm() - } else { - if bu.Height <= s.bs.Verifier.Height { - return - } - rm.BlockUpdates = append(rm.BlockUpdates, bu) - s.l.Debugf("addRelayMessage rms:%d bu:%d ~ %d", len(s.rms), rm.BlockUpdates[0].Height, bu.Height) - } -} - -func (s *SimpleChain) updateRelayMessage(h int64, seq int64) (err error) { - s.rmsMtx.Lock() - defer s.rmsMtx.Unlock() - - s.l.Debugf("updateRelayMessage h:%d seq:%d monitorHeight:%d", h, seq, s.monitorHeight()) - - rrm := 0 -rmLoop: - for i, rm := range s.rms { - if len(rm.ReceiptProofs) > 0 { - rrp := 0 - rpLoop: - for j, rp := range rm.ReceiptProofs { - revt := seq - rp.Events[0].Sequence + 1 - if revt < 1 { - break rpLoop - } - if revt >= int64(len(rp.Events)) { - rrp = j + 1 - } else { - s.l.Debugf("updateRelayMessage rm:%d bu:%d rp:%d removeEventProofs %d ~ %d", - rm.Seq, - rm.BlockUpdates[len(rm.BlockUpdates)-1].Height, - rp.Index, - rp.Events[0].Sequence, - rp.Events[revt-1].Sequence) - rp.Events = rp.Events[revt:] - if len(rp.EventProofs) > 0 { - rp.EventProofs = rp.EventProofs[revt:] - } - } - } - if rrp > 0 { - s.l.Debugf("updateRelayMessage rm:%d bu:%d removeReceiptProofs %d ~ %d", - rm.Seq, - rm.BlockUpdates[len(rm.BlockUpdates)-1].Height, - rm.ReceiptProofs[0].Index, - rm.ReceiptProofs[rrp-1].Index) - rm.ReceiptProofs = rm.ReceiptProofs[rrp:] - } - } - if rm.BlockProof != nil { - if len(rm.ReceiptProofs) > 0 { - if rm.BlockProof, err = s.newBlockProof(rm.BlockProof.BlockWitness.Height, rm.BlockProof.Header); err != nil { - return - } - } else { - rrm = i + 1 - } - } - if len(rm.BlockUpdates) > 0 { - rbu := h - rm.BlockUpdates[0].Height + 1 - if rbu < 1 { - break rmLoop - } - if rbu >= int64(len(rm.BlockUpdates)) { - if len(rm.ReceiptProofs) > 0 { - lbu := rm.BlockUpdates[len(rm.BlockUpdates)-1] - if rm.BlockProof, err = s.newBlockProof(lbu.Height, lbu.Header); err != nil { - return - } - rm.BlockUpdates = rm.BlockUpdates[:0] - } else { - rrm = i + 1 - } - } else { - s.l.Debugf("updateRelayMessage rm:%d removeBlockUpdates %d ~ %d", - rm.Seq, - rm.BlockUpdates[0].Height, - rm.BlockUpdates[rbu-1].Height) - rm.BlockUpdates = rm.BlockUpdates[rbu:] - } - } - } - if rrm > 0 { - s.l.Debugf("updateRelayMessage rms:%d removeRelayMessage %d ~ %d", - len(s.rms), - s.rms[0].Seq, - s.rms[rrm-1].Seq) - s.rms = s.rms[rrm:] - if len(s.rms) == 0 { - s._rm() - } - } - return nil -} - -func (s *SimpleChain) updateMTA(bu *module.BlockUpdate) { - next := s.acc.Height() + 1 - if next < bu.Height { - s.l.Panicf("found missing block next:%d bu:%d", next, bu.Height) - } - if next == bu.Height { - s.acc.AddHash(bu.BlockHash) - if err := s.acc.Flush(); err != nil { - //TODO MTA Flush error handling - s.l.Panicf("fail to MTA Flush err:%+v", err) - } - //s.l.Debugf("updateMTA %d", bu.Height) - } -} - -func (s *SimpleChain) OnBlockOfDst(height int64) error { - s.l.Tracef("OnBlockOfDst height:%d", height) - atomic.StoreInt64(&s.heightOfDst, height) - h, seq := s.bs.Verifier.Height, s.bs.RxSeq - if err := s.RefreshStatus(); err != nil { - return err - } - if h != s.bs.Verifier.Height || seq != s.bs.RxSeq { - h, seq = s.bs.Verifier.Height, s.bs.RxSeq - if err := s.updateRelayMessage(h, seq); err != nil { - return err - } - s.relayCh <- nil - } - return nil -} - -func (s *SimpleChain) OnBlockOfSrc(bu *module.BlockUpdate, rps []*module.ReceiptProof) { - s.l.Tracef("OnBlockOfSrc height:%d, bu.Height:%d", s.acc.Height(), bu.Height) - s.updateMTA(bu) - s.addRelayMessage(bu, rps) - s.relayCh <- nil -} - -func (s *SimpleChain) newBlockProof(height int64, header []byte) (*module.BlockProof, error) { - //at := s.bs.Verifier.Height - //w, err := s.acc.WitnessForWithAccLength(height-s.acc.Offset(), at-s.bs.Verifier.Offset) - at, w, err := s.acc.WitnessForAt(height, s.bs.Verifier.Height, s.bs.Verifier.Offset) - if err != nil { - return nil, err - } - - s.l.Debugf("newBlockProof height:%d, at:%d, w:%d", height, at, len(w)) - bp := &module.BlockProof{ - Header: header, - BlockWitness: &module.BlockWitness{ - Height: at, - Witness: mta.WitnessesToHashes(w), - }, - } - dumpBlockProof(s.acc, height, bp) - return bp, nil -} - -func (s *SimpleChain) prepareDatabase(offset int64) error { - s.l.Debugln("open database", filepath.Join(s.cfg.AbsBaseDir(), s.cfg.Dst.Address.NetworkAddress())) - database, err := db.Open(s.cfg.AbsBaseDir(), string(DefaultDBType), s.cfg.Dst.Address.NetworkAddress()) - if err != nil { - return errors.Wrap(err, "fail to open database") - } - defer func() { - if err != nil { - database.Close() - } - }() - var bk db.Bucket - if bk, err = database.GetBucket("Accumulator"); err != nil { - return err - } - k := []byte("Accumulator") - if offset < 0 { - offset = 0 - } - s.acc = mta.NewExtAccumulator(k, bk, offset) - if bk.Has(k) { - //offset will be ignore - if err = s.acc.Recover(); err != nil { - return errors.Wrapf(err, "fail to acc.Recover cause:%v", err) - } - s.l.Debugf("recover Accumulator offset:%d, height:%d", s.acc.Offset(), s.acc.Height()) - - //TODO [TBD] sync offset - //if s.acc.Offset() > offset { - // hashes := make([][]byte, s.acc.Offset() - offset) - // for i := 0; i < len(hashes); i++ { - // hashes[i] = getBlockHashByHeight(offset) - // offset++ - // } - // s.acc.AddHashesToHead(hashes) - //} else if s.acc.Offset() < offset { - // s.acc.RemoveHashesFromHead(offset-s.acc.Offset()) - //} - } - return nil -} - -func (s *SimpleChain) _skippable(rm *module.RelayMessage) bool { - bs := s.bs - if len(rm.ReceiptProofs) > 0 { - if bs.RotateTerm > 0 { - rotate := 0 - relaybleHeightStart := bs.RotateHeight - int64(bs.RotateTerm+1) - if rm.HeightOfDst > bs.RotateHeight { - rotate = int(math.Ceil(float64(rm.HeightOfDst-bs.RotateHeight) / float64(bs.RotateTerm))) - if rotate > 0 { - relaybleHeightStart += int64(bs.RotateTerm * (rotate - 1)) - } - } - skip := int(math.Ceil(float64(s.monitorHeight()+1-rm.HeightOfDst)/float64(bs.DelayLimit))) - 1 - if skip > 0 { - rotate += skip - relaybleHeightStart = rm.HeightOfDst + int64(bs.DelayLimit*skip) - } - relaybleIndex := bs.BMRIndex - if rotate > 0 { - relaybleIndex += rotate - if relaybleIndex >= len(bs.BMRs) { - relaybleIndex = relaybleIndex % len(bs.BMRs) - } - } - prevFinalizeHeight := relaybleHeightStart + int64(s.s.FinalizeLatency()) - return (relaybleIndex == s.bmrIndex) && (prevFinalizeHeight <= s.monitorHeight()) - } else { - return true - } - } - return false -} - -func (s *SimpleChain) _rotate() { - bs := s.bs - a := s.w.Address() - if bs.RotateTerm > 0 { - //update own bmrIndex of BMRs - bmrIndex := -1 - for i, bmr := range bs.BMRs { - if bmr.Address == a { - bmrIndex = i - break - } - } - s.bmrIndex = bmrIndex - - //predict index and height of rotate on next block - rotate := int(math.Ceil(float64(s.monitorHeight()+1-bs.RotateHeight) / float64(bs.RotateTerm))) - relaybleIndex := bs.BMRIndex - relaybleHeightEnd := bs.RotateHeight - if rotate > 0 { - relaybleIndex += rotate - if relaybleIndex >= len(bs.BMRs) { - relaybleIndex = relaybleIndex % len(bs.BMRs) - } - relaybleHeightEnd += int64(bs.RotateTerm * rotate) - } - prevFinalizeHeight := relaybleHeightEnd - int64(s.bs.RotateTerm) + int64(s.s.FinalizeLatency()) - s.relayble = (relaybleIndex == s.bmrIndex) && (prevFinalizeHeight <= s.monitorHeight()) - s.relaybleIndex = relaybleIndex - s.relaybleHeight = relaybleHeightEnd - //b7214314876a73397c07}] - s.l.Debugf("RefreshStatus %d si:%d status[i:%d rh:%d rxh:%d rxhs:%d] relayble[%v i:%d rh:%d r:%d]", - s.monitorHeight(), - s.bmrIndex, - bs.BMRIndex, - bs.RotateHeight, - bs.RxHeight, - bs.RxHeightSrc, - s.relayble, - relaybleIndex, - relaybleHeightEnd, - rotate) - } -} - -func (s *SimpleChain) RefreshStatus() error { - bmcStatus, err := s.s.GetStatus() - if err != nil { - return err - } - s.bs = bmcStatus - s._rotate() - return nil -} - -func (s *SimpleChain) init() error { - if err := s.RefreshStatus(); err != nil { - return err - } - atomic.StoreInt64(&s.heightOfDst, s.bs.CurrentHeight) - if s.relayCh == nil { - s.relayCh = make(chan *module.RelayMessage, 2) - go func() { - s.l.Debugln("start relayLoop") - defer func() { - s.l.Debugln("stop relayLoop") - }() - for { - select { - case _, ok := <-s.relayCh: - if !ok { - return - } - s._relay() - } - } - }() - } - s.l.Debugf("_init height:%d, dst(%s, height:%d, seq:%d, last:%d), receive:%d", - s.acc.Height(), s.dst, s.bs.Verifier.Height, s.bs.RxSeq, s.bs.Verifier.LastHeight, s.receiveHeight()) - return nil -} - -func (s *SimpleChain) receiveHeight() int64 { - //min(max(s.acc.Height(), s.bs.Verifier.Offset), s.bs.Verifier.LastHeight) - max := s.acc.Height() - if max < s.bs.Verifier.Offset { - max = s.bs.Verifier.Offset - } - max += 1 - min := s.bs.Verifier.LastHeight - if max < min { - min = max - } - return min -} - -func (s *SimpleChain) monitorHeight() int64 { - return atomic.LoadInt64(&s.heightOfDst) -} - -func (s *SimpleChain) Serve() error { - if err := s.init(); err != nil { - return err - } - errCh := make(chan error) - go func() { - err := s.s.MonitorLoop( - s.bs.CurrentHeight, - s.OnBlockOfDst, - func() { - s.l.Debugf("Connect MonitorLoop") - errCh <- nil - }) - select { - case errCh <- err: - default: - } - }() - go func() { - err := s.r.ReceiveLoop( - s.receiveHeight(), - s.bs.RxSeq, - s.OnBlockOfSrc, - func() { - s.l.Debugf("Connect ReceiveLoop") - errCh <- nil - }) - select { - case errCh <- err: - default: - } - }() - for { - select { - case err := <-errCh: - if err != nil { - return err - } - } - } -} - -func NewSimpleChain(cfg *Config, w wallet.Wallet, l log.Logger) (*SimpleChain, error) { - s := &SimpleChain{ - src: cfg.Src.Address, - dst: cfg.Dst.Address, - w: w, - l: l.WithFields(log.Fields{log.FieldKeyChain: - //fmt.Sprintf("%s->%s", cfg.Src.Address.NetworkAddress(), cfg.Dst.Address.NetworkAddress())}), - fmt.Sprintf("%s", cfg.Dst.Address.NetworkID())}), - cfg: cfg, - rms: make([]*module.RelayMessage, 0), - } - s._rm() - - s.r = icon.NewReceiver(cfg.Src.Address, cfg.Dst.Address, cfg.Src.Endpoint, cfg.Src.Options, s.l) - s.s = icon.NewSender(cfg.Src.Address, cfg.Dst.Address, w, cfg.Dst.Endpoint, cfg.Dst.Options, s.l) - - if err := s.prepareDatabase(cfg.Offset); err != nil { - return nil, err - } - return s, nil -} - -func dumpBlockProof(acc *mta.ExtAccumulator, height int64, bp *module.BlockProof) { - if n, err := acc.GetNode(height); err != nil { - fmt.Printf("height:%d, accLen:%d, err:%+v", height, acc.Len(), err) - } else { - fmt.Printf("height:%d, accLen:%d, hash:%s\n", height, acc.Len(), hex.EncodeToString(n.Hash())) - } - - fmt.Print("dumpBlockProof.height:", bp.BlockWitness.Height, ",witness:[") - for _, w := range bp.BlockWitness.Witness { - fmt.Print(hex.EncodeToString(w), ",") - } - fmt.Println("]") - b, _ := codec.RLP.MarshalToBytes(bp) - fmt.Println(base64.URLEncoding.EncodeToString(b)) -} diff --git a/cmd/btpsimple/main.go b/cmd/btpsimple/main.go index 847af44e..80457935 100644 --- a/cmd/btpsimple/main.go +++ b/cmd/btpsimple/main.go @@ -21,19 +21,21 @@ import ( "fmt" "io/ioutil" stdlog "log" + "net/http" + _ "net/http/pprof" "os" - "path" "path/filepath" "strings" + "time" - "github.com/spf13/cobra" - - "github.com/icon-project/btp/cmd/btpsimple/chain" + "github.com/getsentry/sentry-go" + "github.com/icon-project/btp/btp" "github.com/icon-project/btp/common/cli" "github.com/icon-project/btp/common/crypto" "github.com/icon-project/btp/common/errors" "github.com/icon-project/btp/common/log" "github.com/icon-project/btp/common/wallet" + "github.com/spf13/cobra" ) var ( @@ -46,10 +48,10 @@ const ( ) type Config struct { - chain.Config `json:",squash"` //instead of `mapstructure:",squash"` - KeyStoreData json.RawMessage `json:"key_store"` - KeyStorePass string `json:"key_password,omitempty"` - KeySecret string `json:"key_secret,omitempty"` + btp.Config `json:",squash"` //instead of `mapstructure:",squash"` + KeyStoreData json.RawMessage `json:"key_store"` + KeyStorePass string `json:"key_password,omitempty"` + KeySecret string `json:"key_secret,omitempty"` LogLevel string `json:"log_level"` ConsoleLevel string `json:"console_level"` @@ -62,12 +64,12 @@ func (c *Config) Wallet() (wallet.Wallet, error) { if err != nil { return nil, err } - return wallet.NewFromKeyStore(c.KeyStoreData, pw) + return wallet.DecryptKeyStore(c.KeyStoreData, pw) } func (c *Config) resolvePassword() ([]byte, error) { if c.KeySecret != "" { - return ioutil.ReadFile(c.KeySecret) + return ioutil.ReadFile(cfg.ResolveAbsolute(c.KeySecret)) } else { if c.KeyStorePass == "" { return []byte(DefaultKeyStorePass), nil @@ -106,64 +108,32 @@ var logoLines = []string{ " |___/ ", } +var rootCmd, rootVc = cli.NewCommand(nil, nil, "btpsimple", "BTP Relay CLI") +var cfg = &Config{} + func main() { - rootCmd, rootVc := cli.NewCommand(nil, nil, "btpsimple", "BTP Relay CLI") - cfg := &Config{} + go profiling() + rootCmd.Long = "Command Line Interface of Relay for Blockchain Transmission Protocol" cli.SetEnvKeyReplacer(rootVc, strings.NewReplacer(".", "_")) - //rootVc.Debug() - - rootCmd.AddCommand(&cobra.Command{ - Use: "version", - Short: "Print btpsimple version", - Args: cobra.NoArgs, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("btpsimple version", version, build) - }, - }) - - rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { - baseDir := rootVc.GetString("base_dir") - logfile := rootVc.GetString("log_writer.filename") - cfg.FilePath = rootVc.GetString("config") - if cfg.FilePath != "" { - f, err := os.Open(cfg.FilePath) - if err != nil { - return fmt.Errorf("fail to open config file=%s err=%+v", cfg.FilePath, err) - } - rootVc.SetConfigType("json") - err = rootVc.ReadConfig(f) - if err != nil { - return fmt.Errorf("fail to read config file=%s err=%+v", cfg.FilePath, err) - } - cfg.FilePath, _ = filepath.Abs(cfg.FilePath) - } - if err := rootVc.Unmarshal(&cfg, cli.ViperDecodeOptJson); err != nil { - return fmt.Errorf("fail to unmarshall config from env err=%+v", err) - } - if baseDir != "" { - cfg.BaseDir = cfg.ResolveRelative(baseDir) - } - if logfile != "" { - cfg.LogWriter.Filename = cfg.ResolveRelative(logfile) - } - return nil - } + // rootVc.Debug() + // rootPFlags := rootCmd.PersistentFlags() + rootPFlags.String("base_dir", "", "Base directory for data") + rootPFlags.StringP("config", "c", "", "Parsing configuration file") + rootPFlags.String("src.address", "", "BTP Address of source blockchain (PROTOCOL://NID.BLOCKCHAIN/BMC)") rootPFlags.String("src.endpoint", "", "Endpoint of source blockchain") rootPFlags.StringToString("src.options", nil, "Options, comma-separated 'key=value'") rootPFlags.String("dst.address", "", "BTP Address of destination blockchain (PROTOCOL://NID.BLOCKCHAIN/BMC)") rootPFlags.String("dst.endpoint", "", "Endpoint of destination blockchain") rootPFlags.StringToString("dst.options", nil, "Options, comma-separated 'key=value'") + rootPFlags.Int64("offset", 0, "Offset of MTA") rootPFlags.String("key_store", "", "KeyStore") rootPFlags.String("key_password", "", "Password of KeyStore") rootPFlags.String("key_secret", "", "Secret(password) file for KeyStore") // - rootPFlags.String("base_dir", "", "Base directory for data") - rootPFlags.StringP("config", "c", "", "Parsing configuration file") - // rootPFlags.String("log_level", "debug", "Global log level (trace,debug,info,warn,error,fatal,panic)") rootPFlags.String("console_level", "trace", "Console log level (trace,debug,info,warn,error,fatal,panic)") // @@ -179,93 +149,59 @@ func main() { rootPFlags.Int("log_writer.maxbackups", 0, "Maximum number of backups") rootPFlags.Bool("log_writer.localtime", false, "Use localtime on rotated log file instead of UTC") rootPFlags.Bool("log_writer.compress", false, "Use gzip on rotated log file") + // + rootPFlags.String("sentry.dsn", "", "Sentry DSN for monitor bug and alerts") + rootPFlags.String("sentry.env", "", "Sentry environment to distinguish between stages") cli.BindPFlags(rootVc, rootPFlags) - cli.MarkAnnotationCustom(rootPFlags, "src.address", "dst.address", "src.endpoint", "dst.endpoint") - saveCmd := &cobra.Command{ - Use: "save [file]", - Short: "Save configuration", - Args: cli.ArgsWithDefaultErrorFunc(cobra.ExactArgs(1)), - PreRunE: func(cmd *cobra.Command, args []string) error { - if err := cfg.EnsureWallet(); err != nil { - return fmt.Errorf("fail to ensure src wallet err:%+v", err) - } else { - cfg.KeyStorePass = "" - } - return nil - }, - RunE: func(cmd *cobra.Command, args []string) error { - saveFilePath := args[0] - cfg.FilePath, _ = filepath.Abs(saveFilePath) - cfg.BaseDir = cfg.ResolveRelative(cfg.BaseDir) - if cfg.LogWriter != nil { - cfg.LogWriter.Filename = cfg.ResolveRelative(cfg.LogWriter.Filename) + rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + baseDir := rootVc.GetString("base_dir") + logfile := rootVc.GetString("log_writer.filename") + cfg.FilePath = rootVc.GetString("config") + sentryDsn := rootVc.GetString("sentry.dsn") + if cfg.FilePath != "" { + f, err := os.Open(cfg.ResolveAbsolute(cfg.FilePath)) + if err != nil { + return fmt.Errorf("fail to open config file=%s err=%+v", cfg.FilePath, err) } - - if err := cli.JsonPrettySaveFile(saveFilePath, 0644, cfg); err != nil { + // rootVc.SetConfigType("json") + // if err = rootVc.ReadConfig(f); err != nil { + // return fmt.Errorf("fail to read config file=%s err=%+v", cfg.FilePath, err) + // } + if err := json.NewDecoder(f).Decode(cfg); err != nil { return err } - cmd.Println("Save configuration to", saveFilePath) - if saveKeyStore, _ := cmd.Flags().GetString("save_key_store"); saveKeyStore != "" { - if err := cli.JsonPrettySaveFile(saveKeyStore, 0600, cfg.KeyStoreData); err != nil { - return err - } - } - return nil - }, - } - rootCmd.AddCommand(saveCmd) - saveCmd.Flags().String("save_key_store", "", "KeyStore File path to save") - startCmd := &cobra.Command{ - Use: "start", - Short: "Start server", - PreRunE: func(cmd *cobra.Command, args []string) error { - return cli.ValidateFlagsWithViper(rootVc, cmd.Flags()) - }, - RunE: func(cmd *cobra.Command, args []string) error { - for _, l := range logoLines { - log.Println(l) + cfg.FilePath, _ = filepath.Abs(cfg.FilePath) + } else { + if err := rootVc.Unmarshal(cfg, cli.ViperDecodeOptJson); err != nil { + return fmt.Errorf("fail to unmarshall config from env err=%+v", err) } - log.Printf("Version : %s", version) - log.Printf("Build : %s", build) + } - var ( - err error - w wallet.Wallet - ) - if w, err = cfg.Wallet(); err != nil { - return err - } - modLevels, _ := cmd.Flags().GetStringToString("mod_level") - l := setLogger(cfg, w, modLevels) - l.Debugln(cfg.FilePath, cfg.BaseDir) - if cfg.BaseDir == "" { - cfg.BaseDir = path.Join(".", ".btpsimple", cfg.Src.Address.NetworkAddress()) - } + if baseDir != "" { + cfg.BaseDir = cfg.ResolveRelative(baseDir) + } + if logfile != "" { + cfg.LogWriter.Filename = cfg.ResolveRelative(logfile) + } - var sr *chain.SimpleChain - if sr, err = chain.NewSimpleChain(&cfg.Config, w, l); err != nil { - return err - } - return sr.Serve() - }, - } - rootCmd.AddCommand(startCmd) - startFlags := startCmd.Flags() - startFlags.StringToString("mod_level", nil, "Set console log level for specific module ('mod'='level',...)") - startFlags.String("cpuprofile", "", "CPU Profiling data file") - startFlags.String("memprofile", "", "Memory Profiling data file") - startFlags.MarkHidden("mod_level") + if sentryDsn != "" { + sentryInit() + } - cli.BindPFlags(rootVc, startFlags) + return nil + } genMdCmd := cli.NewGenerateMarkdownCommand(rootCmd, rootVc) genMdCmd.Hidden = true rootCmd.SilenceUsage = true if err := rootCmd.Execute(); err != nil { - fmt.Printf("%+v",err) + fmt.Printf("%+v", err) + if rootVc.GetString("sentry.dsn") != "" { + sentry.Flush(2 * time.Second) + } os.Exit(1) } } @@ -323,3 +259,23 @@ func setLogger(cfg *Config, w wallet.Wallet, modLevels map[string]string) log.Lo return l } + +func profiling() { + http.ListenAndServe(":6060", nil) +} + +func sentryInit() { + err := sentry.Init(sentry.ClientOptions{ + // Either set your DSN here or set the SENTRY_DSN environment variable. + Dsn: rootVc.GetString("sentry.dsn"), + // Either set environment and release here or set the SENTRY_ENVIRONMENT + // and SENTRY_RELEASE environment variables. + Environment: rootVc.GetString("sentry.env"), + Release: build, + DebugWriter: log.GlobalLogger().Writer(), + }) + + if err != nil { + log.Fatalf("sentry.Init: %s", err) + } +} diff --git a/cmd/btpsimple/save.go b/cmd/btpsimple/save.go new file mode 100644 index 00000000..359e515e --- /dev/null +++ b/cmd/btpsimple/save.go @@ -0,0 +1,52 @@ +package main + +import ( + "fmt" + "path/filepath" + + "github.com/icon-project/btp/common/cli" + "github.com/spf13/cobra" +) + +func init() { + saveCmd := &cobra.Command{ + Use: "save [file]", + Short: "Save configuration", + Args: cli.ArgsWithDefaultErrorFunc(cobra.ExactArgs(1)), + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := cfg.EnsureWallet(); err != nil { + return fmt.Errorf("fail to ensure src wallet err:%+v", err) + } else { + cfg.KeyStorePass = "" + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + saveFilePath := args[0] + cfg.FilePath, _ = filepath.Abs(saveFilePath) + cfg.BaseDir = cfg.ResolveRelative(cfg.BaseDir) + + if cfg.LogWriter != nil { + cfg.LogWriter.Filename = cfg.ResolveRelative(cfg.LogWriter.Filename) + } + + if err := cli.JsonPrettySaveFile(saveFilePath, 0644, cfg); err != nil { + return err + } + cmd.Println("Save configuration to", saveFilePath) + if saveKeyStore, _ := cmd.Flags().GetString("save_key_store"); saveKeyStore != "" { + if err := cli.JsonPrettySaveFile(saveKeyStore, 0600, cfg.KeyStoreData); err != nil { + return err + } + } + return nil + }, + } + savePFlags := saveCmd.PersistentFlags() + saveCmd.Flags().String("save_key_store", "", "KeyStore File path to save") + + cli.BindPFlags(rootVc, savePFlags) + cli.MarkAnnotationCustom(savePFlags, "src.address", "dst.address", "src.endpoint", "dst.endpoint") + + rootCmd.AddCommand(saveCmd) +} diff --git a/cmd/btpsimple/start.go b/cmd/btpsimple/start.go new file mode 100644 index 00000000..ef75d7f0 --- /dev/null +++ b/cmd/btpsimple/start.go @@ -0,0 +1,60 @@ +package main + +import ( + "path" + + "github.com/icon-project/btp/btp" + "github.com/icon-project/btp/common/cli" + "github.com/icon-project/btp/common/log" + "github.com/icon-project/btp/common/wallet" + "github.com/spf13/cobra" +) + +func init() { + + startCmd := &cobra.Command{ + Use: "start", + Short: "Start server", + PreRunE: func(cmd *cobra.Command, args []string) error { + return cli.ValidateFlagsWithViper(rootVc, cmd.Flags()) + }, + RunE: func(cmd *cobra.Command, args []string) error { + + for _, l := range logoLines { + log.Println(l) + } + log.Printf("Version : %s", version) + log.Printf("Build : %s", build) + + var ( + err error + w wallet.Wallet + ) + if w, err = cfg.Wallet(); err != nil { + return err + } + modLevels, _ := cmd.Flags().GetStringToString("mod_level") + l := setLogger(cfg, w, modLevels) + l.Debugln(cfg.FilePath, cfg.BaseDir) + if cfg.BaseDir == "" { + cfg.BaseDir = path.Join(".", ".btpsimple", cfg.Src.Address.NetworkAddress()) + } + + var sr *btp.BTP + if sr, err = btp.New(&cfg.Config, w, l); err != nil { + return err + } + return sr.Serve() + }, + } + + rootCmd.AddCommand(startCmd) + + startFlags := startCmd.Flags() + startFlags.StringToString("mod_level", nil, "Set console log level for specific module ('mod'='level',...)") + startFlags.String("cpuprofile", "", "CPU Profiling data file") + startFlags.String("memprofile", "", "Memory Profiling data file") + startFlags.MarkHidden("mod_level") + + cli.BindPFlags(rootVc, startFlags) +} diff --git a/cmd/btpsimple/version.go b/cmd/btpsimple/version.go new file mode 100644 index 00000000..3ee08aec --- /dev/null +++ b/cmd/btpsimple/version.go @@ -0,0 +1,18 @@ +package main + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func init() { + rootCmd.AddCommand(&cobra.Command{ + Use: "version", + Short: "Print btpsimple version", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("btpsimple version", version, build) + }, + }) +} diff --git a/cmd/iconvalidators/main.go b/cmd/iconvalidators/main.go new file mode 100644 index 00000000..17207f0a --- /dev/null +++ b/cmd/iconvalidators/main.go @@ -0,0 +1,73 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + "os" + + "github.com/icon-project/btp/chain/icon" + "github.com/icon-project/btp/common/cli" + "github.com/icon-project/btp/common/codec" + "github.com/icon-project/btp/common/intconv" + "github.com/icon-project/btp/common/log" + "github.com/spf13/cobra" +) + +var rootCmd, rootVc = cli.NewCommand(nil, nil, "iconvalidators", "ICON Validators for ICON BMV on other chains") + +func main() { + client := icon.NewClient(os.Getenv("GOLOOP_RPC_URI"), log.New()) + + rootCmd.AddCommand(&cobra.Command{ + Use: "build", + Args: cli.ArgsWithDefaultErrorFunc(cobra.ExactArgs(1)), + RunE: func(cmd *cobra.Command, args []string) error { + height, err := intconv.ParseInt(args[0], 64) + if err != nil { + return err + } + + previousBlockHeaderInByte, err := client.GetBlockHeaderByHeight(&icon.BlockHeightParam{ + Height: icon.NewHexInt(height - 1), + }) + if err != nil { + panic(err) + } + var previousBlockHeader icon.BlockHeader + _, err = codec.RLP.UnmarshalFromBytes(previousBlockHeaderInByte, &previousBlockHeader) + if err != nil { + panic(err) + } + + validatorsInByte, err := client.GetDataByHash(&icon.DataHashParam{ + Hash: icon.NewHexBytes(previousBlockHeader.NextValidatorsHash), + }) + + if err != nil { + panic(err) + } + + return cli.JsonPrettyPrintln(os.Stdout, fmt.Sprintf("0x%x", validatorsInByte)) + }, + }) + + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} diff --git a/common/go-ethereum/rpc/client.go b/common/go-ethereum/rpc/client.go new file mode 100644 index 00000000..e9deb3f6 --- /dev/null +++ b/common/go-ethereum/rpc/client.go @@ -0,0 +1,666 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/url" + "reflect" + "strconv" + "sync/atomic" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +var ( + ErrClientQuit = errors.New("client is closed") + ErrNoResult = errors.New("no result in JSON-RPC response") + ErrSubscriptionQueueOverflow = errors.New("subscription queue overflow") + errClientReconnected = errors.New("client reconnected") + errDead = errors.New("connection lost") +) + +const ( + // Timeouts + defaultDialTimeout = 10 * time.Second // used if context has no deadline + subscribeTimeout = 5 * time.Second // overall timeout eth_subscribe, rpc_modules calls +) + +const ( + // Subscriptions are removed when the subscriber cannot keep up. + // + // This can be worked around by supplying a channel with sufficiently sized buffer, + // but this can be inconvenient and hard to explain in the docs. Another issue with + // buffered channels is that the buffer is static even though it might not be needed + // most of the time. + // + // The approach taken here is to maintain a per-subscription linked list buffer + // shrinks on demand. If the buffer reaches the size below, the subscription is + // dropped. + maxClientSubscriptionBuffer = 20000 +) + +const ( + httpScheme = "http" + wsScheme = "ws" + ipcScheme = "ipc" +) + +// BatchElem is an element in a batch request. +type BatchElem struct { + Method string + Args []interface{} + // The result is unmarshaled into this field. Result must be set to a + // non-nil pointer value of the desired type, otherwise the response will be + // discarded. + Result interface{} + // Error is set if the server returns an error for this request, or if + // unmarshaling into Result fails. It is not set for I/O errors. + Error error +} + +// Client represents a connection to an RPC server. +type Client struct { + idgen func() ID // for subscriptions + scheme string // connection type: http, ws or ipc + services *serviceRegistry + + idCounter uint32 + + // This function, if non-nil, is called when the connection is lost. + reconnectFunc reconnectFunc + + // writeConn is used for writing to the connection on the caller's goroutine. It should + // only be accessed outside of dispatch, with the write lock held. The write lock is + // taken by sending on reqInit and released by sending on reqSent. + writeConn jsonWriter + + // for dispatch + close chan struct{} + closing chan struct{} // closed when client is quitting + didClose chan struct{} // closed when client quits + reconnected chan ServerCodec // where write/reconnect sends the new connection + readOp chan readOp // read messages + readErr chan error // errors from read + reqInit chan *requestOp // register response IDs, takes write lock + reqSent chan error // signals write completion, releases write lock + reqTimeout chan *requestOp // removes response IDs when call timeout expires +} + +type reconnectFunc func(ctx context.Context) (ServerCodec, error) + +type clientContextKey struct{} + +type clientConn struct { + codec ServerCodec + handler *handler +} + +func (c *Client) newClientConn(conn ServerCodec) *clientConn { + ctx := context.WithValue(context.Background(), clientContextKey{}, c) + // Http connections have already set the scheme + if !c.isHTTP() && c.scheme != "" { + ctx = context.WithValue(ctx, "scheme", c.scheme) + } + handler := newHandler(ctx, conn, c.idgen, c.services) + return &clientConn{conn, handler} +} + +func (cc *clientConn) close(err error, inflightReq *requestOp) { + cc.handler.close(err, inflightReq) + cc.codec.close() +} + +type readOp struct { + msgs []*jsonrpcMessage + batch bool +} + +type requestOp struct { + ids []json.RawMessage + err error + resp chan *jsonrpcMessage // receives up to len(ids) responses + sub *ClientSubscription // only set for EthSubscribe requests +} + +func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, error) { + select { + case <-ctx.Done(): + // Send the timeout to dispatch so it can remove the request IDs. + if !c.isHTTP() { + select { + case c.reqTimeout <- op: + case <-c.closing: + } + } + return nil, ctx.Err() + case resp := <-op.resp: + return resp, op.err + } +} + +// Dial creates a new client for the given URL. +// +// The currently supported URL schemes are "http", "https", "ws" and "wss". If rawurl is a +// file name with no URL scheme, a local socket connection is established using UNIX +// domain sockets on supported platforms and named pipes on Windows. If you want to +// configure transport options, use DialHTTP, DialWebsocket or DialIPC instead. +// +// For websocket connections, the origin is set to the local host name. +// +// The client reconnects automatically if the connection is lost. +func Dial(rawurl string) (*Client, error) { + return DialContext(context.Background(), rawurl) +} + +// DialContext creates a new RPC client, just like Dial. +// +// The context is used to cancel or time out the initial connection establishment. It does +// not affect subsequent interactions with the client. +func DialContext(ctx context.Context, rawurl string) (*Client, error) { + u, err := url.Parse(rawurl) + if err != nil { + return nil, err + } + switch u.Scheme { + case "http", "https": + return DialHTTP(rawurl) + case "ws", "wss": + return DialWebsocket(ctx, rawurl, "") + case "stdio": + return DialStdIO(ctx) + case "": + return DialIPC(ctx, rawurl) + default: + return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme) + } +} + +// Client retrieves the client from the context, if any. This can be used to perform +// 'reverse calls' in a handler method. +func ClientFromContext(ctx context.Context) (*Client, bool) { + client, ok := ctx.Value(clientContextKey{}).(*Client) + return client, ok +} + +func newClient(initctx context.Context, connect reconnectFunc) (*Client, error) { + conn, err := connect(initctx) + if err != nil { + return nil, err + } + c := initClient(conn, randomIDGenerator(), new(serviceRegistry)) + c.reconnectFunc = connect + return c, nil +} + +func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client { + scheme := "" + switch conn.(type) { + case *httpConn: + scheme = httpScheme + case *websocketCodec: + scheme = wsScheme + case *jsonCodec: + scheme = ipcScheme + } + c := &Client{ + idgen: idgen, + scheme: scheme, + services: services, + writeConn: conn, + close: make(chan struct{}), + closing: make(chan struct{}), + didClose: make(chan struct{}), + reconnected: make(chan ServerCodec), + readOp: make(chan readOp), + readErr: make(chan error), + reqInit: make(chan *requestOp), + reqSent: make(chan error, 1), + reqTimeout: make(chan *requestOp), + } + if !c.isHTTP() { + go c.dispatch(conn) + } + return c +} + +// RegisterName creates a service for the given receiver type under the given name. When no +// methods on the given receiver match the criteria to be either a RPC method or a +// subscription an error is returned. Otherwise a new service is created and added to the +// service collection this client provides to the server. +func (c *Client) RegisterName(name string, receiver interface{}) error { + return c.services.registerName(name, receiver) +} + +func (c *Client) nextID() json.RawMessage { + id := atomic.AddUint32(&c.idCounter, 1) + return strconv.AppendUint(nil, uint64(id), 10) +} + +// SupportedModules calls the rpc_modules method, retrieving the list of +// APIs that are available on the server. +func (c *Client) SupportedModules() (map[string]string, error) { + var result map[string]string + ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + defer cancel() + err := c.CallContext(ctx, &result, "rpc_modules") + return result, err +} + +// Close closes the client, aborting any in-flight requests. +func (c *Client) Close() { + if c.isHTTP() { + return + } + select { + case c.close <- struct{}{}: + <-c.didClose + case <-c.didClose: + } +} + +// SetHeader adds a custom HTTP header to the client's requests. +// This method only works for clients using HTTP, it doesn't have +// any effect for clients using another transport. +func (c *Client) SetHeader(key, value string) { + if !c.isHTTP() { + return + } + conn := c.writeConn.(*httpConn) + conn.mu.Lock() + conn.headers.Set(key, value) + conn.mu.Unlock() +} + +// Call performs a JSON-RPC call with the given arguments and unmarshals into +// result if no error occurred. +// +// The result must be a pointer so that package json can unmarshal into it. You +// can also pass nil, in which case the result is ignored. +func (c *Client) Call(result interface{}, method string, args ...interface{}) error { + ctx := context.Background() + return c.CallContext(ctx, result, method, args...) +} + +// CallContext performs a JSON-RPC call with the given arguments. If the context is +// canceled before the call has successfully returned, CallContext returns immediately. +// +// The result must be a pointer so that package json can unmarshal into it. You +// can also pass nil, in which case the result is ignored. +func (c *Client) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error { + if result != nil && reflect.TypeOf(result).Kind() != reflect.Ptr { + return fmt.Errorf("call result parameter must be pointer or nil interface: %v", result) + } + msg, err := c.newMessage(method, args...) + if err != nil { + return err + } + op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)} + + if c.isHTTP() { + err = c.sendHTTP(ctx, op, msg) + } else { + err = c.send(ctx, op, msg) + } + if err != nil { + return err + } + + // dispatch has accepted the request and will close the channel when it quits. + switch resp, err := op.wait(ctx, c); { + case err != nil: + return err + case resp.Error != nil: + return resp.Error + case len(resp.Result) == 0: + return ErrNoResult + default: + return json.Unmarshal(resp.Result, &result) + } +} + +// BatchCall sends all given requests as a single batch and waits for the server +// to return a response for all of them. +// +// In contrast to Call, BatchCall only returns I/O errors. Any error specific to +// a request is reported through the Error field of the corresponding BatchElem. +// +// Note that batch calls may not be executed atomically on the server side. +func (c *Client) BatchCall(b []BatchElem) error { + ctx := context.Background() + return c.BatchCallContext(ctx, b) +} + +// BatchCall sends all given requests as a single batch and waits for the server +// to return a response for all of them. The wait duration is bounded by the +// context's deadline. +// +// In contrast to CallContext, BatchCallContext only returns errors that have occurred +// while sending the request. Any error specific to a request is reported through the +// Error field of the corresponding BatchElem. +// +// Note that batch calls may not be executed atomically on the server side. +func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { + msgs := make([]*jsonrpcMessage, len(b)) + op := &requestOp{ + ids: make([]json.RawMessage, len(b)), + resp: make(chan *jsonrpcMessage, len(b)), + } + for i, elem := range b { + msg, err := c.newMessage(elem.Method, elem.Args...) + if err != nil { + return err + } + msgs[i] = msg + op.ids[i] = msg.ID + } + + var err error + if c.isHTTP() { + err = c.sendBatchHTTP(ctx, op, msgs) + } else { + err = c.send(ctx, op, msgs) + } + + // Wait for all responses to come back. + for n := 0; n < len(b) && err == nil; n++ { + var resp *jsonrpcMessage + resp, err = op.wait(ctx, c) + if err != nil { + break + } + // Find the element corresponding to this response. + // The element is guaranteed to be present because dispatch + // only sends valid IDs to our channel. + var elem *BatchElem + for i := range msgs { + if bytes.Equal(msgs[i].ID, resp.ID) { + elem = &b[i] + break + } + } + if resp.Error != nil { + elem.Error = resp.Error + continue + } + if len(resp.Result) == 0 { + elem.Error = ErrNoResult + continue + } + elem.Error = json.Unmarshal(resp.Result, elem.Result) + } + return err +} + +// Notify sends a notification, i.e. a method call that doesn't expect a response. +func (c *Client) Notify(ctx context.Context, method string, args ...interface{}) error { + op := new(requestOp) + msg, err := c.newMessage(method, args...) + if err != nil { + return err + } + msg.ID = nil + + if c.isHTTP() { + return c.sendHTTP(ctx, op, msg) + } + return c.send(ctx, op, msg) +} + +// EthSubscribe registers a subscripion under the "eth" namespace. +func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { + return c.Subscribe(ctx, "eth", channel, args...) +} + +// ShhSubscribe registers a subscripion under the "shh" namespace. +// Deprecated: use Subscribe(ctx, "shh", ...). +func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) { + return c.Subscribe(ctx, "shh", channel, args...) +} + +// Subscribe calls the "_subscribe" method with the given arguments, +// registering a subscription. Server notifications for the subscription are +// sent to the given channel. The element type of the channel must match the +// expected type of content returned by the subscription. +// +// The context argument cancels the RPC request that sets up the subscription but has no +// effect on the subscription after Subscribe has returned. +// +// Slow subscribers will be dropped eventually. Client buffers up to 20000 notifications +// before considering the subscriber dead. The subscription Err channel will receive +// ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure +// that the channel usually has at least one reader to prevent this issue. +func (c *Client) Subscribe(ctx context.Context, namespace string, channel interface{}, args ...interface{}) (*ClientSubscription, error) { + // Check type of channel first. + chanVal := reflect.ValueOf(channel) + if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 { + panic("first argument to Subscribe must be a writable channel") + } + if chanVal.IsNil() { + panic("channel given to Subscribe must not be nil") + } + if c.isHTTP() { + return nil, ErrNotificationsUnsupported + } + + msg, err := c.newMessage(namespace+subscribeMethodSuffix, args...) + if err != nil { + return nil, err + } + op := &requestOp{ + ids: []json.RawMessage{msg.ID}, + resp: make(chan *jsonrpcMessage), + sub: newClientSubscription(c, namespace, chanVal), + } + + // Send the subscription request. + // The arrival and validity of the response is signaled on sub.quit. + if err := c.send(ctx, op, msg); err != nil { + return nil, err + } + if _, err := op.wait(ctx, c); err != nil { + return nil, err + } + return op.sub, nil +} + +func (c *Client) newMessage(method string, paramsIn ...interface{}) (*jsonrpcMessage, error) { + msg := &jsonrpcMessage{Version: vsn, ID: c.nextID(), Method: method} + if paramsIn != nil { // prevent sending "params":null + var err error + if msg.Params, err = json.Marshal(paramsIn); err != nil { + return nil, err + } + } + return msg, nil +} + +// send registers op with the dispatch loop, then sends msg on the connection. +// if sending fails, op is deregistered. +func (c *Client) send(ctx context.Context, op *requestOp, msg interface{}) error { + select { + case c.reqInit <- op: + err := c.write(ctx, msg, false) + c.reqSent <- err + return err + case <-ctx.Done(): + // This can happen if the client is overloaded or unable to keep up with + // subscription notifications. + return ctx.Err() + case <-c.closing: + return ErrClientQuit + } +} + +func (c *Client) write(ctx context.Context, msg interface{}, retry bool) error { + // The previous write failed. Try to establish a new connection. + if c.writeConn == nil { + if err := c.reconnect(ctx); err != nil { + return err + } + } + err := c.writeConn.writeJSON(ctx, msg) + if err != nil { + c.writeConn = nil + if !retry { + return c.write(ctx, msg, true) + } + } + return err +} + +func (c *Client) reconnect(ctx context.Context) error { + if c.reconnectFunc == nil { + return errDead + } + + if _, ok := ctx.Deadline(); !ok { + var cancel func() + ctx, cancel = context.WithTimeout(ctx, defaultDialTimeout) + defer cancel() + } + newconn, err := c.reconnectFunc(ctx) + if err != nil { + log.Trace("RPC client reconnect failed", "err", err) + return err + } + select { + case c.reconnected <- newconn: + c.writeConn = newconn + return nil + case <-c.didClose: + newconn.close() + return ErrClientQuit + } +} + +// dispatch is the main loop of the client. +// It sends read messages to waiting calls to Call and BatchCall +// and subscription notifications to registered subscriptions. +func (c *Client) dispatch(codec ServerCodec) { + var ( + lastOp *requestOp // tracks last send operation + reqInitLock = c.reqInit // nil while the send lock is held + conn = c.newClientConn(codec) + reading = true + ) + defer func() { + close(c.closing) + if reading { + conn.close(ErrClientQuit, nil) + c.drainRead() + } + close(c.didClose) + }() + + // Spawn the initial read loop. + go c.read(codec) + + for { + select { + case <-c.close: + return + + // Read path: + case op := <-c.readOp: + if op.batch { + conn.handler.handleBatch(op.msgs) + } else { + conn.handler.handleMsg(op.msgs[0]) + } + + case err := <-c.readErr: + conn.handler.log.Debug("RPC connection read error", "err", err) + conn.close(err, lastOp) + reading = false + + // Reconnect: + case newcodec := <-c.reconnected: + log.Debug("RPC client reconnected", "reading", reading, "conn", newcodec.remoteAddr()) + if reading { + // Wait for the previous read loop to exit. This is a rare case which + // happens if this loop isn't notified in time after the connection breaks. + // In those cases the caller will notice first and reconnect. Closing the + // handler terminates all waiting requests (closing op.resp) except for + // lastOp, which will be transferred to the new handler. + conn.close(errClientReconnected, lastOp) + c.drainRead() + } + go c.read(newcodec) + reading = true + conn = c.newClientConn(newcodec) + // Re-register the in-flight request on the new handler + // because that's where it will be sent. + conn.handler.addRequestOp(lastOp) + + // Send path: + case op := <-reqInitLock: + // Stop listening for further requests until the current one has been sent. + reqInitLock = nil + lastOp = op + conn.handler.addRequestOp(op) + + case err := <-c.reqSent: + if err != nil { + // Remove response handlers for the last send. When the read loop + // goes down, it will signal all other current operations. + conn.handler.removeRequestOp(lastOp) + } + // Let the next request in. + reqInitLock = c.reqInit + lastOp = nil + + case op := <-c.reqTimeout: + conn.handler.removeRequestOp(op) + } + } +} + +// drainRead drops read messages until an error occurs. +func (c *Client) drainRead() { + for { + select { + case <-c.readOp: + case <-c.readErr: + return + } + } +} + +// read decodes RPC messages from a codec, feeding them into dispatch. +func (c *Client) read(codec ServerCodec) { + for { + msgs, batch, err := codec.readBatch() + if _, ok := err.(*json.SyntaxError); ok { + codec.writeJSON(context.Background(), errorMessage(&parseError{err.Error()})) + } + if err != nil { + c.readErr <- err + return + } + c.readOp <- readOp{msgs, batch} + } +} + +func (c *Client) isHTTP() bool { + return c.scheme == httpScheme +} diff --git a/common/go-ethereum/rpc/client_example_test.go b/common/go-ethereum/rpc/client_example_test.go new file mode 100644 index 00000000..044b57a9 --- /dev/null +++ b/common/go-ethereum/rpc/client_example_test.go @@ -0,0 +1,89 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc_test + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" +) + +// In this example, our client wishes to track the latest 'block number' +// known to the server. The server supports two methods: +// +// eth_getBlockByNumber("latest", {}) +// returns the latest block object. +// +// eth_subscribe("newHeads") +// creates a subscription which fires block objects when new blocks arrive. + +type Block struct { + Number *hexutil.Big +} + +func ExampleClientSubscription() { + // Connect the client. + client, _ := rpc.Dial("ws://127.0.0.1:8545") + subch := make(chan Block) + + // Ensure that subch receives the latest block. + go func() { + for i := 0; ; i++ { + if i > 0 { + time.Sleep(2 * time.Second) + } + subscribeBlocks(client, subch) + } + }() + + // Print events from the subscription as they arrive. + for block := range subch { + fmt.Println("latest block:", block.Number) + } +} + +// subscribeBlocks runs in its own goroutine and maintains +// a subscription for new blocks. +func subscribeBlocks(client *rpc.Client, subch chan Block) { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Subscribe to new blocks. + sub, err := client.EthSubscribe(ctx, subch, "newHeads") + if err != nil { + fmt.Println("subscribe error:", err) + return + } + + // The connection is established now. + // Update the channel with the current block. + var lastBlock Block + err = client.CallContext(ctx, &lastBlock, "eth_getBlockByNumber", "latest", false) + if err != nil { + fmt.Println("can't get latest block:", err) + return + } + subch <- lastBlock + + // The subscription will deliver events to the channel. Wait for the + // subscription to end for any reason, then loop around to re-establish + // the connection. + fmt.Println("connection lost: ", <-sub.Err()) +} diff --git a/common/go-ethereum/rpc/client_test.go b/common/go-ethereum/rpc/client_test.go new file mode 100644 index 00000000..224eb0c5 --- /dev/null +++ b/common/go-ethereum/rpc/client_test.go @@ -0,0 +1,739 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "encoding/json" + "fmt" + "math/rand" + "net" + "net/http" + "net/http/httptest" + "os" + "reflect" + "runtime" + "strings" + "sync" + "testing" + "time" + + "github.com/davecgh/go-spew/spew" + "github.com/ethereum/go-ethereum/log" +) + +func TestClientRequest(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + var resp echoResult + if err := client.Call(&resp, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(resp, echoResult{"hello", 10, &echoArgs{"world"}}) { + t.Errorf("incorrect result %#v", resp) + } +} + +func TestClientResponseType(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + if err := client.Call(nil, "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { + t.Errorf("Passing nil as result should be fine, but got an error: %v", err) + } + var resultVar echoResult + // Note: passing the var, not a ref + err := client.Call(resultVar, "test_echo", "hello", 10, &echoArgs{"world"}) + if err == nil { + t.Error("Passing a var as result should be an error") + } +} + +// This test checks that server-returned errors with code and data come out of Client.Call. +func TestClientErrorData(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + var resp interface{} + err := client.Call(&resp, "test_returnError") + if err == nil { + t.Fatal("expected error") + } + + // Check code. + if e, ok := err.(Error); !ok { + t.Fatalf("client did not return rpc.Error, got %#v", e) + } else if e.ErrorCode() != (testError{}.ErrorCode()) { + t.Fatalf("wrong error code %d, want %d", e.ErrorCode(), testError{}.ErrorCode()) + } + // Check data. + if e, ok := err.(DataError); !ok { + t.Fatalf("client did not return rpc.DataError, got %#v", e) + } else if e.ErrorData() != (testError{}.ErrorData()) { + t.Fatalf("wrong error data %#v, want %#v", e.ErrorData(), testError{}.ErrorData()) + } +} + +func TestClientBatchRequest(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + batch := []BatchElem{ + { + Method: "test_echo", + Args: []interface{}{"hello", 10, &echoArgs{"world"}}, + Result: new(echoResult), + }, + { + Method: "test_echo", + Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, + Result: new(echoResult), + }, + { + Method: "no_such_method", + Args: []interface{}{1, 2, 3}, + Result: new(int), + }, + } + if err := client.BatchCall(batch); err != nil { + t.Fatal(err) + } + wantResult := []BatchElem{ + { + Method: "test_echo", + Args: []interface{}{"hello", 10, &echoArgs{"world"}}, + Result: &echoResult{"hello", 10, &echoArgs{"world"}}, + }, + { + Method: "test_echo", + Args: []interface{}{"hello2", 11, &echoArgs{"world"}}, + Result: &echoResult{"hello2", 11, &echoArgs{"world"}}, + }, + { + Method: "no_such_method", + Args: []interface{}{1, 2, 3}, + Result: new(int), + Error: &jsonError{Code: -32601, Message: "the method no_such_method does not exist/is not available"}, + }, + } + if !reflect.DeepEqual(batch, wantResult) { + t.Errorf("batch results mismatch:\ngot %swant %s", spew.Sdump(batch), spew.Sdump(wantResult)) + } +} + +func TestClientNotify(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + if err := client.Notify(context.Background(), "test_echo", "hello", 10, &echoArgs{"world"}); err != nil { + t.Fatal(err) + } +} + +// func TestClientCancelInproc(t *testing.T) { testClientCancel("inproc", t) } +func TestClientCancelWebsocket(t *testing.T) { testClientCancel("ws", t) } +func TestClientCancelHTTP(t *testing.T) { testClientCancel("http", t) } +func TestClientCancelIPC(t *testing.T) { testClientCancel("ipc", t) } + +// This test checks that requests made through CallContext can be canceled by canceling +// the context. +func testClientCancel(transport string, t *testing.T) { + // These tests take a lot of time, run them all at once. + // You probably want to run with -parallel 1 or comment out + // the call to t.Parallel if you enable the logging. + t.Parallel() + + server := newTestServer() + defer server.Stop() + + // What we want to achieve is that the context gets canceled + // at various stages of request processing. The interesting cases + // are: + // - cancel during dial + // - cancel while performing a HTTP request + // - cancel while waiting for a response + // + // To trigger those, the times are chosen such that connections + // are killed within the deadline for every other call (maxKillTimeout + // is 2x maxCancelTimeout). + // + // Once a connection is dead, there is a fair chance it won't connect + // successfully because the accept is delayed by 1s. + maxContextCancelTimeout := 300 * time.Millisecond + fl := &flakeyListener{ + maxAcceptDelay: 1 * time.Second, + maxKillTimeout: 600 * time.Millisecond, + } + + var client *Client + switch transport { + case "ws", "http": + c, hs := httpTestClient(server, transport, fl) + defer hs.Close() + client = c + case "ipc": + c, l := ipcTestClient(server, fl) + defer l.Close() + client = c + default: + panic("unknown transport: " + transport) + } + + // The actual test starts here. + var ( + wg sync.WaitGroup + nreqs = 10 + ncallers = 10 + ) + caller := func(index int) { + defer wg.Done() + for i := 0; i < nreqs; i++ { + var ( + ctx context.Context + cancel func() + timeout = time.Duration(rand.Int63n(int64(maxContextCancelTimeout))) + ) + if index < ncallers/2 { + // For half of the callers, create a context without deadline + // and cancel it later. + ctx, cancel = context.WithCancel(context.Background()) + time.AfterFunc(timeout, cancel) + } else { + // For the other half, create a context with a deadline instead. This is + // different because the context deadline is used to set the socket write + // deadline. + ctx, cancel = context.WithTimeout(context.Background(), timeout) + } + + // Now perform a call with the context. + // The key thing here is that no call will ever complete successfully. + err := client.CallContext(ctx, nil, "test_block") + switch { + case err == nil: + _, hasDeadline := ctx.Deadline() + t.Errorf("no error for call with %v wait time (deadline: %v)", timeout, hasDeadline) + // default: + // t.Logf("got expected error with %v wait time: %v", timeout, err) + } + cancel() + } + } + wg.Add(ncallers) + for i := 0; i < ncallers; i++ { + go caller(i) + } + wg.Wait() +} + +func TestClientSubscribeInvalidArg(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + check := func(shouldPanic bool, arg interface{}) { + defer func() { + err := recover() + if shouldPanic && err == nil { + t.Errorf("EthSubscribe should've panicked for %#v", arg) + } + if !shouldPanic && err != nil { + t.Errorf("EthSubscribe shouldn't have panicked for %#v", arg) + buf := make([]byte, 1024*1024) + buf = buf[:runtime.Stack(buf, false)] + t.Error(err) + t.Error(string(buf)) + } + }() + client.EthSubscribe(context.Background(), arg, "foo_bar") + } + check(true, nil) + check(true, 1) + check(true, (chan int)(nil)) + check(true, make(<-chan int)) + check(false, make(chan int)) + check(false, make(chan<- int)) +} + +func TestClientSubscribe(t *testing.T) { + server := newTestServer() + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + nc := make(chan int) + count := 10 + sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", count, 0) + if err != nil { + t.Fatal("can't subscribe:", err) + } + for i := 0; i < count; i++ { + if val := <-nc; val != i { + t.Fatalf("value mismatch: got %d, want %d", val, i) + } + } + + sub.Unsubscribe() + select { + case v := <-nc: + t.Fatal("received value after unsubscribe:", v) + case err := <-sub.Err(): + if err != nil { + t.Fatalf("Err returned a non-nil error after explicit unsubscribe: %q", err) + } + case <-time.After(1 * time.Second): + t.Fatalf("subscription not closed within 1s after unsubscribe") + } +} + +// In this test, the connection drops while Subscribe is waiting for a response. +func TestClientSubscribeClose(t *testing.T) { + server := newTestServer() + service := ¬ificationTestService{ + gotHangSubscriptionReq: make(chan struct{}), + unblockHangSubscription: make(chan struct{}), + } + if err := server.RegisterName("nftest2", service); err != nil { + t.Fatal(err) + } + + defer server.Stop() + client := DialInProc(server) + defer client.Close() + + var ( + nc = make(chan int) + errc = make(chan error, 1) + sub *ClientSubscription + err error + ) + go func() { + sub, err = client.Subscribe(context.Background(), "nftest2", nc, "hangSubscription", 999) + errc <- err + }() + + <-service.gotHangSubscriptionReq + client.Close() + service.unblockHangSubscription <- struct{}{} + + select { + case err := <-errc: + if err == nil { + t.Errorf("Subscribe returned nil error after Close") + } + if sub != nil { + t.Error("Subscribe returned non-nil subscription after Close") + } + case <-time.After(1 * time.Second): + t.Fatalf("Subscribe did not return within 1s after Close") + } +} + +// This test reproduces https://github.com/ethereum/go-ethereum/issues/17837 where the +// client hangs during shutdown when Unsubscribe races with Client.Close. +func TestClientCloseUnsubscribeRace(t *testing.T) { + server := newTestServer() + defer server.Stop() + + for i := 0; i < 20; i++ { + client := DialInProc(server) + nc := make(chan int) + sub, err := client.Subscribe(context.Background(), "nftest", nc, "someSubscription", 3, 1) + if err != nil { + t.Fatal(err) + } + go client.Close() + go sub.Unsubscribe() + select { + case <-sub.Err(): + case <-time.After(5 * time.Second): + t.Fatal("subscription not closed within timeout") + } + } +} + +// unsubscribeRecorder collects the subscription IDs of *_unsubscribe calls. +type unsubscribeRecorder struct { + ServerCodec + unsubscribes map[string]bool +} + +func (r *unsubscribeRecorder) readBatch() ([]*jsonrpcMessage, bool, error) { + if r.unsubscribes == nil { + r.unsubscribes = make(map[string]bool) + } + + msgs, batch, err := r.ServerCodec.readBatch() + for _, msg := range msgs { + if msg.isUnsubscribe() { + var params []string + if err := json.Unmarshal(msg.Params, ¶ms); err != nil { + panic("unsubscribe decode error: " + err.Error()) + } + r.unsubscribes[params[0]] = true + } + } + return msgs, batch, err +} + +// This checks that Client calls the _unsubscribe method on the server when Unsubscribe is +// called on a subscription. +func TestClientSubscriptionUnsubscribeServer(t *testing.T) { + t.Parallel() + + // Create the server. + srv := NewServer() + srv.RegisterName("nftest", new(notificationTestService)) + p1, p2 := net.Pipe() + recorder := &unsubscribeRecorder{ServerCodec: NewCodec(p1)} + go srv.ServeCodec(recorder, OptionMethodInvocation|OptionSubscriptions) + defer srv.Stop() + + // Create the client on the other end of the pipe. + client, _ := newClient(context.Background(), func(context.Context) (ServerCodec, error) { + return NewCodec(p2), nil + }) + defer client.Close() + + // Create the subscription. + ch := make(chan int) + sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", 1, 1) + if err != nil { + t.Fatal(err) + } + + // Unsubscribe and check that unsubscribe was called. + sub.Unsubscribe() + if !recorder.unsubscribes[sub.subid] { + t.Fatal("client did not call unsubscribe method") + } + if _, open := <-sub.Err(); open { + t.Fatal("subscription error channel not closed after unsubscribe") + } +} + +// This checks that the subscribed channel can be closed after Unsubscribe. +// It is the reproducer for https://github.com/ethereum/go-ethereum/issues/22322 +func TestClientSubscriptionChannelClose(t *testing.T) { + t.Parallel() + + var ( + srv = NewServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + srv.RegisterName("nftest", new(notificationTestService)) + client, _ := Dial(wsURL) + + for i := 0; i < 100; i++ { + ch := make(chan int, 100) + sub, err := client.Subscribe(context.Background(), "nftest", ch, "someSubscription", maxClientSubscriptionBuffer-1, 1) + if err != nil { + t.Fatal(err) + } + sub.Unsubscribe() + close(ch) + } +} + +// This test checks that Client doesn't lock up when a single subscriber +// doesn't read subscription events. +func TestClientNotificationStorm(t *testing.T) { + server := newTestServer() + defer server.Stop() + + doTest := func(count int, wantError bool) { + client := DialInProc(server) + defer client.Close() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Subscribe on the server. It will start sending many notifications + // very quickly. + nc := make(chan int) + sub, err := client.Subscribe(ctx, "nftest", nc, "someSubscription", count, 0) + if err != nil { + t.Fatal("can't subscribe:", err) + } + defer sub.Unsubscribe() + + // Process each notification, try to run a call in between each of them. + for i := 0; i < count; i++ { + select { + case val := <-nc: + if val != i { + t.Fatalf("(%d/%d) unexpected value %d", i, count, val) + } + case err := <-sub.Err(): + if wantError && err != ErrSubscriptionQueueOverflow { + t.Fatalf("(%d/%d) got error %q, want %q", i, count, err, ErrSubscriptionQueueOverflow) + } else if !wantError { + t.Fatalf("(%d/%d) got unexpected error %q", i, count, err) + } + return + } + var r int + err := client.CallContext(ctx, &r, "nftest_echo", i) + if err != nil { + if !wantError { + t.Fatalf("(%d/%d) call error: %v", i, count, err) + } + return + } + } + if wantError { + t.Fatalf("didn't get expected error") + } + } + + doTest(8000, false) + doTest(24000, true) +} + +func TestClientSetHeader(t *testing.T) { + var gotHeader bool + srv := newTestServer() + httpsrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Header.Get("test") == "ok" { + gotHeader = true + } + srv.ServeHTTP(w, r) + })) + defer httpsrv.Close() + defer srv.Stop() + + client, err := Dial(httpsrv.URL) + if err != nil { + t.Fatal(err) + } + defer client.Close() + + client.SetHeader("test", "ok") + if _, err := client.SupportedModules(); err != nil { + t.Fatal(err) + } + if !gotHeader { + t.Fatal("client did not set custom header") + } + + // Check that Content-Type can be replaced. + client.SetHeader("content-type", "application/x-garbage") + _, err = client.SupportedModules() + if err == nil { + t.Fatal("no error for invalid content-type header") + } else if !strings.Contains(err.Error(), "Unsupported Media Type") { + t.Fatalf("error is not related to content-type: %q", err) + } +} + +func TestClientHTTP(t *testing.T) { + server := newTestServer() + defer server.Stop() + + client, hs := httpTestClient(server, "http", nil) + defer hs.Close() + defer client.Close() + + // Launch concurrent requests. + var ( + results = make([]echoResult, 100) + errc = make(chan error, len(results)) + wantResult = echoResult{"a", 1, new(echoArgs)} + ) + defer client.Close() + for i := range results { + i := i + go func() { + errc <- client.Call(&results[i], "test_echo", wantResult.String, wantResult.Int, wantResult.Args) + }() + } + + // Wait for all of them to complete. + timeout := time.NewTimer(5 * time.Second) + defer timeout.Stop() + for i := range results { + select { + case err := <-errc: + if err != nil { + t.Fatal(err) + } + case <-timeout.C: + t.Fatalf("timeout (got %d/%d) results)", i+1, len(results)) + } + } + + // Check results. + for i := range results { + if !reflect.DeepEqual(results[i], wantResult) { + t.Errorf("result %d mismatch: got %#v, want %#v", i, results[i], wantResult) + } + } +} + +func TestClientReconnect(t *testing.T) { + startServer := func(addr string) (*Server, net.Listener) { + srv := newTestServer() + l, err := net.Listen("tcp", addr) + if err != nil { + t.Fatal("can't listen:", err) + } + go http.Serve(l, srv.WebsocketHandler([]string{"*"})) + return srv, l + } + + ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) + defer cancel() + + // Start a server and corresponding client. + s1, l1 := startServer("127.0.0.1:0") + client, err := DialContext(ctx, "ws://"+l1.Addr().String()) + if err != nil { + t.Fatal("can't dial", err) + } + + // Perform a call. This should work because the server is up. + var resp echoResult + if err := client.CallContext(ctx, &resp, "test_echo", "", 1, nil); err != nil { + t.Fatal(err) + } + + // Shut down the server and allow for some cool down time so we can listen on the same + // address again. + l1.Close() + s1.Stop() + time.Sleep(2 * time.Second) + + // Try calling again. It shouldn't work. + if err := client.CallContext(ctx, &resp, "test_echo", "", 2, nil); err == nil { + t.Error("successful call while the server is down") + t.Logf("resp: %#v", resp) + } + + // Start it up again and call again. The connection should be reestablished. + // We spawn multiple calls here to check whether this hangs somehow. + s2, l2 := startServer(l1.Addr().String()) + defer l2.Close() + defer s2.Stop() + + start := make(chan struct{}) + errors := make(chan error, 20) + for i := 0; i < cap(errors); i++ { + go func() { + <-start + var resp echoResult + errors <- client.CallContext(ctx, &resp, "test_echo", "", 3, nil) + }() + } + close(start) + errcount := 0 + for i := 0; i < cap(errors); i++ { + if err = <-errors; err != nil { + errcount++ + } + } + t.Logf("%d errors, last error: %v", errcount, err) + if errcount > 1 { + t.Errorf("expected one error after disconnect, got %d", errcount) + } +} + +func httpTestClient(srv *Server, transport string, fl *flakeyListener) (*Client, *httptest.Server) { + // Create the HTTP server. + var hs *httptest.Server + switch transport { + case "ws": + hs = httptest.NewUnstartedServer(srv.WebsocketHandler([]string{"*"})) + case "http": + hs = httptest.NewUnstartedServer(srv) + default: + panic("unknown HTTP transport: " + transport) + } + // Wrap the listener if required. + if fl != nil { + fl.Listener = hs.Listener + hs.Listener = fl + } + // Connect the client. + hs.Start() + client, err := Dial(transport + "://" + hs.Listener.Addr().String()) + if err != nil { + panic(err) + } + return client, hs +} + +func ipcTestClient(srv *Server, fl *flakeyListener) (*Client, net.Listener) { + // Listen on a random endpoint. + endpoint := fmt.Sprintf("go-ethereum-test-ipc-%d-%d", os.Getpid(), rand.Int63()) + if runtime.GOOS == "windows" { + endpoint = `\\.\pipe\` + endpoint + } else { + endpoint = os.TempDir() + "/" + endpoint + } + l, err := ipcListen(endpoint) + if err != nil { + panic(err) + } + // Connect the listener to the server. + if fl != nil { + fl.Listener = l + l = fl + } + go srv.ServeListener(l) + // Connect the client. + client, err := Dial(endpoint) + if err != nil { + panic(err) + } + return client, l +} + +// flakeyListener kills accepted connections after a random timeout. +type flakeyListener struct { + net.Listener + maxKillTimeout time.Duration + maxAcceptDelay time.Duration +} + +func (l *flakeyListener) Accept() (net.Conn, error) { + delay := time.Duration(rand.Int63n(int64(l.maxAcceptDelay))) + time.Sleep(delay) + + c, err := l.Listener.Accept() + if err == nil { + timeout := time.Duration(rand.Int63n(int64(l.maxKillTimeout))) + time.AfterFunc(timeout, func() { + log.Debug(fmt.Sprintf("killing conn %v after %v", c.LocalAddr(), timeout)) + c.Close() + }) + } + return c, err +} diff --git a/common/go-ethereum/rpc/constants_unix.go b/common/go-ethereum/rpc/constants_unix.go new file mode 100644 index 00000000..e7ea03b1 --- /dev/null +++ b/common/go-ethereum/rpc/constants_unix.go @@ -0,0 +1,35 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package rpc + +// /* +// #include + +// int max_socket_path_size() { +// struct sockaddr_un s; +// return sizeof(s.sun_path); +// } +// */ +// import "C" +// https://github.com/centrifuge/go-substrate-rpc-client/pull/125 + +var ( + max_path_size = 108 +) diff --git a/common/go-ethereum/rpc/doc.go b/common/go-ethereum/rpc/doc.go new file mode 100644 index 00000000..e0a63246 --- /dev/null +++ b/common/go-ethereum/rpc/doc.go @@ -0,0 +1,110 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +/* + +Package rpc implements bi-directional JSON-RPC 2.0 on multiple transports. + +It provides access to the exported methods of an object across a network or other I/O +connection. After creating a server or client instance, objects can be registered to make +them visible as 'services'. Exported methods that follow specific conventions can be +called remotely. It also has support for the publish/subscribe pattern. + +RPC Methods + +Methods that satisfy the following criteria are made available for remote access: + + - method must be exported + - method returns 0, 1 (response or error) or 2 (response and error) values + +An example method: + + func (s *CalcService) Add(a, b int) (int, error) + +When the returned error isn't nil the returned integer is ignored and the error is sent +back to the client. Otherwise the returned integer is sent back to the client. + +Optional arguments are supported by accepting pointer values as arguments. E.g. if we want +to do the addition in an optional finite field we can accept a mod argument as pointer +value. + + func (s *CalcService) Add(a, b int, mod *int) (int, error) + +This RPC method can be called with 2 integers and a null value as third argument. In that +case the mod argument will be nil. Or it can be called with 3 integers, in that case mod +will be pointing to the given third argument. Since the optional argument is the last +argument the RPC package will also accept 2 integers as arguments. It will pass the mod +argument as nil to the RPC method. + +The server offers the ServeCodec method which accepts a ServerCodec instance. It will read +requests from the codec, process the request and sends the response back to the client +using the codec. The server can execute requests concurrently. Responses can be sent back +to the client out of order. + +An example server which uses the JSON codec: + + type CalculatorService struct {} + + func (s *CalculatorService) Add(a, b int) int { + return a + b + } + + func (s *CalculatorService) Div(a, b int) (int, error) { + if b == 0 { + return 0, errors.New("divide by zero") + } + return a/b, nil + } + + calculator := new(CalculatorService) + server := NewServer() + server.RegisterName("calculator", calculator) + l, _ := net.ListenUnix("unix", &net.UnixAddr{Net: "unix", Name: "/tmp/calculator.sock"}) + server.ServeListener(l) + +Subscriptions + +The package also supports the publish subscribe pattern through the use of subscriptions. +A method that is considered eligible for notifications must satisfy the following +criteria: + + - method must be exported + - first method argument type must be context.Context + - method must have return types (rpc.Subscription, error) + +An example method: + + func (s *BlockChainService) NewBlocks(ctx context.Context) (rpc.Subscription, error) { + ... + } + +When the service containing the subscription method is registered to the server, for +example under the "blockchain" namespace, a subscription is created by calling the +"blockchain_subscribe" method. + +Subscriptions are deleted when the user sends an unsubscribe request or when the +connection which was used to create the subscription is closed. This can be initiated by +the client and server. The server will close the connection for any write error. + +For more information about subscriptions, see https://github.com/ethereum/go-ethereum/wiki/RPC-PUB-SUB. + +Reverse Calls + +In any method handler, an instance of rpc.Client can be accessed through the +ClientFromContext method. Using this client instance, server-to-client method calls can be +performed on the RPC connection. +*/ +package rpc diff --git a/common/go-ethereum/rpc/endpoints.go b/common/go-ethereum/rpc/endpoints.go new file mode 100644 index 00000000..d78ebe28 --- /dev/null +++ b/common/go-ethereum/rpc/endpoints.go @@ -0,0 +1,52 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "net" + "strings" + + "github.com/ethereum/go-ethereum/log" +) + +// StartIPCEndpoint starts an IPC endpoint. +func StartIPCEndpoint(ipcEndpoint string, apis []API) (net.Listener, *Server, error) { + // Register all the APIs exposed by the services. + var ( + handler = NewServer() + regMap = make(map[string]struct{}) + registered []string + ) + for _, api := range apis { + if err := handler.RegisterName(api.Namespace, api.Service); err != nil { + log.Info("IPC registration failed", "namespace", api.Namespace, "error", err) + return nil, nil, err + } + if _, ok := regMap[api.Namespace]; !ok { + registered = append(registered, api.Namespace) + regMap[api.Namespace] = struct{}{} + } + } + log.Debug("IPCs registered", "namespaces", strings.Join(registered, ",")) + // All APIs registered, start the IPC listener. + listener, err := ipcListen(ipcEndpoint) + if err != nil { + return nil, nil, err + } + go handler.ServeListener(listener) + return listener, handler, nil +} diff --git a/common/go-ethereum/rpc/errors.go b/common/go-ethereum/rpc/errors.go new file mode 100644 index 00000000..4c06a745 --- /dev/null +++ b/common/go-ethereum/rpc/errors.go @@ -0,0 +1,103 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import "fmt" + +// HTTPError is returned by client operations when the HTTP status code of the +// response is not a 2xx status. +type HTTPError struct { + StatusCode int + Status string + Body []byte +} + +func (err HTTPError) Error() string { + if len(err.Body) == 0 { + return err.Status + } + return fmt.Sprintf("%v: %s", err.Status, err.Body) +} + +// Error wraps RPC errors, which contain an error code in addition to the message. +type Error interface { + Error() string // returns the message + ErrorCode() int // returns the code +} + +// A DataError contains some data in addition to the error message. +type DataError interface { + Error() string // returns the message + ErrorData() interface{} // returns the error data +} + +// Error types defined below are the built-in JSON-RPC errors. + +var ( + _ Error = new(methodNotFoundError) + _ Error = new(subscriptionNotFoundError) + _ Error = new(parseError) + _ Error = new(invalidRequestError) + _ Error = new(invalidMessageError) + _ Error = new(invalidParamsError) +) + +const defaultErrorCode = -32000 + +type methodNotFoundError struct{ method string } + +func (e *methodNotFoundError) ErrorCode() int { return -32601 } + +func (e *methodNotFoundError) Error() string { + return fmt.Sprintf("the method %s does not exist/is not available", e.method) +} + +type subscriptionNotFoundError struct{ namespace, subscription string } + +func (e *subscriptionNotFoundError) ErrorCode() int { return -32601 } + +func (e *subscriptionNotFoundError) Error() string { + return fmt.Sprintf("no %q subscription in %s namespace", e.subscription, e.namespace) +} + +// Invalid JSON was received by the server. +type parseError struct{ message string } + +func (e *parseError) ErrorCode() int { return -32700 } + +func (e *parseError) Error() string { return e.message } + +// received message isn't a valid request +type invalidRequestError struct{ message string } + +func (e *invalidRequestError) ErrorCode() int { return -32600 } + +func (e *invalidRequestError) Error() string { return e.message } + +// received message is invalid +type invalidMessageError struct{ message string } + +func (e *invalidMessageError) ErrorCode() int { return -32700 } + +func (e *invalidMessageError) Error() string { return e.message } + +// unable to decode supplied params, or an invalid number of parameters +type invalidParamsError struct{ message string } + +func (e *invalidParamsError) ErrorCode() int { return -32602 } + +func (e *invalidParamsError) Error() string { return e.message } diff --git a/common/go-ethereum/rpc/handler.go b/common/go-ethereum/rpc/handler.go new file mode 100644 index 00000000..85f774a1 --- /dev/null +++ b/common/go-ethereum/rpc/handler.go @@ -0,0 +1,417 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "encoding/json" + "reflect" + "strconv" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" +) + +// handler handles JSON-RPC messages. There is one handler per connection. Note that +// handler is not safe for concurrent use. Message handling never blocks indefinitely +// because RPCs are processed on background goroutines launched by handler. +// +// The entry points for incoming messages are: +// +// h.handleMsg(message) +// h.handleBatch(message) +// +// Outgoing calls use the requestOp struct. Register the request before sending it +// on the connection: +// +// op := &requestOp{ids: ...} +// h.addRequestOp(op) +// +// Now send the request, then wait for the reply to be delivered through handleMsg: +// +// if err := op.wait(...); err != nil { +// h.removeRequestOp(op) // timeout, etc. +// } +// +type handler struct { + reg *serviceRegistry + unsubscribeCb *callback + idgen func() ID // subscription ID generator + respWait map[string]*requestOp // active client requests + clientSubs map[string]*ClientSubscription // active client subscriptions + callWG sync.WaitGroup // pending call goroutines + rootCtx context.Context // canceled by close() + cancelRoot func() // cancel function for rootCtx + conn jsonWriter // where responses will be sent + log log.Logger + allowSubscribe bool + + subLock sync.Mutex + serverSubs map[ID]*Subscription +} + +type callProc struct { + ctx context.Context + notifiers []*Notifier +} + +func newHandler(connCtx context.Context, conn jsonWriter, idgen func() ID, reg *serviceRegistry) *handler { + rootCtx, cancelRoot := context.WithCancel(connCtx) + h := &handler{ + reg: reg, + idgen: idgen, + conn: conn, + respWait: make(map[string]*requestOp), + clientSubs: make(map[string]*ClientSubscription), + rootCtx: rootCtx, + cancelRoot: cancelRoot, + allowSubscribe: true, + serverSubs: make(map[ID]*Subscription), + log: log.Root(), + } + if conn.remoteAddr() != "" { + h.log = h.log.New("conn", conn.remoteAddr()) + } + h.unsubscribeCb = newCallback(reflect.Value{}, reflect.ValueOf(h.unsubscribe)) + return h +} + +// handleBatch executes all messages in a batch and returns the responses. +func (h *handler) handleBatch(msgs []*jsonrpcMessage) { + // Emit error response for empty batches: + if len(msgs) == 0 { + h.startCallProc(func(cp *callProc) { + h.conn.writeJSON(cp.ctx, errorMessage(&invalidRequestError{"empty batch"})) + }) + return + } + + // Handle non-call messages first: + calls := make([]*jsonrpcMessage, 0, len(msgs)) + for _, msg := range msgs { + if handled := h.handleImmediate(msg); !handled { + calls = append(calls, msg) + } + } + if len(calls) == 0 { + return + } + // Process calls on a goroutine because they may block indefinitely: + h.startCallProc(func(cp *callProc) { + answers := make([]*jsonrpcMessage, 0, len(msgs)) + for _, msg := range calls { + if answer := h.handleCallMsg(cp, msg); answer != nil { + answers = append(answers, answer) + } + } + h.addSubscriptions(cp.notifiers) + if len(answers) > 0 { + h.conn.writeJSON(cp.ctx, answers) + } + for _, n := range cp.notifiers { + n.activate() + } + }) +} + +// handleMsg handles a single message. +func (h *handler) handleMsg(msg *jsonrpcMessage) { + if ok := h.handleImmediate(msg); ok { + return + } + h.startCallProc(func(cp *callProc) { + answer := h.handleCallMsg(cp, msg) + h.addSubscriptions(cp.notifiers) + if answer != nil { + h.conn.writeJSON(cp.ctx, answer) + } + for _, n := range cp.notifiers { + n.activate() + } + }) +} + +// close cancels all requests except for inflightReq and waits for +// call goroutines to shut down. +func (h *handler) close(err error, inflightReq *requestOp) { + h.cancelAllRequests(err, inflightReq) + h.callWG.Wait() + h.cancelRoot() + h.cancelServerSubscriptions(err) +} + +// addRequestOp registers a request operation. +func (h *handler) addRequestOp(op *requestOp) { + for _, id := range op.ids { + h.respWait[string(id)] = op + } +} + +// removeRequestOps stops waiting for the given request IDs. +func (h *handler) removeRequestOp(op *requestOp) { + for _, id := range op.ids { + delete(h.respWait, string(id)) + } +} + +// cancelAllRequests unblocks and removes pending requests and active subscriptions. +func (h *handler) cancelAllRequests(err error, inflightReq *requestOp) { + didClose := make(map[*requestOp]bool) + if inflightReq != nil { + didClose[inflightReq] = true + } + + for id, op := range h.respWait { + // Remove the op so that later calls will not close op.resp again. + delete(h.respWait, id) + + if !didClose[op] { + op.err = err + close(op.resp) + didClose[op] = true + } + } + for id, sub := range h.clientSubs { + delete(h.clientSubs, id) + sub.close(err) + } +} + +func (h *handler) addSubscriptions(nn []*Notifier) { + h.subLock.Lock() + defer h.subLock.Unlock() + + for _, n := range nn { + if sub := n.takeSubscription(); sub != nil { + h.serverSubs[sub.ID] = sub + } + } +} + +// cancelServerSubscriptions removes all subscriptions and closes their error channels. +func (h *handler) cancelServerSubscriptions(err error) { + h.subLock.Lock() + defer h.subLock.Unlock() + + for id, s := range h.serverSubs { + s.err <- err + close(s.err) + delete(h.serverSubs, id) + } +} + +// startCallProc runs fn in a new goroutine and starts tracking it in the h.calls wait group. +func (h *handler) startCallProc(fn func(*callProc)) { + h.callWG.Add(1) + go func() { + ctx, cancel := context.WithCancel(h.rootCtx) + defer h.callWG.Done() + defer cancel() + fn(&callProc{ctx: ctx}) + }() +} + +// handleImmediate executes non-call messages. It returns false if the message is a +// call or requires a reply. +func (h *handler) handleImmediate(msg *jsonrpcMessage) bool { + start := time.Now() + switch { + case msg.isNotification(): + if strings.HasSuffix(msg.Method, notificationMethodSuffix) { + h.handleSubscriptionResult(msg) + return true + } + return false + case msg.isResponse(): + h.handleResponse(msg) + h.log.Trace("Handled RPC response", "reqid", idForLog{msg.ID}, "t", time.Since(start)) + return true + default: + return false + } +} + +// handleSubscriptionResult processes subscription notifications. +func (h *handler) handleSubscriptionResult(msg *jsonrpcMessage) { + var result subscriptionResult + if err := json.Unmarshal(msg.Params, &result); err != nil { + h.log.Debug("Dropping invalid subscription message") + return + } + if h.clientSubs[result.ID] != nil { + h.clientSubs[result.ID].deliver(result.Result) + } +} + +// handleResponse processes method call responses. +func (h *handler) handleResponse(msg *jsonrpcMessage) { + op := h.respWait[string(msg.ID)] + if op == nil { + h.log.Debug("Unsolicited RPC response", "reqid", idForLog{msg.ID}) + return + } + delete(h.respWait, string(msg.ID)) + // For normal responses, just forward the reply to Call/BatchCall. + if op.sub == nil { + op.resp <- msg + return + } + // For subscription responses, start the subscription if the server + // indicates success. EthSubscribe gets unblocked in either case through + // the op.resp channel. + defer close(op.resp) + if msg.Error != nil { + op.err = msg.Error + return + } + if op.err = json.Unmarshal(msg.Result, &op.sub.subid); op.err == nil { + go op.sub.run() + h.clientSubs[op.sub.subid] = op.sub + } +} + +// handleCallMsg executes a call message and returns the answer. +func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMessage { + start := time.Now() + switch { + case msg.isNotification(): + h.handleCall(ctx, msg) + h.log.Debug("Served "+msg.Method, "t", time.Since(start)) + return nil + case msg.isCall(): + resp := h.handleCall(ctx, msg) + var ctx []interface{} + ctx = append(ctx, "reqid", idForLog{msg.ID}, "t", time.Since(start)) + if resp.Error != nil { + ctx = append(ctx, "err", resp.Error.Message) + if resp.Error.Data != nil { + ctx = append(ctx, "errdata", resp.Error.Data) + } + h.log.Warn("Served "+msg.Method, ctx...) + } else { + h.log.Debug("Served "+msg.Method, ctx...) + } + return resp + case msg.hasValidID(): + return msg.errorResponse(&invalidRequestError{"invalid request"}) + default: + return errorMessage(&invalidRequestError{"invalid request"}) + } +} + +// handleCall processes method calls. +func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { + if msg.isSubscribe() { + return h.handleSubscribe(cp, msg) + } + var callb *callback + if msg.isUnsubscribe() { + callb = h.unsubscribeCb + } else { + callb = h.reg.callback(msg.Method) + } + if callb == nil { + return msg.errorResponse(&methodNotFoundError{method: msg.Method}) + } + args, err := parsePositionalArguments(msg.Params, callb.argTypes) + if err != nil { + return msg.errorResponse(&invalidParamsError{err.Error()}) + } + start := time.Now() + answer := h.runMethod(cp.ctx, msg, callb, args) + + // Collect the statistics for RPC calls if metrics is enabled. + // We only care about pure rpc call. Filter out subscription. + if callb != h.unsubscribeCb { + rpcRequestGauge.Inc(1) + if answer.Error != nil { + failedReqeustGauge.Inc(1) + } else { + successfulRequestGauge.Inc(1) + } + rpcServingTimer.UpdateSince(start) + newRPCServingTimer(msg.Method, answer.Error == nil).UpdateSince(start) + } + return answer +} + +// handleSubscribe processes *_subscribe method calls. +func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage { + if !h.allowSubscribe { + return msg.errorResponse(ErrNotificationsUnsupported) + } + + // Subscription method name is first argument. + name, err := parseSubscriptionName(msg.Params) + if err != nil { + return msg.errorResponse(&invalidParamsError{err.Error()}) + } + namespace := msg.namespace() + callb := h.reg.subscription(namespace, name) + if callb == nil { + return msg.errorResponse(&subscriptionNotFoundError{namespace, name}) + } + + // Parse subscription name arg too, but remove it before calling the callback. + argTypes := append([]reflect.Type{stringType}, callb.argTypes...) + args, err := parsePositionalArguments(msg.Params, argTypes) + if err != nil { + return msg.errorResponse(&invalidParamsError{err.Error()}) + } + args = args[1:] + + // Install notifier in context so the subscription handler can find it. + n := &Notifier{h: h, namespace: namespace} + cp.notifiers = append(cp.notifiers, n) + ctx := context.WithValue(cp.ctx, notifierKey{}, n) + + return h.runMethod(ctx, msg, callb, args) +} + +// runMethod runs the Go callback for an RPC method. +func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *callback, args []reflect.Value) *jsonrpcMessage { + result, err := callb.call(ctx, msg.Method, args) + if err != nil { + return msg.errorResponse(err) + } + return msg.response(result) +} + +// unsubscribe is the callback function for all *_unsubscribe calls. +func (h *handler) unsubscribe(ctx context.Context, id ID) (bool, error) { + h.subLock.Lock() + defer h.subLock.Unlock() + + s := h.serverSubs[id] + if s == nil { + return false, ErrSubscriptionNotFound + } + close(s.err) + delete(h.serverSubs, id) + return true, nil +} + +type idForLog struct{ json.RawMessage } + +func (id idForLog) String() string { + if s, err := strconv.Unquote(string(id.RawMessage)); err == nil { + return s + } + return string(id.RawMessage) +} diff --git a/common/go-ethereum/rpc/http.go b/common/go-ethereum/rpc/http.go new file mode 100644 index 00000000..fa9bfd6a --- /dev/null +++ b/common/go-ethereum/rpc/http.go @@ -0,0 +1,292 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "mime" + "net/http" + "net/url" + "sync" + "time" + + "github.com/hashicorp/go-retryablehttp" + "github.com/icon-project/btp/common/log" +) + +const ( + maxRequestContentLength = 1024 * 1024 * 5 + contentType = "application/json" +) + +// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13 +var acceptedContentTypes = []string{contentType, "application/json-rpc", "application/jsonrequest"} + +type httpConn struct { + client *http.Client + url string + closeOnce sync.Once + closeCh chan interface{} + mu sync.Mutex // protects headers + headers http.Header +} + +// httpConn is treated specially by Client. +func (hc *httpConn) writeJSON(context.Context, interface{}) error { + panic("writeJSON called on httpConn") +} + +func (hc *httpConn) remoteAddr() string { + return hc.url +} + +func (hc *httpConn) readBatch() ([]*jsonrpcMessage, bool, error) { + <-hc.closeCh + return nil, false, io.EOF +} + +func (hc *httpConn) close() { + hc.closeOnce.Do(func() { close(hc.closeCh) }) +} + +func (hc *httpConn) closed() <-chan interface{} { + return hc.closeCh +} + +// HTTPTimeouts represents the configuration params for the HTTP RPC server. +type HTTPTimeouts struct { + // ReadTimeout is the maximum duration for reading the entire + // request, including the body. + // + // Because ReadTimeout does not let Handlers make per-request + // decisions on each request body's acceptable deadline or + // upload rate, most users will prefer to use + // ReadHeaderTimeout. It is valid to use them both. + ReadTimeout time.Duration + + // WriteTimeout is the maximum duration before timing out + // writes of the response. It is reset whenever a new + // request's header is read. Like ReadTimeout, it does not + // let Handlers make decisions on a per-request basis. + WriteTimeout time.Duration + + // IdleTimeout is the maximum amount of time to wait for the + // next request when keep-alives are enabled. If IdleTimeout + // is zero, the value of ReadTimeout is used. If both are + // zero, ReadHeaderTimeout is used. + IdleTimeout time.Duration +} + +// DefaultHTTPTimeouts represents the default timeout values used if further +// configuration is not provided. +var DefaultHTTPTimeouts = HTTPTimeouts{ + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 120 * time.Second, +} + +// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP +// using the provided HTTP Client. +func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { + // Sanity check URL so we don't end up with a client that will fail every request. + _, err := url.Parse(endpoint) + if err != nil { + return nil, err + } + + initctx := context.Background() + headers := make(http.Header, 2) + headers.Set("accept", contentType) + headers.Set("content-type", contentType) + return newClient(initctx, func(context.Context) (ServerCodec, error) { + hc := &httpConn{ + client: client, + headers: headers, + url: endpoint, + closeCh: make(chan interface{}), + } + return hc, nil + }) +} + +// DialHTTP creates a new RPC client that connects to an RPC server over HTTP. +func DialHTTP(endpoint string) (*Client, error) { + retryClient := retryablehttp.NewClient() + retryClient.RetryMax = 10 + retryClient.Logger = log.New() + + standardClient := retryClient.StandardClient() + return DialHTTPWithClient(endpoint, standardClient) +} + +func (c *Client) sendHTTP(ctx context.Context, op *requestOp, msg interface{}) error { + hc := c.writeConn.(*httpConn) + respBody, err := hc.doRequest(ctx, msg) + if err != nil { + return err + } + defer respBody.Close() + + var respmsg jsonrpcMessage + if err := json.NewDecoder(respBody).Decode(&respmsg); err != nil { + return err + } + op.resp <- &respmsg + return nil +} + +func (c *Client) sendBatchHTTP(ctx context.Context, op *requestOp, msgs []*jsonrpcMessage) error { + hc := c.writeConn.(*httpConn) + respBody, err := hc.doRequest(ctx, msgs) + if err != nil { + return err + } + defer respBody.Close() + var respmsgs []jsonrpcMessage + if err := json.NewDecoder(respBody).Decode(&respmsgs); err != nil { + return err + } + for i := 0; i < len(respmsgs); i++ { + op.resp <- &respmsgs[i] + } + return nil +} + +func (hc *httpConn) doRequest(ctx context.Context, msg interface{}) (io.ReadCloser, error) { + body, err := json.Marshal(msg) + if err != nil { + return nil, err + } + req, err := http.NewRequestWithContext(ctx, "POST", hc.url, ioutil.NopCloser(bytes.NewReader(body))) + if err != nil { + return nil, err + } + req.ContentLength = int64(len(body)) + + // set headers + hc.mu.Lock() + req.Header = hc.headers.Clone() + hc.mu.Unlock() + + // do request + resp, err := hc.client.Do(req) + if err != nil { + return nil, err + } + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + var buf bytes.Buffer + var body []byte + if _, err := buf.ReadFrom(resp.Body); err == nil { + body = buf.Bytes() + } + + return nil, HTTPError{ + Status: resp.Status, + StatusCode: resp.StatusCode, + Body: body, + } + } + return resp.Body, nil +} + +// httpServerConn turns a HTTP connection into a Conn. +type httpServerConn struct { + io.Reader + io.Writer + r *http.Request +} + +func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec { + body := io.LimitReader(r.Body, maxRequestContentLength) + conn := &httpServerConn{Reader: body, Writer: w, r: r} + return NewCodec(conn) +} + +// Close does nothing and always returns nil. +func (t *httpServerConn) Close() error { return nil } + +// RemoteAddr returns the peer address of the underlying connection. +func (t *httpServerConn) RemoteAddr() string { + return t.r.RemoteAddr +} + +// SetWriteDeadline does nothing and always returns nil. +func (t *httpServerConn) SetWriteDeadline(time.Time) error { return nil } + +// ServeHTTP serves JSON-RPC requests over HTTP. +func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // Permit dumb empty requests for remote health-checks (AWS) + if r.Method == http.MethodGet && r.ContentLength == 0 && r.URL.RawQuery == "" { + w.WriteHeader(http.StatusOK) + return + } + if code, err := validateRequest(r); err != nil { + http.Error(w, err.Error(), code) + return + } + // All checks passed, create a codec that reads directly from the request body + // until EOF, writes the response to w, and orders the server to process a + // single request. + ctx := r.Context() + ctx = context.WithValue(ctx, "remote", r.RemoteAddr) + ctx = context.WithValue(ctx, "scheme", r.Proto) + ctx = context.WithValue(ctx, "local", r.Host) + if ua := r.Header.Get("User-Agent"); ua != "" { + ctx = context.WithValue(ctx, "User-Agent", ua) + } + if origin := r.Header.Get("Origin"); origin != "" { + ctx = context.WithValue(ctx, "Origin", origin) + } + + w.Header().Set("content-type", contentType) + codec := newHTTPServerConn(r, w) + defer codec.close() + s.serveSingleRequest(ctx, codec) +} + +// validateRequest returns a non-zero response code and error message if the +// request is invalid. +func validateRequest(r *http.Request) (int, error) { + if r.Method == http.MethodPut || r.Method == http.MethodDelete { + return http.StatusMethodNotAllowed, errors.New("method not allowed") + } + if r.ContentLength > maxRequestContentLength { + err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength) + return http.StatusRequestEntityTooLarge, err + } + // Allow OPTIONS (regardless of content-type) + if r.Method == http.MethodOptions { + return 0, nil + } + // Check content-type + if mt, _, err := mime.ParseMediaType(r.Header.Get("content-type")); err == nil { + for _, accepted := range acceptedContentTypes { + if accepted == mt { + return 0, nil + } + } + } + // Invalid content-type + err := fmt.Errorf("invalid content type, only %s is supported", contentType) + return http.StatusUnsupportedMediaType, err +} diff --git a/common/go-ethereum/rpc/http_test.go b/common/go-ethereum/rpc/http_test.go new file mode 100644 index 00000000..97f8d44c --- /dev/null +++ b/common/go-ethereum/rpc/http_test.go @@ -0,0 +1,164 @@ +// Copyright 2017 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func confirmStatusCode(t *testing.T, got, want int) { + t.Helper() + if got == want { + return + } + if gotName := http.StatusText(got); len(gotName) > 0 { + if wantName := http.StatusText(want); len(wantName) > 0 { + t.Fatalf("response status code: got %d (%s), want %d (%s)", got, gotName, want, wantName) + } + } + t.Fatalf("response status code: got %d, want %d", got, want) +} + +func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { + t.Helper() + request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body)) + if len(contentType) > 0 { + request.Header.Set("Content-Type", contentType) + } + code, err := validateRequest(request) + if code == 0 { + if err != nil { + t.Errorf("validation: got error %v, expected nil", err) + } + } else if err == nil { + t.Errorf("validation: code %d: got nil, expected error", code) + } + confirmStatusCode(t, code, expectedStatusCode) +} + +func TestHTTPErrorResponseWithDelete(t *testing.T) { + confirmRequestValidationCode(t, http.MethodDelete, contentType, "", http.StatusMethodNotAllowed) +} + +func TestHTTPErrorResponseWithPut(t *testing.T) { + confirmRequestValidationCode(t, http.MethodPut, contentType, "", http.StatusMethodNotAllowed) +} + +func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) { + body := make([]rune, maxRequestContentLength+1) + confirmRequestValidationCode(t, + http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge) +} + +func TestHTTPErrorResponseWithEmptyContentType(t *testing.T) { + confirmRequestValidationCode(t, http.MethodPost, "", "", http.StatusUnsupportedMediaType) +} + +func TestHTTPErrorResponseWithValidRequest(t *testing.T) { + confirmRequestValidationCode(t, http.MethodPost, contentType, "", 0) +} + +func confirmHTTPRequestYieldsStatusCode(t *testing.T, method, contentType, body string, expectedStatusCode int) { + t.Helper() + s := Server{} + ts := httptest.NewServer(&s) + defer ts.Close() + + request, err := http.NewRequest(method, ts.URL, strings.NewReader(body)) + if err != nil { + t.Fatalf("failed to create a valid HTTP request: %v", err) + } + if len(contentType) > 0 { + request.Header.Set("Content-Type", contentType) + } + resp, err := http.DefaultClient.Do(request) + if err != nil { + t.Fatalf("request failed: %v", err) + } + confirmStatusCode(t, resp.StatusCode, expectedStatusCode) +} + +func TestHTTPResponseWithEmptyGet(t *testing.T) { + confirmHTTPRequestYieldsStatusCode(t, http.MethodGet, "", "", http.StatusOK) +} + +// This checks that maxRequestContentLength is not applied to the response of a request. +func TestHTTPRespBodyUnlimited(t *testing.T) { + const respLength = maxRequestContentLength * 3 + + s := NewServer() + defer s.Stop() + s.RegisterName("test", largeRespService{respLength}) + ts := httptest.NewServer(s) + defer ts.Close() + + c, err := DialHTTP(ts.URL) + if err != nil { + t.Fatal(err) + } + defer c.Close() + + var r string + if err := c.Call(&r, "test_largeResp"); err != nil { + t.Fatal(err) + } + if len(r) != respLength { + t.Fatalf("response has wrong length %d, want %d", len(r), respLength) + } +} + +// Tests that an HTTP error results in an HTTPError instance +// being returned with the expected attributes. +func TestHTTPErrorResponse(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Error(w, "error has occurred!", http.StatusTeapot) + })) + defer ts.Close() + + c, err := DialHTTP(ts.URL) + if err != nil { + t.Fatal(err) + } + + var r string + err = c.Call(&r, "test_method") + if err == nil { + t.Fatal("error was expected") + } + + httpErr, ok := err.(HTTPError) + if !ok { + t.Fatalf("unexpected error type %T", err) + } + + if httpErr.StatusCode != http.StatusTeapot { + t.Error("unexpected status code", httpErr.StatusCode) + } + if httpErr.Status != "418 I'm a teapot" { + t.Error("unexpected status text", httpErr.Status) + } + if body := string(httpErr.Body); body != "error has occurred!\n" { + t.Error("unexpected body", body) + } + + if errMsg := httpErr.Error(); errMsg != "418 I'm a teapot: error has occurred!\n" { + t.Error("unexpected error message", errMsg) + } +} diff --git a/common/go-ethereum/rpc/inproc.go b/common/go-ethereum/rpc/inproc.go new file mode 100644 index 00000000..fbe9a40c --- /dev/null +++ b/common/go-ethereum/rpc/inproc.go @@ -0,0 +1,33 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "net" +) + +// DialInProc attaches an in-process connection to the given RPC server. +func DialInProc(handler *Server) *Client { + initctx := context.Background() + c, _ := newClient(initctx, func(context.Context) (ServerCodec, error) { + p1, p2 := net.Pipe() + go handler.ServeCodec(NewCodec(p1), 0) + return NewCodec(p2), nil + }) + return c +} diff --git a/common/go-ethereum/rpc/ipc.go b/common/go-ethereum/rpc/ipc.go new file mode 100644 index 00000000..07a211c6 --- /dev/null +++ b/common/go-ethereum/rpc/ipc.go @@ -0,0 +1,56 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "net" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/p2p/netutil" +) + +// ServeListener accepts connections on l, serving JSON-RPC on them. +func (s *Server) ServeListener(l net.Listener) error { + for { + conn, err := l.Accept() + if netutil.IsTemporaryError(err) { + log.Warn("RPC accept error", "err", err) + continue + } else if err != nil { + return err + } + log.Trace("Accepted RPC connection", "conn", conn.RemoteAddr()) + go s.ServeCodec(NewCodec(conn), 0) + } +} + +// DialIPC create a new IPC client that connects to the given endpoint. On Unix it assumes +// the endpoint is the full path to a unix socket, and Windows the endpoint is an +// identifier for a named pipe. +// +// The context is used for the initial connection establishment. It does not +// affect subsequent interactions with the client. +func DialIPC(ctx context.Context, endpoint string) (*Client, error) { + return newClient(ctx, func(ctx context.Context) (ServerCodec, error) { + conn, err := newIPCConnection(ctx, endpoint) + if err != nil { + return nil, err + } + return NewCodec(conn), err + }) +} diff --git a/common/go-ethereum/rpc/ipc_js.go b/common/go-ethereum/rpc/ipc_js.go new file mode 100644 index 00000000..453a20bc --- /dev/null +++ b/common/go-ethereum/rpc/ipc_js.go @@ -0,0 +1,38 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build js +// +build js + +package rpc + +import ( + "context" + "errors" + "net" +) + +var errNotSupported = errors.New("rpc: not supported") + +// ipcListen will create a named pipe on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + return nil, errNotSupported +} + +// newIPCConnection will connect to a named pipe with the given endpoint as name. +func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { + return nil, errNotSupported +} diff --git a/common/go-ethereum/rpc/ipc_unix.go b/common/go-ethereum/rpc/ipc_unix.go new file mode 100644 index 00000000..249a9cf0 --- /dev/null +++ b/common/go-ethereum/rpc/ipc_unix.go @@ -0,0 +1,55 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package rpc + +import ( + "context" + "fmt" + "net" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/log" +) + +// ipcListen will create a Unix socket on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + if len(endpoint) > int(max_path_size) { + log.Warn(fmt.Sprintf("The ipc endpoint is longer than %d characters. ", max_path_size), + "endpoint", endpoint) + } + + // Ensure the IPC path exists and remove any previous leftover + if err := os.MkdirAll(filepath.Dir(endpoint), 0751); err != nil { + return nil, err + } + os.Remove(endpoint) + l, err := net.Listen("unix", endpoint) + if err != nil { + return nil, err + } + os.Chmod(endpoint, 0600) + return l, nil +} + +// newIPCConnection will connect to a Unix socket on the given endpoint. +func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { + return new(net.Dialer).DialContext(ctx, "unix", endpoint) +} diff --git a/common/go-ethereum/rpc/ipc_windows.go b/common/go-ethereum/rpc/ipc_windows.go new file mode 100644 index 00000000..adb1826f --- /dev/null +++ b/common/go-ethereum/rpc/ipc_windows.go @@ -0,0 +1,49 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +//go:build windows +// +build windows + +package rpc + +import ( + "context" + "net" + "time" + + "gopkg.in/natefinch/npipe.v2" +) + +// This is used if the dialing context has no deadline. It is much smaller than the +// defaultDialTimeout because named pipes are local and there is no need to wait so long. +const defaultPipeDialTimeout = 2 * time.Second + +// ipcListen will create a named pipe on the given endpoint. +func ipcListen(endpoint string) (net.Listener, error) { + return npipe.Listen(endpoint) +} + +// newIPCConnection will connect to a named pipe with the given endpoint as name. +func newIPCConnection(ctx context.Context, endpoint string) (net.Conn, error) { + timeout := defaultPipeDialTimeout + if deadline, ok := ctx.Deadline(); ok { + timeout = deadline.Sub(time.Now()) + if timeout < 0 { + timeout = 0 + } + } + return npipe.DialTimeout(endpoint, timeout) +} diff --git a/common/go-ethereum/rpc/json.go b/common/go-ethereum/rpc/json.go new file mode 100644 index 00000000..1daee3db --- /dev/null +++ b/common/go-ethereum/rpc/json.go @@ -0,0 +1,342 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "reflect" + "strings" + "sync" + "time" +) + +const ( + vsn = "2.0" + serviceMethodSeparator = "_" + subscribeMethodSuffix = "_subscribe" + unsubscribeMethodSuffix = "_unsubscribe" + notificationMethodSuffix = "_subscription" + + defaultWriteTimeout = 10 * time.Second // used if context has no deadline +) + +var null = json.RawMessage("null") + +type subscriptionResult struct { + ID string `json:"subscription"` + Result json.RawMessage `json:"result,omitempty"` +} + +// A value of this type can a JSON-RPC request, notification, successful response or +// error response. Which one it is depends on the fields. +type jsonrpcMessage struct { + Version string `json:"jsonrpc,omitempty"` + ID json.RawMessage `json:"id,omitempty"` + Method string `json:"method,omitempty"` + Params json.RawMessage `json:"params,omitempty"` + Error *jsonError `json:"error,omitempty"` + Result json.RawMessage `json:"result,omitempty"` +} + +func (msg *jsonrpcMessage) isNotification() bool { + return msg.ID == nil && msg.Method != "" +} + +func (msg *jsonrpcMessage) isCall() bool { + return msg.hasValidID() && msg.Method != "" +} + +func (msg *jsonrpcMessage) isResponse() bool { + return msg.hasValidID() && msg.Method == "" && msg.Params == nil && (msg.Result != nil || msg.Error != nil) +} + +func (msg *jsonrpcMessage) hasValidID() bool { + return len(msg.ID) > 0 && msg.ID[0] != '{' && msg.ID[0] != '[' +} + +func (msg *jsonrpcMessage) isSubscribe() bool { + return strings.HasSuffix(msg.Method, subscribeMethodSuffix) +} + +func (msg *jsonrpcMessage) isUnsubscribe() bool { + return strings.HasSuffix(msg.Method, unsubscribeMethodSuffix) +} + +func (msg *jsonrpcMessage) namespace() string { + elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2) + return elem[0] +} + +func (msg *jsonrpcMessage) String() string { + b, _ := json.Marshal(msg) + return string(b) +} + +func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { + resp := errorMessage(err) + resp.ID = msg.ID + return resp +} + +func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { + enc, err := json.Marshal(result) + if err != nil { + // TODO: wrap with 'internal server error' + return msg.errorResponse(err) + } + return &jsonrpcMessage{Version: vsn, ID: msg.ID, Result: enc} +} + +func errorMessage(err error) *jsonrpcMessage { + msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ + Code: defaultErrorCode, + Message: err.Error(), + }} + ec, ok := err.(Error) + if ok { + msg.Error.Code = ec.ErrorCode() + } + de, ok := err.(DataError) + if ok { + msg.Error.Data = de.ErrorData() + } + return msg +} + +type jsonError struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data,omitempty"` +} + +func (err *jsonError) Error() string { + if err.Message == "" { + return fmt.Sprintf("json-rpc error %d", err.Code) + } + return err.Message +} + +func (err *jsonError) ErrorCode() int { + return err.Code +} + +func (err *jsonError) ErrorData() interface{} { + return err.Data +} + +// Conn is a subset of the methods of net.Conn which are sufficient for ServerCodec. +type Conn interface { + io.ReadWriteCloser + SetWriteDeadline(time.Time) error +} + +type deadlineCloser interface { + io.Closer + SetWriteDeadline(time.Time) error +} + +// ConnRemoteAddr wraps the RemoteAddr operation, which returns a description +// of the peer address of a connection. If a Conn also implements ConnRemoteAddr, this +// description is used in log messages. +type ConnRemoteAddr interface { + RemoteAddr() string +} + +// jsonCodec reads and writes JSON-RPC messages to the underlying connection. It also has +// support for parsing arguments and serializing (result) objects. +type jsonCodec struct { + remote string + closer sync.Once // close closed channel once + closeCh chan interface{} // closed on Close + decode func(v interface{}) error // decoder to allow multiple transports + encMu sync.Mutex // guards the encoder + encode func(v interface{}) error // encoder to allow multiple transports + conn deadlineCloser +} + +// NewFuncCodec creates a codec which uses the given functions to read and write. If conn +// implements ConnRemoteAddr, log messages will use it to include the remote address of +// the connection. +func NewFuncCodec(conn deadlineCloser, encode, decode func(v interface{}) error) ServerCodec { + codec := &jsonCodec{ + closeCh: make(chan interface{}), + encode: encode, + decode: decode, + conn: conn, + } + if ra, ok := conn.(ConnRemoteAddr); ok { + codec.remote = ra.RemoteAddr() + } + return codec +} + +// NewCodec creates a codec on the given connection. If conn implements ConnRemoteAddr, log +// messages will use it to include the remote address of the connection. +func NewCodec(conn Conn) ServerCodec { + enc := json.NewEncoder(conn) + dec := json.NewDecoder(conn) + dec.UseNumber() + return NewFuncCodec(conn, enc.Encode, dec.Decode) +} + +func (c *jsonCodec) remoteAddr() string { + return c.remote +} + +func (c *jsonCodec) readBatch() (messages []*jsonrpcMessage, batch bool, err error) { + // Decode the next JSON object in the input stream. + // This verifies basic syntax, etc. + var rawmsg json.RawMessage + if err := c.decode(&rawmsg); err != nil { + return nil, false, err + } + messages, batch = parseMessage(rawmsg) + for i, msg := range messages { + if msg == nil { + // Message is JSON 'null'. Replace with zero value so it + // will be treated like any other invalid message. + messages[i] = new(jsonrpcMessage) + } + } + return messages, batch, nil +} + +func (c *jsonCodec) writeJSON(ctx context.Context, v interface{}) error { + c.encMu.Lock() + defer c.encMu.Unlock() + + deadline, ok := ctx.Deadline() + if !ok { + deadline = time.Now().Add(defaultWriteTimeout) + } + c.conn.SetWriteDeadline(deadline) + return c.encode(v) +} + +func (c *jsonCodec) close() { + c.closer.Do(func() { + close(c.closeCh) + c.conn.Close() + }) +} + +// Closed returns a channel which will be closed when Close is called +func (c *jsonCodec) closed() <-chan interface{} { + return c.closeCh +} + +// parseMessage parses raw bytes as a (batch of) JSON-RPC message(s). There are no error +// checks in this function because the raw message has already been syntax-checked when it +// is called. Any non-JSON-RPC messages in the input return the zero value of +// jsonrpcMessage. +func parseMessage(raw json.RawMessage) ([]*jsonrpcMessage, bool) { + if !isBatch(raw) { + msgs := []*jsonrpcMessage{{}} + json.Unmarshal(raw, &msgs[0]) + return msgs, false + } + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.Token() // skip '[' + var msgs []*jsonrpcMessage + for dec.More() { + msgs = append(msgs, new(jsonrpcMessage)) + dec.Decode(&msgs[len(msgs)-1]) + } + return msgs, true +} + +// isBatch returns true when the first non-whitespace characters is '[' +func isBatch(raw json.RawMessage) bool { + for _, c := range raw { + // skip insignificant whitespace (http://www.ietf.org/rfc/rfc4627.txt) + if c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d { + continue + } + return c == '[' + } + return false +} + +// parsePositionalArguments tries to parse the given args to an array of values with the +// given types. It returns the parsed values or an error when the args could not be +// parsed. Missing optional arguments are returned as reflect.Zero values. +func parsePositionalArguments(rawArgs json.RawMessage, types []reflect.Type) ([]reflect.Value, error) { + dec := json.NewDecoder(bytes.NewReader(rawArgs)) + var args []reflect.Value + tok, err := dec.Token() + switch { + case err == io.EOF || tok == nil && err == nil: + // "params" is optional and may be empty. Also allow "params":null even though it's + // not in the spec because our own client used to send it. + case err != nil: + return nil, err + case tok == json.Delim('['): + // Read argument array. + if args, err = parseArgumentArray(dec, types); err != nil { + return nil, err + } + default: + return nil, errors.New("non-array args") + } + // Set any missing args to nil. + for i := len(args); i < len(types); i++ { + if types[i].Kind() != reflect.Ptr { + return nil, fmt.Errorf("missing value for required argument %d", i) + } + args = append(args, reflect.Zero(types[i])) + } + return args, nil +} + +func parseArgumentArray(dec *json.Decoder, types []reflect.Type) ([]reflect.Value, error) { + args := make([]reflect.Value, 0, len(types)) + for i := 0; dec.More(); i++ { + if i >= len(types) { + return args, fmt.Errorf("too many arguments, want at most %d", len(types)) + } + argval := reflect.New(types[i]) + if err := dec.Decode(argval.Interface()); err != nil { + return args, fmt.Errorf("invalid argument %d: %v", i, err) + } + if argval.IsNil() && types[i].Kind() != reflect.Ptr { + return args, fmt.Errorf("missing value for required argument %d", i) + } + args = append(args, argval.Elem()) + } + // Read end of args array. + _, err := dec.Token() + return args, err +} + +// parseSubscriptionName extracts the subscription name from an encoded argument array. +func parseSubscriptionName(rawArgs json.RawMessage) (string, error) { + dec := json.NewDecoder(bytes.NewReader(rawArgs)) + if tok, _ := dec.Token(); tok != json.Delim('[') { + return "", errors.New("non-array args") + } + v, _ := dec.Token() + method, ok := v.(string) + if !ok { + return "", errors.New("expected subscription name as first argument") + } + return method, nil +} diff --git a/common/go-ethereum/rpc/metrics.go b/common/go-ethereum/rpc/metrics.go new file mode 100644 index 00000000..7fb6fc0a --- /dev/null +++ b/common/go-ethereum/rpc/metrics.go @@ -0,0 +1,39 @@ +// Copyright 2020 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/metrics" +) + +var ( + rpcRequestGauge = metrics.NewRegisteredGauge("rpc/requests", nil) + successfulRequestGauge = metrics.NewRegisteredGauge("rpc/success", nil) + failedReqeustGauge = metrics.NewRegisteredGauge("rpc/failure", nil) + rpcServingTimer = metrics.NewRegisteredTimer("rpc/duration/all", nil) +) + +func newRPCServingTimer(method string, valid bool) metrics.Timer { + flag := "success" + if !valid { + flag = "failure" + } + m := fmt.Sprintf("rpc/duration/%s/%s", method, flag) + return metrics.GetOrRegisterTimer(m, nil) +} diff --git a/common/go-ethereum/rpc/server.go b/common/go-ethereum/rpc/server.go new file mode 100644 index 00000000..64e078a7 --- /dev/null +++ b/common/go-ethereum/rpc/server.go @@ -0,0 +1,147 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "io" + "sync/atomic" + + mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/log" +) + +const MetadataApi = "rpc" + +// CodecOption specifies which type of messages a codec supports. +// +// Deprecated: this option is no longer honored by Server. +type CodecOption int + +const ( + // OptionMethodInvocation is an indication that the codec supports RPC method calls + OptionMethodInvocation CodecOption = 1 << iota + + // OptionSubscriptions is an indication that the codec supports RPC notifications + OptionSubscriptions = 1 << iota // support pub sub +) + +// Server is an RPC server. +type Server struct { + services serviceRegistry + idgen func() ID + run int32 + codecs mapset.Set +} + +// NewServer creates a new server instance with no registered handlers. +func NewServer() *Server { + server := &Server{idgen: randomIDGenerator(), codecs: mapset.NewSet(), run: 1} + // Register the default service providing meta information about the RPC service such + // as the services and methods it offers. + rpcService := &RPCService{server} + server.RegisterName(MetadataApi, rpcService) + return server +} + +// RegisterName creates a service for the given receiver type under the given name. When no +// methods on the given receiver match the criteria to be either a RPC method or a +// subscription an error is returned. Otherwise a new service is created and added to the +// service collection this server provides to clients. +func (s *Server) RegisterName(name string, receiver interface{}) error { + return s.services.registerName(name, receiver) +} + +// ServeCodec reads incoming requests from codec, calls the appropriate callback and writes +// the response back using the given codec. It will block until the codec is closed or the +// server is stopped. In either case the codec is closed. +// +// Note that codec options are no longer supported. +func (s *Server) ServeCodec(codec ServerCodec, options CodecOption) { + defer codec.close() + + // Don't serve if server is stopped. + if atomic.LoadInt32(&s.run) == 0 { + return + } + + // Add the codec to the set so it can be closed by Stop. + s.codecs.Add(codec) + defer s.codecs.Remove(codec) + + c := initClient(codec, s.idgen, &s.services) + <-codec.closed() + c.Close() +} + +// serveSingleRequest reads and processes a single RPC request from the given codec. This +// is used to serve HTTP connections. Subscriptions and reverse calls are not allowed in +// this mode. +func (s *Server) serveSingleRequest(ctx context.Context, codec ServerCodec) { + // Don't serve if server is stopped. + if atomic.LoadInt32(&s.run) == 0 { + return + } + + h := newHandler(ctx, codec, s.idgen, &s.services) + h.allowSubscribe = false + defer h.close(io.EOF, nil) + + reqs, batch, err := codec.readBatch() + if err != nil { + if err != io.EOF { + codec.writeJSON(ctx, errorMessage(&invalidMessageError{"parse error"})) + } + return + } + if batch { + h.handleBatch(reqs) + } else { + h.handleMsg(reqs[0]) + } +} + +// Stop stops reading new requests, waits for stopPendingRequestTimeout to allow pending +// requests to finish, then closes all codecs which will cancel pending requests and +// subscriptions. +func (s *Server) Stop() { + if atomic.CompareAndSwapInt32(&s.run, 1, 0) { + log.Debug("RPC server shutting down") + s.codecs.Each(func(c interface{}) bool { + c.(ServerCodec).close() + return true + }) + } +} + +// RPCService gives meta information about the server. +// e.g. gives information about the loaded modules. +type RPCService struct { + server *Server +} + +// Modules returns the list of RPC services with their version number +func (s *RPCService) Modules() map[string]string { + s.server.services.mu.Lock() + defer s.server.services.mu.Unlock() + + modules := make(map[string]string) + for name := range s.server.services.services { + modules[name] = "1.0" + } + return modules +} diff --git a/common/go-ethereum/rpc/server_test.go b/common/go-ethereum/rpc/server_test.go new file mode 100644 index 00000000..6a2b09e4 --- /dev/null +++ b/common/go-ethereum/rpc/server_test.go @@ -0,0 +1,152 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "bufio" + "bytes" + "io" + "io/ioutil" + "net" + "path/filepath" + "strings" + "testing" + "time" +) + +func TestServerRegisterName(t *testing.T) { + server := NewServer() + service := new(testService) + + if err := server.RegisterName("test", service); err != nil { + t.Fatalf("%v", err) + } + + if len(server.services.services) != 2 { + t.Fatalf("Expected 2 service entries, got %d", len(server.services.services)) + } + + svc, ok := server.services.services["test"] + if !ok { + t.Fatalf("Expected service calc to be registered") + } + + wantCallbacks := 9 + if len(svc.callbacks) != wantCallbacks { + t.Errorf("Expected %d callbacks for service 'service', got %d", wantCallbacks, len(svc.callbacks)) + } +} + +func TestServer(t *testing.T) { + files, err := ioutil.ReadDir("testdata") + if err != nil { + t.Fatal("where'd my testdata go?") + } + for _, f := range files { + if f.IsDir() || strings.HasPrefix(f.Name(), ".") { + continue + } + path := filepath.Join("testdata", f.Name()) + name := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name())) + t.Run(name, func(t *testing.T) { + runTestScript(t, path) + }) + } +} + +func runTestScript(t *testing.T, file string) { + server := newTestServer() + content, err := ioutil.ReadFile(file) + if err != nil { + t.Fatal(err) + } + + clientConn, serverConn := net.Pipe() + defer clientConn.Close() + go server.ServeCodec(NewCodec(serverConn), 0) + readbuf := bufio.NewReader(clientConn) + for _, line := range strings.Split(string(content), "\n") { + line = strings.TrimSpace(line) + switch { + case len(line) == 0 || strings.HasPrefix(line, "//"): + // skip comments, blank lines + continue + case strings.HasPrefix(line, "--> "): + t.Log(line) + // write to connection + clientConn.SetWriteDeadline(time.Now().Add(5 * time.Second)) + if _, err := io.WriteString(clientConn, line[4:]+"\n"); err != nil { + t.Fatalf("write error: %v", err) + } + case strings.HasPrefix(line, "<-- "): + t.Log(line) + want := line[4:] + // read line from connection and compare text + clientConn.SetReadDeadline(time.Now().Add(5 * time.Second)) + sent, err := readbuf.ReadString('\n') + if err != nil { + t.Fatalf("read error: %v", err) + } + sent = strings.TrimRight(sent, "\r\n") + if sent != want { + t.Errorf("wrong line from server\ngot: %s\nwant: %s", sent, want) + } + default: + panic("invalid line in test script: " + line) + } + } +} + +// This test checks that responses are delivered for very short-lived connections that +// only carry a single request. +func TestServerShortLivedConn(t *testing.T) { + server := newTestServer() + defer server.Stop() + + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("can't listen:", err) + } + defer listener.Close() + go server.ServeListener(listener) + + var ( + request = `{"jsonrpc":"2.0","id":1,"method":"rpc_modules"}` + "\n" + wantResp = `{"jsonrpc":"2.0","id":1,"result":{"nftest":"1.0","rpc":"1.0","test":"1.0"}}` + "\n" + deadline = time.Now().Add(10 * time.Second) + ) + for i := 0; i < 20; i++ { + conn, err := net.Dial("tcp", listener.Addr().String()) + if err != nil { + t.Fatal("can't dial:", err) + } + defer conn.Close() + conn.SetDeadline(deadline) + // Write the request, then half-close the connection so the server stops reading. + conn.Write([]byte(request)) + conn.(*net.TCPConn).CloseWrite() + // Now try to get the response. + buf := make([]byte, 2000) + n, err := conn.Read(buf) + if err != nil { + t.Fatal("read error:", err) + } + if !bytes.Equal(buf[:n], []byte(wantResp)) { + t.Fatalf("wrong response: %s", buf[:n]) + } + } +} diff --git a/common/go-ethereum/rpc/service.go b/common/go-ethereum/rpc/service.go new file mode 100644 index 00000000..bef891ea --- /dev/null +++ b/common/go-ethereum/rpc/service.go @@ -0,0 +1,261 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "errors" + "fmt" + "reflect" + "runtime" + "strings" + "sync" + "unicode" + + "github.com/ethereum/go-ethereum/log" +) + +var ( + contextType = reflect.TypeOf((*context.Context)(nil)).Elem() + errorType = reflect.TypeOf((*error)(nil)).Elem() + subscriptionType = reflect.TypeOf(Subscription{}) + stringType = reflect.TypeOf("") +) + +type serviceRegistry struct { + mu sync.Mutex + services map[string]service +} + +// service represents a registered object. +type service struct { + name string // name for service + callbacks map[string]*callback // registered handlers + subscriptions map[string]*callback // available subscriptions/notifications +} + +// callback is a method callback which was registered in the server +type callback struct { + fn reflect.Value // the function + rcvr reflect.Value // receiver object of method, set if fn is method + argTypes []reflect.Type // input argument types + hasCtx bool // method's first argument is a context (not included in argTypes) + errPos int // err return idx, of -1 when method cannot return error + isSubscribe bool // true if this is a subscription callback +} + +func (r *serviceRegistry) registerName(name string, rcvr interface{}) error { + rcvrVal := reflect.ValueOf(rcvr) + if name == "" { + return fmt.Errorf("no service name for type %s", rcvrVal.Type().String()) + } + callbacks := suitableCallbacks(rcvrVal) + if len(callbacks) == 0 { + return fmt.Errorf("service %T doesn't have any suitable methods/subscriptions to expose", rcvr) + } + + r.mu.Lock() + defer r.mu.Unlock() + if r.services == nil { + r.services = make(map[string]service) + } + svc, ok := r.services[name] + if !ok { + svc = service{ + name: name, + callbacks: make(map[string]*callback), + subscriptions: make(map[string]*callback), + } + r.services[name] = svc + } + for name, cb := range callbacks { + if cb.isSubscribe { + svc.subscriptions[name] = cb + } else { + svc.callbacks[name] = cb + } + } + return nil +} + +// callback returns the callback corresponding to the given RPC method name. +func (r *serviceRegistry) callback(method string) *callback { + elem := strings.SplitN(method, serviceMethodSeparator, 2) + if len(elem) != 2 { + return nil + } + r.mu.Lock() + defer r.mu.Unlock() + return r.services[elem[0]].callbacks[elem[1]] +} + +// subscription returns a subscription callback in the given service. +func (r *serviceRegistry) subscription(service, name string) *callback { + r.mu.Lock() + defer r.mu.Unlock() + return r.services[service].subscriptions[name] +} + +// suitableCallbacks iterates over the methods of the given type. It determines if a method +// satisfies the criteria for a RPC callback or a subscription callback and adds it to the +// collection of callbacks. See server documentation for a summary of these criteria. +func suitableCallbacks(receiver reflect.Value) map[string]*callback { + typ := receiver.Type() + callbacks := make(map[string]*callback) + for m := 0; m < typ.NumMethod(); m++ { + method := typ.Method(m) + if method.PkgPath != "" { + continue // method not exported + } + cb := newCallback(receiver, method.Func) + if cb == nil { + continue // function invalid + } + name := formatName(method.Name) + callbacks[name] = cb + } + return callbacks +} + +// newCallback turns fn (a function) into a callback object. It returns nil if the function +// is unsuitable as an RPC callback. +func newCallback(receiver, fn reflect.Value) *callback { + fntype := fn.Type() + c := &callback{fn: fn, rcvr: receiver, errPos: -1, isSubscribe: isPubSub(fntype)} + // Determine parameter types. They must all be exported or builtin types. + c.makeArgTypes() + + // Verify return types. The function must return at most one error + // and/or one other non-error value. + outs := make([]reflect.Type, fntype.NumOut()) + for i := 0; i < fntype.NumOut(); i++ { + outs[i] = fntype.Out(i) + } + if len(outs) > 2 { + return nil + } + // If an error is returned, it must be the last returned value. + switch { + case len(outs) == 1 && isErrorType(outs[0]): + c.errPos = 0 + case len(outs) == 2: + if isErrorType(outs[0]) || !isErrorType(outs[1]) { + return nil + } + c.errPos = 1 + } + return c +} + +// makeArgTypes composes the argTypes list. +func (c *callback) makeArgTypes() { + fntype := c.fn.Type() + // Skip receiver and context.Context parameter (if present). + firstArg := 0 + if c.rcvr.IsValid() { + firstArg++ + } + if fntype.NumIn() > firstArg && fntype.In(firstArg) == contextType { + c.hasCtx = true + firstArg++ + } + // Add all remaining parameters. + c.argTypes = make([]reflect.Type, fntype.NumIn()-firstArg) + for i := firstArg; i < fntype.NumIn(); i++ { + c.argTypes[i-firstArg] = fntype.In(i) + } +} + +// call invokes the callback. +func (c *callback) call(ctx context.Context, method string, args []reflect.Value) (res interface{}, errRes error) { + // Create the argument slice. + fullargs := make([]reflect.Value, 0, 2+len(args)) + if c.rcvr.IsValid() { + fullargs = append(fullargs, c.rcvr) + } + if c.hasCtx { + fullargs = append(fullargs, reflect.ValueOf(ctx)) + } + fullargs = append(fullargs, args...) + + // Catch panic while running the callback. + defer func() { + if err := recover(); err != nil { + const size = 64 << 10 + buf := make([]byte, size) + buf = buf[:runtime.Stack(buf, false)] + log.Error("RPC method " + method + " crashed: " + fmt.Sprintf("%v\n%s", err, buf)) + errRes = errors.New("method handler crashed") + } + }() + // Run the callback. + results := c.fn.Call(fullargs) + if len(results) == 0 { + return nil, nil + } + if c.errPos >= 0 && !results[c.errPos].IsNil() { + // Method has returned non-nil error value. + err := results[c.errPos].Interface().(error) + return reflect.Value{}, err + } + return results[0].Interface(), nil +} + +// Is t context.Context or *context.Context? +func isContextType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t == contextType +} + +// Does t satisfy the error interface? +func isErrorType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t.Implements(errorType) +} + +// Is t Subscription or *Subscription? +func isSubscriptionType(t reflect.Type) bool { + for t.Kind() == reflect.Ptr { + t = t.Elem() + } + return t == subscriptionType +} + +// isPubSub tests whether the given method has as as first argument a context.Context and +// returns the pair (Subscription, error). +func isPubSub(methodType reflect.Type) bool { + // numIn(0) is the receiver type + if methodType.NumIn() < 2 || methodType.NumOut() != 2 { + return false + } + return isContextType(methodType.In(1)) && + isSubscriptionType(methodType.Out(0)) && + isErrorType(methodType.Out(1)) +} + +// formatName converts to first character of name to lowercase. +func formatName(name string) string { + ret := []rune(name) + if len(ret) > 0 { + ret[0] = unicode.ToLower(ret[0]) + } + return string(ret) +} diff --git a/common/go-ethereum/rpc/stdio.go b/common/go-ethereum/rpc/stdio.go new file mode 100644 index 00000000..be2bab1c --- /dev/null +++ b/common/go-ethereum/rpc/stdio.go @@ -0,0 +1,66 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "errors" + "io" + "net" + "os" + "time" +) + +// DialStdIO creates a client on stdin/stdout. +func DialStdIO(ctx context.Context) (*Client, error) { + return DialIO(ctx, os.Stdin, os.Stdout) +} + +// DialIO creates a client which uses the given IO channels +func DialIO(ctx context.Context, in io.Reader, out io.Writer) (*Client, error) { + return newClient(ctx, func(_ context.Context) (ServerCodec, error) { + return NewCodec(stdioConn{ + in: in, + out: out, + }), nil + }) +} + +type stdioConn struct { + in io.Reader + out io.Writer +} + +func (io stdioConn) Read(b []byte) (n int, err error) { + return io.in.Read(b) +} + +func (io stdioConn) Write(b []byte) (n int, err error) { + return io.out.Write(b) +} + +func (io stdioConn) Close() error { + return nil +} + +func (io stdioConn) RemoteAddr() string { + return "/dev/stdin" +} + +func (io stdioConn) SetWriteDeadline(t time.Time) error { + return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")} +} diff --git a/common/go-ethereum/rpc/subscription.go b/common/go-ethereum/rpc/subscription.go new file mode 100644 index 00000000..942e764e --- /dev/null +++ b/common/go-ethereum/rpc/subscription.go @@ -0,0 +1,375 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "container/list" + "context" + crand "crypto/rand" + "encoding/binary" + "encoding/hex" + "encoding/json" + "errors" + "math/rand" + "reflect" + "strings" + "sync" + "time" +) + +var ( + // ErrNotificationsUnsupported is returned when the connection doesn't support notifications + ErrNotificationsUnsupported = errors.New("notifications not supported") + // ErrNotificationNotFound is returned when the notification for the given id is not found + ErrSubscriptionNotFound = errors.New("subscription not found") +) + +var globalGen = randomIDGenerator() + +// ID defines a pseudo random number that is used to identify RPC subscriptions. +type ID string + +// NewID returns a new, random ID. +func NewID() ID { + return globalGen() +} + +// randomIDGenerator returns a function generates a random IDs. +func randomIDGenerator() func() ID { + var buf = make([]byte, 8) + var seed int64 + if _, err := crand.Read(buf); err == nil { + seed = int64(binary.BigEndian.Uint64(buf)) + } else { + seed = int64(time.Now().Nanosecond()) + } + + var ( + mu sync.Mutex + rng = rand.New(rand.NewSource(seed)) + ) + return func() ID { + mu.Lock() + defer mu.Unlock() + id := make([]byte, 16) + rng.Read(id) + return encodeID(id) + } +} + +func encodeID(b []byte) ID { + id := hex.EncodeToString(b) + id = strings.TrimLeft(id, "0") + if id == "" { + id = "0" // ID's are RPC quantities, no leading zero's and 0 is 0x0. + } + return ID("0x" + id) +} + +type notifierKey struct{} + +// NotifierFromContext returns the Notifier value stored in ctx, if any. +func NotifierFromContext(ctx context.Context) (*Notifier, bool) { + n, ok := ctx.Value(notifierKey{}).(*Notifier) + return n, ok +} + +// Notifier is tied to a RPC connection that supports subscriptions. +// Server callbacks use the notifier to send notifications. +type Notifier struct { + h *handler + namespace string + + mu sync.Mutex + sub *Subscription + buffer []json.RawMessage + callReturned bool + activated bool +} + +// CreateSubscription returns a new subscription that is coupled to the +// RPC connection. By default subscriptions are inactive and notifications +// are dropped until the subscription is marked as active. This is done +// by the RPC server after the subscription ID is send to the client. +func (n *Notifier) CreateSubscription() *Subscription { + n.mu.Lock() + defer n.mu.Unlock() + + if n.sub != nil { + panic("can't create multiple subscriptions with Notifier") + } else if n.callReturned { + panic("can't create subscription after subscribe call has returned") + } + n.sub = &Subscription{ID: n.h.idgen(), namespace: n.namespace, err: make(chan error, 1)} + return n.sub +} + +// Notify sends a notification to the client with the given data as payload. +// If an error occurs the RPC connection is closed and the error is returned. +func (n *Notifier) Notify(id ID, data interface{}) error { + enc, err := json.Marshal(data) + if err != nil { + return err + } + + n.mu.Lock() + defer n.mu.Unlock() + + if n.sub == nil { + panic("can't Notify before subscription is created") + } else if n.sub.ID != id { + panic("Notify with wrong ID") + } + if n.activated { + return n.send(n.sub, enc) + } + n.buffer = append(n.buffer, enc) + return nil +} + +// Closed returns a channel that is closed when the RPC connection is closed. +// Deprecated: use subscription error channel +func (n *Notifier) Closed() <-chan interface{} { + return n.h.conn.closed() +} + +// takeSubscription returns the subscription (if one has been created). No subscription can +// be created after this call. +func (n *Notifier) takeSubscription() *Subscription { + n.mu.Lock() + defer n.mu.Unlock() + n.callReturned = true + return n.sub +} + +// activate is called after the subscription ID was sent to client. Notifications are +// buffered before activation. This prevents notifications being sent to the client before +// the subscription ID is sent to the client. +func (n *Notifier) activate() error { + n.mu.Lock() + defer n.mu.Unlock() + + for _, data := range n.buffer { + if err := n.send(n.sub, data); err != nil { + return err + } + } + n.activated = true + return nil +} + +func (n *Notifier) send(sub *Subscription, data json.RawMessage) error { + params, _ := json.Marshal(&subscriptionResult{ID: string(sub.ID), Result: data}) + ctx := context.Background() + return n.h.conn.writeJSON(ctx, &jsonrpcMessage{ + Version: vsn, + Method: n.namespace + notificationMethodSuffix, + Params: params, + }) +} + +// A Subscription is created by a notifier and tied to that notifier. The client can use +// this subscription to wait for an unsubscribe request for the client, see Err(). +type Subscription struct { + ID ID + namespace string + err chan error // closed on unsubscribe +} + +// Err returns a channel that is closed when the client send an unsubscribe request. +func (s *Subscription) Err() <-chan error { + return s.err +} + +// MarshalJSON marshals a subscription as its ID. +func (s *Subscription) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ID) +} + +// ClientSubscription is a subscription established through the Client's Subscribe or +// EthSubscribe methods. +type ClientSubscription struct { + client *Client + etype reflect.Type + channel reflect.Value + namespace string + subid string + + // The in channel receives notification values from client dispatcher. + in chan json.RawMessage + + // The error channel receives the error from the forwarding loop. + // It is closed by Unsubscribe. + err chan error + errOnce sync.Once + + // Closing of the subscription is requested by sending on 'quit'. This is handled by + // the forwarding loop, which closes 'forwardDone' when it has stopped sending to + // sub.channel. Finally, 'unsubDone' is closed after unsubscribing on the server side. + quit chan error + forwardDone chan struct{} + unsubDone chan struct{} +} + +// This is the sentinel value sent on sub.quit when Unsubscribe is called. +var errUnsubscribed = errors.New("unsubscribed") + +func newClientSubscription(c *Client, namespace string, channel reflect.Value) *ClientSubscription { + sub := &ClientSubscription{ + client: c, + namespace: namespace, + etype: channel.Type().Elem(), + channel: channel, + in: make(chan json.RawMessage), + quit: make(chan error), + forwardDone: make(chan struct{}), + unsubDone: make(chan struct{}), + err: make(chan error, 1), + } + return sub +} + +// Err returns the subscription error channel. The intended use of Err is to schedule +// resubscription when the client connection is closed unexpectedly. +// +// The error channel receives a value when the subscription has ended due to an error. The +// received error is nil if Close has been called on the underlying client and no other +// error has occurred. +// +// The error channel is closed when Unsubscribe is called on the subscription. +func (sub *ClientSubscription) Err() <-chan error { + return sub.err +} + +// Unsubscribe unsubscribes the notification and closes the error channel. +// It can safely be called more than once. +func (sub *ClientSubscription) Unsubscribe() { + sub.errOnce.Do(func() { + select { + case sub.quit <- errUnsubscribed: + <-sub.unsubDone + case <-sub.unsubDone: + } + close(sub.err) + }) +} + +// deliver is called by the client's message dispatcher to send a notification value. +func (sub *ClientSubscription) deliver(result json.RawMessage) (ok bool) { + select { + case sub.in <- result: + return true + case <-sub.forwardDone: + return false + } +} + +// close is called by the client's message dispatcher when the connection is closed. +func (sub *ClientSubscription) close(err error) { + select { + case sub.quit <- err: + case <-sub.forwardDone: + } +} + +// run is the forwarding loop of the subscription. It runs in its own goroutine and +// is launched by the client's handler after the subscription has been created. +func (sub *ClientSubscription) run() { + defer close(sub.unsubDone) + + unsubscribe, err := sub.forward() + + // The client's dispatch loop won't be able to execute the unsubscribe call if it is + // blocked in sub.deliver() or sub.close(). Closing forwardDone unblocks them. + close(sub.forwardDone) + + // Call the unsubscribe method on the server. + if unsubscribe { + sub.requestUnsubscribe() + } + + // Send the error. + if err != nil { + if err == ErrClientQuit { + // ErrClientQuit gets here when Client.Close is called. This is reported as a + // nil error because it's not an error, but we can't close sub.err here. + err = nil + } + sub.err <- err + } +} + +// forward is the forwarding loop. It takes in RPC notifications and sends them +// on the subscription channel. +func (sub *ClientSubscription) forward() (unsubscribeServer bool, err error) { + cases := []reflect.SelectCase{ + {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.quit)}, + {Dir: reflect.SelectRecv, Chan: reflect.ValueOf(sub.in)}, + {Dir: reflect.SelectSend, Chan: sub.channel}, + } + buffer := list.New() + + for { + var chosen int + var recv reflect.Value + if buffer.Len() == 0 { + // Idle, omit send case. + chosen, recv, _ = reflect.Select(cases[:2]) + } else { + // Non-empty buffer, send the first queued item. + cases[2].Send = reflect.ValueOf(buffer.Front().Value) + chosen, recv, _ = reflect.Select(cases) + } + + switch chosen { + case 0: // <-sub.quit + if !recv.IsNil() { + err = recv.Interface().(error) + } + if err == errUnsubscribed { + // Exiting because Unsubscribe was called, unsubscribe on server. + return true, nil + } + return false, err + + case 1: // <-sub.in + val, err := sub.unmarshal(recv.Interface().(json.RawMessage)) + if err != nil { + return true, err + } + if buffer.Len() == maxClientSubscriptionBuffer { + return true, ErrSubscriptionQueueOverflow + } + buffer.PushBack(val) + + case 2: // sub.channel<- + cases[2].Send = reflect.Value{} // Don't hold onto the value. + buffer.Remove(buffer.Front()) + } + } +} + +func (sub *ClientSubscription) unmarshal(result json.RawMessage) (interface{}, error) { + val := reflect.New(sub.etype) + err := json.Unmarshal(result, val.Interface()) + return val.Elem().Interface(), err +} + +func (sub *ClientSubscription) requestUnsubscribe() error { + var result interface{} + return sub.client.Call(&result, sub.namespace+unsubscribeMethodSuffix, sub.subid) +} diff --git a/common/go-ethereum/rpc/subscription_test.go b/common/go-ethereum/rpc/subscription_test.go new file mode 100644 index 00000000..54a053db --- /dev/null +++ b/common/go-ethereum/rpc/subscription_test.go @@ -0,0 +1,220 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "encoding/json" + "fmt" + "net" + "strings" + "testing" + "time" +) + +func TestNewID(t *testing.T) { + hexchars := "0123456789ABCDEFabcdef" + for i := 0; i < 100; i++ { + id := string(NewID()) + if !strings.HasPrefix(id, "0x") { + t.Fatalf("invalid ID prefix, want '0x...', got %s", id) + } + + id = id[2:] + if len(id) == 0 || len(id) > 32 { + t.Fatalf("invalid ID length, want len(id) > 0 && len(id) <= 32), got %d", len(id)) + } + + for i := 0; i < len(id); i++ { + if strings.IndexByte(hexchars, id[i]) == -1 { + t.Fatalf("unexpected byte, want any valid hex char, got %c", id[i]) + } + } + } +} + +func TestSubscriptions(t *testing.T) { + var ( + namespaces = []string{"eth", "shh", "bzz"} + service = ¬ificationTestService{} + subCount = len(namespaces) + notificationCount = 3 + + server = NewServer() + clientConn, serverConn = net.Pipe() + out = json.NewEncoder(clientConn) + in = json.NewDecoder(clientConn) + successes = make(chan subConfirmation) + notifications = make(chan subscriptionResult) + errors = make(chan error, subCount*notificationCount+1) + ) + + // setup and start server + for _, namespace := range namespaces { + if err := server.RegisterName(namespace, service); err != nil { + t.Fatalf("unable to register test service %v", err) + } + } + go server.ServeCodec(NewCodec(serverConn), 0) + defer server.Stop() + + // wait for message and write them to the given channels + go waitForMessages(in, successes, notifications, errors) + + // create subscriptions one by one + for i, namespace := range namespaces { + request := map[string]interface{}{ + "id": i, + "method": fmt.Sprintf("%s_subscribe", namespace), + "version": "2.0", + "params": []interface{}{"someSubscription", notificationCount, i}, + } + if err := out.Encode(&request); err != nil { + t.Fatalf("Could not create subscription: %v", err) + } + } + + timeout := time.After(30 * time.Second) + subids := make(map[string]string, subCount) + count := make(map[string]int, subCount) + allReceived := func() bool { + done := len(count) == subCount + for _, c := range count { + if c < notificationCount { + done = false + } + } + return done + } + for !allReceived() { + select { + case confirmation := <-successes: // subscription created + subids[namespaces[confirmation.reqid]] = string(confirmation.subid) + case notification := <-notifications: + count[notification.ID]++ + case err := <-errors: + t.Fatal(err) + case <-timeout: + for _, namespace := range namespaces { + subid, found := subids[namespace] + if !found { + t.Errorf("subscription for %q not created", namespace) + continue + } + if count, found := count[subid]; !found || count < notificationCount { + t.Errorf("didn't receive all notifications (%d<%d) in time for namespace %q", count, notificationCount, namespace) + } + } + t.Fatal("timed out") + } + } +} + +// This test checks that unsubscribing works. +func TestServerUnsubscribe(t *testing.T) { + p1, p2 := net.Pipe() + defer p2.Close() + + // Start the server. + server := newTestServer() + service := ¬ificationTestService{unsubscribed: make(chan string, 1)} + server.RegisterName("nftest2", service) + go server.ServeCodec(NewCodec(p1), 0) + + // Subscribe. + p2.SetDeadline(time.Now().Add(10 * time.Second)) + p2.Write([]byte(`{"jsonrpc":"2.0","id":1,"method":"nftest2_subscribe","params":["someSubscription",0,10]}`)) + + // Handle received messages. + var ( + resps = make(chan subConfirmation) + notifications = make(chan subscriptionResult) + errors = make(chan error, 1) + ) + go waitForMessages(json.NewDecoder(p2), resps, notifications, errors) + + // Receive the subscription ID. + var sub subConfirmation + select { + case sub = <-resps: + case err := <-errors: + t.Fatal(err) + } + + // Unsubscribe and check that it is handled on the server side. + p2.Write([]byte(`{"jsonrpc":"2.0","method":"nftest2_unsubscribe","params":["` + sub.subid + `"]}`)) + for { + select { + case id := <-service.unsubscribed: + if id != string(sub.subid) { + t.Errorf("wrong subscription ID unsubscribed") + } + return + case err := <-errors: + t.Fatal(err) + case <-notifications: + // drop notifications + } + } +} + +type subConfirmation struct { + reqid int + subid ID +} + +// waitForMessages reads RPC messages from 'in' and dispatches them into the given channels. +// It stops if there is an error. +func waitForMessages(in *json.Decoder, successes chan subConfirmation, notifications chan subscriptionResult, errors chan error) { + for { + resp, notification, err := readAndValidateMessage(in) + if err != nil { + errors <- err + return + } else if resp != nil { + successes <- *resp + } else { + notifications <- *notification + } + } +} + +func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionResult, error) { + var msg jsonrpcMessage + if err := in.Decode(&msg); err != nil { + return nil, nil, fmt.Errorf("decode error: %v", err) + } + switch { + case msg.isNotification(): + var res subscriptionResult + if err := json.Unmarshal(msg.Params, &res); err != nil { + return nil, nil, fmt.Errorf("invalid subscription result: %v", err) + } + return nil, &res, nil + case msg.isResponse(): + var c subConfirmation + if msg.Error != nil { + return nil, nil, msg.Error + } else if err := json.Unmarshal(msg.Result, &c.subid); err != nil { + return nil, nil, fmt.Errorf("invalid response: %v", err) + } else { + json.Unmarshal(msg.ID, &c.reqid) + return &c, nil, nil + } + default: + return nil, nil, fmt.Errorf("unrecognized message: %v", msg) + } +} diff --git a/common/go-ethereum/rpc/testservice_test.go b/common/go-ethereum/rpc/testservice_test.go new file mode 100644 index 00000000..62afc1df --- /dev/null +++ b/common/go-ethereum/rpc/testservice_test.go @@ -0,0 +1,206 @@ +// Copyright 2019 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "encoding/binary" + "errors" + "strings" + "sync" + "time" +) + +func newTestServer() *Server { + server := NewServer() + server.idgen = sequentialIDGenerator() + if err := server.RegisterName("test", new(testService)); err != nil { + panic(err) + } + if err := server.RegisterName("nftest", new(notificationTestService)); err != nil { + panic(err) + } + return server +} + +func sequentialIDGenerator() func() ID { + var ( + mu sync.Mutex + counter uint64 + ) + return func() ID { + mu.Lock() + defer mu.Unlock() + counter++ + id := make([]byte, 8) + binary.BigEndian.PutUint64(id, counter) + return encodeID(id) + } +} + +type testService struct{} + +type echoArgs struct { + S string +} + +type echoResult struct { + String string + Int int + Args *echoArgs +} + +type testError struct{} + +func (testError) Error() string { return "testError" } +func (testError) ErrorCode() int { return 444 } +func (testError) ErrorData() interface{} { return "testError data" } + +func (s *testService) NoArgsRets() {} + +func (s *testService) Echo(str string, i int, args *echoArgs) echoResult { + return echoResult{str, i, args} +} + +func (s *testService) EchoWithCtx(ctx context.Context, str string, i int, args *echoArgs) echoResult { + return echoResult{str, i, args} +} + +func (s *testService) Sleep(ctx context.Context, duration time.Duration) { + time.Sleep(duration) +} + +func (s *testService) Block(ctx context.Context) error { + <-ctx.Done() + return errors.New("context canceled in testservice_block") +} + +func (s *testService) Rets() (string, error) { + return "", nil +} + +//lint:ignore ST1008 returns error first on purpose. +func (s *testService) InvalidRets1() (error, string) { + return nil, "" +} + +func (s *testService) InvalidRets2() (string, string) { + return "", "" +} + +func (s *testService) InvalidRets3() (string, string, error) { + return "", "", nil +} + +func (s *testService) ReturnError() error { + return testError{} +} + +func (s *testService) CallMeBack(ctx context.Context, method string, args []interface{}) (interface{}, error) { + c, ok := ClientFromContext(ctx) + if !ok { + return nil, errors.New("no client") + } + var result interface{} + err := c.Call(&result, method, args...) + return result, err +} + +func (s *testService) CallMeBackLater(ctx context.Context, method string, args []interface{}) error { + c, ok := ClientFromContext(ctx) + if !ok { + return errors.New("no client") + } + go func() { + <-ctx.Done() + var result interface{} + c.Call(&result, method, args...) + }() + return nil +} + +func (s *testService) Subscription(ctx context.Context) (*Subscription, error) { + return nil, nil +} + +type notificationTestService struct { + unsubscribed chan string + gotHangSubscriptionReq chan struct{} + unblockHangSubscription chan struct{} +} + +func (s *notificationTestService) Echo(i int) int { + return i +} + +func (s *notificationTestService) Unsubscribe(subid string) { + if s.unsubscribed != nil { + s.unsubscribed <- subid + } +} + +func (s *notificationTestService) SomeSubscription(ctx context.Context, n, val int) (*Subscription, error) { + notifier, supported := NotifierFromContext(ctx) + if !supported { + return nil, ErrNotificationsUnsupported + } + + // By explicitly creating an subscription we make sure that the subscription id is send + // back to the client before the first subscription.Notify is called. Otherwise the + // events might be send before the response for the *_subscribe method. + subscription := notifier.CreateSubscription() + go func() { + for i := 0; i < n; i++ { + if err := notifier.Notify(subscription.ID, val+i); err != nil { + return + } + } + select { + case <-notifier.Closed(): + case <-subscription.Err(): + } + if s.unsubscribed != nil { + s.unsubscribed <- string(subscription.ID) + } + }() + return subscription, nil +} + +// HangSubscription blocks on s.unblockHangSubscription before sending anything. +func (s *notificationTestService) HangSubscription(ctx context.Context, val int) (*Subscription, error) { + notifier, supported := NotifierFromContext(ctx) + if !supported { + return nil, ErrNotificationsUnsupported + } + s.gotHangSubscriptionReq <- struct{}{} + <-s.unblockHangSubscription + subscription := notifier.CreateSubscription() + + go func() { + notifier.Notify(subscription.ID, val) + }() + return subscription, nil +} + +// largeRespService generates arbitrary-size JSON responses. +type largeRespService struct { + length int +} + +func (x largeRespService) LargeResp() string { + return strings.Repeat("x", x.length) +} diff --git a/common/go-ethereum/rpc/types.go b/common/go-ethereum/rpc/types.go new file mode 100644 index 00000000..d9c2317a --- /dev/null +++ b/common/go-ethereum/rpc/types.go @@ -0,0 +1,231 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// API describes the set of methods offered over the RPC interface +type API struct { + Namespace string // namespace under which the rpc methods of Service are exposed + Version string // api version for DApp's + Service interface{} // receiver instance which holds the methods + Public bool // indication if the methods must be considered safe for public use +} + +// ServerCodec implements reading, parsing and writing RPC messages for the server side of +// a RPC session. Implementations must be go-routine safe since the codec can be called in +// multiple go-routines concurrently. +type ServerCodec interface { + readBatch() (msgs []*jsonrpcMessage, isBatch bool, err error) + close() + jsonWriter +} + +// jsonWriter can write JSON messages to its underlying connection. +// Implementations must be safe for concurrent use. +type jsonWriter interface { + writeJSON(context.Context, interface{}) error + // Closed returns a channel which is closed when the connection is closed. + closed() <-chan interface{} + // RemoteAddr returns the peer address of the connection. + remoteAddr() string +} + +type BlockNumber int64 + +const ( + PendingBlockNumber = BlockNumber(-2) + LatestBlockNumber = BlockNumber(-1) + EarliestBlockNumber = BlockNumber(0) +) + +// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: +// - "latest", "earliest" or "pending" as string arguments +// - the block number +// Returned errors: +// - an invalid block number error when the given argument isn't a known strings +// - an out of range error when the given block number is either too little or too large +func (bn *BlockNumber) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + switch input { + case "earliest": + *bn = EarliestBlockNumber + return nil + case "latest": + *bn = LatestBlockNumber + return nil + case "pending": + *bn = PendingBlockNumber + return nil + } + + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return fmt.Errorf("block number larger than int64") + } + *bn = BlockNumber(blckNum) + return nil +} + +// MarshalText implements encoding.TextMarshaler. It marshals: +// - "latest", "earliest" or "pending" as strings +// - other numbers as hex +func (bn BlockNumber) MarshalText() ([]byte, error) { + switch bn { + case EarliestBlockNumber: + return []byte("earliest"), nil + case LatestBlockNumber: + return []byte("latest"), nil + case PendingBlockNumber: + return []byte("pending"), nil + default: + return hexutil.Uint64(bn).MarshalText() + } +} + +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +} + +type BlockNumberOrHash struct { + BlockNumber *BlockNumber `json:"blockNumber,omitempty"` + BlockHash *common.Hash `json:"blockHash,omitempty"` + RequireCanonical bool `json:"requireCanonical,omitempty"` +} + +func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error { + type erased BlockNumberOrHash + e := erased{} + err := json.Unmarshal(data, &e) + if err == nil { + if e.BlockNumber != nil && e.BlockHash != nil { + return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other") + } + bnh.BlockNumber = e.BlockNumber + bnh.BlockHash = e.BlockHash + bnh.RequireCanonical = e.RequireCanonical + return nil + } + var input string + err = json.Unmarshal(data, &input) + if err != nil { + return err + } + switch input { + case "earliest": + bn := EarliestBlockNumber + bnh.BlockNumber = &bn + return nil + case "latest": + bn := LatestBlockNumber + bnh.BlockNumber = &bn + return nil + case "pending": + bn := PendingBlockNumber + bnh.BlockNumber = &bn + return nil + default: + if len(input) == 66 { + hash := common.Hash{} + err := hash.UnmarshalText([]byte(input)) + if err != nil { + return err + } + bnh.BlockHash = &hash + return nil + } else { + blckNum, err := hexutil.DecodeUint64(input) + if err != nil { + return err + } + if blckNum > math.MaxInt64 { + return fmt.Errorf("blocknumber too high") + } + bn := BlockNumber(blckNum) + bnh.BlockNumber = &bn + return nil + } + } +} + +func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) { + if bnh.BlockNumber != nil { + return *bnh.BlockNumber, true + } + return BlockNumber(0), false +} + +func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) { + if bnh.BlockHash != nil { + return *bnh.BlockHash, true + } + return common.Hash{}, false +} + +func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash { + return BlockNumberOrHash{ + BlockNumber: &blockNr, + BlockHash: nil, + RequireCanonical: false, + } +} + +func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash { + return BlockNumberOrHash{ + BlockNumber: nil, + BlockHash: &hash, + RequireCanonical: canonical, + } +} + +// DecimalOrHex unmarshals a non-negative decimal or hex parameter into a uint64. +type DecimalOrHex uint64 + +// UnmarshalJSON implements json.Unmarshaler. +func (dh *DecimalOrHex) UnmarshalJSON(data []byte) error { + input := strings.TrimSpace(string(data)) + if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { + input = input[1 : len(input)-1] + } + + value, err := strconv.ParseUint(input, 10, 64) + if err != nil { + value, err = hexutil.DecodeUint64(input) + } + if err != nil { + return err + } + *dh = DecimalOrHex(value) + return nil +} diff --git a/common/go-ethereum/rpc/types_test.go b/common/go-ethereum/rpc/types_test.go new file mode 100644 index 00000000..f110dee7 --- /dev/null +++ b/common/go-ethereum/rpc/types_test.go @@ -0,0 +1,155 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" +) + +func TestBlockNumberJSONUnmarshal(t *testing.T) { + tests := []struct { + input string + mustFail bool + expected BlockNumber + }{ + 0: {`"0x"`, true, BlockNumber(0)}, + 1: {`"0x0"`, false, BlockNumber(0)}, + 2: {`"0X1"`, false, BlockNumber(1)}, + 3: {`"0x00"`, true, BlockNumber(0)}, + 4: {`"0x01"`, true, BlockNumber(0)}, + 5: {`"0x1"`, false, BlockNumber(1)}, + 6: {`"0x12"`, false, BlockNumber(18)}, + 7: {`"0x7fffffffffffffff"`, false, BlockNumber(math.MaxInt64)}, + 8: {`"0x8000000000000000"`, true, BlockNumber(0)}, + 9: {"0", true, BlockNumber(0)}, + 10: {`"ff"`, true, BlockNumber(0)}, + 11: {`"pending"`, false, PendingBlockNumber}, + 12: {`"latest"`, false, LatestBlockNumber}, + 13: {`"earliest"`, false, EarliestBlockNumber}, + 14: {`someString`, true, BlockNumber(0)}, + 15: {`""`, true, BlockNumber(0)}, + 16: {``, true, BlockNumber(0)}, + } + + for i, test := range tests { + var num BlockNumber + err := json.Unmarshal([]byte(test.input), &num) + if test.mustFail && err == nil { + t.Errorf("Test %d should fail", i) + continue + } + if !test.mustFail && err != nil { + t.Errorf("Test %d should pass but got err: %v", i, err) + continue + } + if num != test.expected { + t.Errorf("Test %d got unexpected value, want %d, got %d", i, test.expected, num) + } + } +} + +func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) { + tests := []struct { + input string + mustFail bool + expected BlockNumberOrHash + }{ + 0: {`"0x"`, true, BlockNumberOrHash{}}, + 1: {`"0x0"`, false, BlockNumberOrHashWithNumber(0)}, + 2: {`"0X1"`, false, BlockNumberOrHashWithNumber(1)}, + 3: {`"0x00"`, true, BlockNumberOrHash{}}, + 4: {`"0x01"`, true, BlockNumberOrHash{}}, + 5: {`"0x1"`, false, BlockNumberOrHashWithNumber(1)}, + 6: {`"0x12"`, false, BlockNumberOrHashWithNumber(18)}, + 7: {`"0x7fffffffffffffff"`, false, BlockNumberOrHashWithNumber(math.MaxInt64)}, + 8: {`"0x8000000000000000"`, true, BlockNumberOrHash{}}, + 9: {"0", true, BlockNumberOrHash{}}, + 10: {`"ff"`, true, BlockNumberOrHash{}}, + 11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, + 12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, + 13: {`"earliest"`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, + 14: {`someString`, true, BlockNumberOrHash{}}, + 15: {`""`, true, BlockNumberOrHash{}}, + 16: {``, true, BlockNumberOrHash{}}, + 17: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 18: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 19: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)}, + 20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)}, + 21: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)}, + 22: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)}, + 23: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)}, + 24: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)}, + 25: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}}, + } + + for i, test := range tests { + var bnh BlockNumberOrHash + err := json.Unmarshal([]byte(test.input), &bnh) + if test.mustFail && err == nil { + t.Errorf("Test %d should fail", i) + continue + } + if !test.mustFail && err != nil { + t.Errorf("Test %d should pass but got err: %v", i, err) + continue + } + hash, hashOk := bnh.Hash() + expectedHash, expectedHashOk := test.expected.Hash() + num, numOk := bnh.Number() + expectedNum, expectedNumOk := test.expected.Number() + if bnh.RequireCanonical != test.expected.RequireCanonical || + hash != expectedHash || hashOk != expectedHashOk || + num != expectedNum || numOk != expectedNumOk { + t.Errorf("Test %d got unexpected value, want %v, got %v", i, test.expected, bnh) + } + } +} + +func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) { + tests := []struct { + name string + number int64 + }{ + {"max", math.MaxInt64}, + {"pending", int64(PendingBlockNumber)}, + {"latest", int64(LatestBlockNumber)}, + {"earliest", int64(EarliestBlockNumber)}, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number)) + marshalled, err := json.Marshal(bnh) + if err != nil { + t.Fatal("cannot marshal:", err) + } + var unmarshalled BlockNumberOrHash + err = json.Unmarshal(marshalled, &unmarshalled) + if err != nil { + t.Fatal("cannot unmarshal:", err) + } + if !reflect.DeepEqual(bnh, unmarshalled) { + t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled) + } + }) + } +} diff --git a/common/go-ethereum/rpc/websocket.go b/common/go-ethereum/rpc/websocket.go new file mode 100644 index 00000000..5571324a --- /dev/null +++ b/common/go-ethereum/rpc/websocket.go @@ -0,0 +1,300 @@ +// Copyright 2015 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "os" + "strings" + "sync" + "time" + + mapset "github.com/deckarep/golang-set" + "github.com/ethereum/go-ethereum/log" + "github.com/gorilla/websocket" +) + +const ( + wsReadBuffer = 1024 + wsWriteBuffer = 1024 + wsPingInterval = 60 * time.Second + wsPingWriteTimeout = 5 * time.Second + wsPongTimeout = 30 * time.Second + wsMessageSizeLimit = 15 * 1024 * 1024 +) + +var wsBufferPool = new(sync.Pool) + +// WebsocketHandler returns a handler that serves JSON-RPC to WebSocket connections. +// +// allowedOrigins should be a comma-separated list of allowed origin URLs. +// To allow connections with any origin, pass "*". +func (s *Server) WebsocketHandler(allowedOrigins []string) http.Handler { + var upgrader = websocket.Upgrader{ + ReadBufferSize: wsReadBuffer, + WriteBufferSize: wsWriteBuffer, + WriteBufferPool: wsBufferPool, + CheckOrigin: wsHandshakeValidator(allowedOrigins), + } + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Debug("WebSocket upgrade failed", "err", err) + return + } + codec := newWebsocketCodec(conn) + s.ServeCodec(codec, 0) + }) +} + +// wsHandshakeValidator returns a handler that verifies the origin during the +// websocket upgrade process. When a '*' is specified as an allowed origins all +// connections are accepted. +func wsHandshakeValidator(allowedOrigins []string) func(*http.Request) bool { + origins := mapset.NewSet() + allowAllOrigins := false + + for _, origin := range allowedOrigins { + if origin == "*" { + allowAllOrigins = true + } + if origin != "" { + origins.Add(origin) + } + } + // allow localhost if no allowedOrigins are specified. + if len(origins.ToSlice()) == 0 { + origins.Add("http://localhost") + if hostname, err := os.Hostname(); err == nil { + origins.Add("http://" + hostname) + } + } + log.Debug(fmt.Sprintf("Allowed origin(s) for WS RPC interface %v", origins.ToSlice())) + + f := func(req *http.Request) bool { + // Skip origin verification if no Origin header is present. The origin check + // is supposed to protect against browser based attacks. Browsers always set + // Origin. Non-browser software can put anything in origin and checking it doesn't + // provide additional security. + if _, ok := req.Header["Origin"]; !ok { + return true + } + // Verify origin against allow list. + origin := strings.ToLower(req.Header.Get("Origin")) + if allowAllOrigins || originIsAllowed(origins, origin) { + return true + } + log.Warn("Rejected WebSocket connection", "origin", origin) + return false + } + + return f +} + +type wsHandshakeError struct { + err error + status string +} + +func (e wsHandshakeError) Error() string { + s := e.err.Error() + if e.status != "" { + s += " (HTTP status " + e.status + ")" + } + return s +} + +func originIsAllowed(allowedOrigins mapset.Set, browserOrigin string) bool { + it := allowedOrigins.Iterator() + for origin := range it.C { + if ruleAllowsOrigin(origin.(string), browserOrigin) { + return true + } + } + return false +} + +func ruleAllowsOrigin(allowedOrigin string, browserOrigin string) bool { + var ( + allowedScheme, allowedHostname, allowedPort string + browserScheme, browserHostname, browserPort string + err error + ) + allowedScheme, allowedHostname, allowedPort, err = parseOriginURL(allowedOrigin) + if err != nil { + log.Warn("Error parsing allowed origin specification", "spec", allowedOrigin, "error", err) + return false + } + browserScheme, browserHostname, browserPort, err = parseOriginURL(browserOrigin) + if err != nil { + log.Warn("Error parsing browser 'Origin' field", "Origin", browserOrigin, "error", err) + return false + } + if allowedScheme != "" && allowedScheme != browserScheme { + return false + } + if allowedHostname != "" && allowedHostname != browserHostname { + return false + } + if allowedPort != "" && allowedPort != browserPort { + return false + } + return true +} + +func parseOriginURL(origin string) (string, string, string, error) { + parsedURL, err := url.Parse(strings.ToLower(origin)) + if err != nil { + return "", "", "", err + } + var scheme, hostname, port string + if strings.Contains(origin, "://") { + scheme = parsedURL.Scheme + hostname = parsedURL.Hostname() + port = parsedURL.Port() + } else { + scheme = "" + hostname = parsedURL.Scheme + port = parsedURL.Opaque + if hostname == "" { + hostname = origin + } + } + return scheme, hostname, port, nil +} + +// DialWebsocketWithDialer creates a new RPC client that communicates with a JSON-RPC server +// that is listening on the given endpoint using the provided dialer. +func DialWebsocketWithDialer(ctx context.Context, endpoint, origin string, dialer websocket.Dialer) (*Client, error) { + endpoint, header, err := wsClientHeaders(endpoint, origin) + if err != nil { + return nil, err + } + return newClient(ctx, func(ctx context.Context) (ServerCodec, error) { + conn, resp, err := dialer.DialContext(ctx, endpoint, header) + if err != nil { + hErr := wsHandshakeError{err: err} + if resp != nil { + hErr.status = resp.Status + } + return nil, hErr + } + return newWebsocketCodec(conn), nil + }) +} + +// DialWebsocket creates a new RPC client that communicates with a JSON-RPC server +// that is listening on the given endpoint. +// +// The context is used for the initial connection establishment. It does not +// affect subsequent interactions with the client. +func DialWebsocket(ctx context.Context, endpoint, origin string) (*Client, error) { + dialer := websocket.Dialer{ + ReadBufferSize: wsReadBuffer, + WriteBufferSize: wsWriteBuffer, + WriteBufferPool: wsBufferPool, + } + return DialWebsocketWithDialer(ctx, endpoint, origin, dialer) +} + +func wsClientHeaders(endpoint, origin string) (string, http.Header, error) { + endpointURL, err := url.Parse(endpoint) + if err != nil { + return endpoint, nil, err + } + header := make(http.Header) + if origin != "" { + header.Add("origin", origin) + } + if endpointURL.User != nil { + b64auth := base64.StdEncoding.EncodeToString([]byte(endpointURL.User.String())) + header.Add("authorization", "Basic "+b64auth) + endpointURL.User = nil + } + return endpointURL.String(), header, nil +} + +type websocketCodec struct { + *jsonCodec + conn *websocket.Conn + + wg sync.WaitGroup + pingReset chan struct{} +} + +func newWebsocketCodec(conn *websocket.Conn) ServerCodec { + conn.SetReadLimit(wsMessageSizeLimit) + conn.SetPongHandler(func(appData string) error { + conn.SetReadDeadline(time.Time{}) + return nil + }) + wc := &websocketCodec{ + jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec), + conn: conn, + pingReset: make(chan struct{}, 1), + } + wc.wg.Add(1) + go wc.pingLoop() + return wc +} + +func (wc *websocketCodec) close() { + wc.jsonCodec.close() + wc.wg.Wait() +} + +func (wc *websocketCodec) writeJSON(ctx context.Context, v interface{}) error { + err := wc.jsonCodec.writeJSON(ctx, v) + if err == nil { + // Notify pingLoop to delay the next idle ping. + select { + case wc.pingReset <- struct{}{}: + default: + } + } + return err +} + +// pingLoop sends periodic ping frames when the connection is idle. +func (wc *websocketCodec) pingLoop() { + var timer = time.NewTimer(wsPingInterval) + defer wc.wg.Done() + defer timer.Stop() + + for { + select { + case <-wc.closed(): + return + case <-wc.pingReset: + if !timer.Stop() { + <-timer.C + } + timer.Reset(wsPingInterval) + case <-timer.C: + wc.jsonCodec.encMu.Lock() + wc.conn.SetWriteDeadline(time.Now().Add(wsPingWriteTimeout)) + wc.conn.WriteMessage(websocket.PingMessage, nil) + wc.conn.SetReadDeadline(time.Now().Add(wsPongTimeout)) + wc.jsonCodec.encMu.Unlock() + timer.Reset(wsPingInterval) + } + } +} diff --git a/common/go-ethereum/rpc/websocket_test.go b/common/go-ethereum/rpc/websocket_test.go new file mode 100644 index 00000000..24860928 --- /dev/null +++ b/common/go-ethereum/rpc/websocket_test.go @@ -0,0 +1,381 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package rpc + +import ( + "context" + "io" + "net" + "net/http" + "net/http/httptest" + "net/http/httputil" + "net/url" + "reflect" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/gorilla/websocket" +) + +func TestWebsocketClientHeaders(t *testing.T) { + t.Parallel() + + endpoint, header, err := wsClientHeaders("wss://testuser:test-PASS_01@example.com:1234", "https://example.com") + if err != nil { + t.Fatalf("wsGetConfig failed: %s", err) + } + if endpoint != "wss://example.com:1234" { + t.Fatal("User should have been stripped from the URL") + } + if header.Get("authorization") != "Basic dGVzdHVzZXI6dGVzdC1QQVNTXzAx" { + t.Fatal("Basic auth header is incorrect") + } + if header.Get("origin") != "https://example.com" { + t.Fatal("Origin not set") + } +} + +// This test checks that the server rejects connections from disallowed origins. +func TestWebsocketOriginCheck(t *testing.T) { + t.Parallel() + + var ( + srv = newTestServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"http://example.com"})) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + client, err := DialWebsocket(context.Background(), wsURL, "http://ekzample.com") + if err == nil { + client.Close() + t.Fatal("no error for wrong origin") + } + wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"} + if !reflect.DeepEqual(err, wantErr) { + t.Fatalf("wrong error for wrong origin: %q", err) + } + + // Connections without origin header should work. + client, err = DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatal("error for empty origin") + } + client.Close() +} + +// This test checks whether calls exceeding the request size limit are rejected. +func TestWebsocketLargeCall(t *testing.T) { + t.Parallel() + + var ( + srv = newTestServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"})) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + client, err := DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatalf("can't dial: %v", err) + } + defer client.Close() + + // This call sends slightly less than the limit and should work. + var result echoResult + arg := strings.Repeat("x", maxRequestContentLength-200) + if err := client.Call(&result, "test_echo", arg, 1); err != nil { + t.Fatalf("valid call didn't work: %v", err) + } + if result.String != arg { + t.Fatal("wrong string echoed") + } + + // This call sends twice the allowed size and shouldn't work. + arg = strings.Repeat("x", maxRequestContentLength*2) + err = client.Call(&result, "test_echo", arg) + if err == nil { + t.Fatal("no error for too large call") + } +} + +// This test checks that client handles WebSocket ping frames correctly. +func TestClientWebsocketPing(t *testing.T) { + t.Parallel() + + var ( + sendPing = make(chan struct{}) + server = wsPingTestServer(t, sendPing) + ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) + ) + defer cancel() + defer server.Shutdown(ctx) + + client, err := DialContext(ctx, "ws://"+server.Addr) + if err != nil { + t.Fatalf("client dial error: %v", err) + } + defer client.Close() + + resultChan := make(chan int) + sub, err := client.EthSubscribe(ctx, resultChan, "foo") + if err != nil { + t.Fatalf("client subscribe error: %v", err) + } + // Note: Unsubscribe is not called on this subscription because the mockup + // server can't handle the request. + + // Wait for the context's deadline to be reached before proceeding. + // This is important for reproducing https://github.com/ethereum/go-ethereum/issues/19798 + <-ctx.Done() + close(sendPing) + + // Wait for the subscription result. + timeout := time.NewTimer(5 * time.Second) + defer timeout.Stop() + for { + select { + case err := <-sub.Err(): + t.Error("client subscription error:", err) + case result := <-resultChan: + t.Log("client got result:", result) + return + case <-timeout.C: + t.Error("didn't get any result within the test timeout") + return + } + } +} + +// This checks that the websocket transport can deal with large messages. +func TestClientWebsocketLargeMessage(t *testing.T) { + var ( + srv = NewServer() + httpsrv = httptest.NewServer(srv.WebsocketHandler(nil)) + wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:") + ) + defer srv.Stop() + defer httpsrv.Close() + + respLength := wsMessageSizeLimit - 50 + srv.RegisterName("test", largeRespService{respLength}) + + c, err := DialWebsocket(context.Background(), wsURL, "") + if err != nil { + t.Fatal(err) + } + + var r string + if err := c.Call(&r, "test_largeResp"); err != nil { + t.Fatal("call failed:", err) + } + if len(r) != respLength { + t.Fatalf("response has wrong length %d, want %d", len(r), respLength) + } +} + +func TestClientWebsocketSevered(t *testing.T) { + t.Parallel() + + var ( + server = wsPingTestServer(t, nil) + ctx = context.Background() + ) + defer server.Shutdown(ctx) + + u, err := url.Parse("http://" + server.Addr) + if err != nil { + t.Fatal(err) + } + rproxy := httputil.NewSingleHostReverseProxy(u) + var severable *severableReadWriteCloser + rproxy.ModifyResponse = func(response *http.Response) error { + severable = &severableReadWriteCloser{ReadWriteCloser: response.Body.(io.ReadWriteCloser)} + response.Body = severable + return nil + } + frontendProxy := httptest.NewServer(rproxy) + defer frontendProxy.Close() + + wsURL := "ws:" + strings.TrimPrefix(frontendProxy.URL, "http:") + client, err := DialWebsocket(ctx, wsURL, "") + if err != nil { + t.Fatalf("client dial error: %v", err) + } + defer client.Close() + + resultChan := make(chan int) + sub, err := client.EthSubscribe(ctx, resultChan, "foo") + if err != nil { + t.Fatalf("client subscribe error: %v", err) + } + + // sever the connection + severable.Sever() + + // Wait for subscription error. + timeout := time.NewTimer(3 * wsPingInterval) + defer timeout.Stop() + for { + select { + case err := <-sub.Err(): + t.Log("client subscription error:", err) + return + case result := <-resultChan: + t.Error("unexpected result:", result) + return + case <-timeout.C: + t.Error("didn't get any error within the test timeout") + return + } + } +} + +// wsPingTestServer runs a WebSocket server which accepts a single subscription request. +// When a value arrives on sendPing, the server sends a ping frame, waits for a matching +// pong and finally delivers a single subscription result. +func wsPingTestServer(t *testing.T, sendPing <-chan struct{}) *http.Server { + var srv http.Server + shutdown := make(chan struct{}) + srv.RegisterOnShutdown(func() { + close(shutdown) + }) + srv.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Upgrade to WebSocket. + upgrader := websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { return true }, + } + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + t.Errorf("server WS upgrade error: %v", err) + return + } + defer conn.Close() + + // Handle the connection. + wsPingTestHandler(t, conn, shutdown, sendPing) + }) + + // Start the server. + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("can't listen:", err) + } + srv.Addr = listener.Addr().String() + go srv.Serve(listener) + return &srv +} + +func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-chan struct{}) { + // Canned responses for the eth_subscribe call in TestClientWebsocketPing. + const ( + subResp = `{"jsonrpc":"2.0","id":1,"result":"0x00"}` + subNotify = `{"jsonrpc":"2.0","method":"eth_subscription","params":{"subscription":"0x00","result":1}}` + ) + + // Handle subscribe request. + if _, _, err := conn.ReadMessage(); err != nil { + t.Errorf("server read error: %v", err) + return + } + if err := conn.WriteMessage(websocket.TextMessage, []byte(subResp)); err != nil { + t.Errorf("server write error: %v", err) + return + } + + // Read from the connection to process control messages. + var pongCh = make(chan string) + conn.SetPongHandler(func(d string) error { + t.Logf("server got pong: %q", d) + pongCh <- d + return nil + }) + go func() { + for { + typ, msg, err := conn.ReadMessage() + if err != nil { + return + } + t.Logf("server got message (%d): %q", typ, msg) + } + }() + + // Write messages. + var ( + wantPong string + timer = time.NewTimer(0) + ) + defer timer.Stop() + <-timer.C + for { + select { + case _, open := <-sendPing: + if !open { + sendPing = nil + } + t.Logf("server sending ping") + conn.WriteMessage(websocket.PingMessage, []byte("ping")) + wantPong = "ping" + case data := <-pongCh: + if wantPong == "" { + t.Errorf("unexpected pong") + } else if data != wantPong { + t.Errorf("got pong with wrong data %q", data) + } + wantPong = "" + timer.Reset(200 * time.Millisecond) + case <-timer.C: + t.Logf("server sending response") + conn.WriteMessage(websocket.TextMessage, []byte(subNotify)) + case <-shutdown: + conn.Close() + return + } + } +} + +// severableReadWriteCloser wraps an io.ReadWriteCloser and provides a Sever() method to drop writes and read empty. +type severableReadWriteCloser struct { + io.ReadWriteCloser + severed int32 // atomic +} + +func (s *severableReadWriteCloser) Sever() { + atomic.StoreInt32(&s.severed, 1) +} + +func (s *severableReadWriteCloser) Read(p []byte) (n int, err error) { + if atomic.LoadInt32(&s.severed) > 0 { + return 0, nil + } + return s.ReadWriteCloser.Read(p) +} + +func (s *severableReadWriteCloser) Write(p []byte) (n int, err error) { + if atomic.LoadInt32(&s.severed) > 0 { + return len(p), nil + } + return s.ReadWriteCloser.Write(p) +} + +func (s *severableReadWriteCloser) Close() error { + return s.ReadWriteCloser.Close() +} diff --git a/common/jsonrpc/keepalive.go b/common/jsonrpc/keepalive.go new file mode 100644 index 00000000..47f36436 --- /dev/null +++ b/common/jsonrpc/keepalive.go @@ -0,0 +1,25 @@ +package jsonrpc + +import ( + "sync" + "time" +) + +type keepAliveResponse struct { + lastResponse time.Time + sync.RWMutex +} + +func (k *keepAliveResponse) setLastResponse() { + k.Lock() + defer k.Unlock() + + k.lastResponse = time.Now() +} + +func (k *keepAliveResponse) getLastResponse() time.Time { + k.RLock() + defer k.RUnlock() + + return k.lastResponse +} diff --git a/common/jsonrpc/reconws.go b/common/jsonrpc/reconws.go new file mode 100644 index 00000000..30835903 --- /dev/null +++ b/common/jsonrpc/reconws.go @@ -0,0 +1,474 @@ +// From https://github.com/recws-org/recws +// Package recws provides websocket client based on gorilla/websocket +// that will automatically reconnect if the connection is dropped. +package jsonrpc + +import ( + "crypto/tls" + "errors" + "fmt" + "math/rand" + "net/http" + "net/url" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/icon-project/btp/common/log" + "github.com/jpillora/backoff" +) + +// ErrNotConnected is returned when the application read/writes +// a message and the connection is closed +var ErrNotConnected = errors.New("websocket: not connected") + +// The RecConn type represents a Reconnecting WebSocket connection. +type RecConn struct { + Id string + // RecIntvlMin specifies the initial reconnecting interval, + // default to 2 seconds + RecIntvlMin time.Duration + // RecIntvlMax specifies the maximum reconnecting interval, + // default to 30 seconds + RecIntvlMax time.Duration + // RecIntvlFactor specifies the rate of increase of the reconnection + // interval, default to 1.5 + RecIntvlFactor float64 + // HandshakeTimeout specifies the duration for the handshake to complete, + // default to 2 seconds + HandshakeTimeout time.Duration + // Proxy specifies the proxy function for the dialer + // defaults to ProxyFromEnvironment + Proxy func(*http.Request) (*url.URL, error) + // Client TLS config to use on reconnect + TLSClientConfig *tls.Config + // SubscribeHandler fires after the connection successfully establish. + SubscribeHandler func() error + // KeepAliveTimeout is an interval for sending ping/pong messages + // disabled if 0 + KeepAliveTimeout time.Duration + // NonVerbose suppress connecting/reconnecting messages. + NonVerbose bool + + isConnected bool + mu sync.RWMutex + url string + reqHeader http.Header + httpResp *http.Response + dialErr error + dialer *websocket.Dialer + + *websocket.Conn +} + +// CloseAndReconnect will try to reconnect. +func (rc *RecConn) CloseAndReconnect() { + rc.Close() + go rc.connect() +} + +// setIsConnected sets state for isConnected +func (rc *RecConn) setId() { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.Id = fmt.Sprintf("%d", time.Now().UnixNano()/int64(time.Millisecond)) +} + +// setIsConnected sets state for isConnected +func (rc *RecConn) setIsConnected(state bool) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.isConnected = state +} + +func (rc *RecConn) getConn() *websocket.Conn { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.Conn +} + +// Close closes the underlying network connection without +// sending or waiting for a close frame. +func (rc *RecConn) Close() { + if rc.getConn() != nil { + rc.mu.Lock() + rc.Conn.Close() + rc.mu.Unlock() + } + + rc.setIsConnected(false) +} + +// ReadMessage is a helper method for getting a reader +// using NextReader and reading from that reader to a buffer. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) ReadMessage() (messageType int, message []byte, err error) { + err = ErrNotConnected + if rc.IsConnected() { + messageType, message, err = rc.Conn.ReadMessage() + if err != nil { + rc.CloseAndReconnect() + } + } + + return +} + +// WriteMessage is a helper method for getting a writer using NextWriter, +// writing the message and closing the writer. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) WriteMessage(messageType int, data []byte) error { + err := ErrNotConnected + if rc.IsConnected() { + rc.mu.Lock() + err = rc.Conn.WriteMessage(messageType, data) + rc.mu.Unlock() + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +// WriteJSON writes the JSON encoding of v to the connection. +// +// See the documentation for encoding/json Marshal for details about the +// conversion of Go values to JSON. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) WriteJSON(v interface{}) error { + err := ErrNotConnected + if rc.IsConnected() { + rc.mu.Lock() + err = rc.Conn.WriteJSON(v) + rc.mu.Unlock() + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +// ReadJSON reads the next JSON-encoded message from the connection and stores +// it in the value pointed to by v. +// +// See the documentation for the encoding/json Unmarshal function for details +// about the conversion of JSON to a Go value. +// +// If the connection is closed ErrNotConnected is returned +func (rc *RecConn) ReadJSON(v interface{}) error { + err := ErrNotConnected + if rc.IsConnected() { + err = rc.Conn.ReadJSON(v) + if err != nil { + rc.CloseAndReconnect() + } + } + + return err +} + +func (rc *RecConn) setURL(url string) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.url = url +} + +func (rc *RecConn) setReqHeader(reqHeader http.Header) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.reqHeader = reqHeader +} + +// parseURL parses current url +func (rc *RecConn) parseURL(urlStr string) (string, error) { + if urlStr == "" { + return "", errors.New("dial: url cannot be empty") + } + + u, err := url.Parse(urlStr) + + if err != nil { + return "", errors.New("url: " + err.Error()) + } + + if u.Scheme != "ws" && u.Scheme != "wss" { + return "", errors.New("url: websocket uris must start with ws or wss scheme") + } + + if u.User != nil { + return "", errors.New("url: user name and password are not allowed in websocket URIs") + } + + return urlStr, nil +} + +func (rc *RecConn) setDefaultRecIntvlMin() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlMin == 0 { + rc.RecIntvlMin = 2 * time.Second + } +} + +func (rc *RecConn) setDefaultRecIntvlMax() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlMax == 0 { + rc.RecIntvlMax = 30 * time.Second + } +} + +func (rc *RecConn) setDefaultRecIntvlFactor() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.RecIntvlFactor == 0 { + rc.RecIntvlFactor = 1.5 + } +} + +func (rc *RecConn) setDefaultHandshakeTimeout() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.HandshakeTimeout == 0 { + rc.HandshakeTimeout = 10 * time.Second + } +} + +func (rc *RecConn) setDefaultProxy() { + rc.mu.Lock() + defer rc.mu.Unlock() + + if rc.Proxy == nil { + rc.Proxy = http.ProxyFromEnvironment + } +} + +func (rc *RecConn) setDefaultDialer(tlsClientConfig *tls.Config, handshakeTimeout time.Duration) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.dialer = &websocket.Dialer{ + HandshakeTimeout: handshakeTimeout, + Proxy: rc.Proxy, + TLSClientConfig: tlsClientConfig, + } +} + +func (rc *RecConn) getHandshakeTimeout() time.Duration { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.HandshakeTimeout +} + +func (rc *RecConn) getTLSClientConfig() *tls.Config { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.TLSClientConfig +} + +func (rc *RecConn) SetTLSClientConfig(tlsClientConfig *tls.Config) { + rc.mu.Lock() + defer rc.mu.Unlock() + + rc.TLSClientConfig = tlsClientConfig +} + +// Dial creates a new client connection. +// The URL url specifies the host and request URI. Use requestHeader to specify +// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies +// (Cookie). Use GetHTTPResponse() method for the response.Header to get +// the selected subprotocol (Sec-WebSocket-Protocol) and cookies (Set-Cookie). +func (rc *RecConn) Dial(urlStr string, reqHeader http.Header) { + urlStr, err := rc.parseURL(urlStr) + + if err != nil { + log.Fatalf("Dial: %v", err) + } + + // Config + rc.setId() + rc.setURL(urlStr) + rc.setReqHeader(reqHeader) + rc.setDefaultRecIntvlMin() + rc.setDefaultRecIntvlMax() + rc.setDefaultRecIntvlFactor() + rc.setDefaultHandshakeTimeout() + rc.setDefaultProxy() + rc.setDefaultDialer(rc.getTLSClientConfig(), rc.getHandshakeTimeout()) + + // Connect + go rc.connect() + + // wait on first attempt + time.Sleep(rc.getHandshakeTimeout()) +} + +// GetURL returns current connection url +func (rc *RecConn) GetURL() string { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.url +} + +func (rc *RecConn) getNonVerbose() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.NonVerbose +} + +func (rc *RecConn) getBackoff() *backoff.Backoff { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return &backoff.Backoff{ + Min: rc.RecIntvlMin, + Max: rc.RecIntvlMax, + Factor: rc.RecIntvlFactor, + Jitter: true, + } +} + +func (rc *RecConn) hasSubscribeHandler() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.SubscribeHandler != nil +} + +func (rc *RecConn) getKeepAliveTimeout() time.Duration { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.KeepAliveTimeout +} + +func (rc *RecConn) writeControlPingMessage() error { + rc.mu.Lock() + defer rc.mu.Unlock() + + return rc.Conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(10*time.Second)) +} + +func (rc *RecConn) keepAlive() { + var ( + keepAliveResponse = new(keepAliveResponse) + ticker = time.NewTicker(rc.getKeepAliveTimeout()) + ) + + rc.mu.Lock() + rc.Conn.SetPongHandler(func(msg string) error { + keepAliveResponse.setLastResponse() + return nil + }) + rc.mu.Unlock() + + go func() { + defer ticker.Stop() + + for { + if !rc.IsConnected() { + continue + } + + if err := rc.writeControlPingMessage(); err != nil { + log.Println(err) + } + + <-ticker.C + if time.Since(keepAliveResponse.getLastResponse()) > rc.getKeepAliveTimeout() { + rc.CloseAndReconnect() + return + } + } + }() +} + +func (rc *RecConn) connect() { + b := rc.getBackoff() + rand.Seed(time.Now().UTC().UnixNano()) + + for { + nextItvl := b.Duration() + wsConn, httpResp, err := rc.dialer.Dial(rc.url, rc.reqHeader) + + rc.mu.Lock() + rc.Conn = wsConn + rc.dialErr = err + rc.isConnected = err == nil + rc.httpResp = httpResp + rc.mu.Unlock() + + if err == nil { + if !rc.getNonVerbose() { + log.Printf("Dial: connection was successfully established with %s\n", rc.url) + } + + if rc.hasSubscribeHandler() { + if err := rc.SubscribeHandler(); err != nil { + log.Fatalf("Dial: connect handler failed with %s", err.Error()) + } + if !rc.getNonVerbose() { + log.Printf("Dial: connect handler was successfully established with %s\n", rc.url) + } + } + + if rc.getKeepAliveTimeout() != 0 { + rc.keepAlive() + } + + return + } + + if !rc.getNonVerbose() { + log.Println(err) + log.Println("Dial: will try again in", nextItvl, "seconds.") + } + + time.Sleep(nextItvl) + } +} + +// GetHTTPResponse returns the http response from the handshake. +// Useful when WebSocket handshake fails, +// so that callers can handle redirects, authentication, etc. +func (rc *RecConn) GetHTTPResponse() *http.Response { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.httpResp +} + +// GetDialError returns the last dialer error. +// nil on successful connection. +func (rc *RecConn) GetDialError() error { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.dialErr +} + +// IsConnected returns the WebSocket connection state +func (rc *RecConn) IsConnected() bool { + rc.mu.RLock() + defer rc.mu.RUnlock() + + return rc.isConnected +} diff --git a/common/log/log.go b/common/log/log.go index 356fb260..9e16f8d6 100644 --- a/common/log/log.go +++ b/common/log/log.go @@ -10,7 +10,7 @@ import ( ) const ( - LogTimeLayout = "15:04:05.000000" + LogTimeLayout = "20060102-15:04:05.000000" ) type Level int diff --git a/common/mta/accumulator.go b/common/mta/accumulator.go index 6e9222de..915f6d1d 100644 --- a/common/mta/accumulator.go +++ b/common/mta/accumulator.go @@ -351,6 +351,11 @@ func (a *Accumulator) WitnessFor(idx int64) (w []Witness, err error) { return nil, errors.ErrNotFound } offset := len(a.roots) + + // FIXME fix this case MTA root := a.roots[0] + // if offset == 0 { + // } + for offset > 0 { offset -= 1 if a.roots[offset] == nil { @@ -507,13 +512,13 @@ func GetDepthByHeightAndAccLength(height, accLength int64) int { depth := 0 v := accLength //calculate max depth - for ; 0 < v ; v >>= 1 { + for ; 0 < v; v >>= 1 { depth++ } - for ; depth > 0; { + for depth > 0 { depth-- bitFlag := int64(1) << uint(depth) - if accLength & bitFlag == bitFlag { + if accLength&bitFlag == bitFlag { if height < bitFlag { return depth } diff --git a/common/mta/extaccumulator.go b/common/mta/extaccumulator.go index 6d5a3ea0..07c86386 100644 --- a/common/mta/extaccumulator.go +++ b/common/mta/extaccumulator.go @@ -6,6 +6,7 @@ import ( "github.com/icon-project/btp/common/codec" "github.com/icon-project/btp/common/db" "github.com/icon-project/btp/common/errors" + "github.com/icon-project/btp/common/log" ) type ExtAccumulator struct { @@ -97,12 +98,10 @@ func (a *ExtAccumulator) Recover() error { } a.length = s.Height - s.Offset a.serialized = b - if a.offset < s.Offset { - return errors.New("not support recover to lower offset") - } else if a.offset > s.Offset { - //TODO rebuild node - //a.offset = s.Offset - return errors.New("not support recover to higher offset") + if a.offset < s.Offset || a.offset > s.Offset { + a.length = 0 + a.Flush() + log.Debugf("resync with new offset:%d, height:%d", a.Offset(), a.Height()) } return nil } diff --git a/common/wallet/keystore.go b/common/wallet/keystore.go index 14e986a6..092cdee2 100644 --- a/common/wallet/keystore.go +++ b/common/wallet/keystore.go @@ -20,6 +20,7 @@ import ( const ( coinTypeICON = "icx" + coinTypeEVM = "evm" cipherAES128CTR = "aes-128-ctr" kdfScrypt = "scrypt" ) @@ -131,7 +132,7 @@ func EncryptKeyAsKeyStore(s *crypto.PrivateKey, pw []byte) ([]byte, error) { return json.Marshal(&ks) } -func DecryptKeyStore(data, pw []byte) (*crypto.PrivateKey, error) { +func ReadAddressFromKeyStore(data []byte) (*common.Address, error) { var ksData KeyStoreData if err := json.Unmarshal(data, &ksData); err != nil { return nil, err @@ -139,7 +140,43 @@ func DecryptKeyStore(data, pw []byte) (*crypto.PrivateKey, error) { if ksData.CoinType != coinTypeICON { return nil, errors.Errorf("InvalidCoinType(coin=%s)", ksData.CoinType) } + return &ksData.Address, nil +} +func DecryptKeyStore(data, pw []byte) (Wallet, error) { + ksdata, err := NewKeyStoreData(data) + if err != nil { + return nil, err + } + + switch ksdata.CoinType { + case coinTypeICON: + secret, err := DecryptICONKeyStore(ksdata, pw) + if err != nil { + return nil, err + } + return NewIcxWalletFromPrivateKey(secret) + case coinTypeEVM: + key, err := DecryptEvmKeyStore(data, pw) + if err != nil { + return nil, err + } + return NewEvmWalletFromPrivateKey(key) + default: + return nil, errors.Errorf("InvalidCoinType(coin=%s)", ksdata.CoinType) + } +} + +func KeyStoreFromWallet(w interface{}, pw []byte) ([]byte, error) { + s, ok := w.(*softwareWallet) + if ok { + return EncryptKeyAsKeyStore(s.skey, pw) + } else { + return nil, nil + } +} + +func DecryptICONKeyStore(ksData *KeyStoreData, pw []byte) (*crypto.PrivateKey, error) { if ksData.Crypto.Cipher != cipherAES128CTR { return nil, errors.Errorf("UnsupportedCipher(cipher=%s)", ksData.Crypto.Cipher) @@ -195,30 +232,11 @@ func DecryptKeyStore(data, pw []byte) (*crypto.PrivateKey, error) { return secret, nil } -func ReadAddressFromKeyStore(data []byte) (*common.Address, error) { +func NewKeyStoreData(data []byte) (*KeyStoreData, error) { var ksData KeyStoreData if err := json.Unmarshal(data, &ksData); err != nil { return nil, err } - if ksData.CoinType != coinTypeICON { - return nil, errors.Errorf("InvalidCoinType(coin=%s)", ksData.CoinType) - } - return &ksData.Address, nil -} - -func NewFromKeyStore(data, pw []byte) (*softwareWallet, error) { - secret, err := DecryptKeyStore(data, pw) - if err != nil { - return nil, err - } - return NewFromPrivateKey(secret) -} -func KeyStoreFromWallet(w interface{}, pw []byte) ([]byte, error) { - s, ok := w.(*softwareWallet) - if ok { - return EncryptKeyAsKeyStore(s.skey, pw) - } else { - return nil, nil - } + return &ksData, nil } diff --git a/common/wallet/keystore_evm.go b/common/wallet/keystore_evm.go new file mode 100644 index 00000000..d34fa880 --- /dev/null +++ b/common/wallet/keystore_evm.go @@ -0,0 +1,15 @@ +package wallet + +import ( + "crypto/ecdsa" + + "github.com/ethereum/go-ethereum/accounts/keystore" +) + +func DecryptEvmKeyStore(ksData, pw []byte) (*ecdsa.PrivateKey, error) { + key, err := keystore.DecryptKey(ksData, string(pw)) + if err != nil { + return nil, err + } + return key.PrivateKey, nil +} diff --git a/common/wallet/wallet.go b/common/wallet/wallet.go index 93ae19f2..911e1464 100644 --- a/common/wallet/wallet.go +++ b/common/wallet/wallet.go @@ -34,7 +34,7 @@ func (w *softwareWallet) PublicKey() []byte { } func (w *softwareWallet) ECDH(pubKey []byte) ([]byte, error) { - pkey,err := crypto.ParsePublicKey(pubKey) + pkey, err := crypto.ParsePublicKey(pubKey) if err != nil { return nil, err } @@ -49,7 +49,7 @@ func New() *softwareWallet { } } -func NewFromPrivateKey(sk *crypto.PrivateKey) (*softwareWallet, error) { +func NewIcxWalletFromPrivateKey(sk *crypto.PrivateKey) (*softwareWallet, error) { pk := sk.PublicKey() return &softwareWallet{ skey: sk, diff --git a/common/wallet/wallet_evm.go b/common/wallet/wallet_evm.go new file mode 100644 index 00000000..6f7a8ed7 --- /dev/null +++ b/common/wallet/wallet_evm.go @@ -0,0 +1,40 @@ +package wallet + +import ( + "crypto/ecdsa" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" +) + +type EvmWallet struct { + Skey *ecdsa.PrivateKey + Pkey *ecdsa.PublicKey +} + +func (w *EvmWallet) Address() string { + pubBytes := w.PublicKey() + return common.BytesToAddress(crypto.Keccak256(pubBytes[1:])[12:]).Hex() +} + +func (w *EvmWallet) Sign(data []byte) ([]byte, error) { + //TODO: Not implemented yet + return nil, errors.New("Not implemented yet") +} + +func (w *EvmWallet) PublicKey() []byte { + return crypto.FromECDSAPub(w.Pkey) +} + +func (w *EvmWallet) ECDH(pubKey []byte) ([]byte, error) { + //TODO: Not implemented yet + return nil, nil +} + +func NewEvmWalletFromPrivateKey(sk *ecdsa.PrivateKey) (*EvmWallet, error) { + return &EvmWallet{ + Skey: sk, + Pkey: &sk.PublicKey, + }, nil +} diff --git a/doc/bmr.md b/doc/bmr.md index c1beb313..dd89f207 100644 --- a/doc/bmr.md +++ b/doc/bmr.md @@ -6,13 +6,422 @@ * Monitor BTP events * Gather proofs for the events +BTP use a trustless verification method to prove a transaction origin and destination. Firstly, origin network will use [pbft](#practical-byzantine-fault-tolerance-pbft) algorithm to finalize the given block, at the precommits, the neccessary amount of validator/block producer signature will be on BlockHeader. If the amount is sufficient, the BlockHeader is considered as finalized by the origin network. From the verified BlockHeader, a field of State/Receipt/Storage hash contains the Merkle Proof root of events, using this field we can have a prove the validity of BTP event. + +``` +RelayMessage + BlockUpdates -> BlockHeader + Votes + BlockProofs -> BlockHeader + MTA witness + ReceiptProofs -> Events contains (BTPMessage) + StateRoot in BlockHeader +``` + +When the ReceiptProofs of the BTPMessage with found in the block smaller of BMV (BTP Message Verifier) MTA (Merkle Tree Acummulator) height. BMR need to send the BlockHeader again, BMV need to prove that BlockHeader is valid, so we BMR build MTA witness. So when BMR send a BlockProof the condition is BTPMessage found with a correct sequence and BTPMessage found and the blocks contains BTPMessage is smaller than BMV MTA height. + +### Practical byzantine fault tolerance (pbft) + +- Votes: how validators/block producers confirm the finality of a block at PreCommit stage. Reference to thesis (https://www.microsoft.com/en-us/research/wp-content/uploads/2017/01/thesis-mcastro.pdf) thes +- Depends on each chain implementation, for ICON please check [icon doc](icon.md), for Polkadot parachain with Frontier support please check [parachain doc](polkadot_parachain_with_frontier.md) + ## Integrated blockchain * [ICON](icon.md) * [ICON Enterprise Edition](iconee.md) +* [Polkadot/Kusama Parachain with Frontier Support](polkadot_parachain_with_frontier.md) + * Moonbeam/Moonriver ## General -* [Build Guide](build.md) -* [Tutorial](tutorial.md) +* [Build Guide](build.md): please notice that for BMR, we only need to build executables +* [Keystore](keystore.md) +* [PoC ICON-ICON Tutorial](tutorial.md) + +## Overall architecture + +![Diagram](./img/bmr_overall_architecture.drawio.svg) +## Simple run with Moonbeam developmnent node + +* Example assumptions + * ICON endpoint is `http://goloop:9080/api/v3/icon` + * Moonriver endpoint are `wss://moonbeam:34008` and `http://moonbeam:34007` + * ICON BMC BTP address: `btp://0x3.icon/cx8eb24849a7ceb16b8fa537f5a8b378c6af4a0247` + * PRA BMC BTP address: `btp://0x501.pra/0x5b5B619E6A040EBCB620155E0aAAe89AfA45D090` + +* To generate relay keystore: please follow [keystore](keystore.md). +* To get BMCStatusLink, to getStatus get offset and check relays list contains address of relay keystore + 1. dst chain is goloop/gochain: please run multiple instructions as below + ```bash + $ goloop rpc --uri http://goloop:9080/api/v3/icon \ + call --to $JAVA_SCORE_BMC_ADDRESS \ + --method getStatus \ + --param _link=$BTPSIMPLE_SRC_ADDRESS + # Example + $ goloop rpc --uri http://goloop:9080/api/v3/icon \ + call --to cx8eb24849a7ceb16b8fa537f5a8b378c6af4a0247 \ + --method getStatus \ + --param _link=btp://0x501.pra/0x5b5B619E6A040EBCB620155E0aAAe89AfA45D090 + { + "block_interval_dst": "0x3e8", + "block_interval_src": "0x7d0", + "cur_height": "0x14d54", + "delay_limit": "0x3", + "max_agg": "0x10", + "relay_idx": "0x0", + "relays": [ + { + "address": "hx2dbd4f999f0e6b3c017c029d569dd86950c23104", + "block_count": "0xd00", + "msg_count": "0x0" + } + ], + "rotate_height": "0x137ea", + "rotate_term": "0x8", + "rx_height": "0xca", + "rx_height_src": "0x16770", + "rx_seq": "0x0", + "tx_seq": "0x2", + "verifier": { + "height": "0x235fe", + "last_height": "0x234a0", + "offset": "0x234a0" + } + } + + $ echo $((0x234a0)) + 144544 + ``` + 2. dst chain is EVM: please run multiple instructions as below + + ```javascript + $ make dist-sol + $ export SOLIDITY_DIST_DIR=${PWD}/build/contracts/solidity + $ yarn install --production --cwd $SOLIDITY_DIST_DIR/bmc + $ truffle console --network moonbeamlocal + $ truffle(moonbeamlocal)> let bmcPeriphery = await BMCPeriphery.at("0x5b5B619E6A040EBCB620155E0aAAe89AfA45D090") + $ truffle(moonbeamlocal)> await bmcPeriphery.getStatus("btp://0x3.icon/cx8eb24849a7ceb16b8fa537f5a8b378c6af4a0247") + // Example of BMCStatusLink + [ + '0', + '26', + [ + '3710', + '3562', + '490', + '0x', + heightMTA: '3710', + offsetMTA: '3562', + lastHeight: '490', + extra: '0x' + ], + [ + [ + '0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0', + '3220', + '0', + addr: '0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0', + blockCount: '3220', + msgCount: '0' + ] + ], + '0', + '94486', + '15', + '3', + '5', + '3710', + '92401', + '1000', + '3000', + '171550', + rxSeq: '0', + txSeq: '26', + verifier: [ + '3710', + '3562', + '3710', + '0x', + heightMTA: '3710', + offsetMTA: '3562', # Offset is here + lastHeight: '3710', + extra: '0x' + ], + relays: [ + [ + '0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0', + '3220', + '0', + addr: '0x3Cd0A705a2DC65e5b1E1205896BaA2be8A07c6e0', + blockCount: '3220', + msgCount: '0' + ] + ], + relayIdx: '0', + rotateHeight: '94486', + rotateTerm: '15', + delayLimit: '3', + maxAggregation: '5', + rxHeightSrc: '490', + rxHeight: '92401', + blockIntervalSrc: '1000', + blockIntervalDst: '3000', + currentHeight: '171550' + ] + ``` + +* Build btpsimple executable: + +```bash +make +# or +make **btpsimple** +``` + +* To create a configuration file + ```bash + # Make sure btpsimple executable exist + make btpsimple + + # Set btpsimple in path + export PATH="$PATH:${PWD}/bin" + + BTPSIMPLE_CONFIG=path/to/config/dst.config.json \ + BTPSIMPLE_SRC_ADDRESS=btp://0x3.icon/cx8eb24849a7ceb16b8fa537f5a8b378c6af4a0247 \ + BTPSIMPLE_SRC_ENDPOINT=http://goloop:9080/api/v3/icon \ + BTPSIMPLE_DST_ADDRESS=btp://0x501.pra/0x5b5B619E6A040EBCB620155E0aAAe89AfA45D090 \ + BTPSIMPLE_DST_ENDPOINT=wss://moonbeam:34008 \ + BTPSIMPLE_OFFSET=3562 \ + BTPSIMPLE_KEY_STORE=path/to/config/dst.ks.json \ + BTPSIMPLE_KEY_SECRET=path/to/config/dst.secret \ + BTPSIMPLE_LOG_WRITER_FILENAME=path/to/config/log/dst.log \ + ./entrypoint.sh + ``` +* To run a btpsimple + ```bash + bin/btpsimple start --config path/to/config/dst.config.json + ``` + +## Run with Moonbeam Public Network and RelayChain + +* Example assumptions + * ICON endpoint is `[http://goloop:9080/api/v3/icon](https://btp.net.solidwallet.io/api/v3/icon_dex)` + * Moonriver endpoint are `wss://wss.testnet.moonbeam.network` and `https://rpc.testnet.moonbeam.network` + * ICON BMC BTP address: `btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3` + * PRA BMC BTP address: `btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356` + +* To generate relay keystore: please follow [keystore](keystore.md). +* To get BMCStatusLink, to getStatus get offset and check relays list contains address of relay keystore + 1. dst chain is goloop/gochain: please run multiple instructions as below + ```bash + $ GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" \ + call --to $JAVA_SCORE_BMC_ADDRESS \ + --method getStatus \ + --param _link=$BTPSIMPLE_SRC_ADDRESS + # Example + $ GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" \ + goloop rpc call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 \ + --method getStatus \ + --param _link=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 + { + "block_interval_dst": "0xc", + "block_interval_src": "0x7d0", + "cur_height": "0x4efbd2", + "delay_limit": "0x2", + "max_agg": "0xa", + "relay_idx": "0x0", + "relays": [ + { + "address": "hx2dbd4f999f0e6b3c017c029d569dd86950c23104", + "block_count": "0x402", + "msg_count": "0x3" + } + ], + "rotate_height": "0x4efb99", + "rotate_term": "0x1", + "rx_height": "0x4ee330", + "rx_height_src": "0xffd6f", + "rx_seq": "0x3", + "sack_height": "0x0", + "sack_next": "0x0", + "sack_seq": "0x0", + "sack_term": "0x0", + "tx_seq": "0x4", + "verifier": { + "height": "0x100062", + "last_height": "0xffd6f", + "offset": "0xffc60" + } + } + + $ echo $((0xffc60)) + 1047648 + ``` + 2. dst chain is EVM: please run multiple instructions as below + + ```javascript + $ make dist-sol-bmc + $ export SOLIDITY_DIST_DIR=${PWD}/build/contracts/solidity + $ truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + $ truffle(moonbase)> let bmcP = await BMCPeriphery.at("0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356") + $ truffle(moonbase)> bmcP.getStatus("btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3") + [ + '3', + '3', + [ + '5176449', + '5173762', + '5169389', + '0x', + heightMTA: '5176449', + offsetMTA: '5173762', + lastHeight: '5169389', + extra: '0x' + ], + [ + [ + '0x126AD520629a0152b749Af26d5fd342Cb67Ac6CE', + '8831', + '3', + addr: '0x126AD520629a0152b749Af26d5fd342Cb67Ac6CE', + blockCount: '8831', + msgCount: '3' + ] + ], + '0', + '1048846', + '1', + '3', + '10', + '5167618', + '1047957', + '1000', + '12', + '1048846', + rxSeq: '3', + txSeq: '3', + verifier: [ + '5176449', + '5173762', + '5169389', + '0x', + heightMTA: '5176449', + offsetMTA: '5173762', + lastHeight: '5169389', + extra: '0x' + ], + relays: [ + [ + '0x126AD520629a0152b749Af26d5fd342Cb67Ac6CE', + '8831', + '3', + addr: '0x126AD520629a0152b749Af26d5fd342Cb67Ac6CE', + blockCount: '8831', + msgCount: '3' + ] + ], + relayIdx: '0', + rotateHeight: '1048846', + rotateTerm: '1', + delayLimit: '3', + maxAggregation: '10', + rxHeightSrc: '5167618', + rxHeight: '1047957', + blockIntervalSrc: '1000', + blockIntervalDst: '12', + currentHeight: '1048846' + ] + ``` + +* Build btpsimple executable: + +```bash +make +# or +make **btpsimple** +``` + +* To create a configuration file + ```bash + # Make sure btpsimple executable exist + make btpsimple + + # Set btpsimple in path + export PATH="$PATH:${PWD}/bin" + + BTPSIMPLE_CONFIG=path/to/config/dst.config.json \ + BTPSIMPLE_SRC_ADDRESS=btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 \ + BTPSIMPLE_SRC_ENDPOINT=https://btp.net.solidwallet.io/api/v3/icon_dex \ + BTPSIMPLE_DST_ADDRESS=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 \ + BTPSIMPLE_DST_ENDPOINT=wss://wss.testnet.moonbeam.network \ + BTPSIMPLE_OFFSET=3562 \ + BTPSIMPLE_KEY_STORE=path/to/config/dst.ks.json \ + BTPSIMPLE_KEY_SECRET=path/to/config/dst.secret \ + BTPSIMPLE_LOG_WRITER_FILENAME=log/icon2pra.btp.moonbase.log \ + ./entrypoint.sh + ``` +* To run a btpsimple + ```bash + bin/btpsimple start --config path/to/config/dst.config.json + ``` + +For the flow from Parachain to ICON, it's requires options in "src.options" +- relayEndpoint: is the websocket or http endpoint of relay chain +- relayBtpAddress: is btp of relaychain with specname ![spec](img/pra_get_spec.png) +- iconEndpoint: the iconEndpoint similar in dst endpoint +- iconBmvAddress: Parachain BMV smart contracts on ICON network to get MTA of relaychain +- relaychainOffset: is the Relaychain MTA offset + +Example +```json +{ + "base_dir": ".", + "src": { + "address": "btp://0x507.pra/0x7ccEE5DDc2cE545fEa75b7B398dFAC9419A18624", + "endpoint": "wss://wss.testnet.moonbeam.network", + "options": { + "relayEndpoint": "wss://wss-relay.testnet.moonbeam.network", + "relayBtpAddress": "btp://westend.relay/", + "iconEndpoint": "https://btp.net.solidwallet.io/api/v3/icon_dex", + "iconBmvAddress": "btp://0x42.icon/cx4e658b8a0590c4c1a74a8f53d762c8d5e89a81cc", + "relayOffset": 2126340 + } + }, + "dst": { + "address": "btp://0x42.icon/cx8c97ee5a26801a168bf7b7708a73d14cec3d6ad5", + "endpoint": "https://btp.net.solidwallet.io/api/v3/icon_dex" + }, + "offset": 1007699, + "key_store": {}, + "key_secret": "...", + "log_level": "trace", + "console_level": "debug", + "log_forwarder": { + "vendor": "", + "address": "", + "level": "info", + "name": "" + }, + "log_writer": { + "filename": "log/pra2icon.btp.moonbase.log", + "maxsize": 1, + "maxage": 0, + "maxbackups": 0, + "localtime": false, + "compress": false + } +} +``` +## Run in docker container + +TODO wait for docker implementation ## Management -* [btpsimple command line](btpsimple_cli.md) \ No newline at end of file +* [btpsimple command line](btpsimple_cli.md) + +## Testing + +```bash +# Test all in short mode, with log, without cache +go test -v -count=1 -short ./... +``` + +```bash +# Test all with log, without cache +go test -v -count=1 ./... +``` \ No newline at end of file diff --git a/doc/btpsimple_cli.md b/doc/btpsimple_cli.md index 849a9e8f..d062e5b2 100644 --- a/doc/btpsimple_cli.md +++ b/doc/btpsimple_cli.md @@ -35,7 +35,8 @@ Command Line Interface of Relay for Blockchain Transmission Protocol | --offset | BTPSIMPLE_OFFSET | false | 0 | Offset of MTA | | --src.address | BTPSIMPLE_SRC_ADDRESS | true | | BTP Address of source blockchain (PROTOCOL://NID.BLOCKCHAIN/BMC) | | --src.endpoint | BTPSIMPLE_SRC_ENDPOINT | true | | Endpoint of source blockchain | -| --src.options | BTPSIMPLE_SRC_OPTIONS | false | [] | Options, comma-separated 'key=value' | +| --sentry.dns | BTPSIMPLE_SENTRY_DSN | false | | Sentry DSN for monitor bug and alerts | +| --sentry.env | BTPSIMPLE_SENTRY_ENV | false | | Sentry environment to distinguish between stages | ### Child commands |Command | Description| @@ -85,6 +86,7 @@ Save configuration | --src.address | BTPSIMPLE_SRC_ADDRESS | true | | BTP Address of source blockchain (PROTOCOL://NID.BLOCKCHAIN/BMC) | | --src.endpoint | BTPSIMPLE_SRC_ENDPOINT | true | | Endpoint of source blockchain | | --src.options | BTPSIMPLE_SRC_OPTIONS | false | [] | Options, comma-separated 'key=value' | +| --sentry.dns | BTPSIMPLE_SENTRY_DSN | false | | Options, comma-separated 'key=value' | ### Parent command |Command | Description| diff --git a/doc/build.md b/doc/build.md index 6ddfe6f9..b621f247 100644 --- a/doc/build.md +++ b/doc/build.md @@ -9,7 +9,7 @@ brew install go ``` -### ICON SCORE(Smart Contract On Reliable Environment) +### ICON PYTHONSCORE(Smart Contract On Reliable Environment) * Python 3.7+ Virtual Environment @@ -19,6 +19,40 @@ pip install virtualenv setuptools wheel ``` +### ICON JAVASCORE(Smart Contract On Reliable Environment) + +* Java 11+ and Grade 6.7+ + + **Mac OSX** + ``` + if [ ! -d "$HOME/.sdkman" ]; then + curl -s "https://get.sdkman.io" | bash + fi + source $HOME/.sdkman/bin/sdkman-init.sh + sdk install java 11.0.11.hs-adpt + sdk default java 11.0.11.hs-adpt + sdk install gradle 6.7.1 + sdk default gradle 6.7.1 + ``` + +### Ethereum SOLIDITY + +* Node.js v14.0.0, Yarn v1.22.0, Truffle v5.3.0 + + **Mac OSX** + ``` + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash + source $HOME/.nvm/ + + echo 'export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.zshrc + + source ~/.zshrc + + nvm install v14.17.0 + nvm alias default v14.17.0 + npm install --global yarn truffle@5.3.0 + ``` ## Environment ### Source checkout @@ -37,12 +71,25 @@ make ``` Output binaries are placed under `bin/` directory. +**** +### Build ICON PYTHONSCORE + +```bash +make dist-py +``` +Output files are placed under `build/contracts/pyscore/` directory. -### Build ICON SCORE +### Build ICON JAVASCORE ```bash -make dist-py +make dist-java +``` + +### Build Ethereum SOLIDITY + +```bash +make dist-sol ``` -Output files are placed under `build/pyscore/` directory. +Output files are placed under `build/contracts/solidity/` directory. diff --git a/doc/deployment.md b/doc/deployment.md new file mode 100644 index 00000000..fa4d5bb9 --- /dev/null +++ b/doc/deployment.md @@ -0,0 +1,1252 @@ +# How to deploy contracts to networks + +## Prerequisites + +Please check your installation with [build](build.md) prerequisities and make sure running these deployments on a computer/server have a stable network condition + +## Solidity +### Prepare environment for deployment + +Notes: This deployment will require user, to use raw text private key as environment variable, make sure you don't let any party access to process manager while deployment process is running. And clear up the shell history after deployment + + +1. `PRIVATE_KEYS` + +User will prepare this key with raw test and add it for each deploy command to avoid export it to other process, this key will use for all deployment. For example: + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" BMC_BTP_NET="0x507.pra" truffle migrate --network moonbase +``` + +2. `SOLIDITY_DIST_DIR` + +```bash +make dist-sol +export SOLIDITY_DIST_DIR=${PWD}/build/contracts/solidity +``` + +### WARNING + +If you have error while deployment, just rerun the truffle migrate command. And, after you finished deployments please save the `.openzeppelin` folder, this is neccessary to keep the address of implementations/proxies/proxyadmin contracts. + + +### Notify + +When you use truffle console and got the problem like, you could to only use abi interface with the contract + +``` +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bmcPer = await BMCPeriphery.deployed() +Uncaught: +Error: BMCPeriphery has not been deployed to detected network (network/artifact mismatch) + at processTicksAndRejections (internal/process/task_queues.js:95:5) +truffle(moonbase)> bmcPer = await BMCPeriphery.at("0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356") +undefined +``` + +### Deploy BMC + +```bash +yarn install --production --cwd $SOLIDITY_DIST_DIR/bmc +rm -rf $SOLIDITY_DIST_DIR/bmc/build + +truffle compile --all --working_directory $SOLIDITY_DIST_DIR/bmc + +# @param +# - BMC_BTP_NET: Chain ID and name of a network that BMC is going to deploy on, e.g. +# To get Chain ID. Depends on chain +# curl https://rpc.testnet.moonbeam.network \ +# -X POST \ +# -H "Content-Type: application/json" \ +# -d '{"jsonrpc":"2.0","method":"eth_chainId","params": [],"id":1}' +# {"jsonrpc":"2.0","result":"0x507","id":1} +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +BMC_BTP_NET="0x507.pra" \ +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory + +Compiling your contracts... +=========================== +> Everything is up to date, there is nothing to compile. + + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_deploy_bmc.js +=============== + + Deploying 'BMCManagement' + ------------------------- + > transaction hash: 0xb7b6461bb87f0818aa5a62af82a0783542837923547be174df053a2c4077577d + > Blocks: 2 Seconds: 18 + > contract address: 0x8efAe8b69095b6467859496624ea31468142A2D8 + > block number: 1047640 + > block timestamp: 1635402936 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.373698834469765665 + > gas used: 5036796 (0x4cdafc) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.010073592 ETH + + + Deploying 'ProxyAdmin' + ---------------------- + > transaction hash: 0x45db47377061fbd9a1d36801cb22a06617147800a0f0af4b79025235d35d4758 + > Blocks: 2 Seconds: 16 + > contract address: 0xf070CDa0F732562e25402e6b31cE37817C43ED26 + > block number: 1047642 + > block timestamp: 1635402960 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.372733394469765665 + > gas used: 482720 (0x75da0) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.00096544 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x5fa1efe21821b3c5048fd4543609379be19fbdfa9be4dfcb3464fb16cb793e82 + > Blocks: 2 Seconds: 16 + > contract address: 0xb3aD0707F494393A7d922F14A412E3518eD0B6bc + > block number: 1047644 + > block timestamp: 1635402984 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.371413960469765665 + > gas used: 659717 (0xa1105) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.001319434 ETH + + + Deploying 'BMCPeriphery' + ------------------------ + > transaction hash: 0x1dcb9f5d00f3b7782c19b6c0a2c042cd6c80472ad37c16687e81815673b8ac3b + > Blocks: 1 Seconds: 12 + > contract address: 0x4394a448c7032C93C3d7dDcde65Ef21c72804272 + > block number: 1047646 + > block timestamp: 1635403008 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.362084700469765665 + > gas used: 4664630 (0x472d36) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.00932926 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x257611de4463ee1efd51ba2d9398bfc5c224b8fb8867d1a0452ea1f9c93eff2f + > Blocks: 1 Seconds: 12 + > contract address: 0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 + > block number: 1047648 + > block timestamp: 1635403032 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.360601754469765665 + > gas used: 741473 (0xb5061) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.001482946 ETH + + > Saving artifacts + ------------------------------------- + > Total cost: 0.023170672 ETH + + +Summary +======= +> Total deployments: 5 +> Final cost: 0.023170672 ETH + + +``` + +### Get BMC proxy addresses + +```bash +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bmcPer = await BMCPeriphery.deployed() +undefined +truffle(moonbase)> bmcPer.address +'0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356' +truffle(moonbase)> let bmcM = await BMCManagement.deployed() +undefined +truffle(moonbase)> bmcM.address +'0xb3aD0707F494393A7d922F14A412E3518eD0B6bc' +truffle(moonbase)> .exit + +``` +### Deploy NativeCoin BSH + +```bash +yarn install --production --cwd $SOLIDITY_DIST_DIR/bsh +rm -rf $SOLIDITY_DIST_DIR/bsh/build + +truffle compile --all --working_directory $SOLIDITY_DIST_DIR/bsh + +# @params: +# - BMC_PERIPHERY_ADDRESS: an address on chain of BMCPeriphery contract +# This address is queried after deploying BMC contracts +# For example: BMC_PERIPHERY_ADDRESS = 0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 +# - BSH_COIN_NAME: a native coin name of Moonbase Alpha Testnet Network - DEV +# - BSH_COIN_FEE: a charging fee ratio of each request, e.g. 100/10000 = 1% +# - BSH_SERVICE: a service name of BSH contract, e.g. 'CoinTransfer' +# This service name is unique in one network. And it must be registered to BMC contract to activate +# BMC contract checks its service name whether it's already existed +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +BMC_PERIPHERY_ADDRESS="0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356" \ +BSH_COIN_URL="https://moonbeam.network/" \ +BSH_SERVICE="nativecoin" \ +BSH_COIN_NAME="DEV" \ +BSH_COIN_FEE="100" \ +BSH_FIXED_FEE="500000" \ +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh + +### Output +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory + +Compiling your contracts... +=========================== +> Everything is up to date, there is nothing to compile. + + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_deploy_bsh.js +=============== + + Deploying 'BSHCore' + ------------------- + > transaction hash: 0x7f809e38445fc33c0a6f98b8ddd0575de35dd3ff1b4325540d8069c8ff5f26c5 + > Blocks: 2 Seconds: 16 + > contract address: 0xde4C26226BD6258Cc699CE9E88cc798C21B4b94D + > block number: 1047668 + > block timestamp: 1635403302 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.351025576469765665 + > gas used: 4740289 (0x4854c1) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.009480578 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x06de84189e02f396c89cd59175652b2216c9f0c0d3146f6610baeb0305ff1700 + > Blocks: 2 Seconds: 24 + > contract address: 0x2a17B6814a172419a5E84d7B746aBEb95a84E76B + > block number: 1047671 + > block timestamp: 1635403338 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.349246786469765665 + > gas used: 889395 (0xd9233) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.00177879 ETH + + + Deploying 'BSHPeriphery' + ------------------------ + > transaction hash: 0x09400aa94dbb7e16ab0aaccde2b835906b47e579962abc647b283429cf42eec6 + > Blocks: 2 Seconds: 20 + > contract address: 0x909174976471750601cBA5BcBEe24465B6D32C2d + > block number: 1047673 + > block timestamp: 1635403368 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.341733146469765665 + > gas used: 3756820 (0x395314) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.00751364 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x72b9aa4a36b1a843618d49ecc2fb4b26a89899bf93469c4dce93454a170dd4e1 + > Blocks: 2 Seconds: 32 + > contract address: 0xccf66A1a9D82EC13b0B2a5002EdA4dF411BE4754 + > block number: 1047675 + > block timestamp: 1635403410 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.340405332469765665 + > gas used: 663907 (0xa2163) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.001327814 ETH + + > Saving artifacts + ------------------------------------- + > Total cost: 0.020100822 ETH + + +Summary +======= +> Total deployments: 4 +> Final cost: 0.020100822 ETH + + +``` + +### Get BSH Periphery proxy address + +```bash +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bshP = await BSHPeriphery.deployed() +undefined +truffle(moonbase)> bshP.address +'0xccf66A1a9D82EC13b0B2a5002EdA4dF411BE4754' +truffle(moonbase)> let bshCore = await BSHCore.deployed() +undefined +truffle(moonbase)> bshCore.address +'0x2a17B6814a172419a5E84d7B746aBEb95a84E76B' +truffle(moonbase)> .exit +``` + + +### Deploy nativecoinERC20_BSH + +```bash +yarn install --production --cwd $SOLIDITY_DIST_DIR/nativecoinERC20 +rm -rf $SOLIDITY_DIST_DIR/nativecoinERC20/build + +truffle compile --all --working_directory $SOLIDITY_DIST_DIR/nativecoinERC20 + +# @params: +# - BSH_COIN_NAME: Native coin Name +# - BSH_COIN_FEE: Fees to be charged +# - BSH_FIXED_FEE: basic fixed fees +# - BSH_TOKEN_NAME: ERC20_token Name same as symbol +# - BSH_TOKEN_SYMBOL: ERC20_token Name +# - BSH_INITIAL_SUPPLY: inital supply of the erc20 token +# - BMC_PERIPHERY_ADDRESS: an address on chain of BMCPeriphery contract +# This address is queried after deploying BMC contracts +# For example: BMC_PERIPHERY_ADDRESS = 0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 +# - BSH_SERVICE: a service name of BSH contract, e.g. 'NativeCoinIRC2BSH' +# This service name is unique in one network. And it must be registered to BMC contract to activate +# BMC contract checks its service name whether it's already existed +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +BSH_COIN_NAME="MOVR" \ +BSH_COIN_FEE="100" \ +BSH_FIXED_FEE="50000" \ +BSH_TOKEN_NAME="ICX" \ +BSH_TOKEN_SYMBOL="ICX" \ +BSH_INITIAL_SUPPLY="100000" \ +BMC_PERIPHERY_ADDRESS="0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356" \ +BSH_SERVICE="NativeCoinIRC2BSH" \ +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/nativecoinERC20 +``` + +### Deloy BMV + +For example the next BTP address is: `btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3` +And the offset is: `5167618` + +```bash +yarn install --production --cwd $SOLIDITY_DIST_DIR/bmv +rm -rf $SOLIDITY_DIST_DIR/bmv/build + +truffle compile --all --working_directory $SOLIDITY_DIST_DIR/bmv + +# @params +# - BMC_PERIPHERY_ADDRESS: an address on chain of BMCPeriphery contract +# This address is queried after **deploying** BMC contracts +# For example: BMC_PERIPHERY_ADDRESS = 0x6047341C88B9A45957E9Ad68439cA941D464c706 +# - BMV_ICON_NET: Chain ID and name of a network that BMV is going to verify BTP Message +# - BMV_ICON_INIT_OFFSET: a block height when ICON-BMC was deployed +# - BMV_ICON_ENCODED_VALIDATORS: a result of ICON JSON-RPC method `icx_getDataByHash` with the input is +# PreviousBlockHeader.NextValidatorHash. So, to get this param for block N, you must get BlockHeader of N - 1 +# User can execute to ge the result : +# +# make iconvalidators +# +# GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" bin/iconvalidators build 5167618 +# +# - BMV_ICON_LASTBLOCK_HASH: a hash of the above block +# GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" goloop rpc blockbyheight 5167618 | jq -r '.block_hash' +# Remember adding 0x +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +BMC_PERIPHERY_ADDRESS="0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356" \ +BMV_ICON_NET="0x42.icon" \ +BMV_ICON_ENCODED_VALIDATORS="0xf8589500edeb1b82b94d548ec440df553f522144ca83fb8d9500d63c4c73b623e97f67407d687af4efcfe486a51595007882dace25ff7e947d3a25178a2a1162874cfddc95000458d8b6f649a9e005963dc9a72669c89ed52d85" \ +BMV_ICON_INIT_OFFSET="5167618" \ +BMV_ICON_INIT_ROOTSSIZE="12" \ +BMV_ICON_INIT_CACHESIZE="12" \ +BMV_ICON_LASTBLOCK_HASH="0x2b666571f5bb83147d6a000326a88d0a35c9e2c7f25a88b7dfbc7325ba8ee927" \ +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmv + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory + +Compiling your contracts... +=========================== +> Everything is up to date, there is nothing to compile. + + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_deploy_bmv.js +=============== + + Deploying 'DataValidator' + ------------------------- + > transaction hash: 0x77b237b9ae256630356431b086db0ac0e628f23abead3de3c0934a6605158426 + > Blocks: 2 Seconds: 21 + > contract address: 0xE8F00eA24C53C1990bd365Ad41b1C70eE62676cc + > block number: 1047697 + > block timestamp: 1635403686 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.336069806469765665 + > gas used: 2120414 (0x205ade) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.004240828 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0xc33701b3c3cc24ce8e700e056e28631cf7140214f1cfa61c69576765418e9191 + > Blocks: 1 Seconds: 12 + > contract address: 0x8ceD43FDF0Fe705E10965637e40b7b3F084E3c40 + > block number: 1047699 + > block timestamp: 1635403710 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.334834690469765665 + > gas used: 617558 (0x96c56) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.001235116 ETH + + + Deploying 'BMV' + --------------- + > transaction hash: 0x7114ebec49ac1463b29a12b6c4b533b0a40cf8c65e0f3da6eb97563a9d8b4c9b + > Blocks: 4 Seconds: 48 + > contract address: 0xbAc7d8f664c6E5CeDf9fe3335A40CB08d1100871 + > block number: 1047704 + > block timestamp: 1635403770 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.326152214469765665 + > gas used: 4341238 (0x423df6) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.008682476 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0xf715d23dd0dea30a82b5b10c526207f0033166d0f4e8369a837ba9ff294b8d3c + > Blocks: 3 Seconds: 28 + > contract address: 0x7D25dc35670873E90bcCa0096FAEbeE576911F52 + > block number: 1047707 + > block timestamp: 1635403806 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 763.323900116469765665 + > gas used: 1126049 (0x112ea1) + > gas price: 2 gwei + > value sent: 0 ETH + > total cost: 0.002252098 ETH + + > Saving artifacts + ------------------------------------- + > Total cost: 0.016410518 ETH + + +Summary +======= +> Total deployments: 4 +> Final cost: 0.016410518 ETH + +``` + +### Get BMV address from proxy + +```bash +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmv +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bmv = await BMV.deployed() +undefined +truffle(moonbase)> bmv.address +'0x7D25dc35670873E90bcCa0096FAEbeE576911F52' +truffle(moonbase)> .exit +``` + +### Configure Contracts + +```bash +# - CURRENTLINK_BMV_ADDRESS: from Get BMV address from proxy +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +NEXTLINK_BTP_NET="0x42.icon" \ +CURRENTLINK_BMV_ADDRESS="0x7D25dc35670873E90bcCa0096FAEbeE576911F52" \ +truffle exec $SOLIDITY_DIST_DIR/bmc/scripts/add_verifier.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +Using network 'moonbase'. + +{ + tx: '0x816de0f01d6817de82bd68c0e254086ed2d58599fcc5583bb051cd8a0b76a9aa', + receipt: { + blockHash: '0xd06a77b0d11b89ce36e3ac6345da9fae780bf9f4d3cea500312a54782887a5ee', + blockNumber: 1047716, + contractAddress: null, + cumulativeGasUsed: 91146, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 91146, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0x816de0f01d6817de82bd68c0e254086ed2d58599fcc5583bb051cd8a0b76a9aa', + transactionIndex: 0, + rawLogs: [] + }, + logs: [] +} +``` + +For example the next BTP address is: `btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3` +And the offset is: `5167618` +And deployer repair the keystore with address `126ad520629a0152b749af26d5fd342cb67ac6ce` + +```bash +# Deploy on what relay you want to add +# - NEXTLINK_BTP_ADDRESS: the BMC address from 0x42.icon network +# - NEXTLINK_BLOCK_INTERVAL: the block interval from the deploy network +# - NEXTLINK_ROTATION_MAX_AGGERATION: the max period of BMR rotation algorithm to +# - NEXTLINK_ROTATION_DELAY_LIMIT: expect delay in sending transactions RelayMessage +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +NEXTLINK_BTP_ADDRESS="btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3" \ +NEXTLINK_BLOCK_INTERVAL=12 \ +NEXTLINK_ROTATION_MAX_AGGERATION=10 \ +NEXTLINK_ROTATION_DELAY_LIMIT=3 \ +RELAY_ADDRESSES="0x126ad520629a0152b749af26d5fd342cb67ac6ce" \ +truffle exec $SOLIDITY_DIST_DIR/bmc/scripts/add_link_set_link_add_relay.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +Using network 'moonbase'. + +{ + tx: '0x3a17d026cadeefc4c33853aa5d546ef8d88bcbb949baeb519e33c0647d33867e', + receipt: { + blockHash: '0xbd7497080cce34cf6d9fba69ac7e2222cc37fa600ac1bd5b85a9e6d885dc5ae9', + blockNumber: 1047729, + contractAddress: null, + cumulativeGasUsed: 457359, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 436359, + logs: [], + logsBloom: '0x00000002000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000200000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0x3a17d026cadeefc4c33853aa5d546ef8d88bcbb949baeb519e33c0647d33867e', + transactionIndex: 1, + rawLogs: [ [Object] ] + }, + logs: [] +} +{ + tx: '0xaa1f9cb7097dddffc205fc60ed0599b4e2fab3eae184f4dabf583940a1773510', + receipt: { + blockHash: '0x032a2c81f570a6a62c2205de1421ff3c0a78b2cda3d024f0dda05ab18db2de98', + blockNumber: 1047733, + contractAddress: null, + cumulativeGasUsed: 179122, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 179122, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0xaa1f9cb7097dddffc205fc60ed0599b4e2fab3eae184f4dabf583940a1773510', + transactionIndex: 0, + rawLogs: [] + }, + logs: [] +} +{ + tx: '0x87913e2186b32407e259025fdacf7534fffc772358c3ff11fe4b1fbc897c704e', + receipt: { + blockHash: '0xe7ed1ec83d7fa93fc6b7c5d796eebfc9c024880f30817b237af7009f07010955', + blockNumber: 1047745, + contractAddress: null, + cumulativeGasUsed: 94482, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 94482, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0x87913e2186b32407e259025fdacf7534fffc772358c3ff11fe4b1fbc897c704e', + transactionIndex: 0, + rawLogs: [] + }, + logs: [] +} +``` + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +CURRENTLINK_BSH_SERVICENAME="nativecoin" \ +CURRENTLINK_BSH_ADDRESS="0xccf66A1a9D82EC13b0B2a5002EdA4dF411BE4754" \ +truffle exec $SOLIDITY_DIST_DIR/bmc/scripts/add_bsh_service.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +Using network 'moonbase'. + +{ + tx: '0xc1f4a4806bf29f9fb34d4e6e5d12f279b0676fb556fde24142c21aa7b1b71bb3', + receipt: { + blockHash: '0x8521521ca0ee82dc09f5080f0c8f0a330c2acad84ceef129f873a5c0e11017c3', + blockNumber: 1047753, + contractAddress: null, + cumulativeGasUsed: 112049, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 91049, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0xc1f4a4806bf29f9fb34d4e6e5d12f279b0676fb556fde24142c21aa7b1b71bb3', + transactionIndex: 1, + rawLogs: [] + }, + logs: [] +} +``` +##### Add ERC20_BSH service + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +CURRENTLINK_BSH_SERVICENAME="NativeCoinIRC2BSH" \ +CURRENTLINK_BSH_ADDRESS="0xccf66A1a9D82EC13b0B2a5002EdA4dF411BE4754" \ +truffle exec $SOLIDITY_DIST_DIR/bmc/scripts/add_bsh_service.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc +``` + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +NEXTLINK_BTP_NATIVECOIN_NAME="BTC" \ +truffle exec $SOLIDITY_DIST_DIR/bsh/scripts/register_coin.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh + +## Output + +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +Using network 'moonbase'. + +{ + tx: '0x1abea0a03be823bd172c04b4b68dc1d9b8ed45db43b4c12ef262eac1cb6f66bb', + receipt: { + blockHash: '0x457271a8b995bd270b8db2c6b71409f89149ec9f016a8c289262896859faa7e4', + blockNumber: 1047758, + contractAddress: null, + cumulativeGasUsed: 70597, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 70597, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0x2a17b6814a172419a5e84d7b746abeb95a84e76b', + transactionHash: '0x1abea0a03be823bd172c04b4b68dc1d9b8ed45db43b4c12ef262eac1cb6f66bb', + transactionIndex: 0, + rawLogs: [] + }, + logs: [] +} +[ 'DEV', 'ICX' ] +``` + +Register BTC + +``` +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +NEXTLINK_BTP_NATIVECOIN_NAME="BTC" \ +truffle exec $SOLIDITY_DIST_DIR/bsh/scripts/register_coin.js --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +Using network 'moonbase'. + +{ + tx: '0xa5397a30220fb2fab7f52b844c5ccec707afe5049336dce59e64228359449aeb', + receipt: { + blockHash: '0xb371b5edb22242a7656a454ef1cf5f4308e432d6a6b383b26c67a8cc15f2480f', + blockNumber: 1048430, + contractAddress: null, + cumulativeGasUsed: 133597, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 70597, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0x2a17b6814a172419a5e84d7b746abeb95a84e76b', + transactionHash: '0xa5397a30220fb2fab7f52b844c5ccec707afe5049336dce59e64228359449aeb', + transactionIndex: 3, + rawLogs: [] + }, + logs: [] +} +[ 'DEV', 'ICX', 'BTC' ] +``` + +#### Add another relay + +**WARNING** remember this is an override update, so, get the existing relays and add new one + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc + +## Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bmcM = await BMCManagement.deployed() +undefined +truffle(moonbase)> await bmcM.getRelays("btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3") +truffle(moonbase)> await bmcM.addRelay("btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3", ["0x126ad520629a0152b749af26d5fd342cb67ac6ce"]) +{ + tx: '0x87913e2186b32407e259025fdacf7534fffc772358c3ff11fe4b1fbc897c704e', + receipt: { + blockHash: '0xe7ed1ec83d7fa93fc6b7c5d796eebfc9c024880f30817b237af7009f07010955', + blockNumber: 1047745, + contractAddress: null, + cumulativeGasUsed: 94482, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 94482, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0x87913e2186b32407e259025fdacf7534fffc772358c3ff11fe4b1fbc897c704e', + transactionIndex: 0, + rawLogs: [] + }, + logs: [] +} +truffle(moonbase)> await bmcM.getRelays("btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3") +[ '0x126AD520629a0152b749Af26d5fd342Cb67Ac6CE' ] +``` + +### Redeploy new BMV + +For example the next BTP address is: `btp://0x42.icon/cx11a5a7510b128e0ab16546e1493e38b2d7e299c3` +And the offset is: `5174702`. Why `5174702`? Because the next BTPMessage is on `5174703`, so we have to redeploy new BMV with the offset smaller than the next BTPMessage to prevent the system fails. + +```bash +yarn install --production --cwd $SOLIDITY_DIST_DIR/bmv +rm -rf $SOLIDITY_DIST_DIR/bmv/build + +truffle compile --all --working_directory $SOLIDITY_DIST_DIR/bmv + +# @params +# - BMC_PERIPHERY_ADDRESS: an address on chain of BMCPeriphery contract +# This address is queried after **deploying** BMC contracts +# For example: BMC_PERIPHERY_ADDRESS = 0x6047341C88B9A45957E9Ad68439cA941D464c706 +# - BMV_ICON_NET: Chain ID and name of a network that BMV is going to verify BTP Message +# - BMV_ICON_INIT_OFFSET: a block height when ICON-BMC was deployed +# - BMV_ICON_ENCODED_VALIDATORS: a result of ICON JSON-RPC method `icx_getDataByHash` with the input is +# PreviousBlockHeader.NextValidatorHash. So, to get this param for block N, you must get BlockHeader of N - 1 +# User can execute to ge the result : +# +# make iconvalidators +# +# GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" bin/iconvalidators build 5167618 +# +# - BMV_ICON_LASTBLOCK_HASH: a hash of the above block +# GOLOOP_RPC_URI="https://btp.net.solidwallet.io/api/v3/icon_dex" goloop rpc blockbyheight 5167618 | jq -r '.block_hash' +# Remember adding 0x +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" \ +BMC_PERIPHERY_ADDRESS="0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356" \ +BMV_ICON_NET="0x42.icon" \ +BMV_ICON_ENCODED_VALIDATORS="0xf8589500edeb1b82b94d548ec440df553f522144ca83fb8d9500d63c4c73b623e97f67407d687af4efcfe486a51595007882dace25ff7e947d3a25178a2a1162874cfddc95000458d8b6f649a9e005963dc9a72669c89ed52d85" \ +BMV_ICON_INIT_OFFSET="5174702" \ +BMV_ICON_INIT_ROOTSSIZE="12" \ +BMV_ICON_INIT_CACHESIZE="12" \ +BMV_ICON_LASTBLOCK_HASH="0xcdaf4a7ad9fb77bac8e9c6cf296233c3cfba789ffbb4f2fb3412a3266951e7be" \ +truffle migrate --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmv + +### Output +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory + +Compiling your contracts... +=========================== +> Everything is up to date, there is nothing to compile. + + + +Starting migrations... +====================== +> Network name: 'moonbase' +> Network id: 1287 +> Block gas limit: 15000000 (0xe4e1c0) + + +1_deploy_bmv.js +=============== + + Deploying 'DataValidator' + ------------------------- + > transaction hash: 0xa309cfc09dab73e63c602d40218fe43ce26e17b1b5ebc5316d9cda16bd5e5261 + > Blocks: 3 Seconds: 44 + > contract address: 0xa6a147af24D6416B3CC8E91372af1eaf477c0E4f + > block number: 1054138 + > block timestamp: 1635495480 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 313.319527535469765665 + > gas used: 2120414 (0x205ade) + > gas price: 1 gwei + > value sent: 0 ETH + > total cost: 0.002120414 ETH + + + Deploying 'ProxyAdmin' + ---------------------- + > transaction hash: 0x4205479afe06571c7495479cfddd64ea5d17d82eb606e312bbc9ff38a7b5b03a + > Blocks: 13 Seconds: 192 + > contract address: 0x1EccC74336c47f4f696Ffd210E4884C1F480bB94 + > block number: 1054152 + > block timestamp: 1635495684 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 313.319044815469765665 + > gas used: 482720 (0x75da0) + > gas price: 1 gwei + > value sent: 0 ETH + > total cost: 0.00048272 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x2a35d39fb9e36071fc7d1f02761b0a9c9842877b5a211c874ff3f0c78d895f6a + > Blocks: 3 Seconds: 37 + > contract address: 0xD20d590dd12fF328d836D0F87D30464c20bF8f20 + > block number: 1054155 + > block timestamp: 1635495726 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 313.318427257469765665 + > gas used: 617558 (0x96c56) + > gas price: 1 gwei + > value sent: 0 ETH + > total cost: 0.000617558 ETH + + + Deploying 'BMV' + --------------- + > transaction hash: 0x00ae73c60709dc17aeb704c5eb8f7ae907cfacb0d444c6427292c9673b0e7090 + > Blocks: 2 Seconds: 32 + > contract address: 0x192db7A8AE41A712314eD8ab7d16910C16A3B72E + > block number: 1054158 + > block timestamp: 1635495762 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 313.314086019469765665 + > gas used: 4341238 (0x423df6) + > gas price: 1 gwei + > value sent: 0 ETH + > total cost: 0.004341238 ETH + + + Deploying 'TransparentUpgradeableProxy' + --------------------------------------- + > transaction hash: 0x0a1c3a18ce5f57467496b155431b44e8b0d9e11986371a9c453d0fd8abe734da + > Blocks: 19 Seconds: 244 + > contract address: 0x22cFb9cb119DCBfBEBae735ece2AbC3b9e40be81 + > block number: 1054177 + > block timestamp: 1635496020 + > account: 0x4bb718Cb404787BF97bB012Bb08096602fb9544B + > balance: 313.312959958469765665 + > gas used: 1126061 (0x112ead) + > gas price: 1 gwei + > value sent: 0 ETH + > total cost: 0.001126061 ETH + + > Saving artifacts + ------------------------------------- + > Total cost: 0.008687991 ETH + + +Summary +======= +> Total deployments: 5 +> Final cost: 0.008687991 ETH +``` + +- Extract BMV address + +```bash +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmv +> Warning: possible unsupported (undocumented in help) command line option(s): --working_directory +truffle(moonbase)> let bmv = await BMV.deployed() +undefined +truffle(moonbase)> bmv.address +'0x22cFb9cb119DCBfBEBae735ece2AbC3b9e40be81' +truffle(moonbase)> .exit +``` + +- Add new verifier - BMV + +```bash +PRIVATE_KEYS="${YOUR_PRIVATE_KEY}" truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bmc +truffle(moonbase)> let bmcM = await BMCManagement.at("0xb3aD0707F494393A7d922F14A412E3518eD0B6bc") +undefined +truffle(moonbase)> bmcM.removeVerifier("0x42.icon") +truffle(moonbase)> bmcM.addVerifier("0x42.icon", "0x22cFb9cb119DCBfBEBae735ece2AbC3b9e40be81") +{ + tx: '0x78e99c900a286ec74743381d2934ed75b7e6e8f2a8f2dcb363d051c57114317e', + receipt: { + blockHash: '0x337047c156d1242726b30046f7c25e89ae0e859f517ab0c8d905b9703418669f', + blockNumber: 1054203, + contractAddress: null, + cumulativeGasUsed: 1018760, + from: '0x4bb718cb404787bf97bb012bb08096602fb9544b', + gasUsed: 91146, + logs: [], + logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + status: true, + to: '0xb3ad0707f494393a7d922f14a412e3518ed0b6bc', + transactionHash: '0x78e99c900a286ec74743381d2934ed75b7e6e8f2a8f2dcb363d051c57114317e', + transactionIndex: 32, + rawLogs: [] + }, + logs: [] +} +truffle(moonbase)> .exit +``` + +### JAVAScore + +#### Deploy BMC + +``` +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/bmc/build/libs/bmc-0.1.0-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 66 --step_limit 13610920001 \ + --content_type application/java \ + --param _net="0x42.icon" + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x3d9eb499c6744447033dad678d6e0d2f01542d227b56647c67b061a107b20a14 +``` + +#### Deploy CPS + +``` +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/fee_aggregation/CPFTreasury.zip \ + --key_store godWallet.json --key_password gochain \ + --nid 66 --step_limit 13610920001 \ + --content_type application/zip + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x998ddf8593155f1790f7cec11483616503807adc5dcd974b799b092eebc54b6c +``` + +#### Deploy Fee Aggregator + +``` +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/fee_aggregation/build/libs/fee-aggregation-system-1.0-optimized.jar --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 --content_type application/java --param _cps_address=cx2cfe2207351afac374150fbd64540c3f797b4ce7 --param _band_protocol_address=cx2cfe2207351afac374150fbd64540c3f797b4ce7 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x6e3dc5e88c0f3992ef9531299eeb97bec52bd96da1f7475d63f7bc4aed897847 +``` + +#### Set Fee Aggregator address to BMC contract +``` +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method setFeeAggregator --param _addr=cx37465423027f2dd3cc961d8b37a6ad04ddb17138 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xbca569ad3a21f0e9d5b42412b8873452f8ec1f16dd3774df81c527179fb7c4ef +``` + +#### Deploy Event Decoder and Para BMV + +```bash +cd btp/javascore/bmv/helper +yarn deployParaBMV +``` + +- Update `.env` file: + +``` +BMC_ADDRESS=cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 +DST_NET_ADDRESS=0x507.pra +# wss://wss-relay.testnet.moonbeam.network +RELAY_ENDPOINT=wss://wss-relay.testnet.moonbeam.network +# wss://moonbase.moonbeam.elara.patract.io +PARA_ENDPOINT=wss://wss.testnet.moonbeam.network + +RELAY_CHAIN_OFFSET=2214750 +PARA_CHAIN_OFFSET=1047648 +MTA_ROOT_SIZE=16 +MTA_CACHE_SIZE=16 +MTA_IS_ALLOW_WITNESS=true + +ICON_ENDPOINT=https://btp.net.solidwallet.io/api/v3 +ICON_KEYSTORE_PATH=/Users/leclevietnam/mwork/btp/javascore/bmv/godWallet.json +ICON_KEYSTORE_PASSWORD=gochain +ICON_NID=66 +``` + +- Run script to deploy event decoder and BMV: + +``` +$ yarn deployParaBMV + +yarn run v1.22.10 +warning package.json: No license field +$ yarn ts-node src/deployParaBMV.ts +warning package.json: No license field +$ /Users/leclevietnam/mwork/btp/javascore/bmv/helper/node_modules/.bin/ts-node src/deployParaBMV.ts +2021-10-28 13:48:00 METADATA: Unknown types found, no types for AssetRegistrarMetadata, AssetType, AuthorId, BlockV0, Collator2, CollatorSnapshot, CurrencyId, ExitQ, InflationInfo, Nominator2, NominatorAdded, OrderedSet, ParachainBondConfig, RegistrationInfo, RelayChainAccountId, RewardInfo, RoundInfo, VestingBlockNumber + Relay genesis hash: 0xe1ea3ab1d46ba8f4898b6b4b9c54ffc05282d299f89e84bd0fd08067758c9443 + Relay chain name: "Moonbase Relay Testnet" + Para genesis hash: 0x91bc6e169807aaa54802737e1c504b2577d4fafedd5a02c10293b1cd60e39527 + Para chain name: "Moonbase Alpha" + Get metadata of relay chain... + Build event decoder for relay chain... + Deploy relay chain event decoder... +------ transactionId: 0x10a294d5bfd654bef930cd09ca42302376e3e6ae3220c4e7be4db71c82c01e4d + Get metadata of para chain... + Build event decoder for para chain... + Deploy para chain event decoder... +------ transactionId: 0x81743bd67cef1d6848911c4bf58db47f8b3e39232a646246e170a486ac315b3e + Build para chain BMV... + Deploy para chain BMV... +------ transactionId: 0x6122c412df795945fca8b638fd6588fb686273e6f8735da2d679eb39773fc690 + + --------------------- DONE ---------------------- +- Relay event decoder score address: cx2e0ba47b2df8fc78af75001e1eb06732ea71dfde +- Para event decoder score address: cx447ee0788b359e571a283f415a820b1977bf0713 +- Para chain bmv score address: cxb358e6a851909bd3215e1ee42cd79f0a676b65cc +``` + +#### Set Para BMV to BMC contract: + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method addVerifier --param _net=0x507.pra --param _addr=cxb358e6a851909bd3215e1ee42cd79f0a676b65cc --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xf52cce4260af2b92210ddaa9d8035e95d7a2e440ffc8f921fdb651fdf1f3f064 +``` + +#### Add parachain link to BMC: + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method addLink --param _link=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x58c83e52228eef9f0fe9b3059b0f04b8361d3c7c65a1aa4dadaf07a5ffb2671a +``` + +#### Config parachain link of BMC: + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method setLinkRotateTerm --param _link=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 --param _block_interval=12 --param _max_agg=10 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x1b7c9b73bf066bdaac269713a1084723f8407c0a0f3a87eae2a78a64b4516749 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method setLinkDelayLimit --param _link=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 --param _value=2 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x2cb56e3a91ecc43c4268fc1eab50859b09daefd2656b4c741ec4c11a6eece00e +``` + +#### add relay address to parachain link of BMC: + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method addRelay --param _link=btp://0x507.pra/0x3e525eD7a82B87bE30cdADE89d32204cA0F1C356 --param _addr=hx2dbd4f999f0e6b3c017c029d569dd86950c23104 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xdabca08cf388c374bf61e05dd32a2d6bde0f37e2d1225a447a144662ecc73b13 +``` + +#### Deploy IRC31 token contract: + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy javascore/javaee-tokens/build/libs/irc31-0.1.0-optimized.jar --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 --content_type application/java + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xbfb1c886274b972534d9cccd3e5b07f5ab6258428400d5de4e5b39232fa00236 +``` + +#### Deploy BSH + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/nativecoin/build/libs/nativecoin-0.1.0-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 0x42 --step_limit 3519157719 \ + --content_type application/java \ + --param _irc31=cxc1e92e175e1e5f98edf62b192ae051caae994a97 \ + --param _bmc=cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 \ + --param _name=ICX + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xdd53327a0f5b5e2b433c49ec43d8c9f45b54295de81b3cd74db99be75257810c +``` + +#### Deploy IRC2 token Jar + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/nativecoin/build/libs/irc2-0.1.0-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 0x42 --step_limit 3519157719 \ + --content_type application/java \ + --param _name=MOVR \ + --param _symbol=MOVR \ + --param _initialSupply=0x186A0 \ + --param _decimals=0x12 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xdd53327a0f5b5e2b433c49ec43d8c9f45b54295de81b3cd74db99be75257810c +``` + + +#### Deploy IRC2_BSH + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx deploy btp/javascore/nativecoin/build/libs/nativecoinIRC2-0.1.0-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 0x42 --step_limit 3519157719 \ + --content_type application/java \ + --param _bmc=cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 \ + --param _irc2=cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 \ + --param _name=ICX \ + --param _tokenName=MOVR + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xdd53327a0f5b5e2b433c49ec43d8c9f45b54295de81b3cd74db99be75257810c +``` + +#### Register coin names to BSH + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --method register --param _name=DEV --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xa6cacdc4a8783f62dd981999f3ab7c08340618c8f01e8f5c84369e15c72831d9 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --method register --param _name=BTC --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x41845171790b40df01dd2838e39569020ff24498034ac5c54eaeb28e33488d39 +``` + +#### Set BSH fee ratio + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --method setFeeRatio --param _feeNumerator=1000 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xddfb4763b746b44b8ec4540824425dc2b6dcf760b4630f5f3a2611e4de82690b +``` + +#### Set IRC2_BSH fee ratio + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --method setFeeRatio --param _feeNumerator=100 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0xddfb4763b746b44b8ec4540824425dc2b6dcf760b4630f5f3a2611e4de82690b +``` + +#### Add BSH service to BMC + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method addService --param _svc=nativecoin --param _addr=cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x9ba88bbb9907cf1fe29ff2b50706c97d6fd08d09ea6394138444c8d34b40dd3d +``` + +#### Add IRC2_BSH service to BMC + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx11a5a7510b128e0ab16546e1493e38b2d7e299c3 --method addService --param _svc=NativeCoinIRC2BSH --param _addr=cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x9ba88bbb9907cf1fe29ff2b50706c97d6fd08d09ea6394138444c8d34b40dd3d +``` + +#### Add BSH address to owner of IRC31 contract + +```bash +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cxc1e92e175e1e5f98edf62b192ae051caae994a97 --method addOwner \ + --key_store godWallet.json --key_password gochain \ + --nid 0x42 --step_limit 3519157719 \ + --param _addr=cx047d8cd08015a75deab90ef5f9e0f6878d5563bd + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x3f2ec91804204a9e3e3faa5f40b4674b72b217257eb389b4ccc13c703ef5f5f7 +``` + +### Transfer ICX to DEV + +```bash +PRIVATE_KEYS="${YOUR_USER_PRIVATE_KEY}" \ +truffle console --network moonbase --working_directory $SOLIDITY_DIST_DIR/bsh + +> Warning: possible unsupported (undocumented in help) command line option: --working_directory +truffle(moonbase)> let bshCore = await BSHCore.deployed() +undefined +truffle(moonbase)> await bshCore.setApprovalForAll(bshCore.address, true, {from: accounts[0]}) +{ + tx: '0xf8f96cefdfc83ef096ddd62e601c7c845733b09757731c1875fdb4715ef05b08', + receipt: { + blockHash: '0x814fea05ae02c7d8cfcefe8028ad7780d64a5cf05f7d6046bcc7cfd5e7001151', + blockNumber: 1047774, + contractAddress: null, + cumulativeGasUsed: 48353, + from: '0xd07d078373be60dd10e35f352559ef1f25029daf', + gasUsed: 48353, + logs: [ [Object] ], + logsBloom: '0x00000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000400000000000000000000000000010000000100000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000025000000000000000000200800000000000000000000000000000000000000000000000000000000', + status: true, + to: '0x2a17b6814a172419a5e84d7b746abeb95a84e76b', + transactionHash: '0xf8f96cefdfc83ef096ddd62e601c7c845733b09757731c1875fdb4715ef05b08', + transactionIndex: 0, + rawLogs: [ [Object] ] + }, + logs: [ + { + address: '0x2a17B6814a172419a5E84d7B746aBEb95a84E76B', + blockHash: '0x814fea05ae02c7d8cfcefe8028ad7780d64a5cf05f7d6046bcc7cfd5e7001151', + blockNumber: 1047774, + logIndex: 0, + removed: false, + transactionHash: '0xf8f96cefdfc83ef096ddd62e601c7c845733b09757731c1875fdb4715ef05b08', + transactionIndex: 0, + transactionLogIndex: '0x0', + id: 'log_38adde4c', + event: 'ApprovalForAll', + args: [Result] + } + ] +} +> truffle(moonbase)> await bshCore.transferNativeCoin("btp://0x42.icon/hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", {value: 1000000000000000000}) +truffle(moonbase)> { + tx: '0xa61ae9e879d1b5e559e24032f4b9a7adc818ec5698e3bca754ca19da246c73e8', + receipt: { + blockHash: '0xdb7e464bdc03cce547232956cc46395115269acd104226b3a5987fd7dce63ce6', + blockNumber: 1047779, + contractAddress: null, + cumulativeGasUsed: 528972, + from: '0xd07d078373be60dd10e35f352559ef1f25029daf', + gasUsed: 528972, + logs: [], + logsBloom: '0x00000002000000400000010000000000000000000400000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000040000000000400000000000000000000000000000000000100000000000000000040000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000200000000000000000000000000020000000000000000000000000000000000200000000000000000000008000000000000000000000000000000000000000', + status: true, + to: '0x2a17b6814a172419a5e84d7b746abeb95a84e76b', + transactionHash: '0xa61ae9e879d1b5e559e24032f4b9a7adc818ec5698e3bca754ca19da246c73e8', + transactionIndex: 0, + rawLogs: [ [Object], [Object] ] + }, + logs: [] +} + +``` +### Transfer ICX to Moonbeam Parachain + +``` +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ sendtx call --to cx047d8cd08015a75deab90ef5f9e0f6878d5563bd --method transferNativeCoin --param _to=btp://0x507.pra/0xD07d078373bE60dd10e35f352559ef1f25029DAf --value 1000000000000000000 --key_store godWallet.json --key_password gochain --nid 0x42 --step_limit 3519157719 + +goloop rpc --uri https://btp.net.solidwallet.io/api/v3/ txresult 0x815271f1c27ebefa1936c282a998571ee5810221123058b37ab453435d9d92f0 +``` \ No newline at end of file diff --git a/doc/icon.md b/doc/icon.md index 4de66087..d159de5e 100644 --- a/doc/icon.md +++ b/doc/icon.md @@ -19,7 +19,7 @@ |:--------------------|:--------------|:-------------------------------------------------------------------------------------------------------------------------------------------------| | BlockHeader | Bytes | fips-sha3-256(BlockHeader) → *BlockID*
BlockHeader.Result.ReceiptRootHash → *ReceiptRootHash*
BlockHeader.Height → *BlockHeight*
| | Votes | Bytes | [Prove with Votes](#prove-with-votes) | -| NextVaidators | Bytes | Validators = decode(NextValidators).ToAddress() | +| NextValidators | Bytes | Validators = decode(NextValidators).ToAddress() | | *BlockHeight* | Integer | Check BlockHeight == LastHeight | | *BlockID* | Bytes | [Prove with MTA](#prove-with-mta) | | BlockProof | List of Bytes | [Prove with MTA](#prove-with-mta) | diff --git a/doc/img/bmr_overall_architecture.drawio.svg b/doc/img/bmr_overall_architecture.drawio.svg new file mode 100644 index 00000000..131a3595 --- /dev/null +++ b/doc/img/bmr_overall_architecture.drawio.svg @@ -0,0 +1,4 @@ + + + +
add new a Relay Message
read receiverRequence
add new a Relay Message...
build Merkle Tree Accumulator
build Merkle Tree Accumulator
ReceiveLoop
ReceiveLoop
update dest BMCStatusLink
update Relay Messages
update dest BMCStatusLink...
Monitor Loop
Monitor Loop
conditionally build,
send Relay Messages
and process result
conditionally build,...
build BlockProof from Merkle Tree Accumulator
build BlockProof from Merkle Tree Accumulator
Relay Loop
Relay Loop
Embedded Database
Embedded Database
store Merkle Tree Accumulator
store Merkle Tree Accumulator
Ram contains
- buffered Relay Messages
- destination 
BMCStatusLink
- receiveSequence

Ram contains...
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/doc/img/pra_get_spec.png b/doc/img/pra_get_spec.png new file mode 100644 index 00000000..9117d8b3 Binary files /dev/null and b/doc/img/pra_get_spec.png differ diff --git a/doc/keystore.md b/doc/keystore.md new file mode 100644 index 00000000..bb40c18f --- /dev/null +++ b/doc/keystore.md @@ -0,0 +1,46 @@ +# Create and manage keystore guild + +## Platform preparation + +### ICON + +* goloop cli + ``` + go get github.com/icon-project/goloop/cmd/goloop + ``` + +* create new store, more [details](https://github.com/icon-project/goloop/blob/master/doc/goloop_cli.md#goloop-ks) + ``` + goloop ks gen --out keystore.json --password $YOUR_PASSWORD + ``` +* import existing privite key + +### EVM + +* install ethkey cli + ``` + go get github.com/ethereum/go-ethereum/cmd/ethkey + ``` +* install jq v1.6 + + **Mac OSX** + ``` + brew install jq + ``` + +* create new store, more [details](https://github.com/ethereum/go-ethereum/tree/v1.10.5/cmd/ethkey) + ``` + ethkey generate keystore.json + Password: # typing $YOUR_PASSWORD + Repeat password: # reapeating typing $YOUR_PASSWORD + Address: # CLI will generate $YOUR_ACCOUNT_ADDRESS + + # coinType requires by BTP project + cat <<< $(jq '. += {"coinType":"evm"}' keystore.json) > keystore.json + ``` + +* import existing privite key + +### Password raw + +After creating your keystore, BTP requires users to enter and raw password to a file and provide in configuration. The format of file is `keystore.secret` diff --git a/doc/known_issues.md b/doc/known_issues.md new file mode 100644 index 00000000..ae9d6dc7 --- /dev/null +++ b/doc/known_issues.md @@ -0,0 +1,35 @@ +# Known issues + +1. Don't use multiple BMRs setup, when BMV height not up to date with the network. + + When testing on testnet of Moonbeam (Moonbase alpha) the limit of ICON blocks per transactions is 11 blocks. And to keep the BMV up to date with BTP testnet of ICON, we send a lot of transactions. The problem is other BMR don't know when the other BMR transaction confirms and they use the old state of BMV MTA height to build and send transactions, which cause BMVRevertInvalidBlockUpdateLower. So, we recommend on use multiple BMRs setup with up-to-date BMV height, to limit the error rate + +2. The transaction sending to EVM might be failed if there are a lot of BTP messages in one ICON block because the limit of gas + + Because some of RelayMessages can be quite hungry for EVM gas consumption, an [example](https://moonbase-blockscout.testnet.moonbeam.network/tx/0x60a5c624ddde0bbed802c38914491c94e645fa67c4333f651f1db0013bb7825d/internal-transactions) of it reaches limit of gas + +3. The missed BTP message couldn't recover in case the missed BTP msg in the block which already behind the block MTA offset of MTA + + Because BMV can verify BlockHeader in the range of BMV MTA offset and BMV MTA height. The solution is BMV must be redeployed + +4. User must edit offset in config when BMV MTA offset changes when restart + + Because BMR currently get the offset from config in the initialized phase of BMR. This could be fixed to flush all data and get offset directly on BMV MTA offset + +5. Local network setup in docker-compose/goloop2moonbeam is not the same as public networks + + Because the setup of relaychain and parachain locally of moonbeam requires complex setup. [Work in progress](https://github.com/icon-project/btp/pull/89) + +6. BMR `common/jsonrpc/client.go Client` not recover after receive HTTP error + + It is recommend to run BMR at recovery mode with script like + + ```shell + #!/bin/bash + while ! bin/btpsimple start --config /home/ubuntu/btp/.config/icon2pra.config.json # or pra2icon.config.json + do + sleep 0.1 + echo "Restarting program..." + done + ``` + \ No newline at end of file diff --git a/doc/polkadot_parachain_with_frontier.md b/doc/polkadot_parachain_with_frontier.md new file mode 100644 index 00000000..6406230f --- /dev/null +++ b/doc/polkadot_parachain_with_frontier.md @@ -0,0 +1,492 @@ +# BTP Message Verifier for Moonbeam parchain (BMV) + +## Introduction + +BTP Message Verifier verify and decode Relay Message to +[BTP Message](btp.md#btp-message)s. +Relay Message is composed of both BTP Messages and with proof of +existence of BTP Messages. + +## Polkadot +### Relay chain + +The Relay Chain is the central chain of Polkadot. All validators of Polkadot are staked on the Relay Chain in chain's token and validate for the Relay Chain. The Relay Chain is composed of a relatively small number of transaction types that include ways to interact with the governance mechanism, parachain auctions, and participating in NPoS. The Relay Chain has deliberately minimal functionality - for instance, smart contracts are not supported. The main responsibility is to coordinate the system as a whole, including parachains. Other specific work is delegated to the parachains, which have different implementations and features. + +### Parachain + +Polkadot can support a number of execution slots. These slots are like cores on a computer's processor (a modern laptop's processor may have eight cores, for example). Each one of these cores can run one process at a time. Polkadot allows these slots using two subscription models: parachains and parathreads. Parachains have a dedicated slot (core) for their chain and are like a process that runs constantly. Parathreads share slots amongst a group, and are thus more like processes that need to be woken up and run less frequently. + +Most of the computation that happens across the Polkadot network as a whole will be delegated to specific parachain or parathread implementations that handle various use cases. Polkadot places no constraints over what parachains can do besides that they must be able to generate a proof that can be validated by the validators assigned to the parachain. This proof verifies the state transition of the parachain. Some parachains may be specific to a particular application, others may focus on specific features like smart contracts, privacy, or scalability — still, others might be experimental architectures that are not necessarily blockchain in nature. + +Polkadot provides many ways to secure a slot for a parachain slot for a particular length of time. Parathreads are part of a pool that shares slots and must-win auctions for individual blocks. Parathreads and parachains have the same API; their difference is economic. Parachains will have to reserve DOT for the duration of their slot lease; parathreads will pay on a per-block basis. Parathreads can become parachains, and vice-versa. + +### Consensus + +Relay chain use GRANDPA consensus algorithm to finalized block. Simply it is Proof of Work, validators and nominators stake chain token, KSM on Kusama or DOT on Polakdot. The ones have most stake and vote by nominators will become validators of relay chain. These participants play a crucial role in produce new blocks and finalize block in the Relay Chain and validate parachain block. + +Validators perform two functions: + +- Verifying that the information contained in an assigned set of parachain blocks is valid (such as the identities of the transacting parties and the subject matter of the contract). Each block, list of random validators, about five validators in Kusama, will be assigned to validate parachain block proof and include parachain block to relay chain block. In order to support the goal of 100 parachains, it require to a lot number of validators compare to other chain, currently 900 validators in Kusama relay chain. + +- Participating in the consensus mechanism to produce the Relay Chain blocks based on validity statements from other validators. As soon as more than 2/3 of validators attest to a chain containing a certain block, all blocks leading up to that one are finalized at once. A notable distinction is that GRANDPA reaches agreements on chains rather than blocks, greatly speeding up the finalization process, even after long-term network partitioning or other networking failures. It means that we can not find finalized message, Polkadot call it `Justification` in every finalized block. + +Parachain has their own block production mechanism, It call [BABE](https://docs.substrate.io/v3/advanced/consensus#babe) or [AURA](https://docs.substrate.io/v3/advanced/consensus#aura), block producers in parachain called collator. But there are no finalization process, blocks are finalized for the parachains when they are finalized by Polkadot's Relay Chain, the main chain of the system. So that in order to verify that parachain's blocks are finalized, we need to prove that their corresponding blocks are finalized on relay chain. + +Because of that reason, Relay chain data are included in relay message to prove that parachain block has been finalized. + +Reference: + +- [GRANDPA](https://github.com/w3f/consensus/blob/master/pdf/grandpa.pdf) +- [Security of the network](https://wiki.polkadot.network/docs/learn-security) +- [Polkadot Consensus](https://wiki.polkadot.network/docs/learn-consensus) +- [The Path of a Parachain Block](https://polkadot.network/blog/the-path-of-a-parachain-block/) + +## Data structure + +Both parachain and relay chain are built base on Substrate. Most of data structures of their chain are the same, such as block header, state merkle partricia trie, some of common event. Substrate using [SCALE](https://docs.substrate.io/v3/advanced/scale-codec) codec to encode data instead of RLP in Ethereum and ICON. +### Subtrate block header + +| Name | Type | Description | +|:----------------|:----------------|:------------| +| parent_hash | Hash | hash of previous block | +| number | integer | block number | +| state_root | Hash | State MPT root | +| extrinsics_root | Hash | extrinsics MPT root | +| digest | block digest | Digest, contains consensus message | + +How to calculate block hash: + +- SCALE encoded block header +- Hash encoded data above by blake2b-256 algorithm + +In Moonbeam parachain to ICON flow, relay get SCALE encoded block header of Moonbeam parachain and Kusama relay chain then included it in relay message. Para BMV decoded and calculate block hash in [here](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/blockHeader/BlockHeader.java) + +### Merkle patricia trie + +Substrate modified Merkle Patricia Trie compare to the one that use in Ethereum and ICON. The following is what they modified: + +- Remove extension node +- Use xxHash and Blake2 to create key +- Use SCALE encoding instead of RLP + +Substrate use Merkle Patricia Trie to store state data of chain. State data includes block number, block hash, event, validators set, state data of smart contract... We can get meta data of chain to know what data the chain store in state storage. + +With state_root in header and state proof from relay, BMV can verify storage to be valid or not. Pra-BMV implement code to verify MPT storage in [mpt](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt) + +Reference: + +- [Substrate Storage Deep Dive](https://www.shawntabrizi.com/substrate/substrate-storage-deep-dive/) +- [Querying Substrate Storage via RPC](https://www.shawntabrizi.com/substrate/querying-substrate-storage-via-rpc/) + +### Justification + +For GRANDPA, Justification is list signatures of validator and ancestry headers of all votes in the commit (so that we can verify the signatures). + +| Name | Type | Description | +|:------------------|:----------------|:------------| +| round | u64 | current vote round | +| commit | Commit | vote commit | +| votes_ancestries | Vec
| ancestry headers | + + +- Commit type: + +| Name | Type | Description | +|:------------------|:----------------|:------------| +| targetHash | 256 bytes hash | hash of finalized block | +| targetNumber | Block number | number of finalized block | +| precommits | Vector SignedPrecommit | List of Signed Precommit | + +- SignedPrecommit type: + +| Name | Type | Description | +|:------------------|:----------------|:------------| +| precommit | Precommit | infomation of signed block | +| signature | Signature | signature of validator | +| id | Pubic key | public key of validator | + +- Precommit type: + +| Name | Type | Description | +|:------------------|:-----------------|:-------------------| +| targetHash | 256 bytes hash | block hash that validator vote for | +| targetNumber | U32 | block number that validator vote for | + +Because GRANDPA votes on chains rather than blocks, we can not find Justification for every block. Substrate only store Justification for two cases: + +- Latest finalized block. As soon as block n received enough 2/3 validator signatures, node store its Justification and use it to prove any block x < n to be finalized by api [proveFinality](https://polkadot.js.org/docs/substrate/rpc#provefinalitybegin-blockhash-end-blockhash-authoritiessetid-u64-optionencodedfinalityproofs). But then block m > n finalized, Justification of block n is remove and replace by Justification of block m. Substrate provide api [subscribeJustifications](https://polkadot.js.org/docs/substrate/rpc#subscribejustifications-justificationnotification) to get latest Justification of chain. + +- Block has event to change validators list. + +Message that validator sign to approve block to be finalized that is SCALE encoded of: + +| Name | Type | Description | +|:------------------|:-----------------|:-------------------| +| message | Precommit | infomation of signed block | +| round | U64 | current vote round | +| setId | U64 | validator set id | + +Each validator set has it own set id number, starting from 0, when validator set change it increase by 1 and so on. + +Relay get signatures of validator from Justification, encoded it to relay message. Moonbeam BMV verify block that has been finalized by verify relay signature, ensure enough 2/3+ 1 validator sign for block. + +Reference: + +- [Getting finalised block data](https://stackoverflow.com/questions/63064040/getting-finalised-block-data) +- [grandpa: always store justification for the latest finalized block ](https://github.com/paritytech/substrate/pull/8392) +- [Polkadot js](https://polkadot.js.org/docs/substrate/rpc#grandpa) + +### Event + +List of events with different type and structure are SCALE encoded and store in one storage of MPT. In order to decode event storage, we must start from begining of encoded bytes, get event index, determine what data contains in event data and decoded it, one by one until the end of encoded bytes. In other words, we must know structure of all events in each chains. + +EventRecord structure: + +| Name | Type | Description | +|:------------------|:-----------------|:-------------------| +| phase | Phase | event phase | +| event | Event | Event infomation | +| topics | Vec | list of event topics | + +Event structure: + +| Name | Type | Description | +|:------------------|:-----------------|:-------------------| +| index | U16 | event index | +| data | Struct | Data of event | + +For reducing code size of BMV contract, we implement separate contract to decode event. Contract only determine size of event data and decode it without specific data fields inside. We defined all type size of Moonbeam, Kusama and Edgeware event at [here](../javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/SizeDecoder.java) + +Because each chain define their own event type and index, we can not implemented one contract to decode events of all substrate chains. Instead we create script to generate code of event decoder base on chain's metadata that implemented [build.gradle](../javascore/bmv/eventDecoder/build.gradle). + +For more information how to build event decoder, please refer to [here](../javascore/bmv/Readme.md#buildEventDecoder) + +BMV call `decodeEvent` method of event decoder to decode event. Method return list of eventRecord without specific event data. From para BMV contract, we only decode specific data of event that BMV need to verify and ignore others. There are three events para BMV need to decode and get specific data inside: + +1. CandidateIncludeEvent, event of relay chain notify that one block of parachain has been accepted to relay chain + +| Name | Type | Description | +|:------------------|:---------------------|:---------------------------| +| candidateReceipt | CandidateReceipt | Receipt of prachain block | +| headData | HeadData | Head data of parachain | +| coreIndex | u16 | | +| groupIndex | u16 | | + +- Candidate receipt + +| Name | Type | Description | +|:------------------|:---------------------|:---------------------------| +| descriptor | CandidateDescriptor | The descriptor of the candidate block | +| commitments_hash | Hash | The hash of the encoded commitments made as a result of candidate execution | + +- CandidateDescriptor: + +| Name | Type | Description | +|:-------------------------------|:------------|:---------------------------| +| para_id | U32 | The ID of the para this is a candidate for | +| relay_parent | Hash | The hash of the relay-chain block this is executed in the context of | +| collator | Public key | The collator's sr25519 public key | +| persisted_validation_data_hash | Hash | The blake2-256 hash of the persisted validation data. These are extra parameters derived from relay-chain state that influence the validity of the block which must also be kept available for secondary checkers | +| pov_hash | Hash | The blake2-256 hash of the `pov-block` | +| erasure_root | Hash | The root of a block's erasure encoding Merkle tree | +| signature | Signature | Signature on blake2-256 of components of this receipt: The parachain index, the relay parent, the validation data hash, and the `pov_hash` | +| para_head | Hash | Hash of the para header that is being generated by this candidate | +| validation_code_hash | Hash | The blake2-256 hash of the validation code bytes. | + + +Code to decode this event implemented at [CandidateIncludedEvent.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/CandidateIncludedEvent.java) + +2. EVMLogEvent, event of parachain, it is log from EVM or Solidity smart contract. + +| Name | Type | Description | +|:------------------|:---------------------|:---------------------------| +| address | Address | 20 bytes address of contract | +| topics | Vec | List of topics of event | +| data | EVM event data | Parameter logs from solidity smart contract, encoded using solidity ABI | + +- Data of BTP message event + +| Name | Type | Description | +|:------------------|:------------------|:-----------------------------| +| seq | Integer | sequence number of message | +| next | String | btp address of receiver | +| msg | Bytes | BTP message | + +Code to decode this event implemented at [EVMLogEvent.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EVMLogEvent.java) + +3. NewAuthoritiesEvent, event of relay chain, notify that new validators list replace current validators list. + +| Name | Type | Description | +|:------------------|:------------------|:-----------------------------| +| authority_set | AuthorityList | new authority list | + +- Authority list: + +| Name | Type | Description | +|:------------------|:------------------|:-----------------------------| +| validator | Public Key | public key of address | +| weight | u64 | weight of validator, all validators has the same weight is 1 | + +Code to decode this event implemented at [NewAuthoritiesEvent.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/NewAuthoritiesEvent.java) + +Relay monitor that event and send to BMV, we will verify event data then update new validator list to db, increase validator setId by 1. Because validators included setId into vote message, relay must send NewAuthoritiesEvent at the last block of RelayBlockUpdates with validator's signatures of that block, before update any relay block higher. + +For example: + +- Given: + - current setId store in BMV: 10 + - current block number: 100 +- When: + - has newAuthorities in block 110 + - current setId increase to 101 at block 111 +- Then: + - If relay submit to BMV relay block from 100-130 and votes of block 130, BMV will throw error `verify signature for invalid validator set id` because block 130 validator sign for setId 11 but in BMV still store setId 10. + - In this case relay should send relay block from 100-110, votes of block 110 and newAuthorities in block 110, BMV will update new authorities list and setId to db and then next block signatures will be verify properly. + - As mentioned in Justification session, validators vote always stored in block that has change validators list, so that relay can get validtor's signature form that block + +There are the case that relay may submit the same `newAuthorities` event for many times, to prevent that case, we store last block of relay chain that update validator set in [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 46. Each time relay push newAuthorities event, BMV get that compare relay block number with last block of relay chain that update validator set, if it already updated, BMV ignore it, line 465-468. + +## Relay message structure + +### RelayMessage: + +| Name | Type | Description | +|:--------------------|:----------------|:-----------------------------| +| paraBlockUpdates | List of Bytes | list of RLP encoded of ParaBlockUpdate | +| blockProof | Bytes | RLP encoded of BlockProof | +| stateProof | List of Bytes | List of RLP encoded of StateProof | + +Code to decode RelayMessage implemented in [RelayMessage.java](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayMessage/RelayMessage.java) + +### ParaBlockUpdate: + +| Name | Type | Description | +|:--------------------|:----------------|:-------------------------------| +| paraBlockHeader | bytes | SCALE encoded of para chain block header | +| relayChainData | bytes | RLP encoded of RelayChainData | + +Code to decode ParaBlockUpdate implemented in [ParaBlockUpdate.java](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/ParaBlockUpdate.java) + +Relay chain data only need for last block in paraBlockUpdates, if it included in other blocks, it will be ignore. + +There is the case that data of both chains are large, and it not able to execute in one transaction. We allow relay to update relay block first, in this case paraBlockUpdates only contains one item and para block header is null, only contains relayChainData. At first, for more transparent, We propose to change separate relay chain data and para chain data, but it will break current interface of relay message, so we come back to edit current relay message structure like that. + +### BlockProof: + +| Name | Type | Description | +|:--------------------|:----------------|:-------------------------------| +| blockHeader | bytes | SCALE encoded of block header that want to prove by MTA | +| height | bytes | MTA height of relay use to generate witness | +| witness | bytes | list of hash of leaf point to prove that block include in Merkle tree accumulator, low level leaf first | + +Code to decode and verify BlockProof implemented in [BlockProof.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/blockProof/BlockProof.java) + +The cases that need BlockProof: + +1. Last block of paraBlockUpdates that not includes in last block of relayBlockUpdates. In this case, relay send blockProof of relay block that contains event include parachain block + +Example: + +- Given: + - relay block height in BMV 1000 + - para block height in BMV 500 +- When: + - last updating relay block height 1010 + - last updating para block height 505 + - para block 505 included in block 1008 of relay chain +- Then: + - relay submit to BMV: + - Relay block update from 1000-1010 + - Votes of block 1010 + - Relay BlockProof of block 1008 + - State proof of relay block 1008, contains CandidateIncluded event + - ParaBlockUpdates from 500-505 + +2. Last block of paraBlockUpdates that includes in block of relay chain that already updated in previous transaction, In this case, relay don't need to send relayBlockUpdates, relay only send blockProof of relay block that contains event include parachain block. + +Example: + +- Given: + - relay block height in BMV 1010 + - para block height in BMV 500 +- When: + - last updating para block height 505 + - para block 505 included in block 1008 of relay chain +- Then: + - relay submit to BMV: + - Relay BlockProof of block 1008 + - State proof of relay block 1008, contains CandidateIncluded event + - ParaBlockUpdates from 500-505 + +3. BTP message not includes in last block of paraBlockUpdates. In this case, relay send blockProof of para block that contains EVM log event. + +Example: + +- Given: + - para block height in BMV 500 +- When: + - last updating para block height 515 + - BTP message contains in block 510 of para +- Then: + - relay submit to BMV: + - `ParaBlockUpdates` from 500-515 + - Para BlockProof of block 510 + - State proof of para block 510, contains EVM log event of source BMC contract + +4. BTP message includes in parachain block that already updated in previous transaction. In this case, relay don't need to update paraBlockUpdates, relay only need to send blockProof of para block that contains EVM log event. + +Example: + +- Given: + - para block height in BMV 530 +- When: + - BTP message contains in block 510 of para +- Then: + - relay submit to BMV: + - Para BlockProof of block 510 + - State proof of para block 510, contains EVM log event of source BMC contract + +### StateProof: + +| Name | Type | Description | +|:--------------------|:----------------|:-------------------------------| +| key | bytes | Key of MPT storage | +| proofs | List of bytes | Proof to prove MPT storage, get from api, https://polkadot.js.org/docs/substrate/rpc#getreadproofkeys-vecstoragekey-at-blockhash-readproof, data of MPT node (branch node, leaf node) to prove that storage | + +Code to decode and verify StateProof implemented in [StateProof.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/stateProof/StateProof.java) + +### RelayChainData: + +| Name | Type | Description | +|:--------------------|:----------------|:-----------------------------| +| relayBlockUpdates | List of Bytes | list of RLP encoded of RelayBlockUpdate | +| blockProof | Bytes | RLP encoded of BlockProof | +| stateProof | List of Bytes | List of RLP encoded of StateProof | + +Code to decode RelayChainData implemented in [RelayChainData.java](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayChainData/RelayChainData.java) + +As mentioned in polkadot consensus session, in order to verify that parachain's blocks are finalized, we need to prove that their corresponding blocks are finalized on relay chain. That's why we need relay to subbmit relay chain data here. + +### RelayBlockUpdate: + +| Name | Type | Description | +|:--------------------|:----------------|:-----------------------------| +| blockHeader | Bytes | SCALE encoded of relay block header | +| votes | Bytes | RLP encode of Votes, signatures of validators with fields that included in vote | + +**note** votes only require for last block of RelayBlockUpdates list, if it exist in other blocks, it will be ignore + +Code to decode RelayBlockUpdate implemented in [RelayBlockUpdate.java](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/RelayBlockUpdate.java) + +### Votes: + +| Name | Type | Description | +|:--------------------|:---------------------|:-----------------------------| +| voteMessage | Bytes | SCALE codec of Vote Message | +| signatures | ValidatorSignature | list RLP of ValidatorSignature | + +**note** that Votes only contain in RelayBlockUpdate, ParaBlockUpdate has no vote, and it finalization depend on RelayChainData + +Relay send validator signature and vote message to BMV in RelayBlockUpdate, BMV do the following step to validate: + +1. Decode vote message, implemented in [Votes.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java) line 53-62 + +2. Validate vote message, targetHash, targetNumber, currentSetId equals to current validate block and setId. Code implemented in [Votes.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java) line 66-76 + +3. Verify each signature, check that signer is contain in current validator list and no signatures is duplicated. Code implemented in [Votes.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java) line 79-94. + +4. Check that number of valid signatures greater than 2/3 number of current validators + +5. Relay block finalized if all conditions above satisfy + +### ValidatorSignature: + +| Name | Type | Description | +|:--------------------|:----------------|:-----------------------------| +| signature | Bytes | Signature of validator for relay chain block | +| validator | Bytes | 32 byte public key of validator | + + +Code to decode and ValidatorSignature implemented in [ValidatorSignature.java](../javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/ValidatorSignature.java) + +## handleRelayMessage + +We will go through step to step from BMV receive relay message from relay, verify message and return BTP message to BMC. + +### decode Base64: + +- Code is implemented in [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java#L202) line 200-206 +- throw `DECODE_ERROR = 37;`, if can not decode + +### Decode RLP: + +- Decode RLP with structure as defined above + +- Code is implemented in [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java#L212) line 208-214 + +- throw `DECODE_ERROR = 37;`, if can not decode + +### Check that relay message has block update or block proof + +- If relay message missing both block update and block proof, it can not prove any data that valid in parachain. So we have nothing to do with that. + +- Code is implemented in [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java#L216) line 216 - 218 +- throw `BMV_ERROR = 25;`, message `invalid RelayMessage not exists BlockUpdate and BlockProof` + +### Verify parachain block data + +Code to verify parachain block data implementd in function `verifyParaChainBlock`. This function return `stateRoot` and `lastHeight` of expected block that contain btp message if exist. + +There are two cases that going to be happened here: + +1. Relay want to sync both relay chain block and parachain block in one relay message + +- Code is implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 403-433 +- Relay message contains list of parachain blocks +- BMV compare previous hash of para block n + 1 with hash of para block n, to make sure they are in the same chain. For the first block of list, we compare it with the last para block hash that store in db in previous transaction. +- Last block in list contains relay chain data to prove that list parachain block has been included in finalized chain of relay, +- BMV pass relay chain data to `verifyRelayChainBlock` function, this function verify relay chain block update and block proof of relay and return `stateRoot` and `lastHeight` of expected block contain event of relay chain if exist. Detail about what function do will be explain later. +- BMV pass BlockVerifyResult (`stateRoot` and `lastHeight`) to `verifyRelayChainState` function, this function use MPT to verify event storage return hash of parachain block that has been included in relay chain if it exists. Detail about what function do will be explain later. +- BMV compare block hash return from `verifyRelayChainState` function with last block hash of parachain block update, if it is the same parachain blocks prove to be finalized. + +2. In case data of both chain is large, and not able process it in one transaction. Relay just want to update relay chain block without parachain block. + +- Implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 395-401 +- Relay message contains para block updates with only one item. In this item, para block header null and only contains relay chain data. +- The same with follow 1, but we don't need to verify any parachain block in this case, just use two function `verifyRelayChainBlock` and `verifyRelayChainState` to verify relay chain data. + +After verify relay and para chain blocks are valid, BMV verify block proof of parachain chain if it exists. Code to verify BlockProof implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 437-442 + +### Verify relay chain data + +As mentioned above, there are two function to verify relay chain data `verifyRelayChainBlock` and `verifyRelayChainState`. + +1. verifyRelayChainBlock + +- Code is implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 348-387 +- First, verify relay chain blocks are on the same chain, we compare hash of block n + 1 with hash of block n of relay chain block updates. For the first block of list, we compare it with the last para block hash that store in db in previous transaction. +- To make sure that blocks has been finalized, we verify signatures of validator is valid and enough 2/3 + 1 validators signed. +- Verify block proof of relay chain if exist. +- return stateRoot and lastHeight + +2. verifyRelayChainState + +- Code is implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 453-490 +- First, we use MPT to verify state proof and get encoded storage. +- If storage is event, call relay event decoder contract to decode event. +- Loop through all events and find if there are any NewAuthorities or CandidateIncluded event. +- In case of NewAuthorities event, we update new validators list, inscrease validator set id, last update validator set block number to db +- In case of CandidateIncluded event, we check that it include block of target parachain by compare its parachain id with target parachain id in db. If it is our target parachain, return hash of included block. + +### Verify parachain state proof and return BTP message to BMC + +- Implemented at [BMV](../javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java) line 226-262 +- First, we use MPT to verify state proof and get encoded storage. +- If storage is event, call parachain event decoder contract to decode event +- Loop through all events and find if there are any EVM log event. +- If it contains EVM log event, BMV continue to check: + - It's BTP message event by compare evm topic + - Event was logged by BMC address of source chain + - Sequence number of event equal to current sequence of BMC + 1 + - Add BTP message to list if all conditions satisfy +- Return BTP message list to BMC \ No newline at end of file diff --git a/doc/tutorial.md b/doc/tutorial.md index 11d1560b..95e2ce1d 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -429,18 +429,25 @@ goloop rpc sendtx call --to $(cat token_bsh.dst) \ ## Docker-compose -Tutorial with [Docker-compose](https://docs.docker.com/compose/) +Tutorial with [Docker-compose](https://docs.docker.com/compose/). + +To demonstrate chain integration, we utilize docker-compose. The docker-compose files are kept in the [docker-compose](https://github.com/icon-project/btp/tree/master/docker-compose) folder's child folders. As the name suggests, each child folder has a docker-compose file that isolates an integration between two chains. + +- [goloop2goloop](https://github.com/icon-project/btp/tree/master/docker-compose/goloop2goloop): integration between 2 goloop chains. +- [goloop2moonbeam](https://github.com/icon-project/btp/tree/master/docker-compose/goloop2moonbeam): ingration betwwen goloop and moonbeam. ### Preparation -Prepare 'btpsimple' docker image via `make btpsimple-image` and Copy files from project source to `/path/to/tutorial` +Prepare 'btpsimple' docker image via `make btpsimple-image` and Copy files from project source to `/path/to/tutorial`. + ````shell make btpsimple-image mkdir -p /path/to/tutorial -cp docker-compose/* /path/to/tutorial/ +cp docker-compose/goloop2goloop/* /path/to/tutorial/ +cd /path/to/tutorial/ ```` ### Run chain and relay -`docker-compose up` will build `tutorial_goloop` docker image that provisioned of belows +`make run` will build `tutorial_goloop` docker image that provisioned of belows * scripts files in `/goloop/bin` * source chain and destination chain (channel name : `src`, `dst`) diff --git a/docker-compose/goloop2goloop/Makefile b/docker-compose/goloop2goloop/Makefile new file mode 100644 index 00000000..4c87be2d --- /dev/null +++ b/docker-compose/goloop2goloop/Makefile @@ -0,0 +1,12 @@ +build: + docker-compose build + +run: + docker-compose up + +clean: + docker-compose down -v --remove-orphans + rm -rf ./config ./data + +run-test-scenario: + docker-compose exec goloop sh /goloop/bin/scenario_test.sh \ No newline at end of file diff --git a/docker-compose/docker-compose.yml b/docker-compose/goloop2goloop/docker-compose.yml similarity index 75% rename from docker-compose/docker-compose.yml rename to docker-compose/goloop2goloop/docker-compose.yml index 3e33cbf5..3f70ce4e 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/goloop2goloop/docker-compose.yml @@ -1,24 +1,28 @@ version: "3.9" # optional since v1.27.0 services: goloop: + container_name: g2g_goloop build: - context: . + context: ./goloop args: BTPSIMPLE_VERSION: latest - GOLOOP_IMAGE: goloop:latest + GOLOOP_IMAGE: iconloop/goloop:0.9.5 + logging: + driver: none ports: - "9080:9080" volumes: - - config:/goloop/config - - data:/goloop/data + - ./config:/goloop/config + - ./data:/goloop/data environment: - GOLOOP_NODE_DIR=/goloop/data/goloop - GOLOOP_LOG_WRITER_FILENAME=/goloop/data/log/goloop.log btpsimple_src: + container_name: g2g_btp_src image: btpsimple:latest volumes: - - config:/btpsimple/config - - data:/btpsimple/data + - ./config:/btpsimple/config + - ./data:/btpsimple/data environment: - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_src - BTPSIMPLE_CONFIG=/btpsimple/config/src.config.json @@ -30,13 +34,15 @@ services: - BTPSIMPLE_KEY_STORE=/btpsimple/config/src.ks.json - BTPSIMPLE_KEY_SECRET=/btpsimple/config/src.secret - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/log/btpsimple_src.log - links: - - goloop + depends_on: + goloop: + condition: service_healthy btpsimple_dst: + container_name: g2g_btp_dst image: btpsimple:latest volumes: - - config:/btpsimple/config - - data:/btpsimple/data + - ./config:/btpsimple/config + - ./data:/btpsimple/data environment: - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_dst - BTPSIMPLE_CONFIG=/btpsimple/config/dst.config.json @@ -48,8 +54,6 @@ services: - BTPSIMPLE_KEY_STORE=/btpsimple/config/dst.ks.json - BTPSIMPLE_KEY_SECRET=/btpsimple/config/dst.secret - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/log/btpsimple_dst.log - links: - - goloop -volumes: - config: {} - data: {} \ No newline at end of file + depends_on: + goloop: + condition: service_healthy \ No newline at end of file diff --git a/docker-compose/Dockerfile b/docker-compose/goloop2goloop/goloop/Dockerfile similarity index 65% rename from docker-compose/Dockerfile rename to docker-compose/goloop2goloop/goloop/Dockerfile index 946c8cf9..ec0be557 100644 --- a/docker-compose/Dockerfile +++ b/docker-compose/goloop2goloop/goloop/Dockerfile @@ -1,10 +1,10 @@ ARG BTPSIMPLE_VERSION=latest -ARG GOLOOP_IMAGE=goloop:latest +ARG GOLOOP_IMAGE=iconloop/goloop-icon:latest FROM btpsimple:${BTPSIMPLE_VERSION} AS btpsimple FROM ${GOLOOP_IMAGE} -RUN apk add --no-cache jq +RUN apk add --no-cache jq curl ENV GOLOOP_PROVISION=/goloop/provisioning ENV GOLOOP_PROVISION_CONFIG=${GOLOOP_PROVISION}/config @@ -15,4 +15,6 @@ COPY ./*.sh /goloop/bin/ COPY ./entrypoint / RUN provision.sh -WORKDIR /goloop/config \ No newline at end of file +WORKDIR /goloop/config + +HEALTHCHECK --interval=3s --timeout=3s --start-period=5s --retries=10 CMD ["curl", "-f", "http://127.0.0.1:9080/metrics" ] diff --git a/docker-compose/btp.sh b/docker-compose/goloop2goloop/goloop/btp.sh similarity index 100% rename from docker-compose/btp.sh rename to docker-compose/goloop2goloop/goloop/btp.sh diff --git a/docker-compose/entrypoint b/docker-compose/goloop2goloop/goloop/entrypoint similarity index 100% rename from docker-compose/entrypoint rename to docker-compose/goloop2goloop/goloop/entrypoint diff --git a/docker-compose/keystore.sh b/docker-compose/goloop2goloop/goloop/keystore.sh similarity index 100% rename from docker-compose/keystore.sh rename to docker-compose/goloop2goloop/goloop/keystore.sh diff --git a/docker-compose/provision.sh b/docker-compose/goloop2goloop/goloop/provision.sh similarity index 84% rename from docker-compose/provision.sh rename to docker-compose/goloop2goloop/goloop/provision.sh index 6f47d389..cf0e88a0 100755 --- a/docker-compose/provision.sh +++ b/docker-compose/goloop2goloop/goloop/provision.sh @@ -18,7 +18,10 @@ GOLOOP_CONFIG=${GOLOOP_PROVISION_CONFIG}/server.json ensure_server_start goloop gn gen --out src.genesis.json $GOLOOP_KEY_STORE +echo $(cat src.genesis.json | jq -r '.*{"chain":{"fee":{"stepLimit":{"invoke":"0x12A05F200","query":"0x2faf080"}}}}') > src.genesis.json + goloop gn gen --out dst.genesis.json $GOLOOP_KEY_STORE +echo $(cat dst.genesis.json | jq -r '.*{"chain":{"fee":{"stepLimit":{"invoke":"0x12A05F200","query":"0x2faf080"}}}}') > dst.genesis.json goloop chain join --genesis_template src.genesis.json --channel src --auto_start goloop chain join --genesis_template dst.genesis.json --channel dst --auto_start @@ -69,4 +72,4 @@ bsh_register src bsh_register dst goloop chain stop src -goloop chain stop dst +goloop chain stop dst \ No newline at end of file diff --git a/docker-compose/rpc.sh b/docker-compose/goloop2goloop/goloop/rpc.sh similarity index 100% rename from docker-compose/rpc.sh rename to docker-compose/goloop2goloop/goloop/rpc.sh diff --git a/docker-compose/goloop2goloop/goloop/scenario_test.sh b/docker-compose/goloop2goloop/goloop/scenario_test.sh new file mode 100755 index 00000000..2f3e8e4e --- /dev/null +++ b/docker-compose/goloop2goloop/goloop/scenario_test.sh @@ -0,0 +1,91 @@ +cd ${GOLOOP_PROVISION_CONFIG} + +source btp.sh +source token.sh +source rpc.sh + +generate_keystores() { + echo ${GOLOOP_PROVISION_CONFIG} + if [ ! -f "${GOLOOP_PROVISION_CONFIG}/alice.secret" ];then + echo "Generating keystore for Alice" + + echo -n $(date|md5sum|head -c16) > alice.secret + goloop ks gen -o alice.ks.json -p $(cat alice.secret) + fi; + + if [ ! -f "${GOLOOP_PROVISION_CONFIG}/bob.secret" ];then + echo "Generating keystore for Bob" + + echo -n $(date|md5sum|head -c16) > bob.secret + goloop ks gen -o bob.ks.json -p $(cat bob.secret) + fi; +} + +mint_token_alice() { + echo "Minting 10 irc2_token for Alice" + + rpcch src + rpcks $GOLOOP_KEY_STORE $GOLOOP_KEY_SECRET + + TX=$(goloop rpc sendtx call --to $(cat irc2_token.src) \ + --method transfer \ + --param _to=$(jq -r .address alice.ks.json) \ + --param _value=10 | jq -r .) + echo $TX + ensure_txresult $TX + + echo "Alice's balance: $(goloop rpc call --to $(cat irc2_token.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json))" +} + +alice_deposit_tokens() { + echo "Alice desposits 10 irc2_token to BSH" + + rpcch src + rpcks alice.ks.json alice.secret + + tx=$(goloop rpc sendtx call --to $(cat irc2_token.src) \ + --method transfer \ + --param _to=$(cat token_bsh.src) \ + --param _value=10 | jq -r .) + ensure_txresult $tx + + echo "Alice's balance: $(goloop rpc call --to $(cat token_bsh.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json))" +} + +alice_transfer_tokens() { + echo "Alice transfer 10 irc2_token to Bob via BSH" + + rpcch src + rpcks alice.ks.json alice.secret + + tx=$(goloop rpc sendtx call --to $(cat token_bsh.src) \ + --method transfer \ + --param _tokenName=IRC2Token \ + --param _to=btp://$(cat net.btp.dst)/$(jq -r .address bob.ks.json) \ + --param _value=10 | jq -r .) + ensure_txresult $tx + + echo "Alice's balance: $(goloop rpc call --to $(cat token_bsh.src) --method balanceOf --param _owner=$(jq -r .address alice.ks.json))" +} + +bob_withdraw_tokens() { + echo "Bob withdraw 10 irc2_token from BSH" + + rpcch dst + rpcks bob.ks.json bob.secret + + tx=$(goloop rpc sendtx call --to $(cat token_bsh.dst) \ + --method reclaim \ + --param _tokenName=IRC2Token \ + --param _value=10 | jq -r .) + ensure_txresult $tx + + echo "Bob's balance: $(goloop rpc call --to $(cat irc2_token.dst) --method balanceOf --param _owner=$(jq -r .address bob.ks.json))" +} + + +generate_keystores +mint_token_alice +alice_deposit_tokens +alice_transfer_tokens +bob_withdraw_tokens \ No newline at end of file diff --git a/docker-compose/server.sh b/docker-compose/goloop2goloop/goloop/server.sh similarity index 99% rename from docker-compose/server.sh rename to docker-compose/goloop2goloop/goloop/server.sh index 1b1c6f36..2fb81d12 100755 --- a/docker-compose/server.sh +++ b/docker-compose/goloop2goloop/goloop/server.sh @@ -63,4 +63,4 @@ server_pid() { rm ${GOLOOP_PID} fi fi -} +} \ No newline at end of file diff --git a/docker-compose/token.sh b/docker-compose/goloop2goloop/goloop/token.sh similarity index 100% rename from docker-compose/token.sh rename to docker-compose/goloop2goloop/goloop/token.sh diff --git a/docker-compose/goloop2moonbeam/Dockerfile b/docker-compose/goloop2moonbeam/Dockerfile new file mode 100644 index 00000000..c7d09f57 --- /dev/null +++ b/docker-compose/goloop2moonbeam/Dockerfile @@ -0,0 +1,35 @@ +ARG GOLOOP_IMAGE=iconloop/goloop-icon:latest +FROM ${GOLOOP_IMAGE} as goloop +FROM canhlinh/truffle:5.3.0-alpine as truffle +FROM btpsimple:latest + +# install truffle cli +COPY --from=truffle /usr/local/node /usr/local/node +RUN apk add --no-cache libstdc++ git bash jq curl ca-certificates +RUN apk add --no-cache --virtual .build-deps +ENV PATH $PATH:/usr/local/node/bin +RUN node -v +RUN yarn --version +RUN truffle version + +# install goloop cli +COPY --from=goloop /goloop/bin /goloop/bin +ENV PATH $PATH:/goloop/bin +RUN goloop version + +# install eth cli +RUN npm install -g eth-cli +RUN eth -v + +ADD scripts /btpsimple/scripts +RUN chmod u+x /btpsimple/scripts/*.sh +ENV PATH $PATH:/btpsimple/scripts + +ADD entrypoint.sh /entrypoint.sh +RUN chmod u+x /entrypoint.sh + +ADD moonbeam/keystore /moonbeam/keystore +WORKDIR /btpsimple/config +ENV CONFIG_DIR /btpsimple/config + +ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/docker-compose/goloop2moonbeam/Makefile b/docker-compose/goloop2moonbeam/Makefile new file mode 100644 index 00000000..199be966 --- /dev/null +++ b/docker-compose/goloop2moonbeam/Makefile @@ -0,0 +1,36 @@ +clean: + docker-compose down -v --remove-orphans + rm -rf data + rm -rf config + +build: + docker-compose build + +run: + docker-compose up --remove-orphans + +transfer_icx: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_icx.sh + +transfer_dev: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_dev.sh + +transfer_movr_from_icon: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_movr_from_icon.sh + +transfer_movr_from_moonbeam: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_movr_from_moonbeam.sh + +transfer_icx_from_icon: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_icx_from_icon.sh + +transfer_icx_from_moonbeam: + docker-compose exec btp_icon sh /btpsimple/scripts/transfer_icx_from_moonbeam.sh + +# To run with gochain, please build gochain image from goloop source. +# Command line to build gochain: GL_TAG=0.9.9 make gochain-icon-image +build_with_gochain: + docker-compose -f docker-compose-gochain.yml build + +run_with_gochain: + docker-compose -f docker-compose-gochain.yml up --remove-orphans \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/README.MD b/docker-compose/goloop2moonbeam/README.MD new file mode 100644 index 00000000..4e10e70a --- /dev/null +++ b/docker-compose/goloop2moonbeam/README.MD @@ -0,0 +1,83 @@ +# Docker integration of BTP betwwen ICON and MOONBEAM + +This document will walk you through setting up a Docker environment to demonstrate how to run BTP between ICON and MOONBEAM. +(Tested on ubuntu 18.04+) + +This is for development usage only, because running Blockchain Nodes requires larger computer resources. + +## Prerequisites: +``` +- docker version 20.10.5+ +- docker-compose version 1.29.0+ +- make +- zip +- go version 1.16.3 +``` + +## Setup steps: + +For easy setup, we recommend to use a fresh ubuntu 21.04. Hence, run those commands to installe prerequire tools +``` +apt update +apt install git make zip golang-go docker.io docker-compose --fix-missing -y +sudo groupadd docker +sudo usermod -aG docker $USER +``` + +### Build image btpsimple:latest + +``` +cd ~/work +git clone git@github.com:icon-project/btp.git +cd ~/work/btp +git fetch +git checkout icondao +make btpsimple-image +``` + +### Run docker-compose + +``` +cd ~/work/btp/docker-compose/goloop2moonbeam +make build +make run +``` + +Now all that remains is to wait for the provisioning process to be completed. +The folder `~/work/btp/docker-compose/goloop2moonbeam/config` will contain all of the configuration files. +Note: If you want a fresh test, run `make clean` to remove the old test data. + +### Run test scenarios: + +Keep the docker-compose running and open an other terminal. +Run this command to transfer the native coin ICX from ICON to MOONBEAN. +``` +make transfer_icx +``` + +Run this command to transfer the native coin DEV from MOONBEAN to ICON: +``` +make transfer_dev +``` + +### Run test for scenarios ERC20 wrapped & Native token: + +Run this command to transfer the ERC20 Wrapped MOVR from ICON to MOONBEAM. +``` +make transfer_movr_from_icon +``` + +Run this command to transfer the native coin MOVR from MOONBEAM to ICON: +``` +make transfer_movr_from_moonbeam +``` + +Run this command to transfer the native coin ICX from ICON to MOONBEAM: +``` +make transfer_icx_from_icon +``` + +Run this command to transfer the ERC20 Wrapped from MOONBEAM to ICON: +``` +make transfer_icx_from_moonbeam +``` \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/btp.share.env b/docker-compose/goloop2moonbeam/btp.share.env new file mode 100644 index 00000000..53b0137e --- /dev/null +++ b/docker-compose/goloop2moonbeam/btp.share.env @@ -0,0 +1,5 @@ +GOLOOPCHAIN=goloop +RELAY_ENDPOINT=wss://kusama-rpc.polkadot.io +PARA_ENDPOINT=ws://moonbeam:9944 +MOONBEAM_RPC_URL=http://moonbeam:9933 +RELAY_CHAIN_OFFSET=8511058 diff --git a/docker-compose/goloop2moonbeam/docker-compose-gochain.yml b/docker-compose/goloop2moonbeam/docker-compose-gochain.yml new file mode 100644 index 00000000..b8571776 --- /dev/null +++ b/docker-compose/goloop2moonbeam/docker-compose-gochain.yml @@ -0,0 +1,95 @@ +version: "3.9" +x-common-args: &common-args + GOLOOP_IMAGE: iconloop/goloop-icon:v0.9.9 +services: + gochain: + container_name: gochain + build: + context: ./gochain + args: + - GOCHAIN_IMAGE=goloop/gochain-icon:0.9.9 + logging: + driver: none + volumes: + - ${PWD}/config:/gochain/config + - ${PWD}/data/gochain:/gochain/data + ports: + - 9080:9080 + moonbeam: + container_name: g2m_moonbeam + build: + context: ./moonbeam + args: + MOONBEAM_VERSION: v0.9.6 + logging: + driver: none + volumes: + - ${PWD}/data/moonbase_dev:/data/chains/moonbase_dev + ports: + - "9933:9933" + - "9944:9944" + command: ['--dev', '--ws-external', '--rpc-external', '--rpc-cors', 'all', '--base-path', '/data', '--ethapi', 'debug', '--sealing', '3000'] + btp_icon: + container_name: g2m_btp_icon + build: + context: . + args: *common-args + depends_on: + gochain: + condition: service_healthy + moonbeam: + condition: service_healthy + volumes: + - ./scripts:/btpsimple/scripts + - ${PWD}/config:/btpsimple/config + - ${PWD}/data/btpsimple_icon:/btpsimple/data + healthcheck: + test: ["CMD", "test", "-f", "/btpsimple/config/provision.started"] + interval: 3s + timeout: 3s + retries: 10 + environment: + - GOLOOPCHAIN=gochain + - RELAY_ENDPOINT=wss://kusama-rpc.polkadot.io + - PARA_ENDPOINT=ws://moonbeam:9944 + - MOONBEAM_RPC_URL=http://moonbeam:9933 + - RELAY_CHAIN_OFFSET=8511058 + - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_icon + - BTPSIMPLE_CONFIG=/btpsimple/config/config.icon.json + - BTPSIMPLE_SRC_ADDRESS=/btpsimple/config/btp.icon + - BTPSIMPLE_SRC_ENDPOINT=http://gochain:9080/api/v3/icon + - BTPSIMPLE_DST_ADDRESS=/btpsimple/config/btp.moonbeam + - BTPSIMPLE_DST_ENDPOINT=http://moonbeam:9933 + - BTPSIMPLE_OFFSET=/btpsimple/config/offset.icon + - BTPSIMPLE_KEY_STORE=/btpsimple/config/moonbeam.keystore.json + - BTPSIMPLE_KEY_SECRET=/btpsimple/config/moonbeam.keysecret + - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/btpsimple_icon/btp.icon.log + btp_moonbeam: + container_name: g2m_btp_moonbeam + build: + context: . + args: *common-args + depends_on: + btp_icon: + condition: service_healthy + volumes: + - ./scripts:/btpsimple/scripts + - ${PWD}/config:/btpsimple/config + - ${PWD}/data/btpsimple_moonbeam:/btpsimple/data + environment: + - GOLOOPCHAIN=gochain + - RELAY_ENDPOINT=wss://kusama-rpc.polkadot.io + - PARA_ENDPOINT=ws://moonbeam:9944 + - MOONBEAM_RPC_URL=http://moonbeam:9933 + - RELAY_CHAIN_OFFSET=8511058 + - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_moonbeam + - BTPSIMPLE_CONFIG=/btpsimple/config/config.moonbeam.json + - BTPSIMPLE_SRC_ADDRESS=/btpsimple/config/btp.moonbeam + - BTPSIMPLE_SRC_ENDPOINT=http://moonbeam:9933 + - BTPSIMPLE_DST_ADDRESS=/btpsimple/config/btp.icon + - BTPSIMPLE_DST_ENDPOINT=http://gochain:9080/api/v3/icon + - BTPSIMPLE_DST_OPTIONS="stepLimit=5000000000" + - BTPSIMPLE_OFFSET=/btpsimple/config/offset.moonbeam_parachain + - BTPSIMPLE_KEY_STORE=/btpsimple/config/gochain.keystore.json + - BTPSIMPLE_KEY_SECRET=/btpsimple/config/gochain.keysecret + - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/btpsimple_moonbeam/btpsimple_moonbeam.log \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/docker-compose.yml b/docker-compose/goloop2moonbeam/docker-compose.yml new file mode 100644 index 00000000..9e617bc2 --- /dev/null +++ b/docker-compose/goloop2moonbeam/docker-compose.yml @@ -0,0 +1,88 @@ +version: "3.9" +x-common-args: &common-args + GOLOOP_IMAGE: iconloop/goloop-icon:v0.9.9 +services: + goloop: + container_name: g2m_goloop + env_file: ./btp.share.env + build: + context: ./goloop + args: *common-args + logging: + driver: none + volumes: + - ${PWD}/config:/goloop/config + - ${PWD}/data/goloop:/goloop/data + ports: + - 9080:9080 + moonbeam: + container_name: g2m_moonbeam + build: + context: ./moonbeam + args: + MOONBEAM_VERSION: v0.13.2-upgrade-hotfix + logging: + driver: none + volumes: + - ${PWD}/data/moonbase_dev:/data/chains/moonbase_dev + - ${PWD}/data/moonbase_dev/wasm:/data/moonbase_dev/wasm + ports: + - "9933:9933" + - "9944:9944" + command: ['--dev', '--ws-external', '--rpc-external', '--rpc-cors', 'all', '--base-path', '/data', '--ethapi', 'debug', '--wasm-runtime-overrides', '/data/moonbase_dev/wasm', '--sealing', '3000'] + btp_icon: + container_name: g2m_btp_icon + build: + context: . + args: *common-args + depends_on: + goloop: + condition: service_healthy + moonbeam: + condition: service_healthy + volumes: + - ./scripts:/btpsimple/scripts + - ${PWD}/config:/btpsimple/config + - ${PWD}/data/btpsimple_icon:/btpsimple/data + env_file: ./btp.share.env + healthcheck: + test: ["CMD", "test", "-f", "/btpsimple/config/provision.started"] + interval: 3s + timeout: 3s + retries: 10 + environment: + - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_icon + - BTPSIMPLE_CONFIG=/btpsimple/config/config.icon.json + - BTPSIMPLE_SRC_ADDRESS=/btpsimple/config/btp.icon + - BTPSIMPLE_SRC_ENDPOINT=http://goloop:9080/api/v3/icon + - BTPSIMPLE_DST_ADDRESS=/btpsimple/config/btp.moonbeam + - BTPSIMPLE_DST_ENDPOINT=http://moonbeam:9933 + - BTPSIMPLE_OFFSET=/btpsimple/config/offset.icon + - BTPSIMPLE_KEY_STORE=/btpsimple/config/moonbeam.keystore.json + - BTPSIMPLE_KEY_SECRET=/btpsimple/config/moonbeam.keysecret + - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/btpsimple_icon/btp.icon.log + btp_moonbeam: + container_name: g2m_btp_moonbeam + build: + context: . + args: *common-args + depends_on: + btp_icon: + condition: service_healthy + volumes: + - ./scripts:/btpsimple/scripts + - ${PWD}/config:/btpsimple/config + - ${PWD}/data/btpsimple_moonbeam:/btpsimple/data + env_file: ./btp.share.env + environment: + - BTPSIMPLE_BASE_DIR=/btpsimple/data/btpsimple_moonbeam + - BTPSIMPLE_CONFIG=/btpsimple/config/config.moonbeam.json + - BTPSIMPLE_SRC_ADDRESS=/btpsimple/config/btp.moonbeam + - BTPSIMPLE_SRC_ENDPOINT=http://moonbeam:9933 + - BTPSIMPLE_DST_ADDRESS=/btpsimple/config/btp.icon + - BTPSIMPLE_DST_ENDPOINT=http://goloop:9080/api/v3/icon + - BTPSIMPLE_DST_OPTIONS="stepLimit=5000000000" + - BTPSIMPLE_OFFSET=/btpsimple/config/offset.moonbeam_parachain + - BTPSIMPLE_KEY_STORE=/btpsimple/config/goloop.keystore.json + - BTPSIMPLE_KEY_SECRET=/btpsimple/config/goloop.keysecret + - BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/btpsimple_moonbeam/btpsimple_moonbeam.log \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/entrypoint.sh b/docker-compose/goloop2moonbeam/entrypoint.sh new file mode 100644 index 00000000..8f7a9fba --- /dev/null +++ b/docker-compose/goloop2moonbeam/entrypoint.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +cp -u /moonbeam/keystore/* /btpsimple/config/ +source provision.sh + +if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) +fi + +if [ "$BTPSIMPLE_CONFIG" != "" ] && [ ! -f "$BTPSIMPLE_CONFIG" ]; then + UNSET="BTPSIMPLE_CONFIG" + CMD="btpsimple save $BTPSIMPLE_CONFIG" + if [ "$BTPSIMPLE_KEY_SECRET" != "" ] && [ ! -f "$BTPSIMPLE_KEY_SECRET" ]; then + mkdir -p $(dirname $BTPSIMPLE_KEY_SECRET) + echo -n $(date|md5sum|head -c16) > $BTPSIMPLE_KEY_SECRET + fi + if [ "$BTPSIMPLE_KEY_STORE" != "" ] && [ ! -f "$BTPSIMPLE_KEY_STORE" ]; then + UNSET="$UNSET BTPSIMPLE_KEY_STORE" + CMD="$CMD --save_key_store=$BTPSIMPLE_KEY_STORE" + fi + if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) + fi + + if [ "$BTPSIMPLE_SRC_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_SRC_ADDRESS" ]; then + export BTPSIMPLE_SRC_ADDRESS=$(cat ${BTPSIMPLE_SRC_ADDRESS}) + fi + if [ "$BTPSIMPLE_SRC_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_SRC_ENDPOINT" ]; then + export BTPSIMPLE_SRC_ENDPOINT=$(cat ${BTPSIMPLE_SRC_ENDPOINT}) + fi + if [ "$BTPSIMPLE_DST_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_DST_ADDRESS" ]; then + export BTPSIMPLE_DST_ADDRESS=$(cat ${BTPSIMPLE_DST_ADDRESS}) + fi + if [ "$BTPSIMPLE_DST_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_DST_ENDPOINT" ]; then + export BTPSIMPLE_DST_ENDPOINT=$(cat ${BTPSIMPLE_DST_ENDPOINT}) + fi + sh -c "unset $UNSET ; $CMD" +fi + +timeout=10 +while [ ! -f $BTPSIMPLE_CONFIG ]; +do + if [ "$timeout" == 0 ]; then + echo "ERROR: Timeout while waiting for the file $BTPSIMPLE_CONFIG." + exit 1 + fi + sleep 1 + timeout=$(expr $timeout - 1) + echo "waiting for the config file: $BTPSIMPLE_CONFIG" +done + +btpsimple start --config $BTPSIMPLE_CONFIG diff --git a/docker-compose/goloop2moonbeam/gochain/Dockerfile b/docker-compose/goloop2moonbeam/gochain/Dockerfile new file mode 100644 index 00000000..9daff384 --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/Dockerfile @@ -0,0 +1,26 @@ +ARG GOCHAIN_IMAGE=goloop/gochain-icon:latest +FROM ${GOCHAIN_IMAGE} as gochain + +RUN apk add --no-cache jq curl + +ENV PROVISION_CONFIG=/gochain/provision/config +ENV GOCHAIN_CONFIG=/gochain/config/gochain.server.json +ENV GOCHAIN_DATA=/gochain/data/iconee +ENV GOCHAIN_LOGFILE=/gochain/data/gochain.log +ENV GOCHAIN_CLEAN_DATA=false +ENV JAVAEE_BIN=/goloop/execman/bin/execman +ENV PYEE_VERIFY_PACKAGE=true + +ADD config $PROVISION_CONFIG +RUN cat $PROVISION_CONFIG/gochain.server.json | jq -r .genesis.nid > $PROVISION_CONFIG/nid.icon + +ADD entrypoint /entrypoint +RUN chmod u+x /entrypoint + +EXPOSE 9080/tcp +EXPOSE 8080/tcp + +ENTRYPOINT [ "/entrypoint" ] +CMD /goloop/run.sh + +HEALTHCHECK --interval=3s --timeout=3s --start-period=5s --retries=10 CMD ["curl", "-f", "http://127.0.0.1:9080/metrics" ] diff --git a/docker-compose/goloop2moonbeam/gochain/config/gochain.keysecret b/docker-compose/goloop2moonbeam/gochain/config/gochain.keysecret new file mode 100644 index 00000000..2fc16e85 --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/config/gochain.keysecret @@ -0,0 +1 @@ +gochain \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/gochain/config/gochain.keystore.json b/docker-compose/goloop2moonbeam/gochain/config/gochain.keystore.json new file mode 100644 index 00000000..85a16546 --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/config/gochain.keystore.json @@ -0,0 +1,22 @@ +{ + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/gochain/config/gochain.server.json b/docker-compose/goloop2moonbeam/gochain/config/gochain.server.json new file mode 100644 index 00000000..5d3672a0 --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/config/gochain.server.json @@ -0,0 +1,85 @@ +{ + "nid": 3, + "channel": "icon", + "concurrency_level": 1, + "db_type": "goleveldb", + "ee_instances": 1, + "ee_socket": "", + "engines": "python,java", + "p2p": "127.0.0.1:8080", + "p2p_listen": "", + "role": 2, + "rpc_addr": ":9080", + "rpc_debug": true, + "rpc_dump": false, + "log_level": "trace", + "seed_addr": "", + "genesis": { + "accounts": [ + { + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "balance": "0x2961fff8ca4a62327800000", + "name": "god" + }, + { + "address": "hx1000000000000000000000000000000000000000", + "balance": "0x0", + "name": "treasury" + } + ], + "chain": { + "revision": "0xd", + "auditEnabled": "0x0", + "fee": { + "stepPrice": "0x2e90edd00", + "stepLimit": { + "invoke": "0x12A05F200", + "query": "0x2faf080" + }, + "stepCosts": { + "default": "0x186a0", + "contractCall": "0x61a8", + "contractCreate": "0x3b9aca00", + "contractUpdate": "0x5f5e1000", + "contractDestruct": "-0x11170", + "contractSet": "0x7530", + "get": "0x0", + "set": "0x140", + "replace": "0x50", + "delete": "-0xf0", + "input": "0xc8", + "eventLog": "0x64", + "apiCall": "0x2710" + } + }, + "validatorList": [ + "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd" + ] + }, + "message": "genesis for local node", + "nid": "0x3" + }, + "key_store": { + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } + }, + "key_password": "gochain" +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/gochain/config/nid.icon b/docker-compose/goloop2moonbeam/gochain/config/nid.icon new file mode 100644 index 00000000..3704501d --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/config/nid.icon @@ -0,0 +1 @@ +0x3 diff --git a/docker-compose/goloop2moonbeam/gochain/entrypoint b/docker-compose/goloop2moonbeam/gochain/entrypoint new file mode 100755 index 00000000..9d59dbc7 --- /dev/null +++ b/docker-compose/goloop2moonbeam/gochain/entrypoint @@ -0,0 +1,16 @@ +#!/bin/sh +set -e + +GOCHAIN_CONFIG_DIR=$(dirname ${GOCHAIN_CONFIG}) +if [ "${PROVISION_CONFIG}" != "" ] && [ "${PROVISION_CONFIG}" != "${GOCHAIN_CONFIG_DIR}" ]; then + echo "Provisioning config ${PROVISION_CONFIG} to ${GOCHAIN_CONFIG_DIR}" + mkdir -p ${GOCHAIN_CONFIG_DIR} + cp -u -r ${PROVISION_CONFIG}/* ${GOCHAIN_CONFIG_DIR}/ +fi + +if [ "${GOCHAIN_DATA}" != "" ]; then + mkdir -p ${GOCHAIN_DATA} +fi + +source /goloop/venv/bin/activate +exec "$@" \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/Dockerfile b/docker-compose/goloop2moonbeam/goloop/Dockerfile new file mode 100644 index 00000000..21bf6a37 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/Dockerfile @@ -0,0 +1,30 @@ +ARG GOLOOP_IMAGE=iconloop/goloop-icon:latest +FROM ${GOLOOP_IMAGE} as goloop + +RUN apk add --no-cache jq curl + +ENV GOLOOP_PROVISION=/goloop/provisioning +ENV GOLOOP_PROVISION_CONFIG=${GOLOOP_PROVISION}/config +ENV GOLOOP_PROVISION_DATA=${GOLOOP_PROVISION}/data +ENV GOLOOP_DATA_ROOT=/goloop/data +ENV GOLOOP_NODE_DIR=/goloop/data +ENV GOLOOP_CONFIG=/goloop/config/goloop.server.json +ENV GOLOOP_KEY_STORE=/goloop/config/goloop.keystore.json +ENV GOLOOP_KEY_SECRET=/goloop/config/goloop.keysecret +ENV GOLOOP_LOG_WRITER_FILENAME=/goloop/data/log/goloop.log +ENV GOLOOP_P2P_LISTEN=":8080" +ENV GOLOOP_RPC_ADDR=":9080" +ENV GOLOOP_CHAINSCORE=cx0000000000000000000000000000000000000000 +ENV GOLOOP_ENGINES="python,java" +ENV PYEE_VERIFY_PACKAGE="true" + +RUN mkdir -p ${GOLOOP_PROVISION_CONFIG} + +COPY ./*.sh /goloop/bin/ +COPY ./entrypoint / +COPY config ${GOLOOP_PROVISION_CONFIG}/ + +RUN provision.sh +WORKDIR /goloop/config + +HEALTHCHECK --interval=3s --timeout=3s --start-period=5s --retries=10 CMD ["curl", "-f", "http://127.0.0.1:9080/metrics" ] diff --git a/docker-compose/goloop2moonbeam/goloop/config/goloop.keysecret b/docker-compose/goloop2moonbeam/goloop/config/goloop.keysecret new file mode 100644 index 00000000..2fc16e85 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/config/goloop.keysecret @@ -0,0 +1 @@ +gochain \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/config/goloop.keystore.json b/docker-compose/goloop2moonbeam/goloop/config/goloop.keystore.json new file mode 100644 index 00000000..85a16546 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/config/goloop.keystore.json @@ -0,0 +1,22 @@ +{ + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/config/goloop.server.json b/docker-compose/goloop2moonbeam/goloop/config/goloop.server.json new file mode 100644 index 00000000..1eded59f --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/config/goloop.server.json @@ -0,0 +1,43 @@ +{ + "node_sock": "", + "p2p": "127.0.0.1:8080", + "p2p_listen": ":8080", + "rpc_addr": ":9080", + "rpc_dump": false, + "ee_socket": "", + "engines": "python,java", + "backup_dir": "", + "node_dir": "../data", + "key_store": { + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } + }, + "log_level": "debug", + "console_level": "trace", + "log_writer": { + "filename": "../goloop.log", + "maxsize": 100, + "maxage": 0, + "maxbackups": 0, + "localtime": false, + "compress": false + } +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/config/icon.genesis.json b/docker-compose/goloop2moonbeam/goloop/config/icon.genesis.json new file mode 100644 index 00000000..b4bf0d51 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/config/icon.genesis.json @@ -0,0 +1,45 @@ +{ + "accounts": [ + { + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "balance": "0x2961fff8ca4a62327800000", + "name": "god" + }, + { + "address": "hx1000000000000000000000000000000000000000", + "balance": "0x0", + "name": "treasury" + } + ], + "chain": { + "revision": "0x8", + "auditEnabled": "0x1", + "deployerWhiteListEnabled": "0x0", + "fee": { + "stepPrice": "0x2e90edd00", + "stepLimit": { + "invoke": "0x12A05F200", + "query": "0x2faf080" + }, + "stepCosts": { + "default": "0x186a0", + "contractCall": "0x61a8", + "contractCreate": "0x3b9aca00", + "contractUpdate": "0x5f5e1000", + "contractDestruct": "-0x11170", + "contractSet": "0x7530", + "get": "0x0", + "set": "0x140", + "replace": "0x50", + "delete": "-0xf0", + "input": "0xc8", + "eventLog": "0x64", + "apiCall": "0x2710" + } + }, + "validatorList": [ + "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd" + ] + }, + "message": "generated 2021-08-19 10:37:34.329357452 +0000 UTC m=+0.010340239" +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/entrypoint b/docker-compose/goloop2moonbeam/goloop/entrypoint new file mode 100755 index 00000000..a6e1a7ac --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/entrypoint @@ -0,0 +1,21 @@ +#!/bin/sh +set -e + +GOLOOP_CONFIG_DIR=$(dirname ${GOLOOP_CONFIG}) +if [ "${GOLOOP_PROVISION_CONFIG}" != "" ] && [ "${GOLOOP_PROVISION_CONFIG}" != "${GOLOOP_CONFIG_DIR}" ]; then + echo "Provisioning config ${GOLOOP_PROVISION_CONFIG} to ${GOLOOP_CONFIG_DIR}" + mkdir -p ${GOLOOP_CONFIG_DIR} + cp -u -r ${GOLOOP_PROVISION_CONFIG}/* ${GOLOOP_CONFIG_DIR}/ +fi + +if [ "${GOLOOP_PROVISION_DATA}" != "" ] && [ "${GOLOOP_PROVISION_DATA}" != "${GOLOOP_NODE_DIR}" ]; then + echo "Provisioning data ${GOLOOP_PROVISION_DATA} to ${GOLOOP_NODE_DIR}" + mkdir -p ${GOLOOP_NODE_DIR} + cp -u -r ${GOLOOP_PROVISION_DATA}/* ${GOLOOP_NODE_DIR}/ +fi + +source server.sh +ensure_config + +source /goloop/venv/bin/activate +exec "$@" \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/keystore.sh b/docker-compose/goloop2moonbeam/goloop/keystore.sh new file mode 100644 index 00000000..6f405cce --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/keystore.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +ensure_key_secret() { + if [ $# -lt 1 ] ; then + echo "Usage: ensure_key_secret SECRET_PATH" + return 1 + fi + local KEY_SECRET=$1 + if [ ! -f "${KEY_SECRET}" ]; then + mkdir -p $(dirname ${KEY_SECRET}) + echo -n $(date|md5sum|head -c16) > ${KEY_SECRET} + fi + echo ${KEY_SECRET} +} + +ensure_key_store() { + if [ $# -lt 2 ] ; then + echo "Usage: ensure_key_store KEYSTORE_PATH SECRET_PATH" + return 1 + fi + local KEY_STORE=$1 + local KEY_SECRET=$(ensure_key_secret $2) + if [ ! -f "${KEY_STORE}" ]; then + goloop ks gen --out $KEY_STORE -p $(cat ${KEY_SECRET}) > /dev/null 2>&1 + fi + echo ${KEY_STORE} +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/goloop/provision.sh b/docker-compose/goloop2moonbeam/goloop/provision.sh new file mode 100755 index 00000000..21073b47 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/provision.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +GOLOOP_PROVISION=${GOLOOP_PROVISION:-/goloop/provisioning} +GOLOOP_PROVISION_CONFIG=${GOLOOP_PROVISION_CONFIG:-${GOLOOP_PROVISION}/config} +GOLOOP_PROVISION_DATA=${GOLOOP_PROVISION_DATA:-${GOLOOP_PROVISION}/data} + +GOLOOP_NODE_DIR=${GOLOOP_PROVISION_DATA} +GOLOOP_LOG_WRITER_FILENAME=${GOLOOP_PROVISION}/goloop.log +GOLOOP_KEY_SECRET=${GOLOOP_PROVISION_CONFIG}/goloop.keysecret +GOLOOP_KEY_STORE=${GOLOOP_PROVISION_CONFIG}/goloop.keystore.json +GOLOOP_CONFIG=${GOLOOP_PROVISION_CONFIG}/goloop.server.json + +cd ${GOLOOP_PROVISION_CONFIG} +########################## +# Chain setup +source server.sh +ensure_server_start + +goloop chain join --genesis_template icon.genesis.json --channel icon --auto_start +goloop chain inspect icon --format {{.NID}} > nid.icon + +goloop chain start icon +goloop chain stop icon diff --git a/docker-compose/goloop2moonbeam/goloop/server.sh b/docker-compose/goloop2moonbeam/goloop/server.sh new file mode 100755 index 00000000..5533e2f9 --- /dev/null +++ b/docker-compose/goloop2moonbeam/goloop/server.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +source keystore.sh + +ensure_config() { + local CONFIG=${1:-${GOLOOP_CONFIG:-/goloop/config/goloop.server.json}} + if [ ! -f "${CONFIG}" ]; then + export GOLOOP_KEY_SECRET=$(ensure_key_secret ${GOLOOP_KEY_SECRET:-/goloop/config/goloop.keysecret}) + export GOLOOP_KEY_STORE=$(ensure_key_store ${GOLOOP_KEY_STORE:-/goloop/config/goloop.keystore.json} ${GOLOOP_KEY_SECRET}) + if [ "$GOLOOP_CONFIG" != "" ]; then + OLD_GOLOOP_CONFIG=$GOLOOP_CONFIG + unset GOLOOP_CONFIG + fi + RESULT=$(goloop server save ${CONFIG} 2>&1) + if [ "$OLD_GOLOOP_CONFIG" != "" ]; then + export GOLOOP_CONFIG=${OLD_GOLOOP_CONFIG} + fi + fi + echo ${CONFIG} +} + +GOLOOP_PID=${GOLOOP_PID:-/var/run/goloop.pid} + +server_start() { + source /goloop/venv/bin/activate + PID=$(server_pid) + if [ -z "${PID}" ]; then + export GOLOOP_LOG_WRITER_FILENAME=${GOLOOP_LOG_WRITER_FILENAME:-/goloop/data/goloop.log} + export GOLOOP_CONFIG=$(ensure_config) + goloop server start > /dev/null 2>&1 & + echo "$!" > ${GOLOOP_PID} + fi +} + +ensure_server_start() { + server_start + + RET=$(goloop system info > /dev/null 2>&1;echo $?) + while [ "0" != "$RET" ]; do + RET=$(goloop system info > /dev/null 2>&1;echo $?) + sleep 1 + done +} + +server_stop() { + PID=$(server_pid) + if [ ! -z "${PID}" ]; then + local SIG=${1:-TERM} + #kill -${SIG} ${PID} + PGID=$(ps -o pid,pgid,comm | grep goloop | grep ${PID} | grep -v 'grep' | tr -s ' ' | cut -d ' ' -f 3) + kill -${SIG} -${PGID} + rm ${GOLOOP_PID} + fi +} + +server_pid() { + if [ -f "${GOLOOP_PID}" ]; then + PID=$(cat ${GOLOOP_PID}) + STATUS=$(ps -ef | grep $PID | grep -v grep) + if [ ! -z "${STATUS}" ]; then + echo ${PID} + else + rm ${GOLOOP_PID} + fi + fi +} diff --git a/docker-compose/goloop2moonbeam/moonbeam/Dockerfile b/docker-compose/goloop2moonbeam/moonbeam/Dockerfile new file mode 100644 index 00000000..ba65c4ee --- /dev/null +++ b/docker-compose/goloop2moonbeam/moonbeam/Dockerfile @@ -0,0 +1,10 @@ +ARG MOONBEAM_VERSION=latest + +FROM debian:buster-slim as base +FROM purestake/moonbeam:${MOONBEAM_VERSION} + +USER root +COPY --from=base /usr/bin /usr/bin +RUN apt-get update && apt-get install -y curl + +HEALTHCHECK --interval=3s --timeout=3s --start-period=5s --retries=10 CMD ["curl", "-f", "http://127.0.0.1:9933/health" ] \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keysecret b/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keysecret new file mode 100644 index 00000000..99b7caf8 --- /dev/null +++ b/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keysecret @@ -0,0 +1 @@ +testnet_icondao \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keystore.json b/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keystore.json new file mode 100644 index 00000000..e96dd289 --- /dev/null +++ b/docker-compose/goloop2moonbeam/moonbeam/keystore/moonbeam.keystore.json @@ -0,0 +1,22 @@ +{ + "version": 3, + "id": "64cc9110-5ce8-444c-aff2-16bf0e3c84cd", + "address": "3cd0a705a2dc65e5b1e1205896baa2be8a07c6e0", + "coinType": "evm", + "crypto": { + "ciphertext": "0c867feb1bb7ded3ff68a183dd4609e8a799af2288eaa8d4410b7390a30bb784", + "cipherparams": { + "iv": "48f3a53870071e939c2389ce41fe1f3b" + }, + "cipher": "aes-128-ctr", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "salt": "f7ce44c429966b3438bbd5652d0950af6d5ea6a8b9656fbee67b0d1dcaada1ff", + "n": 131072, + "r": 8, + "p": 1 + }, + "mac": "808908b79bfd3c8a6582b83a0139fb7e7988e5fa41b1e2c5153a0727c471f0f5" + } +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/deploy_javascore.sh b/docker-compose/goloop2moonbeam/scripts/deploy_javascore.sh new file mode 100644 index 00000000..e8a5e91e --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/deploy_javascore.sh @@ -0,0 +1,300 @@ +#!/bin/sh +ICON_RECEIVER_FEE_ADDRESS=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd +ICON_BAND_PROTOCOL_ADDRESS=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd + +deploy_javascore_bmc() { + echo "deploying javascore bmc" + + cd $CONFIG_DIR + echo "$GOLOOP_RPC_NID.icon" > net.btp.icon + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/bmc-0.1.0-optimized.jar \ + --content_type application/java \ + --param _net=$(cat net.btp.icon) | jq -r . > tx.icon.deploy_bmc + + extract_scoreAddress tx.icon.deploy_bmc bmc.icon + ensure_file_exist $CONFIG_DIR bmc.icon + echo "btp://$(cat net.btp.icon)/$(cat bmc.icon)" > btp.icon +} + +_deploy_kusamaDecoder(){ + echo "deploying javascore kusamaDecoder" + + cd $CONFIG_DIR + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/KusamaEventDecoder-optimized.jar \ + --content_type application/java | jq -r . > tx.icon.deploy_kusamaDecoder + + extract_scoreAddress tx.icon.deploy_kusamaDecoder kusamaDecoder.icon + ensure_file_exist $CONFIG_DIR kusamaDecoder.icon +} + +_deploy_moonriverDecoder(){ + echo "deploying javascore moonriverDecoder" + + cd $CONFIG_DIR + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/MoonriverEventDecoder-optimized.jar \ + --content_type application/java | jq -r . > tx.icon.deploy_moonriverDecoder + + extract_scoreAddress tx.icon.deploy_moonriverDecoder moonriverDecoder.icon + ensure_file_exist $CONFIG_DIR moonriverDecoder.icon +} + +_prepare_javascore_bmv() { + _deploy_kusamaDecoder + _deploy_moonriverDecoder + + export PARA_CHAIN_OFFSET=$(moonbeam_blocknumber) + export RELAY_CHAIN_OFFSET=${RELAY_CHAIN_OFFSET:-8511058} + export RELAY_ENDPOINT=${RELAY_ENDPOINT:-'wss://kusama-rpc.polkadot.io'} + export PARA_ENDPOINT=${PARA_ENDPOINT:-'ws://moonbeam:9944'} + + echo "getting BMVInitializeParams at PARA_CHAIN_OFFSET:$PARA_CHAIN_OFFSET RELAY_CHAIN_OFFSET:$RELAY_CHAIN_OFFSET" + cd $JAVASCORE_HELPER_DIR + yarn && yarn getBMVInitializeParams + wait_file_created $JAVASCORE_HELPER_DIR BMVInitializeData.json + + echo $PARA_CHAIN_OFFSET > $CONFIG_DIR/offset.moonbeam_parachain + echo $RELAY_CHAIN_OFFSET > $CONFIG_DIR/offset.moonbeam_relaychain + + cp -f BMVInitializeData.json $CONFIG_DIR/ + rm -rf ./node_modules +} + +deploy_javascore_bmv() { + echo "deploying javascore bmv" + _prepare_javascore_bmv + + cd $CONFIG_DIR + tmp=$(cat BMVInitializeData.json) + relayMtaOffset=$(echo "$tmp" | jq -r .relayMtaOffset) + paraMtaOffset=$(echo "$tmp" | jq -r .paraMtaOffset) + relayLastBlockHash=$(echo "$tmp" | jq -r .relayLastBlockHash) + paraLastBlockHash=$(echo "$tmp" | jq -r .paraLastBlockHash) + encodedValidators=$(echo "$tmp" | jq -r .encodedValidators) + relayCurrentSetId=$(echo "$tmp" | jq -r .relayCurrentSetId) + evmEventIndex=$(echo "$tmp" | jq -r .evmEventIndex) \ + newAuthoritiesEventIndex=$(echo "$tmp" | jq -r .newAuthoritiesEventIndex) \ + candidateIncludedEventIndex=$(echo "$tmp" | jq -r .candidateIncludedEventIndex) + # paraChainId=$(echo "$tmp" | jq -r .paraChainId) # # Currently deployment of moonbeam is dev the default is 0x0, not relates to Kusama + + echo "parachain height:$paraMtaOffset block_hash:$paraLastBlockHash" + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/parachain-BMV-optimized.jar \ + --content_type application/java \ + --param relayMtaOffset=$relayMtaOffset \ + --param paraMtaOffset=$paraMtaOffset \ + --param bmc=$(cat bmc.icon) \ + --param net=$(cat net.btp.moonbeam) \ + --param mtaRootSize=0x10 \ + --param mtaCacheSize=0x10 \ + --param mtaIsAllowNewerWitness=0x1 \ + --param relayLastBlockHash=$relayLastBlockHash \ + --param paraLastBlockHash=$paraLastBlockHash \ + --param relayEventDecoderAddress=$(cat kusamaDecoder.icon) \ + --param paraEventDecoderAddress=$(cat moonriverDecoder.icon) \ + --param relayCurrentSetId=$relayCurrentSetId \ + --param paraChainId=0x0 \ + --param encodedValidators=$encodedValidators \ + --param evmEventIndex=$evmEventIndex \ + --param newAuthoritiesEventIndex=$newAuthoritiesEventIndex \ + --param candidateIncludedEventIndex=$candidateIncludedEventIndex \ + | jq -r . > tx.icon.deploy_bmv + + extract_scoreAddress tx.icon.deploy_bmv bmv.icon + ensure_file_exist $CONFIG_DIR bmv.icon +} + +deploy_javascore_IRC31Token() { + echo "deploy_javascore_IRC31Token" + cd $CONFIG_DIR + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/irc31-0.1.0-optimized.jar \ + --content_type application/java | jq -r . > tx.icon.deploy_irc31token + + extract_scoreAddress tx.icon.deploy_irc31token irc31token.icon + ensure_file_exist $CONFIG_DIR irc31token.icon +} + +deploy_javascore_IRC2Token() { + echo "deploy_javascore_IRC2Token" + cd $CONFIG_DIR + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/irc2-0.1.0-optimized.jar \ + --content_type application/java \ + --param _name=${TOKEN_NAME} \ + --param _symbol=${TOKEN_SYM} \ + --param _initialSupply=${TOKEN_SUPPLY} \ + --param _decimals=${TOKEN_DECIMALS} | jq -r . > tx.icon.deploy_irc2token + + extract_scoreAddress tx.icon.deploy_irc2token irc2token.icon + ensure_file_exist $CONFIG_DIR irc2token.icon +} + +deploy_javascore_NativeCoinIRC2BSH() { + echo "deploy_javascore_NativeCoinIRC2BSH" + + cd $CONFIG_DIR + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/nativecoinIRC2-0.1.0-optimized.jar \ + --content_type application/java \ + --param _bmc=$(cat bmc.icon) \ + --param _irc2=$(cat irc2token.icon) \ + --param _name=ICX \ + --param _tokenName=${TOKEN_NAME} | jq -r . > tx.icon.deploy_nativeCoinIRC2Bsh + + extract_scoreAddress tx.icon.deploy_nativeCoinIRC2Bsh nativeCoinIRC2Bsh.icon + ensure_file_exist $CONFIG_DIR nativeCoinIRC2Bsh.icon +} + +deploy_javascore_NativeCoinBSH() { + echo "deploy_javascore_NativeCoinBSH" + + cd $CONFIG_DIR + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/nativecoin-0.1.0-optimized.jar \ + --content_type application/java \ + --param _bmc=$(cat bmc.icon) \ + --param _irc31=$(cat irc31token.icon) \ + --param _name=ICX | jq -r . > tx.icon.deploy_nativeCoinBsh + + extract_scoreAddress tx.icon.deploy_nativeCoinBsh nativeCoinBsh.icon + ensure_file_exist $CONFIG_DIR nativeCoinBsh.icon +} + +deploy_javascore_FeeAggregation() { + echo "deploy_javascore_FeeAggregation" + + cd $CONFIG_DIR + + goloop rpc sendtx deploy $JAVASCORE_DIST_DIR/fee-aggregation-system-1.0-optimized.jar \ + --param _cps_address=$ICON_RECEIVER_FEE_ADDRESS \ + --param _band_protocol_address=$ICON_BAND_PROTOCOL_ADDRESS \ + --content_type application/java | jq -r . > tx.icon.deploy_feeAggregation + + extract_scoreAddress tx.icon.deploy_feeAggregation feeAggregation.icon + ensure_file_exist $CONFIG_DIR feeAggregation.icon +} + +goloop_bmc_addVerifier() { + echo "goloop_bmc_addVerifier" + cd $CONFIG_DIR + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method addVerifier \ + --param _net=$(cat net.btp.moonbeam) \ + --param _addr=$(cat bmv.icon) | jq -r . > tx.icon.addVerifier + + ensure_txresult tx.icon.addVerifier +} + +goloop_bmc_addLink() { + echo "goloop_bmc_addLink" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method addLink \ + --param _link=$(cat btp.moonbeam) | jq -r . > tx.icon.addLink + ensure_txresult tx.icon.addLink + + echo "goloop_bmc_setLinkRotateTerm" + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method setLinkRotateTerm \ + --param _link=$(cat btp.moonbeam) \ + --param _block_interval=0x1770 \ + --param _max_agg=0x08 \ + | jq -r . > tx.icon.setLinkRotateTerm + ensure_txresult tx.icon.setLinkRotateTerm + + echo "goloop_bmc_setLinkDelayLimit" + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method setLinkDelayLimit \ + --param _link=$(cat btp.moonbeam) \ + --param _value=4 \ + | jq -r . > tx.icon.setLinkDelayLimit + ensure_txresult tx.icon.setLinkDelayLimit + + echo "finished goloop_bmc_addLink" +} + +goloop_bmc_addService() { + echo "goloop_bmc_addService" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method addService \ + --param _addr=$(cat nativeCoinBsh.icon) \ + --param _svc=nativecoin \ + | jq -r . > tx.icon.addService + ensure_txresult tx.icon.addService +} + +goloop_bmc_addServiceIRC2() { + echo "goloop_bmc_addServiceIRC2" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method addService \ + --param _addr=$(cat nativeCoinIRC2Bsh.icon) \ + --param _svc=$SVC_NAME \ + | jq -r . > tx.icon.addService + ensure_txresult tx.icon.addService +} + +goloop_bmc_addRelay() { + echo "goloop_bmc_addRelay" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method addRelay \ + --param _link=$(cat btp.moonbeam) \ + --param _addr=$(jq -r .address $GOLOOP_KEY_STORE) \ + | jq -r . > tx.icon.addRelay + ensure_txresult tx.icon.addRelay +} + +goloop_bmc_setFeeAggregator() { + echo "goloop_bmc_setFeeAggregator" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat bmc.icon) \ + --method setFeeAggregator \ + --param _addr=$(cat feeAggregation.icon) \ + | jq -r . > tx.icon.setFeeAggregator + ensure_txresult tx.icon.setFeeAggregator +} + +goloop_bsh_config_native_coin() { + echo "goloop_bsh_config_native_coin" + cd $CONFIG_DIR + + goloop rpc sendtx call --to $(cat nativeCoinBsh.icon) \ + --method register \ + --param _name=DEV \ + | jq -r . > tx.icon.registerCoin + ensure_txresult tx.icon.registerCoin + + echo "goloop_bsh_setFeeRatio" + goloop rpc sendtx call --to $(cat nativeCoinBsh.icon) \ + --method setFeeRatio \ + --param _feeNumerator=100 \ + | jq -r . > tx.icon.setFeeRatio + ensure_txresult tx.icon.setFeeRatio + + echo "goloop_bsh_addOwner" + goloop rpc sendtx call --to $(cat irc31token.icon) \ + --method addOwner \ + --param _addr=$(cat nativeCoinBsh.icon) \ + | jq -r . > tx.icon.addOwnerIrc31 + ensure_txresult tx.icon.addOwnerIrc31 +} + +goloop_bsh_config_native_coin_IRC2() { + echo "goloop_bsh_config_native_coin_IRC2" + cd $CONFIG_DIR + + echo "goloop_bsh_setFeeRatio" + goloop rpc sendtx call --to $(cat nativeCoinIRC2Bsh.icon) \ + --method setFeeRatio \ + --param _feeNumerator=100 \ + | jq -r . > tx.icon.setFeeRatio + ensure_txresult tx.icon.setFeeRatio +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/deploy_solidity.sh b/docker-compose/goloop2moonbeam/scripts/deploy_solidity.sh new file mode 100644 index 00000000..88b091a3 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/deploy_solidity.sh @@ -0,0 +1,189 @@ +#!/bin/sh + +deploy_solidity_bmc() { + echo "deploying solidity bmc" + echo "$(printf "0x%01x" $MOONBEAM_CHAIN_ID).pra" > $CONFIG_DIR/net.btp.moonbeam + + cd $SOLIDITY_DIST_DIR/bmc + sed -i 's/"http:\/\/localhost:9933"/process.env.MOONBEAM_RPC_URL/' truffle-config.js + rm -rf .openzeppelin build node_modules + yarn && yarn add fs@0.0.1-security + + truffle compile --all + cat ./build/contracts/BMCManagement.json | jq -r .abi > $CONFIG_DIR/abi.bmc_management.json + cat ./build/contracts/BMCPeriphery.json | jq -r .abi > $CONFIG_DIR/abi.bmc_periphery.json + + BMC_BTP_NET=$(cat $CONFIG_DIR/net.btp.moonbeam) \ + truffle migrate --network moonbeamlocal + truffle exec $SCRIPT_DIR/mb_extract_bmc.js --network moonbeamlocal + + wait_file_created $CONFIG_DIR bmc.moonbeam + echo "btp://$(cat $CONFIG_DIR/net.btp.moonbeam)/$(cat $CONFIG_DIR/bmc.moonbeam)" > $CONFIG_DIR/btp.moonbeam +} + +deploy_solidity_bsh() { + echo "deploying solidity bsh" + + cd $SOLIDITY_DIST_DIR/bsh + sed -i 's/"http:\/\/localhost:9933"/process.env.MOONBEAM_RPC_URL/' truffle-config.js + rm -rf .openzeppelin build node_modules + yarn && yarn add fs@0.0.1-security + truffle compile --all + cat ./build/contracts/BSHCore.json | jq -r .abi > $CONFIG_DIR/abi.bsh_core.json + cat ./build/contracts/BSHPeriphery.json | jq -r .abi > $CONFIG_DIR/abi.bsh_periphery.json + + BSH_COIN_URL=https://moonbeam.network \ + BSH_COIN_NAME=DEV \ + BSH_COIN_FEE=100 \ + BSH_FIXED_FEE=50000 \ + BMC_PERIPHERY_ADDRESS=$(cat $CONFIG_DIR/bmc.moonbeam) \ + BSH_SERVICE=nativecoin \ + truffle migrate --network moonbeamlocal + + truffle exec $SCRIPT_DIR/mb_extract_bsh.js --network moonbeamlocal + wait_file_created $CONFIG_DIR bsh.moonbeam +} + +deploy_solidity_nativeCoinERC20() { + echo "deploying solidity bsh nativecoinERC20" + + cd $SOLIDITY_DIST_DIR/nativecoinERC20 + sed -i 's/"http:\/\/localhost:9933"/process.env.MOONBEAM_RPC_URL/' truffle-config.js + rm -rf .openzeppelin build node_modules + yarn && yarn add fs@0.0.1-security + truffle compile --all + cat ./build/contracts/BSHCore.json | jq -r .abi > $CONFIG_DIR/abi.bsh_core_erc20.json + cat ./build/contracts/BSHPeriphery.json | jq -r .abi > $CONFIG_DIR/abi.bsh_periphery_erc20.json + + BSH_COIN_NAME=MOVR \ + BSH_COIN_FEE=100 \ + BSH_FIXED_FEE=50000 \ + BSH_TOKEN_NAME=ICX \ + BSH_TOKEN_SYMBOL=ICX \ + BSH_INITIAL_SUPPLY=100000 \ + BMC_PERIPHERY_ADDRESS=$(cat $CONFIG_DIR/bmc.moonbeam) \ + BSH_SERVICE=$SVC_NAME \ + truffle migrate --network moonbeamlocal + + truffle exec $SCRIPT_DIR/mb_extract_bsh_ERC20.js --network moonbeamlocal + wait_file_created $CONFIG_DIR bsh_erc20.moonbeam +} + +deploy_solidity_bmv() { + echo "deploying solidity bmv" + + cd $SOLIDITY_DIST_DIR/bmv + sed -i 's/"http:\/\/localhost:9933"/process.env.MOONBEAM_RPC_URL/' truffle-config.js + rm -rf .openzeppelin build node_modules + yarn && yarn add fs@0.0.1-security + truffle compile --all + cat ./build/contracts/BMV.json | jq -r .abi > $CONFIG_DIR/abi.bmv.json + cat ./build/contracts/DataValidator.json | jq -r .abi > $CONFIG_DIR/abi.data_validator.json + + LAST_BOCK=$(latest_block_goloop) + LAST_HEIGHT=$(echo $LAST_BOCK | jq -r .height) + LAST_HASH=0x$(echo $LAST_BOCK | jq -r .block_hash) + echo "goloop height:$LAST_HEIGHT hash:$LAST_HASH" + echo $LAST_HEIGHT > $CONFIG_DIR/offset.icon + + # - BMC_PERIPHERY_ADDRESS: an address on chain of BMCPeriphery contract + # This address is queried after deploying BMC contracts + # For example: BMC_PERIPHERY_ADDRESS = 0x5CC307268a1393AB9A764A20DACE848AB8275c46 + # - BMV_ICON_NET: Chain ID and name of a network that BMV is going to verify BTP Message + # - BMV_ICON_INIT_OFFSET: a block height when ICON-BMC was deployed + # - BMV_ICON_LASTBLOCK_HASH: a hash of the above block + # - BMV_ICON_ENCODED_VALIDATORS: RLP encoding of Validators. It can be generated in two ways: + # + Using library: https://www.npmjs.com/package/rlp + # + Web App: https://toolkit.abdk.consulting/ethereum#rlp + # Get the validators by call this command 'goloop rpc call --to $GOLOOP_CHAINSCORE --method getValidators' + # The curruent list of validators, which is being used in this example, is ["hxb6b5791be0b5ef67063b3c10b840fb81514db2fd"] + # Replace 'hx' by '0x00' -> RLP encode -> 0xd69500b6b5791be0b5ef67063b3c10b840fb81514db2fd + + BMC_PERIPHERY_ADDRESS=$(cat $CONFIG_DIR/bmc.moonbeam) \ + BMV_ICON_NET=$(cat $CONFIG_DIR/net.btp.icon) \ + BMV_ICON_ENCODED_VALIDATORS=0xd69500b6b5791be0b5ef67063b3c10b840fb81514db2fd \ + BMV_ICON_INIT_OFFSET=$LAST_HEIGHT \ + BMV_ICON_INIT_ROOTSSIZE=8 \ + BMV_ICON_INIT_CACHESIZE=8 \ + BMV_ICON_LASTBLOCK_HASH=$LAST_HASH \ + truffle migrate --network moonbeamlocal + + truffle exec $SCRIPT_DIR/mb_extract_bmv.js --network moonbeamlocal + wait_file_created $CONFIG_DIR bmv.moonbeam +} + +moonbeam_bmc_addVerifier() { + echo "moonbeam_bmc_addVerifier" + + + cd $SOLIDITY_DIST_DIR/bmc + cp $SCRIPT_DIR/mb_bmc_add_verifier.js . + + ICON_NET=$(cat $CONFIG_DIR/net.btp.icon) \ + BMV_MOONBEAM=$(cat $CONFIG_DIR/bmv.moonbeam) \ + truffle exec mb_bmc_add_verifier.js --network moonbeamlocal +} + +moonbeam_bmc_addLink() { + echo "moonbeam_bmc_addLink" + + cd $SOLIDITY_DIST_DIR/bmc + cp $SCRIPT_DIR/mb_bmc_add_link.js . + + ICON_BTP_ADDRESS=$(cat $CONFIG_DIR/btp.icon) \ + truffle exec mb_bmc_add_link.js --network moonbeamlocal +} + +moonbeam_bmc_addService() { + echo "moonbeam_bmc_addService" + + cd $SOLIDITY_DIST_DIR/bmc + cp $SCRIPT_DIR/mb_bmc_add_service.js . + + SVC_NAME=nativecoin \ + BSH_MOONBEAM=$(cat $CONFIG_DIR/bsh.moonbeam) \ + ICON_BTP_ADDRESS=$(cat $CONFIG_DIR/btp.icon) \ + RELAY_ADDRESS=0x$(cat $CONFIG_DIR/moonbeam.keystore.json | jq -r .address) \ + truffle exec mb_bmc_add_service.js --network moonbeamlocal +} + +moonbeam_bmc_addService_ERC20() { + echo "moonbeam_bmc_addService_ERC20" + + cd $SOLIDITY_DIST_DIR/bmc + cp $SCRIPT_DIR/mb_bmc_add_service.js . + + SVC_NAME=$SVC_NAME \ + BSH_MOONBEAM=$(cat $CONFIG_DIR/bsh_erc20.moonbeam) \ + ICON_BTP_ADDRESS=$(cat $CONFIG_DIR/btp.icon) \ + RELAY_ADDRESS=0x$(cat $CONFIG_DIR/moonbeam.keystore.json | jq -r .address) \ + truffle exec mb_bmc_add_service.js --network moonbeamlocal +} + +moonbeam_bsh_registerCoin() { + echo "moonbeam_bsh_registerCoin" + cd $SOLIDITY_DIST_DIR/bsh + cp $SCRIPT_DIR/mb_bsh_register_coin.js . + truffle exec mb_bsh_register_coin.js --network moonbeamlocal +} + +clean_solidity_build() { + echo "cleaning solidity build" + for module in bmc bmv bsh + do + cd $SOLIDITY_DIST_DIR/$module && rm -rf .openzeppelin build node_modules + done +} + +PRECISION=18 +COIN_UNIT=$((10 ** $PRECISION)) + +coin2wei() { + amount=$1 + printf '%s * %s\n' $COIN_UNIT $amount | bc +} + +wei2coin() { + amount=$1 + printf 'scale=%s; %s / %s\n' $PRECISION $amount $COIN_UNIT | bc +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_link.js b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_link.js new file mode 100644 index 00000000..cea60d3d --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_link.js @@ -0,0 +1,17 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const fs = require('fs'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + const addLink = await bmcManagement.addLink(process.env.ICON_BTP_ADDRESS); + const setLink = await bmcManagement.setLink(process.env.ICON_BTP_ADDRESS, 3000, 5, 3) + + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.addLink", addLink.tx); + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.setLink", setLink.tx); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_service.js b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_service.js new file mode 100644 index 00000000..e1676bd3 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_service.js @@ -0,0 +1,17 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const fs = require('fs'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + const addService = await bmcManagement.addService(process.env.SVC_NAME, process.env.BSH_MOONBEAM) + const addRelay = await bmcManagement.addRelay(process.env.ICON_BTP_ADDRESS, [process.env.RELAY_ADDRESS]) + + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.addService", addService.tx); + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.addRelay", addRelay.tx); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_verifier.js b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_verifier.js new file mode 100644 index 00000000..84018943 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_bmc_add_verifier.js @@ -0,0 +1,14 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const fs = require('fs'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + const addVerifier = await bmcManagement.addVerifier(process.env.ICON_NET, process.env.BMV_MOONBEAM); + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.addVerifier", addVerifier.tx); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_bmc_get_linkStat.js b/docker-compose/goloop2moonbeam/scripts/mb_bmc_get_linkStat.js new file mode 100644 index 00000000..df1947f1 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_bmc_get_linkStat.js @@ -0,0 +1,15 @@ +const BMCPeriphery = artifacts.require('BMCPeriphery'); +const fs = require('fs'); + +module.exports = async function (callback) { + try { + const bmcPeriphery = await BMCPeriphery.deployed(); + let linkStats = await bmcPeriphery.getStatus(process.env.ICON_BTP_ADDRESS); + var offset = {'offsetMTA': linkStats.verifier.offsetMTA}; + fs.writeFileSync(process.env.CONFIG_DIR + "/bmc_linkstats.moonbeam", JSON.stringify(offset)) + } + catch (error) { + console.log(error) + } + callback() +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/mb_bsh_register_coin.js b/docker-compose/goloop2moonbeam/scripts/mb_bsh_register_coin.js new file mode 100644 index 00000000..0cb29330 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_bsh_register_coin.js @@ -0,0 +1,15 @@ +const BSHCore = artifacts.require("BSHCore"); +const fs = require('fs'); + +module.exports = async function (callback) { + try { + const bshCore = await BSHCore.deployed(); + const registerICX = await bshCore.register("ICX"); + fs.writeFileSync(process.env.CONFIG_DIR + "/tx.moonbeam.registerICX", registerICX.tx); + await bshCore.coinNames(); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_extract_bmc.js b/docker-compose/goloop2moonbeam/scripts/mb_extract_bmc.js new file mode 100644 index 00000000..59cf5948 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_extract_bmc.js @@ -0,0 +1,25 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const BMCPeriphery = artifacts.require('BMCPeriphery'); +const fs = require('fs') + +module.exports = async function (callback) { + try { + await BMCManagement.deployed(); + await BMCPeriphery.deployed(); + + fs.writeFileSync(process.env.CONFIG_DIR + "/bmc.moonbeam", BMCPeriphery.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + fs.writeFileSync(process.env.CONFIG_DIR + "/bmc_management.moonbeam", BMCManagement.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_extract_bmv.js b/docker-compose/goloop2moonbeam/scripts/mb_extract_bmv.js new file mode 100644 index 00000000..1f1f533a --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_extract_bmv.js @@ -0,0 +1,18 @@ +const BMV = artifacts.require("BMV"); +const fs = require('fs') + +module.exports = async function (callback) { + try { + await BMV.deployed(); + + fs.writeFileSync(process.env.CONFIG_DIR + "/bmv.moonbeam", BMV.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh.js b/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh.js new file mode 100644 index 00000000..bfca5f01 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh.js @@ -0,0 +1,25 @@ +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const fs = require('fs') + +module.exports = async function (callback) { + try { + await BSHCore.deployed(); + await BSHPeriphery.deployed(); + + fs.writeFileSync(process.env.CONFIG_DIR + "/bsh_core.moonbeam", BSHCore.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + fs.writeFileSync(process.env.CONFIG_DIR + "/bsh.moonbeam", BSHPeriphery.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh_ERC20.js b/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh_ERC20.js new file mode 100644 index 00000000..1403aafb --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_extract_bsh_ERC20.js @@ -0,0 +1,25 @@ +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const fs = require('fs') + +module.exports = async function (callback) { + try { + await BSHCore.deployed(); + await BSHPeriphery.deployed(); + + fs.writeFileSync(process.env.CONFIG_DIR + "/bsh_core_erc20.moonbeam", BSHCore.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + fs.writeFileSync(process.env.CONFIG_DIR + "/bsh_erc20.moonbeam", BSHPeriphery.address, function (err, data) { + if (err) { + return console.log(err); + } + }); + } + catch (error) { + console.log(error) + } + callback() +} diff --git a/docker-compose/goloop2moonbeam/scripts/mb_fund_bmr.js b/docker-compose/goloop2moonbeam/scripts/mb_fund_bmr.js new file mode 100644 index 00000000..377eea21 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/mb_fund_bmr.js @@ -0,0 +1,19 @@ +const BMCManagement = artifacts.require('BMCManagement'); + +module.exports = async function (callback) { + try { + const accounts = await web3.eth.getAccounts(); + await web3.eth.sendTransaction( + { + from: accounts[0], + to: process.env.MOON_BMR, + value: web3.utils.toBN("10000000000000000000000") + } + ); + console.log('BMR.MOONBEAM balance: ', await web3.eth.getBalance(process.env.MOON_BMR)) + } + catch (error) { + console.log(error) + } + callback() +} \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/provision.sh b/docker-compose/goloop2moonbeam/scripts/provision.sh new file mode 100644 index 00000000..dfef52b7 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/provision.sh @@ -0,0 +1,59 @@ +source util.sh + +provision() { + echo "start provisioning at: $(date)" > $PROVISION_STATUS_STARTED + echo "provisioning..." + + + source deploy_javascore.sh + source deploy_solidity.sh + + # deploy contracts + deploy_javascore_bmc + deploy_solidity_bmc + deploy_solidity_bsh + deploy_solidity_nativeCoinERC20 + deploy_solidity_bmv + + deploy_javascore_bmv + deploy_javascore_IRC31Token + deploy_javascore_NativeCoinBSH + deploy_javascore_FeeAggregation + deploy_javascore_IRC2Token + deploy_javascore_NativeCoinIRC2BSH + # ------------------------------ + + # config contracts + goloop_bmc_addVerifier + goloop_bmc_addLink + goloop_bmc_addService + goloop_bmc_addServiceIRC2 + goloop_bmc_addRelay + goloop_bmc_setFeeAggregator + goloop_bsh_config_native_coin + goloop_bsh_config_native_coin_IRC2 + + moonbeam_bmc_addVerifier + moonbeam_bmc_addLink + moonbeam_bmc_addService + moonbeam_bmc_addService_ERC20 + moonbeam_bsh_registerCoin + # ------------------------------- + + ## finalizing + echo "finished provisioning at: $(date)" > $PROVISION_STATUS_DONE + clean_solidity_build + sleep 1 +} + +if [ ! -f "$PROVISION_STATUS_DONE" ]; then + if [ ! -f "$PROVISION_STATUS_STARTED" ]; then + provision + else + while [ ! -f $PROVISION_STATUS_DONE ]; + do + echo "waiting for an other BTP to finish provisioning settings..." + sleep 10 + done + fi +fi diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_dev.sh b/docker-compose/goloop2moonbeam/scripts/transfer_dev.sh new file mode 100644 index 00000000..a8300bb3 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_dev.sh @@ -0,0 +1,78 @@ +#!/bin/sh +set -e + +source transfer_util.sh + +MOONBEAM_PREFUND_PK=39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68 +MOONBEAM_GAS_LIMIT=6721975 +DEV_DEPOSIT_AMOUNT=$(coin2wei 10) +DEV_TRANSER_AMOUNT=$(coin2wei 1) + +deposit_DEV_for_bob() { + get_bob_balance + read -r -p "Do you want to deposit $(wei2coin $DEV_DEPOSIT_AMOUNT) DEV to BOB ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $DEV_DEPOSIT_AMOUNT) DEV for Bob" + + cd ${CONFIG_DIR} + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $MOONBEAM_PREFUND_PK \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(get_bob_address) \ + --value $DEV_DEPOSIT_AMOUNT | jq -r > tx.bob.deposit + + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.deposit) | jq -r .receipt + get_bob_balance + ;; + *) + echo "$1. Skip depositing DEV for Bob" + return 0 + ;; + esac +} + +transfer_DEV_from_bob_to_alice() { + echo "$1. Transfering $(wei2coin $DEV_TRANSER_AMOUNT) DEV from Bob to Alice" + + cd ${CONFIG_DIR} + encoded_data=$(eth method:encode abi.bsh_core.json "transferNativeCoin('$(cat alice.btp.address)')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $(get_bob_private_key) \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core.moonbeam) \ + --data $encoded_data \ + --value $DEV_TRANSER_AMOUNT | jq -r > tx.bob.transfer + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.transfer) | jq -r .receipt + get_bob_balance +} + +check_alice_balance_in_Goloop() { + echo "$1. Checking Alice's balance after 10 seconds..." + sleep 10 + + cd $CONFIG_DIR + coin_id=$(goloop rpc call \ + --to $(cat nativeCoinBsh.icon) \ + --method coinId --param _coinName=DEV | jq -r ) + echo "Alice coin_id: $coin_id" + + balance=$(goloop rpc call \ + --to $(cat irc31token.icon) \ + --method balanceOf \ + --param _owner=$(get_alice_address) \ + --param _id=$coin_id | jq -r ) + + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "Alice balance: $balance (DEV)" +} + +echo "This script demonstrates how to transfer a NativeCoin from MOONBEAM to ICON." +create_bob_account_in_Moonbeam "1" +deposit_DEV_for_bob "2" +create_alice_account_in_Gochain "3" +transfer_DEV_from_bob_to_alice "4" +check_alice_balance_in_Goloop "5" diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_icx.sh b/docker-compose/goloop2moonbeam/scripts/transfer_icx.sh new file mode 100644 index 00000000..18900863 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_icx.sh @@ -0,0 +1,85 @@ +#!/bin/sh +set -e +source transfer_util.sh + +ICX_DEPOSIT_AMOUNT=$(coin2wei 1000) # 1000 ICX +ICX_TRANSER_AMOUNT=$(coin2wei 10) # 1 ICX + +deposit_ICX_for_Alice() { + get_alice_balance + read -r -p "Do you want to deposit $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX to Alice ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX to Alice" + + cd ${CONFIG_DIR} + goloop rpc sendtx transfer \ + --to $(get_alice_address) \ + --value $ICX_DEPOSIT_AMOUNT | jq -r . > tx.alice.deposit + ensure_txresult tx.alice.deposit + get_alice_balance + ;; + *) return 0 + ;; + esac +} + +transfer_ICX_from_Alice_to_Bob() { + echo "$1. Transfer $(wei2coin $ICX_TRANSER_AMOUNT) ICX from Alice to Bob" + + cd ${CONFIG_DIR} + goloop rpc sendtx call \ + --to $(cat nativeCoinBsh.icon) --method transferNativeCoin \ + --param _to=$(cat bob.btp.address) --value $ICX_TRANSER_AMOUNT \ + --key_store alice.ks.json --key_secret alice.secret \ + | jq -r . > tx.alice.transfer + ensure_txresult tx.alice.transfer +} + +bob_balance_in_moonbeam_before_transfering() { + cd $CONFIG_DIR + eth abi:add bshcore abi.bsh_core.json + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core.moonbeam) "getBalanceOf('$(get_bob_address)', 'ICX')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + echo "$1. Bob's balance before transfering: $(wei2coin $BOB_BALANCE) (ICX)" +} + +bob_balance_in_moonbeam_after_transfering() { + echo "$1. Checking Bob's balance after transfering with 60s timeout" + cd $CONFIG_DIR + + BF_TF=$BOB_BALANCE + TIMEOUT=60 + + printf "[" + while true; + do + printf "â–“" + if [ $TIMEOUT -le 0 ]; then + printf "] error!\n" + echo "Timeout while checking Bob's balance." + exit 1 + fi + # sleep 3 seconds as the moonbeam block interval + sleep 3 + TIMEOUT=$(expr $TIMEOUT - 3) + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core.moonbeam) "getBalanceOf('$(get_bob_address)', 'ICX')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + if [ "$BOB_BALANCE" != "$BF_TF" ]; then + printf "] done!\n" + break + fi + done + echo "Bob's balance after transfering: $(wei2coin $BOB_BALANCE) (ICX)" +} + + +echo "This script demonstrates how to transfer a NativeCoin from ICON to MOONBEAM." +create_alice_account_in_Gochain "1" +deposit_ICX_for_Alice "2" +create_bob_account_in_Moonbeam "3" +bob_balance_in_moonbeam_before_transfering "4" +transfer_ICX_from_Alice_to_Bob "5" +bob_balance_in_moonbeam_after_transfering "6" diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_icon.sh b/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_icon.sh new file mode 100644 index 00000000..69213ef0 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_icon.sh @@ -0,0 +1,85 @@ +#!/bin/sh +set -e +source transfer_util.sh + +ICX_DEPOSIT_AMOUNT=$(coin2wei 1000) # 1000 ICX +ICX_TRANSER_AMOUNT=$(coin2wei 10) # 1 ICX + +deposit_ICX_for_Alice() { + get_alice_balance + read -r -p "Do you want to deposit $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX to Alice ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX to Alice" + + cd ${CONFIG_DIR} + goloop rpc sendtx transfer \ + --to $(get_alice_address) \ + --value $ICX_DEPOSIT_AMOUNT | jq -r . > tx.alice.deposit + ensure_txresult tx.alice.deposit + get_alice_balance + ;; + *) return 0 + ;; + esac +} + +transfer_ICX_from_Alice_to_Bob() { + echo "$1. Transfer $(wei2coin $ICX_TRANSER_AMOUNT) ICX from Alice to Bob" + + cd ${CONFIG_DIR} + goloop rpc sendtx call \ + --to $(cat nativeCoinIRC2Bsh.icon) --method transferNativeCoin \ + --param _to=$(cat bob.btp.address) --value $ICX_TRANSER_AMOUNT \ + --key_store alice.ks.json --key_secret alice.secret \ + | jq -r . > tx.alice.transfer + ensure_txresult tx.alice.transfer +} + +bob_balance_in_moonbeam_before_transfering() { + cd $CONFIG_DIR + eth abi:add bshcore abi.bsh_core_erc20.json + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core_erc20.moonbeam) "getBalanceOf('$(get_bob_address)', 'ICX')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + echo "$1. Bob's balance before transfering: $(wei2coin $BOB_BALANCE) (ICX)" +} + +bob_balance_in_moonbeam_after_transfering() { + echo "$1. Checking Bob's balance after transfering with 60s timeout" + cd $CONFIG_DIR + + BF_TF=$BOB_BALANCE + TIMEOUT=60 + + printf "[" + while true; + do + printf "â–“" + if [ $TIMEOUT -le 0 ]; then + printf "] error!\n" + echo "Timeout while checking Bob's balance." + exit 1 + fi + # sleep 3 seconds as the moonbeam block interval + sleep 3 + TIMEOUT=$(expr $TIMEOUT - 3) + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core_erc20.moonbeam) "getBalanceOf('$(get_bob_address)', 'ICX')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + if [ "$BOB_BALANCE" != "$BF_TF" ]; then + printf "] done!\n" + break + fi + done + echo "Bob's balance after transfering: $(wei2coin $BOB_BALANCE) (ICX)" +} + + +echo "This script demonstrates how to transfer a ICX from ICON to MOONBEAM." +create_alice_account_in_Gochain "1" +deposit_ICX_for_Alice "2" +create_bob_account_in_Moonbeam "3" +bob_balance_in_moonbeam_before_transfering "4" +transfer_ICX_from_Alice_to_Bob "5" +bob_balance_in_moonbeam_after_transfering "6" diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_moonbeam.sh b/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_moonbeam.sh new file mode 100644 index 00000000..b2490de0 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_icx_from_moonbeam.sh @@ -0,0 +1,105 @@ +#!/bin/sh +set -e + +source transfer_util.sh + +MOONBEAM_PREFUND_PK=39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68 +MOONBEAM_GAS_LIMIT=6721975 +ICX_DEPOSIT_AMOUNT=$(coin2wei 10) +ICX_TRANSER_AMOUNT=$(coin2wei 1) + +deposit_ICX_for_bob() { + get_bob_balance + read -r -p "Do you want to deposit $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX to BOB ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $ICX_DEPOSIT_AMOUNT) MOVR for Bob (for transaction fees)" + + cd ${CONFIG_DIR} + #fund bob with MOVR native coin for transaction gas + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $MOONBEAM_PREFUND_PK \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(get_bob_address) \ + --value $ICX_DEPOSIT_AMOUNT | jq -r > tx.bob.deposit + + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.deposit) | jq -r .receipt + get_bob_balance_MOVR + + echo "$1. Depositing $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX for Bob" + ##Fund bob with ERC20_ICX to transfer + encoded_data=$(eth method:encode abi.bsh_core_erc20.json "transfer('$(get_bob_address)', '$ICX_DEPOSIT_AMOUNT')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk 5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133 \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core_erc20.moonbeam) \ + --data $encoded_data | jq -r > tx.bob.transfer + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.transfer) | jq -r .receipt + + ;; + *) + echo "$1. Skip depositing ICX for Bob" + return 0 + ;; + esac +} + +transfer_ICX_from_bob_to_alice() { + echo "$1. Transfering $(wei2coin $ICX_TRANSER_AMOUNT) ICX from Bob to Alice" + + cd ${CONFIG_DIR} + + encoded_data1=$(eth method:encode abi.bsh_core_erc20.json "approve('$(cat bsh_core_erc20.moonbeam)','$ICX_TRANSER_AMOUNT')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $(get_bob_private_key) \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core_erc20.moonbeam) \ + --data $encoded_data1 | jq -r > tx.bob.approve.transfer + + encoded_data=$(eth method:encode abi.bsh_core_erc20.json "transferWrappedCoin('ICX','$ICX_TRANSER_AMOUNT','$(cat alice.btp.address)')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $(get_bob_private_key) \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core_erc20.moonbeam) \ + --data $encoded_data | jq -r > tx.bob.transfer + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.transfer) | jq -r .receipt +} + +fund_bsh_in_Icon() { + echo "$1. Fund ICON BSH with $(wei2coin $ICX_DEPOSIT_AMOUNT) ICX" + + cd ${CONFIG_DIR} + # goloop rpc sendtx transfer \ + # --to $(cat nativeCoinIRC2Bsh.icon) \ + # --value 1 | jq -r . > tx.fundBSH.icx.irc2 + # ensure_txresult tx.fundBSH.icx.irc2 + + goloop rpc sendtx call \ + --to $(cat nativeCoinIRC2Bsh.icon) --method transferNativeCoin \ + --param _to=$(cat bob.btp.address) --value $ICX_DEPOSIT_AMOUNT \ + | jq -r . > tx.fund.bsh.native.icon + ensure_txresult tx.fund.bsh.native.icon +} + +get_alice_balance_before_transfer(){ + get_alice_balance +} + +get_alice_balance_after_transfer(){ + echo "$1. Checking Alice's ICX balance after 10 seconds..." + sleep 10 + get_alice_balance +} + +echo "This script demonstrates how to transfer a ICX from MOONBEAM to ICON." +create_bob_account_in_Moonbeam "1" +deposit_ICX_for_bob "2" +create_alice_account_in_Gochain "3" +fund_bsh_in_Icon "4" +get_alice_balance_before_transfer "5" +transfer_ICX_from_bob_to_alice "6" +get_alice_balance_after_transfer "7" \ No newline at end of file diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_icon.sh b/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_icon.sh new file mode 100644 index 00000000..9e716939 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_icon.sh @@ -0,0 +1,138 @@ +#!/bin/sh +set -e +source transfer_util.sh + + +MOONBEAM_PREFUND_PK=39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68 +MOONBEAM_GAS_LIMIT=6721975 + +MOVR_DEPOSIT_AMOUNT=$(coin2wei 1000) # 1000 MOVR +MOVR_TRANSER_AMOUNT=$(coin2wei 10) # 1 MOVR + + +deposit_ICX_for_Alice() { + get_alice_balance + + echo "$1. Funding $(wei2coin $MOVR_DEPOSIT_AMOUNT) ICX to Alice for transaction Gas" + + cd ${CONFIG_DIR} + goloop rpc sendtx transfer \ + --to $(get_alice_address) \ + --value $MOVR_DEPOSIT_AMOUNT | jq -r . > tx.alice.deposit + ensure_txresult tx.alice.deposit + get_alice_balance +} + + +deposit_MOVR_for_Alice() { + get_alice_balance + read -r -p "Do you want to Fund $(wei2coin $MOVR_DEPOSIT_AMOUNT) MOVR to Alice ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $MOVR_DEPOSIT_AMOUNT) MOVR to Alice" + + cd ${CONFIG_DIR} + goloop rpc sendtx call --to $(cat irc2token.icon) \ + --method transfer \ + --param _to=$(get_alice_address) \ + --param _value=$MOVR_DEPOSIT_AMOUNT | jq -r . > tx.alice.deposit.irc2 + ensure_txresult tx.alice.deposit.irc2 + get_alice_irc2_balance + ;; + *) return 0 + ;; + esac +} + +Alice_deposit_MOVR_to_BSH(){ + echo "$1. Deposit $(wei2coin $MOVR_TRANSER_AMOUNT) MOVR from Alice to BSH" + cd ${CONFIG_DIR} + goloop rpc sendtx call --to $(cat irc2token.icon) \ + --key_store alice.ks.json --key_secret alice.secret \ + --method transfer \ + --param _to=$(cat nativeCoinIRC2Bsh.icon) --param _value=$MOVR_TRANSER_AMOUNT | jq -r . > tx.alice.deposit.irc2.bsh + + ensure_txresult tx.alice.deposit.irc2.bsh +} + +transfer_MOVR_from_Alice_to_Bob() { + echo "$1. Transfer $(wei2coin $MOVR_TRANSER_AMOUNT) MOVR from Alice to Bob" + + cd ${CONFIG_DIR} + goloop rpc sendtx call \ + --to $(cat nativeCoinIRC2Bsh.icon) --method transfer \ + --param _coinName=MOVR \ + --param _to=$(cat bob.btp.address) \ + --param _value=$MOVR_TRANSER_AMOUNT \ + --key_store alice.ks.json --key_secret alice.secret \ + | jq -r . > tx.alice.transfer.irc2 + ensure_txresult tx.alice.transfer.irc2 +} + +bob_balance_in_moonbeam_before_transfering() { + cd $CONFIG_DIR + eth abi:add bshcore abi.bsh_core.json + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core.moonbeam) "getBalanceOf('$(get_bob_address)', 'MOVR')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + echo "$1. Bob's balance before transfering: $(wei2coin $BOB_BALANCE) (MOVR)" +} + + +fund_bsh_in_Moonbeam(){ + echo "$1. Funding MoonBeam BSH" + + cd ${CONFIG_DIR} + encoded_data=$(eth method:encode abi.bsh_core.json "transferNativeCoin('$(cat alice.btp.address)')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $MOONBEAM_PREFUND_PK \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core_erc20.moonbeam) \ + --data $encoded_data \ + --value $(coin2wei 10000) | jq -r > tx.bob.transfer + RESULT=$(eth address:balance --network $MOONBEAM_RPC_URL $(cat bsh_core_erc20.moonbeam)) + echo "BSH balance after funding: $RESULT (MOVR)" +} + +bob_balance_in_moonbeam_after_transfering() { + echo "$1. Checking Bob's balance after transfering with 60s timeout" + cd $CONFIG_DIR + + BF_TF=$BOB_BALANCE + TIMEOUT=60 + + printf "[" + while true; + do + printf "â–“" + if [ $TIMEOUT -le 0 ]; then + printf "] error!\n" + echo "Timeout while checking Bob's balance." + exit 1 + fi + # sleep 3 seconds as the moonbeam block interval + sleep 3 + TIMEOUT=$(expr $TIMEOUT - 3) + + RESULT=$(eth contract:call --network $MOONBEAM_RPC_URL bshcore@$(cat bsh_core_erc20.moonbeam) "getBalanceOf('$(get_bob_address)', 'MOVR')") + BOB_BALANCE=$(echo "$RESULT" | awk '/_usableBalance/ {print $2}' | sed 's/[^0-9]*//g') + if [ "$BOB_BALANCE" != "$BF_TF" ]; then + printf "] done!\n" + break + fi + done + echo "Bob's balance after transfering: $(wei2coin $BOB_BALANCE) (MOVR)" +} + + +echo "This script demonstrates how to transfer a MOVR from ICON to MOONBEAM." +create_alice_account_in_Gochain "1" +deposit_ICX_for_Alice "2" +deposit_MOVR_for_Alice "3" +Alice_deposit_MOVR_to_BSH "4" +create_bob_account_in_Moonbeam "5" +fund_bsh_in_Moonbeam "6" +bob_balance_in_moonbeam_before_transfering "7" +transfer_MOVR_from_Alice_to_Bob "8" +bob_balance_in_moonbeam_after_transfering "9" diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_moonbeam.sh b/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_moonbeam.sh new file mode 100644 index 00000000..b109804d --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_movr_from_moonbeam.sh @@ -0,0 +1,115 @@ +#!/bin/sh +set -e + +source transfer_util.sh + +MOONBEAM_PREFUND_PK=39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68 +MOONBEAM_GAS_LIMIT=6721975 +MOVR_DEPOSIT_AMOUNT=$(coin2wei 10) +MOVR_TRANSER_AMOUNT=$(coin2wei 1) + +deposit_MOVR_for_bob() { + get_bob_balance_MOVR + read -r -p "Do you want to deposit $(wei2coin $MOVR_DEPOSIT_AMOUNT) (MOVR) to BOB ? [Y/n] " confirm + case $confirm in + [yY][eE][sS]|[yY]) + echo "$1. Depositing $(wei2coin $MOVR_DEPOSIT_AMOUNT) MOVR for Bob" + + cd ${CONFIG_DIR} + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $MOONBEAM_PREFUND_PK \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(get_bob_address) \ + --value $MOVR_DEPOSIT_AMOUNT | jq -r > tx.bob.deposit + + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.deposit) | jq -r .receipt + get_bob_balance + ;; + *) + echo "$1. Skip depositing MOVR for Bob" + return 0 + ;; + esac +} + +transfer_MOVR_from_bob_to_alice() { + echo "$1. Transfering $(wei2coin $MOVR_TRANSER_AMOUNT) (MOVR) from Bob to Alice" + + cd ${CONFIG_DIR} + encoded_data=$(eth method:encode abi.bsh_core.json "transferNativeCoin('$(cat alice.btp.address)')") + eth transaction:send \ + --network $MOONBEAM_RPC_URL \ + --pk $(get_bob_private_key) \ + --gas $MOONBEAM_GAS_LIMIT \ + --to $(cat bsh_core_erc20.moonbeam) \ + --data $encoded_data \ + --value $MOVR_TRANSER_AMOUNT | jq -r > tx.bob.transfer + eth transaction:get --network $MOONBEAM_RPC_URL $(cat tx.bob.transfer) | jq -r .receipt + get_bob_balance_MOVR +} + + +fund_bsh_in_Icon() { + echo "$1. Fund ICON BSH with $(wei2coin $MOVR_DEPOSIT_AMOUNT) MOVR" + + cd ${CONFIG_DIR} + goloop rpc sendtx call \ + --to $(cat irc2token.icon) --method transfer \ + --param _to=$(cat nativeCoinIRC2Bsh.icon) \ + --param _value=$MOVR_DEPOSIT_AMOUNT \ + | jq -r . > tx.fundBSH.irc2 + ensure_txresult tx.fundBSH.irc2 + get_bsh_irc2_balance +} + + +get_bsh_irc2_balance() { + cd $CONFIG_DIR + balance=$(goloop rpc call --to $(cat irc2token.icon) \ + --method balanceOf \ + --param _owner=$(cat nativeCoinIRC2Bsh.icon) | jq -r .) + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "BSH balance:: $balance (MOVR)" +} + + +check_alice_balance_in_Goloop_before_transfer() { + echo "$1. Checking Alice's balance Before Transfer" + cd $CONFIG_DIR + + balance=$(goloop rpc call \ + --to $(cat irc2token.icon) \ + --method balanceOf \ + --param _owner=$(get_alice_address) | jq -r ) + + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "Alice balance: $balance (MOVR)" +} + +check_alice_balance_in_Goloop() { + echo "$1. Checking Alice's balance after 10 seconds..." + sleep 10 + + cd $CONFIG_DIR + + balance=$(goloop rpc call \ + --to $(cat irc2token.icon) \ + --method balanceOf \ + --param _owner=$(get_alice_address) | jq -r ) + + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "Alice balance: $balance (MOVR)" +} + +echo "This script demonstrates how to transfer a MOVR from MOONBEAM to ICON." +create_bob_account_in_Moonbeam "1" +deposit_MOVR_for_bob "2" +create_alice_account_in_Gochain "3" +fund_bsh_in_Icon "4" +check_alice_balance_in_Goloop_before_transfer "5" +transfer_MOVR_from_bob_to_alice "6" +check_alice_balance_in_Goloop "7" diff --git a/docker-compose/goloop2moonbeam/scripts/transfer_util.sh b/docker-compose/goloop2moonbeam/scripts/transfer_util.sh new file mode 100644 index 00000000..88488828 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/transfer_util.sh @@ -0,0 +1,87 @@ +#!/bin/sh +set -e + +source util.sh +if [ ! -f "$PROVISION_STATUS_DONE" ]; then + echo "provisioning not done yet" + exit 1 +fi + +PRECISION=18 +COIN_UNIT=$((10 ** $PRECISION)) + +coin2wei() { + amount=$1 + printf '%s * %s\n' $COIN_UNIT $amount | bc +} + +wei2coin() { + amount=$1 + printf 'scale=%s; %s / %s\n' $PRECISION $amount $COIN_UNIT | bc +} + +create_bob_account_in_Moonbeam() { + cd ${CONFIG_DIR} + if [ ! -f bob.btp.address ]; then + echo "$1. Creating Bob's account in Moonbeam" + + eth address:random > bob.account + echo "btp://$(cat net.btp.moonbeam)/$(get_bob_address)" > bob.btp.address + else + echo "$1. Skip creating Bob account. Already existed" + fi + echo "Bob's btp address: $(cat bob.btp.address)" +} + +get_bob_address() { + cat ${CONFIG_DIR}/bob.account | jq -r .address +} + +get_bob_private_key() { + cat ${CONFIG_DIR}/bob.account | jq -r .privateKey | sed -e 's/^0x//' +} + +get_bob_balance() { + bob_balance=$(eth address:balance --network $MOONBEAM_RPC_URL $(get_bob_address)) + echo "Bob's balance: $bob_balance (DEV)" +} + +create_alice_account_in_Gochain() { + cd ${CONFIG_DIR} + if [ ! -f alice.secret ]; then + echo "$1. Creating Alice account in ICON" + + echo -n $(date|md5sum|head -c16) > alice.secret + goloop ks gen -o alice.ks.json -p $(cat alice.secret) + echo "btp://$(cat net.btp.icon)/$(cat alice.ks.json | jq -r .address)" > alice.btp.address + else + echo "$1. Skip creating Alice account. Already existed" + fi + echo "Alice's btp address: $(cat alice.btp.address)" +} + +get_alice_address() { + cat alice.ks.json | jq -r .address +} + +get_alice_balance() { + balance=$(goloop rpc balance $(get_alice_address) | jq -r) + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "Alice's balance: $balance (ICX)" +} + +get_alice_irc2_balance() { + cd $CONFIG_DIR + balance=$(goloop rpc call --to $(cat irc2token.icon) \ + --method balanceOf \ + --param _owner=$(get_alice_address) | jq -r .) + balance=$(hex2int $balance) + balance=$(wei2coin $balance) + echo "Alice's balance:: $balance (MOVR)" +} + +get_bob_balance_MOVR() { + bob_balance=$(eth address:balance --network $MOONBEAM_RPC_URL $(get_bob_address)) + echo "Bob's balance: $bob_balance (MOVR)" +} diff --git a/docker-compose/goloop2moonbeam/scripts/util.sh b/docker-compose/goloop2moonbeam/scripts/util.sh new file mode 100644 index 00000000..9c371408 --- /dev/null +++ b/docker-compose/goloop2moonbeam/scripts/util.sh @@ -0,0 +1,132 @@ +export CONFIG_DIR=${CONFIG_DIR:-/btpsimple/config} +export SCRIPT_DIR=${SCRIPT_DIR:-/btpsimple/scripts} +export SOLIDITY_DIST_DIR=${SOLIDITY_DIST_DIR:-/btpsimple/contracts/solidity} +export JAVASCORE_DIST_DIR=${JAVASCORE_DIST_DIR:-/btpsimple/contracts/javascore} +export JAVASCORE_HELPER_DIR=${JAVASCORE_HELPER_DIR:-$JAVASCORE_DIST_DIR/helper} +export MOONBEAM_CHAIN_ID=1281 # https://github.com/PureStake/moonbeam#chain-ids +export MOONBEAM_RPC_URL=${MOONBEAM_RPC_URL:-'http://moonbeam:9933'} +export PROVISION_STATUS_DONE=$CONFIG_DIR/provision.done +export PROVISION_STATUS_STARTED=$CONFIG_DIR/provision.started + +# goloop env +export GOLOOP_CONFIG=$CONFIG_DIR/$GOLOOPCHAIN.server.json +export GOLOOP_KEY_STORE=$CONFIG_DIR/$GOLOOPCHAIN.keystore.json +export GOLOOP_KEY_SECRET=$CONFIG_DIR/$GOLOOPCHAIN.keysecret +export GOLOOP_RPC_URI=http://$GOLOOPCHAIN:9080/api/v3/icon +export GOLOOP_RPC_KEY_STORE=$CONFIG_DIR/$GOLOOPCHAIN.keystore.json +export GOLOOP_RPC_KEY_SECRET=$CONFIG_DIR/$GOLOOPCHAIN.keysecret +export GOLOOP_RPC_NID=${GOLOOP_RPC_NID:-$(cat $CONFIG_DIR/nid.icon)} +export GOLOOP_RPC_STEP_LIMIT=${GOLOOP_RPC_STEP_LIMIT:-5000000000} +export GOLOOP_CHAINSCORE=cx0000000000000000000000000000000000000000 +GOLOOPCHAIN=${GOLOOPCHAIN:-'goloop'} + +# disable line lenght to support big number operators +export BC_LINE_LENGTH=0 + + +#Token Config +export TOKEN_NAME=MOVR +export TOKEN_SYM=MOVR +export TOKEN_SUPPLY=0x186A0 +export TOKEN_DECIMALS=0x12 +export SVC_NAME=NativeCoinIRC2BSH + +latest_block_goloop() { + goloop rpc lastblock +} + +moonbeam_blocknumber() { + curl -s -X POST 'http://moonbeam:9933' --header 'Content-Type: application/json' \ + --data-raw '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[], "id": 1}' | jq -r .result | xargs printf "%d\n" +} + +wait_file_created() { + FILE_DIR=$1 + FILE_NAME=$2 + + timeout=10 + while [ ! -f $FILE_DIR/$FILE_NAME ]; + do + if [ "$timeout" == 0 ]; then + echo "ERROR: Timeout while waiting for the file $FILE_NAME." + exit 1 + fi + sleep 1 + timeout=$(expr $timeout - 1) + + echo "waiting for the output file: $FILE_NAME" + done +} + +ensure_txresult() { + OLD_SET_OPTS=$(set +o) + set +e + local TX=$1 + + if [ -f "${TX}" ]; then + TX=$(cat ${TX}) + fi + + sleep 2 + RESULT=$(goloop rpc txresult ${TX}) + RET=$? + echo $RESULT + while [ "$RET" != "0" ] && [ "$(echo $RESULT | grep -E 'Executing|Pending')" == "$RESULT" ]; do + sleep 1 + RESULT=$(goloop rpc txresult ${TX}) + RET=$? + echo $RESULT + done + eval "${OLD_SET_OPTS}" + + if [ "$RET" != "0" ]; then + echo $RESULT + exit $RET + else + STATUS=$(echo $RESULT | jq -r .status) + if [ "$STATUS" == "0x1" ]; then + return 0 + else + echo $RESULT + exit 1 + fi + fi +} + +extract_scoreAddress() { + local TX=$1 + local ADDR=$2 + + RESULT=$(ensure_txresult $TX) + RET=$? + + if [ "$RET" != "0" ]; then + echo $RESULT + return $RET + else + SCORE=$(echo $RESULT | jq -r .scoreAddress) + echo $SCORE | tee ${ADDR} + fi +} + +uppercase() { + input=$1 + printf '%s\n' "$input" | awk '{ print toupper($0) }' +} + +hex2int() { + input=$1 + input=$(echo $input | sed 's/^0x//g') + input=$(uppercase $input) + echo "ibase=16; $input" | bc +} + +ensure_file_exist() { + FILE_DIR=$1 + FILE_NAME=$2 + + if [ ! -f $FILE_DIR/$FILE_NAME ]; then + echo "Missing file $FILE_NAME" + exit 1 + fi +} \ No newline at end of file diff --git a/docker/btpsimple/Dockerfile b/docker/btpsimple/Dockerfile index 8fd9cfec..bb306649 100644 --- a/docker/btpsimple/Dockerfile +++ b/docker/btpsimple/Dockerfile @@ -13,7 +13,14 @@ COPY dist/bin/btpsimple /btpsimple/bin/btpsimple ENV PATH $PATH:/btpsimple/bin # copy extras -COPY dist/pyscore/*.zip /btpsimple/contracts/pyscore/ +COPY dist/contracts/pyscore/*.zip /btpsimple/contracts/pyscore/ +COPY dist/contracts/javascore /btpsimple/contracts/javascore +COPY dist/contracts/solidity /btpsimple/contracts/solidity + +ENV JAVASCORE_DIST_DIR=/btpsimple/contracts/javascore +ENV SOLIDITY_DIST_DIR=/btpsimple/contracts/solidity +ENV PYSCORE_DIST_DIR=/btpsimple/contracts/pyscore + WORKDIR /btpsimple # container configuration @@ -27,40 +34,8 @@ ENV BTPSIMPLE_KEY_SECRET=/btpsimple/config/keysecret ENV BTPSIMPLE_LOG_WRITER_FILENAME=/btpsimple/data/btpsimple.log # entrypoint -RUN { \ - echo '#!/bin/sh'; \ - echo 'set -e'; \ - echo 'if [ "$BTPSIMPLE_CONFIG" != "" ] && [ ! -f "$BTPSIMPLE_CONFIG" ]; then'; \ - echo ' UNSET="BTPSIMPLE_CONFIG"' ; \ - echo ' CMD="btpsimple save $BTPSIMPLE_CONFIG"'; \ - echo ' if [ "$BTPSIMPLE_KEY_SECRET" != "" ] && [ ! -f "$BTPSIMPLE_KEY_SECRET" ]; then'; \ - echo ' mkdir -p $(dirname $BTPSIMPLE_KEY_SECRET)'; \ - echo ' echo -n $(date|md5sum|head -c16) > $BTPSIMPLE_KEY_SECRET' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_KEY_STORE" != "" ] && [ ! -f "$BTPSIMPLE_KEY_STORE" ]; then'; \ - echo ' UNSET="$UNSET BTPSIMPLE_KEY_STORE"' ; \ - echo ' CMD="$CMD --save_key_store=$BTPSIMPLE_KEY_STORE"' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_SRC_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_SRC_ADDRESS" ]; then'; \ - echo ' export BTPSIMPLE_SRC_ADDRESS=$(cat ${BTPSIMPLE_SRC_ADDRESS})' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_SRC_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_SRC_ENDPOINT" ]; then'; \ - echo ' export BTPSIMPLE_SRC_ENDPOINT=$(cat ${BTPSIMPLE_SRC_ENDPOINT})' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_DST_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_DST_ADDRESS" ]; then'; \ - echo ' export BTPSIMPLE_DST_ADDRESS=$(cat ${BTPSIMPLE_DST_ADDRESS})' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_DST_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_DST_ENDPOINT" ]; then'; \ - echo ' export BTPSIMPLE_DST_ENDPOINT=$(cat ${BTPSIMPLE_DST_ENDPOINT})' ; \ - echo ' fi'; \ - echo ' if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then'; \ - echo ' export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET})' ; \ - echo ' fi'; \ - echo ' sh -c "unset $UNSET ; $CMD"' ; \ - echo 'fi'; \ - echo 'exec "$@"'; \ - } > /entrypoint \ - && chmod +x /entrypoint -ENTRYPOINT ["/entrypoint"] +ADD entrypoint.sh /entrypoint.sh +RUN chmod u+x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] CMD btpsimple start diff --git a/docker/btpsimple/entrypoint.sh b/docker/btpsimple/entrypoint.sh new file mode 100644 index 00000000..a36c3f3a --- /dev/null +++ b/docker/btpsimple/entrypoint.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -e + + +if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) +fi + +if [ "$BTPSIMPLE_CONFIG" != "" ] && [ ! -f "$BTPSIMPLE_CONFIG" ]; then + UNSET="BTPSIMPLE_CONFIG" + CMD="btpsimple save $BTPSIMPLE_CONFIG" + if [ "$BTPSIMPLE_KEY_SECRET" != "" ] && [ ! -f "$BTPSIMPLE_KEY_SECRET" ]; then + mkdir -p $(dirname $BTPSIMPLE_KEY_SECRET) + printf $(date|md5sum|head -c16) > $BTPSIMPLE_KEY_SECRET + fi + if [ "$BTPSIMPLE_KEY_STORE" != "" ] && [ ! -f "$BTPSIMPLE_KEY_STORE" ]; then + UNSET="$UNSET BTPSIMPLE_KEY_STORE" + CMD="$CMD --save_key_store=$BTPSIMPLE_KEY_STORE" + fi + if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) + fi + + if [ "$BTPSIMPLE_SRC_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_SRC_ADDRESS" ]; then + export BTPSIMPLE_SRC_ADDRESS=$(cat ${BTPSIMPLE_SRC_ADDRESS}) + fi + if [ "$BTPSIMPLE_SRC_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_SRC_ENDPOINT" ]; then + export BTPSIMPLE_SRC_ENDPOINT=$(cat ${BTPSIMPLE_SRC_ENDPOINT}) + fi + if [ "$BTPSIMPLE_DST_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_DST_ADDRESS" ]; then + export BTPSIMPLE_DST_ADDRESS=$(cat ${BTPSIMPLE_DST_ADDRESS}) + fi + if [ "$BTPSIMPLE_DST_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_DST_ENDPOINT" ]; then + export BTPSIMPLE_DST_ENDPOINT=$(cat ${BTPSIMPLE_DST_ENDPOINT}) + fi + sh -c "unset $UNSET ; $CMD" +fi +exec "$@" \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 00000000..a36c3f3a --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -e + + +if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) +fi + +if [ "$BTPSIMPLE_CONFIG" != "" ] && [ ! -f "$BTPSIMPLE_CONFIG" ]; then + UNSET="BTPSIMPLE_CONFIG" + CMD="btpsimple save $BTPSIMPLE_CONFIG" + if [ "$BTPSIMPLE_KEY_SECRET" != "" ] && [ ! -f "$BTPSIMPLE_KEY_SECRET" ]; then + mkdir -p $(dirname $BTPSIMPLE_KEY_SECRET) + printf $(date|md5sum|head -c16) > $BTPSIMPLE_KEY_SECRET + fi + if [ "$BTPSIMPLE_KEY_STORE" != "" ] && [ ! -f "$BTPSIMPLE_KEY_STORE" ]; then + UNSET="$UNSET BTPSIMPLE_KEY_STORE" + CMD="$CMD --save_key_store=$BTPSIMPLE_KEY_STORE" + fi + if [ "$BTPSIMPLE_OFFSET" != "" ] && [ -f "$BTPSIMPLE_OFFSET" ]; then + export BTPSIMPLE_OFFSET=$(cat ${BTPSIMPLE_OFFSET}) + fi + + if [ "$BTPSIMPLE_SRC_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_SRC_ADDRESS" ]; then + export BTPSIMPLE_SRC_ADDRESS=$(cat ${BTPSIMPLE_SRC_ADDRESS}) + fi + if [ "$BTPSIMPLE_SRC_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_SRC_ENDPOINT" ]; then + export BTPSIMPLE_SRC_ENDPOINT=$(cat ${BTPSIMPLE_SRC_ENDPOINT}) + fi + if [ "$BTPSIMPLE_DST_ADDRESS" != "" ] && [ -f "$BTPSIMPLE_DST_ADDRESS" ]; then + export BTPSIMPLE_DST_ADDRESS=$(cat ${BTPSIMPLE_DST_ADDRESS}) + fi + if [ "$BTPSIMPLE_DST_ENDPOINT" != "" ] && [ -f "$BTPSIMPLE_DST_ENDPOINT" ]; then + export BTPSIMPLE_DST_ENDPOINT=$(cat ${BTPSIMPLE_DST_ENDPOINT}) + fi + sh -c "unset $UNSET ; $CMD" +fi +exec "$@" \ No newline at end of file diff --git a/go.mod b/go.mod index b22b1fb2..75ae8006 100644 --- a/go.mod +++ b/go.mod @@ -4,30 +4,51 @@ go 1.13 require ( github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 // indirect + github.com/ChainSafe/go-schnorrkel v0.0.0-20210527232834-58622d036665 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/bshuster-repo/logrus-logstash-hook v0.4.1 + github.com/centrifuge/go-substrate-rpc-client/v3 v3.0.2 + github.com/davecgh/go-spew v1.1.1 + github.com/deckarep/golang-set v1.7.1 github.com/dgraph-io/badger v1.5.4 - github.com/dgryski/go-farm v0.0.0-20190416075124-e1214b5e05dc // indirect + github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 // indirect + github.com/ethereum/go-ethereum v1.10.4 github.com/evalphobia/logrus_fluent v0.5.4 github.com/fluent/fluent-logger-golang v1.4.0 // indirect - github.com/gofrs/uuid v3.2.0+incompatible - github.com/gorilla/websocket v1.4.0 + github.com/gammazero/workerpool v1.1.2 + github.com/getsentry/sentry-go v0.2.1 + github.com/gofrs/uuid v3.3.0+incompatible + github.com/google/go-cmp v0.5.5 // indirect + github.com/gorilla/websocket v1.4.2 github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6 + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/itering/scale.go v1.1.23 + github.com/itering/substrate-api-rpc v0.4.6 + github.com/jpillora/backoff v1.0.0 github.com/jroimartin/gocui v0.4.0 - github.com/labstack/echo/v4 v4.1.10 - github.com/mattn/go-runewidth v0.0.4 // indirect + github.com/labstack/echo/v4 v4.1.11 + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/mitchellh/mapstructure v1.1.2 github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e // indirect - github.com/philhofer/fwd v1.0.0 // indirect - github.com/pkg/errors v0.8.1 - github.com/sirupsen/logrus v1.4.2 - github.com/spf13/cobra v0.0.5 - github.com/spf13/pflag v1.0.3 - github.com/spf13/viper v1.4.0 - github.com/stretchr/testify v1.4.0 - github.com/syndtr/goleveldb v1.0.0 + github.com/philhofer/fwd v1.1.1 // indirect + github.com/pingcap/errors v0.11.4 // indirect + github.com/pkg/errors v0.9.1 + github.com/shopspring/decimal v1.3.0 // indirect + github.com/sirupsen/logrus v1.6.0 + github.com/spf13/cobra v1.1.1 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.7.0 + github.com/stretchr/testify v1.7.0 + github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 github.com/tinylib/msgp v1.1.2 // indirect github.com/vmihailenco/msgpack/v4 v4.3.11 - golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/sys v0.0.0-20211020064051-0ec99a608a1b // indirect + gopkg.in/ini.v1 v1.51.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce ) + +replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v1.10.4 diff --git a/go.sum b/go.sum index 8d70a539..48c95d1c 100644 --- a/go.sum +++ b/go.sum @@ -1,255 +1,804 @@ 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/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/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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +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/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +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= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= 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-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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20201021020641-d3c6d3118d10/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v0.0.0-20210318173838-ccb5cd955283/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v0.0.0-20210527232834-58622d036665 h1:Cx+AS7a+D7kT247ns4BH09NKDUOZzZN0YtMiCAx7ZBE= +github.com/ChainSafe/go-schnorrkel v0.0.0-20210527232834-58622d036665/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c3fqvvgKm5o= +github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= +github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= +github.com/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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aws/aws-sdk-go-v2 v1.2.0/go.mod h1:zEQs02YRBw1DjK0PoJv3ygDYOFTre1ejlJWl8FwAuQo= +github.com/aws/aws-sdk-go-v2/config v1.1.1/go.mod h1:0XsVy9lBI/BCXm+2Tuvt39YmdHwS5unDQmxZOYe8F5Y= +github.com/aws/aws-sdk-go-v2/credentials v1.1.1/go.mod h1:mM2iIjwl7LULWtS6JCACyInboHirisUUdkBPoTHMOUo= +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/service/internal/presigned-url v1.0.2/go.mod h1:45MfaXZ0cNbeuT0KQ1XJylq8A6+OpVV2E5kvY/Kq+u8= +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/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= +github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= +github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +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/bshuster-repo/logrus-logstash-hook v0.4.1 h1:pgAtgj+A31JBVtEHu2uHuEx0n+2ukqUJnS2vVe5pQNA= github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +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/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +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/centrifuge/go-substrate-rpc-client/v3 v3.0.2 h1:SQNaOeTmW2y2fmJgR5a7KIozjaOYi34GxafQ4efGc5U= +github.com/centrifuge/go-substrate-rpc-client/v3 v3.0.2/go.mod h1:ZYSX8OuIJgZ9aVdKLhIi1G4Rj42Ys4nZNsWW70yfCJc= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= +github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= +github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q= 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/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +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= +github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= +github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY= +github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= +github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= +github.com/decred/base58 v1.0.3 h1:KGZuh8d1WEMIrK0leQRM47W85KqCAdl2N+uagbctdDI= +github.com/decred/base58 v1.0.3/go.mod h1:pXP9cXCfM2sFLb2viz2FNIdeMWmZDBKG3ZBYbiSM78E= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/dgraph-io/badger v1.5.4 h1:gVTrpUTbbr/T24uvoCaqY2KSHfNLVGm0w+hbee2HMeg= github.com/dgraph-io/badger v1.5.4/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190416075124-e1214b5e05dc h1:VxEJYcOh1LMAdhIiHkofa6UC0PZvCmielUgJXgAAWFU= -github.com/dgryski/go-farm v0.0.0-20190416075124-e1214b5e05dc/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +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 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +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/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= +github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ethereum/go-ethereum v1.10.4 h1:JPZPL2MHbegfFStcaOrrggMVIcf57OQHQ0J3UhjQ+xQ= +github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg= github.com/evalphobia/logrus_fluent v0.5.4 h1:G4BSBTm7+L+oanWfFtA/A5Y3pvL2OMxviczyZPYO5xc= github.com/evalphobia/logrus_fluent v0.5.4/go.mod h1:hasyj+CXm3BDP1YhFk/rnTcjlegyqvkokV9A25cQsaA= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fluent/fluent-logger-golang v1.4.0 h1:uT1Lzz5yFV16YvDwWbjX6s3AYngnJz8byTCsMTIS0tU= github.com/fluent/fluent-logger-golang v1.4.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/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 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/gammazero/deque v0.1.0 h1:f9LnNmq66VDeuAlSAapemq/U7hJ2jpIWa4c09q8Dlik= +github.com/gammazero/deque v0.1.0/go.mod h1:KQw7vFau1hHuM8xmI9RbgKFbAsQFWmBpqQ2KenFLk6M= +github.com/gammazero/workerpool v1.1.2 h1:vuioDQbgrz4HoaCi2q1HLlOXdpbap5AET7xu5/qj87g= +github.com/gammazero/workerpool v1.1.2/go.mod h1:UelbXcO0zCIGFcufcirHhq2/xtLXJdQ29qZNlXG9OjQ= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/getsentry/sentry-go v0.2.1 h1:zCyr8wS1NQM7ixbWNh8lBRMlQZSM3aMXQCqXgCDwI44= +github.com/getsentry/sentry-go v0.2.1/go.mod h1:2QfSdvxz4IZGyB5izm1TtADFhlhfj1Dcesrg8+A/T9Y= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +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-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +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-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-sourcemap/sourcemap v2.1.2+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 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= -github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84= +github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +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/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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +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/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I= +github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/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/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= +github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6 h1:HE4YDtvtpZgjRJ2tCOmaXlcpBTFG2e0jvfNntM5sXOs= github.com/haltingstate/secp256k1-go v0.0.0-20151224084235-572209b26df6/go.mod h1:73mKQiY8bLnscfGakn57WAJZTzT0eSUAy3qgMQNR/DI= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru 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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= +github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88 h1:bcAj8KroPf552TScjFPIakjH2/tdIrIH8F+cc4v4SRo= +github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 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.8.3/go.mod h1:JugdFhsvvI8gadxOI6noqNeeBHvWNTbfYGtiAn+2jhI= +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/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/itering/scale.go v1.1.22/go.mod h1:Eg8T/07SKybkN9E1LmiEdSN/pLtV4rZDN9zuj486Gbs= +github.com/itering/scale.go v1.1.23 h1:l1c0mU8p7zCN30X7RO+qO0hyHd/OOCXVXGu1uOkpAgw= +github.com/itering/scale.go v1.1.23/go.mod h1:Eg8T/07SKybkN9E1LmiEdSN/pLtV4rZDN9zuj486Gbs= +github.com/itering/substrate-api-rpc v0.4.6 h1:XmIFxwEPeCLVgJtuoECH/wFy1Tc9b+VDoiuhV4oZlrM= +github.com/itering/substrate-api-rpc v0.4.6/go.mod h1:Vid403JTqrVkVnVVVbRbHm17b6dZSCGg1hPTI7V6VC8= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458 h1:6OvNmYgJyexcZ3pYbTI9jWx5tHo1Dee/tWbLMfPe2TA= +github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +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 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +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/jroimartin/gocui v0.4.0 h1:52jnalstgmc25FmtGcWqa0tcbMEWS6RpFLsOIO+I+E8= github.com/jroimartin/gocui v0.4.0/go.mod h1:7i7bbj99OgFHzo7kB2zPb8pXLqMBSQegY7azfqXMkyY= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +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 h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/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/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw= +github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= 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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +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/cpuid v0.0.0-20170728055534-ae7887de9fa5/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 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +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 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/labstack/echo/v4 v4.1.10 h1:/yhIpO50CBInUbE/nHJtGIyhBv0dJe2cDAYxc3V3uMo= -github.com/labstack/echo/v4 v4.1.10/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= +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 v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.1.11 h1:z0BZoArY4FqdpUEl+wlHp4hnr/oSR6MTmQmv8OHSoww= +github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +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.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/mattn/go-runewidth v0.0.3/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.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94= +github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/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/nsf/termbox-go v0.0.0-20190325093121-288510b9734e h1:Vbib8wJAaMEF9jusI/kMSYMr/LtRzM7+F9MJgt/nH8k= github.com/nsf/termbox-go v0.0.0-20190325093121-288510b9734e/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= +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.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +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 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +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.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +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/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/paulbellamy/ratecounter v0.2.0/go.mod h1:Hfx1hDpSGoqxkVVpBi/IlYD7kChlfo5C6hzIHwPqfFE= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/peterh/liner v1.0.1-0.20180619022028-8c1271fcf47f/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7 h1:oYW+YCJ1pachXTQmzR3rNLYGGz4g/UgFcjb28p/viDM= +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/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/xxHash v0.1.5 h1:n/jBpwTHiER4xYvK3/CdPVnLDPchj8eTJFFLUb4QHBo= +github.com/pierrec/xxHash v0.1.5/go.mod h1:w2waW5Zoa/Wc4Yqe0wgrIYAGKqRMf7czn2HNKXmuL+I= +github.com/pingcap/errors v0.11.1/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= 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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= 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/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= 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/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= 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/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= +github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rjeczalik/notify v0.9.1 h1:CLCKso/QK1snAlnhNR/CNvNiFU2saUtjV0bx3EwNeCE= +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/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +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/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.0 h1:KK3gWIXskZ2O1U/JNTisNcvH+jveJxZYrjbTsrbbnh8= +github.com/shopspring/decimal v1.3.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +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 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4 h1:Gb2Tyox57NRNuZ2d3rmvB3pcmbu7O1RS3m8WRx7ilrg= +github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q= 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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= +github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +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/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= +github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= +github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4= +github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI= +github.com/tklauser/numcpus v0.2.2 h1:oyhllyrScuYI6g+h/zUvNXNp1wy7x8qQy3t/piefldA= +github.com/tklauser/numcpus v0.2.2/go.mod h1:x3qojaO3uyYt0i56EW/VUYs7uBvdl2fkfZFu0T9wgjM= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -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/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4= +github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/vedhavyas/go-subkey v1.0.2 h1:EW6U+1us4k38AtrBfFOEZTpW9FcF/cIUOxw/pHbNNQ0= +github.com/vedhavyas/go-subkey v1.0.2/go.mod h1:T9SEs84XZxRULMZLWtIl48s9rBNE7h6GnkqTgJR8+MU= github.com/vmihailenco/msgpack/v4 v4.3.11 h1:Q47CePddpNGNhk4GCnAx9DDtASi2rasatE0cd26cZoE= github.com/vmihailenco/msgpack/v4 v4.3.11/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= +github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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.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/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-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-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +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-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/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 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +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-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/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/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/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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= +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-20190724013045-ca1201d0de80/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 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/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 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-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ= +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-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201221093633-bc327ba9c2f0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/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-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211020064051-0ec99a608a1b h1:byBDhtWGQmWDrv1MlEv/BzGRMkw36h9QqsNnZQcDhRw= +golang.org/x/sys v0.0.0-20211020064051-0ec99a608a1b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +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 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +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-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/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-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-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-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +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= +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/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= +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/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 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 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/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +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.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= +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-20200619000410-60c24ae608a6/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 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 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/javascore/bmc/build.gradle b/javascore/bmc/build.gradle new file mode 100644 index 00000000..2549d62e --- /dev/null +++ b/javascore/bmc/build.gradle @@ -0,0 +1,94 @@ +version = '0.1.0' + +dependencies { + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + implementation project(':score-util') + implementation project(':lib') + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2") + + testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion") + testImplementation project(':test-lib') + testAnnotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion") + testImplementation("foundation.icon:javaee-score-client:$scoreClientVersion") + testImplementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + testImplementation("foundation.icon:icon-sdk:$iconsdkVersion") + testImplementation("com.github.javafaker:javafaker:1.0.2") +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.bmc.BTPMessageCenter' +// archivesBaseName = 'bmc' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = scoreTest.url + nid = scoreTest.parseNid(scoreTest.nid) + } + } + keystore = scoreTest.default.keyStore + password = scoreTest.default.resolvedKeyPassword + parameters { + arg('_net', scoreTest.nid+'.icon') + } +} + +test { + useJUnitPlatform { + if (!integrationTest) { + excludeTags("integration") + } + } + + options { + testLogging.showStandardStreams = true + systemProperty 'url', scoreTest.url + systemProperty 'nid', scoreTest.nid + systemProperty 'keyStore', scoreTest.default.keyStore + systemProperty 'keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'address', "cx9d9febc3a11565e26b6a0dbea2a4c3fd402bae86" +// systemProperty 'isUpdate', "true" + dependsOn optimizedJar + systemProperty 'scoreFilePath', project.tasks.optimizedJar.outputJarName + project.extensions.deployJar.arguments.each { + arg -> systemProperty 'params.'+arg.name, arg.value + } + + //for foundation.icon.btp.test.ScoreIntegrationTest + systemProperty 'tester.keyStore', scoreTest.tester.keyStore + systemProperty 'tester.keySecret', scoreTest.tester.keySecret + + //for bmv-mock client + systemProperty 'bmv-mock.url', scoreTest.url + systemProperty 'bmv-mock.nid', scoreTest.nid + systemProperty 'bmv-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bmv-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bmv-mock.address', "cx0f549056aeebed9c06833bebfe8b17113958532f" +// systemProperty 'bmv-mock.isUpdate', "true" + dependsOn ":test-lib:optimizedJarMockBMV" + systemProperty 'bmv-mock.scoreFilePath', tasks.getByPath(":test-lib:optimizedJarMockBMV").outputJarName + + //for bmv-mock client + systemProperty 'bsh-mock.url', scoreTest.url + systemProperty 'bsh-mock.nid', scoreTest.nid + systemProperty 'bsh-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bsh-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bsh-mock.address', "cx47853622a461b34885c42d74e8354b03a3e88a42" +// systemProperty 'bsh-mock.isUpdate', "true" + dependsOn ":test-lib:optimizedJarMockBSH" + systemProperty 'bsh-mock.scoreFilePath', tasks.getByPath(":test-lib:optimizedJarMockBSH").outputJarName + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCException.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCException.java new file mode 100644 index 00000000..e056809a --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCException.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPException; + +public class BMCException extends BTPException.BMC { + + public BMCException(Code c) { + super(c, c.name()); + } + + public BMCException(Code c, String message) { + super(c, message); + } + + public static BMCException unknown(String message) { + return new BMCException(Code.Unknown, message); + } + public static BMCException unauthorized() { + return new BMCException(Code.Unauthorized); + } + public static BMCException unauthorized(String message) { + return new BMCException(Code.Unauthorized, message); + } + public static BMCException invalidSn() { + return new BMCException(Code.InvalidSn); + } + public static BMCException alreadyExistsBMV() { + return new BMCException(Code.AlreadyExistsBMV); + } + public static BMCException notExistsBMV() { + return new BMCException(Code.NotExistsBMV); + } + public static BMCException alreadyExistsBSH() { + return new BMCException(Code.AlreadyExistsBSH); + } + public static BMCException notExistsBSH() { + return new BMCException(Code.NotExistsBSH); + } + public static BMCException alreadyExistsLink() { + return new BMCException(Code.AlreadyExistsLink); + } + public static BMCException notExistsLink() { + return new BMCException(Code.NotExistsLink); + } + public static BMCException alreadyExistsBMR() { + return new BMCException(Code.AlreadyExistsBMR); + } + public static BMCException notExistsBMR() { + return new BMCException(Code.NotExistsBMR); + } + public static BMCException unreachable() { + return new BMCException(Code.Unreachable); + } + + //BTPException.BMC => 10 ~ 24 + public enum Code implements BTPException.Coded{ + Unknown(0), + Unauthorized(1), + InvalidSn(2), + AlreadyExistsBMV(3), + NotExistsBMV(4), + AlreadyExistsBSH(5), + NotExistsBSH(6), + AlreadyExistsLink(7), + NotExistsLink(8), + AlreadyExistsBMR(9), + NotExistsBMR(10), + Unreachable(11); + + final int code; + Code(int code){ this.code = code; } + + @Override + public int code() { return code; } + + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCMessage.java new file mode 100644 index 00000000..71d8f643 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCMessage.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class BMCMessage { + private String type; + private byte[] payload; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public byte[] getPayload() { + return payload; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMCMessage{"); + sb.append("type='").append(type).append('\''); + sb.append(", payload=").append(StringUtil.toString(payload)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BMCMessage obj) { + obj.writeObject(writer); + } + + public static BMCMessage readObject(ObjectReader reader) { + BMCMessage obj = new BMCMessage(); + reader.beginList(); + obj.setType(reader.readNullable(String.class)); + obj.setPayload(reader.readNullable(byte[].class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getType()); + writer.writeNullable(this.getPayload()); + writer.end(); + } + + public static BMCMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BMCMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BMCMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCProperties.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCProperties.java new file mode 100644 index 00000000..71a48870 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BMCProperties.java @@ -0,0 +1,98 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.*; + +public class BMCProperties { + public static final BMCProperties DEFAULT; + + static { + DEFAULT = new BMCProperties(); + } + + private long feeGatheringTerm; + private long feeGatheringNext; + private Address feeAggregator; + + public long getFeeGatheringTerm() { + return feeGatheringTerm; + } + + public void setFeeGatheringTerm(long feeGatheringTerm) { + this.feeGatheringTerm = feeGatheringTerm; + } + + public long getFeeGatheringNext() { + return feeGatheringNext; + } + + public void setFeeGatheringNext(long feeGatheringNext) { + this.feeGatheringNext = feeGatheringNext; + } + + public Address getFeeAggregator() { + return feeAggregator; + } + + public void setFeeAggregator(Address feeAggregator) { + this.feeAggregator = feeAggregator; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMCProperties{"); + sb.append("feeGatheringTerm=").append(feeGatheringTerm); + sb.append(", feeGatheringNext=").append(feeGatheringNext); + sb.append(", feeAggregator=").append(feeAggregator); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BMCProperties obj) { + obj.writeObject(writer); + } + + public static BMCProperties readObject(ObjectReader reader) { + BMCProperties obj = new BMCProperties(); + reader.beginList(); + obj.setFeeGatheringTerm(reader.readLong()); + obj.setFeeGatheringNext(reader.readLong()); + obj.setFeeAggregator(reader.readNullable(Address.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.write(this.getFeeGatheringTerm()); + writer.write(this.getFeeGatheringNext()); + writer.writeNullable(this.getFeeAggregator()); + writer.end(); + } + + public static BMCProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BMCProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BMCProperties.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessage.java new file mode 100644 index 00000000..e568a7a9 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessage.java @@ -0,0 +1,123 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class BTPMessage { + private BTPAddress src; + private BTPAddress dst; + private String svc; + private BigInteger sn; + private byte[] payload; + + public BTPAddress getSrc() { + return src; + } + + public void setSrc(BTPAddress src) { + this.src = src; + } + + public BTPAddress getDst() { + return dst; + } + + public void setDst(BTPAddress dst) { + this.dst = dst; + } + + public String getSvc() { + return svc; + } + + public void setSvc(String svc) { + this.svc = svc; + } + + public BigInteger getSn() { + return sn; + } + + public void setSn(BigInteger sn) { + this.sn = sn; + } + + public byte[] getPayload() { + return payload; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BTPMessage{"); + sb.append("src=").append(src); + sb.append(", dst=").append(dst); + sb.append(", svc='").append(svc).append('\''); + sb.append(", sn=").append(sn); + sb.append(", payload=").append(StringUtil.bytesToHex(payload)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BTPMessage obj) { + obj.writeObject(writer); + } + + public static BTPMessage readObject(ObjectReader reader) { + BTPMessage obj = new BTPMessage(); + reader.beginList(); + obj.setSrc(reader.readNullable(BTPAddress.class)); + obj.setDst(reader.readNullable(BTPAddress.class)); + obj.setSvc(reader.readNullable(String.class)); + obj.setSn(reader.readNullable(BigInteger.class)); + obj.setPayload(reader.readNullable(byte[].class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(5); + writer.writeNullable(this.getSrc()); + writer.writeNullable(this.getDst()); + writer.writeNullable(this.getSvc()); + writer.writeNullable(this.getSn()); + writer.writeNullable(this.getPayload()); + writer.end(); + } + + public static BTPMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BTPMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BTPMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java new file mode 100644 index 00000000..4028d515 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/BTPMessageCenter.java @@ -0,0 +1,1194 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.*; +import foundation.icon.score.util.*; +import foundation.icon.score.util.ArrayUtil; +import foundation.icon.score.util.Logger; +import foundation.icon.score.util.StringUtil; +import score.*; +import score.annotation.EventLog; +import score.annotation.External; +import score.annotation.Payable; +import scorex.util.ArrayList; +import scorex.util.HashMap; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +public class BTPMessageCenter implements BMC, BMCEvent, ICONSpecific, OwnerManager { + private static final Logger logger = Logger.getLogger(BTPMessageCenter.class); + public static final int BLOCK_INTERVAL_MSEC = 2000; + public static final String INTERNAL_SERVICE = "bmc"; + public enum Internal { Init, Link, Unlink, FeeGathering, Sack } + + // + private final BTPAddress btpAddr; + private final VarDB properties = Context.newVarDB("properties", BMCProperties.class); + + // + private final OwnerManager ownerManager = new OwnerManagerImpl("owners"); + private final ArrayDB serviceCandidates = Context.newArrayDB("serviceCandidates", ServiceCandidate.class); + private final BranchDB>> fragments = Context.newBranchDB("fragments", String.class); + + // + private final Verifiers verifiers = new Verifiers("verifiers"); + private final Services services = new Services("services"); + private final Routes routes = new Routes("routes"); + private final Links links = new Links("links"); + private final Relayers relayers = new Relayers("relayers"); + public static final int DEFAULT_REWARD_PERCENT_SCALE_FACTOR = 4; + + public BTPMessageCenter(String _net) { + this.btpAddr = new BTPAddress(BTPAddress.PROTOCOL_BTP, _net, Context.getAddress().toString()); + } + + public BMCProperties getProperties() { + return properties.getOrDefault(BMCProperties.DEFAULT); + } + + public void setProperties(BMCProperties properties) { + this.properties.set(properties); + } + + @External(readonly = true) + public String getBtpAddress() { + return btpAddr.toString(); + } + + @External + public void addVerifier(String _net, Address _addr) { + requireOwnerAccess(); + if (btpAddr.net().equals(_net)) { + throw BMCException.unknown("invalid _net"); + } + if (verifiers.containsKey(_net)) { + throw BMCException.alreadyExistsBMV(); + } + verifiers.put(_net, _addr); + } + + @External + public void removeVerifier(String _net) { + requireOwnerAccess(); + if (!verifiers.containsKey(_net)) { + throw BMCException.notExistsBMV(); + } + verifiers.remove(_net); + } + + @External(readonly = true) + public Map getVerifiers() { + return verifiers.toMap(); + } + + private BMVScoreInterface getVerifier(String _net) { + if (!verifiers.containsKey(_net)) { + throw BMCException.notExistsBMV(); + } + Address address = verifiers.get(_net); + return new BMVScoreInterface(address); + } + + @External + public void addService(String _svc, Address _addr) { + requireOwnerAccess(); + if (!StringUtil.isAlphaNumeric(_svc)) { + throw BMCException.unknown("invalid service name"); + } + if (services.containsKey(_svc) || INTERNAL_SERVICE.equals(_svc)) { + throw BMCException.alreadyExistsBSH(); + } + if (getServiceCandidateIndex(_svc, _addr) >= 0) { + removeServiceCandidate(_svc, _addr); + } + services.put(_svc, _addr); + } + + @External + public void removeService(String _svc) { + requireOwnerAccess(); + if (!services.containsKey(_svc)) { + throw BMCException.notExistsBSH(); + } + services.remove(_svc); + } + + @External(readonly = true) + public Map getServices() { + return services.toMap(); + } + + private BSHScoreInterface getService(String _svc) { + if (!services.containsKey(_svc)) { + throw BMCException.notExistsBSH(); + } + Address address = services.get(_svc); + return new BSHScoreInterface(address); + } + + private void requireLink(BTPAddress link) { + if (!links.containsKey(link)) { + throw BMCException.notExistsLink(); + } + } + + //TODO flushable + private Link getLink(BTPAddress link) { + requireLink(link); + return links.get(link); + } + + private void putLink(Link link) { + links.put(link.getAddr(), link); + } + + @External + public void addLink(String _link) { + requireOwnerAccess(); + BTPAddress target = BTPAddress.valueOf(_link); + if (!verifiers.containsKey(target.net())) { + throw BMCException.notExistsBMV(); + } + if (links.containsKey(target)) { + throw BMCException.alreadyExistsLink(); + } + LinkMessage linkMsg = new LinkMessage(); + linkMsg.setLink(target); + propagateInternal(Internal.Link, linkMsg.toBytes()); + + List list = this.links.keySet(); + int size = list.size(); + BTPAddress[] links = new BTPAddress[size]; + for (int i = 0; i < size; i++) { + links[i] = list.get(i); + } + InitMessage initMsg = new InitMessage(); + initMsg.setLinks(links); + + Link link = new Link(); + link.setAddr(target); + link.setRxSeq(BigInteger.ZERO); + link.setTxSeq(BigInteger.ZERO); + link.setBlockIntervalSrc(BLOCK_INTERVAL_MSEC); + link.setSackSeq(BigInteger.ZERO); + link.setReachable(new ArrayList<>()); + putLink(link); + + sendInternal(target, Internal.Init, initMsg.toBytes()); + } + + @External + public void removeLink(String _link) { + requireOwnerAccess(); + BTPAddress target = BTPAddress.valueOf(_link); + if (!links.containsKey(target)) { + throw BMCException.notExistsLink(); + } + if (routes.values().contains(target)) { + throw BMCException.unknown("could not remove, referred by route"); + } + UnlinkMessage unlinkMsg = new UnlinkMessage(); + unlinkMsg.setLink(target); + propagateInternal(Internal.Unlink, unlinkMsg.toBytes()); + Link link = links.remove(target); + link.getRelays().clear(); + } + + @External(readonly = true) + public BMCStatus getStatus(String _link) { + BTPAddress target = BTPAddress.valueOf(_link); + BMCStatus status = new BMCStatus(); + + Link link = getLink(target); + status.setTx_seq(link.getTxSeq()); + status.setRx_seq(link.getRxSeq()); + status.setRelay_idx(link.getRelayIdx()); + status.setRotate_height(link.getRotateHeight()); + status.setRotate_term(link.rotateTerm()); + status.setDelay_limit(link.getDelayLimit()); + status.setMax_agg(link.getMaxAggregation()); + status.setRx_height(link.getRxHeight()); + status.setRx_height_src(link.getRxHeightSrc()); + status.setBlock_interval_dst(link.getBlockIntervalDst()); + status.setBlock_interval_src(link.getBlockIntervalSrc()); + status.setSack_term(link.getSackTerm()); + status.setSack_next(link.getSackNext()); + status.setSack_height(link.getSackHeight()); + status.setSack_seq(link.getSackSeq()); + status.setCur_height(Context.getBlockHeight()); + + Map relayMap = link.getRelays().toMap(); + BMRStatus[] relays = new BMRStatus[relayMap.size()]; + int i = 0; + for (Map.Entry entry : relayMap.entrySet()) { + Relay relay = entry.getValue(); + BMRStatus bmrStatus = new BMRStatus(); + bmrStatus.setAddress(relay.getAddress()); + bmrStatus.setBlock_count(relay.getBlockCount()); + bmrStatus.setMsg_count(relay.getMsgCount()); + relays[i++] = bmrStatus; + } + status.setRelays(relays); + + BMVScoreInterface bmv = getVerifier(link.getAddr().net()); + BMVStatus bmvStatus = bmv.getStatus(); + status.setVerifier(bmvStatus); + return status; + } + + @External(readonly = true) + public String[] getLinks() { + List keySet = links.keySet(); + int len = keySet.size(); + String[] links = new String[len]; + for (int i = 0; i < len; i++) { + links[i] = keySet.get(i).toString(); + } + return links; + } + + @External + public void addRoute(String _dst, String _link) { + requireOwnerAccess(); + if (routes.containsKey(_dst)) { + throw BMCException.unknown("already exists route"); + } + BTPAddress target = BTPAddress.valueOf(_link); + requireLink(target); + routes.put(_dst, target); + } + + @External + public void removeRoute(String _dst) { + requireOwnerAccess(); + if (!routes.containsKey(_dst)) { + throw BMCException.unknown("not exists route"); + } + routes.remove(_dst); + } + + @External(readonly = true) + public Map getRoutes() { + Map stringMap = new HashMap<>(); + for (Map.Entry entry : routes.toMap().entrySet()) { + stringMap.put(entry.getKey(), entry.getValue().toString()); + } + return stringMap; + } + + private BTPAddress resolveRoute(String _net) { + if (routes.containsKey(_net)) { + return routes.get(_net); + } else { + for (String key : routes.keySet()) { + if (_net.equals(BTPAddress.parse(key).net())) { + return routes.get(key); + } + } + } + return null; + } + + private Link resolveNext(String _net) throws BMCException { + BTPAddress next = resolveRoute(_net); + if (next == null) { + for (BTPAddress key : links.keySet()) { + if (_net.equals(key.net())) { + return links.get(key); + } + } + for (BTPAddress key : links.keySet()) { + Link link = links.get(key); + for (BTPAddress reachable : link.getReachable()) { + if (_net.equals(reachable.net())) { + return link; + } + } + } + throw BMCException.unreachable(); + } else { + return getLink(next); + } + } + + @External + public void handleRelayMessage(String _prev, String _msg) { + BTPAddress prev = BTPAddress.valueOf(_prev); + Link link = getLink(prev); + BMVScoreInterface verifier = getVerifier(link.getAddr().net()); + BMVStatus prevStatus = verifier.getStatus(); + // decode and verify relay message + byte[][] serializedMsgs = null; + try { + serializedMsgs = verifier.handleRelayMessage(btpAddr.toString(), _prev, link.getRxSeq(), _msg); + } catch (UserRevertedException e) { + logger.println("handleRelayMessage", + "fail to verifier.handleRelayMessage", + "code:", e.getCode(), "msg:", e.getMessage()); + throw BTPException.of(e); + } + long msgCount = serializedMsgs.length; + + // rotate and check valid relay + long currentHeight = Context.getBlockHeight(); + BMVStatus status = verifier.getStatus(); + Relays relays = link.getRelays(); + Relay relay = link.rotate(currentHeight, status.getLast_height(), msgCount > 0); + if (relay == null) { + //rotateRelay is disabled. + relay = relays.get(Context.getOrigin()); + if (relay == null) { + throw BMCException.unauthorized("not registered relay"); + } + } else if (!relay.getAddress().equals(Context.getOrigin())) { + throw BMCException.unauthorized("invalid relay"); + } + relay.setBlockCount(relay.getBlockCount() + status.getHeight() - prevStatus.getHeight()); + relay.setMsgCount(relay.getMsgCount().add(BigInteger.valueOf(msgCount))); + relays.put(relay.getAddress(), relay); + + link.setRxSeq(link.getRxSeq().add(BigInteger.valueOf(msgCount))); + putLink(link); + + // dispatch BTPMessages + for (byte[] serializedMsg : serializedMsgs) { + BTPMessage msg = null; + try { + //TODO [TBD] how to catch exception while processing in ByteArrayObjectReader? + msg = BTPMessage.fromBytes(serializedMsg); + } catch (Exception e) { + //TODO [TBD] ignore BTPMessage parse failure? + logger.println("handleRelayMessage","fail to parse BTPMessage err:", e.getMessage()); + } + if (msg != null) { + if (btpAddr.equals(msg.getDst())) { + handleMessage(prev, msg); + } else { + try { + Link next = resolveNext(msg.getDst().net()); + sendMessage(next.getAddr(), msg); + } catch (BTPException e) { + sendError(prev, msg, e); + } + } + } + } + + //sack + link = getLink(prev); + long sackTerm = link.getSackTerm(); + long sackNext = link.getSackNext(); + if (sackTerm > 0 && sackNext <= currentHeight) { + sendSack(prev, status.getHeight(), link.getRxSeq()); + while(sackNext <= currentHeight) { + sackNext += sackTerm; + } + link.setSackNext(sackNext); + putLink(link); + } + + //feeGathering + BMCProperties properties = getProperties(); + Address feeAggregator = properties.getFeeAggregator(); + long feeGatheringTerm = properties.getFeeGatheringTerm(); + long feeGatheringNext = properties.getFeeGatheringNext(); + if (services.size() > 0 && feeAggregator != null && + feeGatheringTerm > 0 && + feeGatheringNext <= currentHeight) { + String[] svcs = ArrayUtil.toStringArray(services.keySet()); + sendFeeGathering(feeAggregator, svcs); + while(feeGatheringNext <= currentHeight) { + feeGatheringNext += feeGatheringTerm; + } + properties.setFeeGatheringNext(feeGatheringNext); + setProperties(properties); + } + + distributeRelayerReward(); + } + + private void handleMessage(BTPAddress prev, BTPMessage msg) { + if (msg.getSvc().equals(INTERNAL_SERVICE)) { + handleInternal(prev, msg); + } else { + handleService(prev, msg); + } + } + + private void handleInternal(BTPAddress prev, BTPMessage msg) { + BMCMessage bmcMsg = BMCMessage.fromBytes(msg.getPayload()); + byte[] payload = bmcMsg.getPayload(); + Internal internal = null; + try { + internal = Internal.valueOf(bmcMsg.getType()); + } catch (IllegalArgumentException e) { + //TODO exception handling + logger.println("handleInternal", "not supported internal type", e.getMessage()); + return; + } + + if (!prev.equals(msg.getSrc())) { + throw BMCException.unknown("internal message not allowed from " + msg.getSrc().toString()); + } + + try { + switch (internal) { + case Init: + InitMessage initMsg = InitMessage.fromBytes(payload); + handleInit(prev, initMsg); + break; + case Link: + LinkMessage linkMsg = LinkMessage.fromBytes(payload); + handleLink(prev, linkMsg); + break; + case Unlink: + UnlinkMessage unlinkMsg = UnlinkMessage.fromBytes(payload); + handleUnlink(prev, unlinkMsg); + break; + case FeeGathering: + FeeGatheringMessage feeGatheringMsg = FeeGatheringMessage.fromBytes(payload); + handleFeeGathering(prev, feeGatheringMsg); + break; + case Sack: + SackMessage sackMsg = SackMessage.fromBytes(payload); + handleSack(prev, sackMsg); + break; + } + } catch (BTPException e) { + //TODO exception handling + logger.println("handleInternal", internal, e); + } + } + + private void handleInit(BTPAddress prev, InitMessage msg) { + logger.println("handleInit", "prev:", prev, "msg:", msg.toString()); + try { + Link link = getLink(prev); + for (BTPAddress reachable : msg.getLinks()) { + link.getReachable().add(reachable); + } + putLink(link); + } catch (BMCException e) { + //TODO exception handling + if (!BMCException.Code.NotExistsLink.equals(e)) { + throw e; + } + } + } + + private void handleLink(BTPAddress prev, LinkMessage msg) { + logger.println("handleLink", "prev:", prev, "msg:", msg.toString()); + try { + Link link = getLink(prev); + BTPAddress reachable = msg.getLink(); + if (!link.getReachable().contains(reachable)) { + link.getReachable().add(reachable); + putLink(link); + } + } catch (BMCException e) { + //TODO exception handling + if (!BMCException.Code.NotExistsLink.equals(e)) { + throw e; + } + } + } + + private void handleUnlink(BTPAddress prev, UnlinkMessage msg) { + logger.println("handleUnlink", "prev:", prev, "msg:", msg.toString()); + try { + Link link = getLink(prev); + BTPAddress reachable = msg.getLink(); + if (link.getReachable().contains(reachable)) { + link.getReachable().remove(reachable); + putLink(link); + } + } catch (BMCException e) { + //TODO exception handling + if (!BMCException.Code.NotExistsLink.equals(e)) { + throw e; + } + } + } + + private void handleSack(BTPAddress prev, SackMessage msg) { + logger.println("handleSack", "prev:", prev, "msg:", msg.toString()); + Link link = getLink(prev); + link.setSackHeight(msg.getHeight()); + link.setSackSeq(msg.getSeq()); + putLink(link); + } + + private void handleFeeGathering(BTPAddress prev, FeeGatheringMessage msg) { + logger.println("handleFeeGathering", "prev:", prev, "msg:", msg.toString()); + if (!prev.net().equals(msg.getFa().net())) { + throw BMCException.unknown("not allowed GatherFeeMessage from:"+prev.net()); + } + String[] svcs = msg.getSvcs(); + if (svcs.length < 1) { + throw BMCException.unknown("requires svcs.length > 1"); + } + String fa = msg.getFa().toString(); + for (String svc : svcs) { + try { + BSHScoreInterface service = getService(svc); + service.handleFeeGathering(fa, svc); + } catch (BTPException e) { + if (!BMCException.Code.NotExistsBSH.equals(e)) { + //TODO exception handling + logger.println("handleGatherFee", svc, e); + } + } catch (UserRevertedException e) { + //TODO exception handling + logger.println("handleGatherFee", "fail to service.handleFeeGathering", + "code:", e.getCode(), "msg:", e.getMessage()); + } catch (Exception e) { + //TODO handle uncatchable exception? + logger.println("handleGatherFee", "fail to service.handleFeeGathering", + "Exception:", e.toString()); + } + } + } + + private void handleService(BTPAddress prev, BTPMessage msg) { + //TODO throttling in a tx, EOA_LIMIT, handleService_LIMIT each Link + // limit in block + String svc = msg.getSvc(); + BigInteger sn = msg.getSn(); + if (sn.compareTo(BigInteger.ZERO) > -1) { + try { + BSHScoreInterface service = getService(svc); + service.handleBTPMessage(msg.getSrc().net(), svc, msg.getSn(), msg.getPayload()); + } catch (BTPException e) { + logger.println("handleService","fail to getService", + "code:", e.getCode(), "msg:", e.getMessage()); + sendError(prev, msg, e); + } catch (UserRevertedException e) { + logger.println("handleService", "fail to service.handleBTPMessage", + "code:", e.getCode(), "msg:", e.getMessage()); + sendError(prev, msg, BTPException.of(e)); + } catch (Exception e) { + //TODO handle uncatchable exception? + logger.println("handleService", "fail to service.handleBTPMessage", + "Exception:", e.toString()); + sendError(prev, msg, BTPException.unknown(e.getMessage())); + } + } else { + sn = sn.negate(); + try { + ErrorMessage errorMsg = ErrorMessage.fromBytes(msg.getPayload()); + try { + BSHScoreInterface service = getService(svc); + service.handleBTPError(msg.getSrc().toString(), svc, sn, errorMsg.getCode(), errorMsg.getMsg() == null ? "" : errorMsg.getMsg()); + } catch (BTPException e) { + logger.println("handleService","fail to getService", + "code:", e.getCode(), "msg:", e.getMessage()); + ErrorOnBTPError(svc, sn, errorMsg.getCode(), errorMsg.getMsg(), e.getCode(), e.getMessage()); + } catch (UserRevertedException e) { + logger.println("handleService", "fail to service.handleBTPError", + "code:", e.getCode(), "msg:", e.getMessage()); + ErrorOnBTPError(svc, sn, errorMsg.getCode(), errorMsg.getMsg(), e.getCode(), e.getMessage()); + } catch (Exception e) { + logger.println("handleService", "fail to service.handleBTPError", + "Exception:", e.toString()); + ErrorOnBTPError(svc, sn, -1, null, -1, e.getMessage()); + } + } catch (Exception e) { + logger.println("handleService", "fail to ErrorMessage.fromBytes", + "Exception:", e.toString()); + ErrorOnBTPError(svc, sn, -1, null, -1, e.getMessage()); + } + } + } + + @External + public void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { + Address addr = services.get(_svc); + if (addr == null) { + throw BMCException.notExistsBSH(); + } + if (!Context.getCaller().equals(addr)) { + throw BMCException.unauthorized(); + } + if (_sn.compareTo(BigInteger.ZERO) < 1) { + throw BMCException.invalidSn(); + } + Link link = resolveNext(_to); + + //TODO (txSeq > sackSeq && (currentHeight - sackHeight) > THRESHOLD) ? revert + // THRESHOLD = (delayLimit * NUM_OF_ROTATION) + BTPMessage btpMsg = new BTPMessage(); + btpMsg.setSrc(btpAddr); + btpMsg.setDst(link.getAddr()); + btpMsg.setSvc(_svc); + btpMsg.setSn(_sn); + btpMsg.setPayload(_msg); + sendMessage(link.getAddr(), btpMsg); + } + + private void sendMessage(BTPAddress to, BTPMessage msg) { + sendMessage(to, msg.toBytes()); + } + + private void sendMessage(BTPAddress to, byte[] serializedMsg) { + Link link = getLink(to); + link.setTxSeq(link.getTxSeq().add(BigInteger.ONE)); + putLink(link); + Message(to.toString(), link.getTxSeq(), serializedMsg); + } + + private void sendError(BTPAddress prev, BTPMessage msg, BTPException e) { + if (msg.getSn().compareTo(BigInteger.ZERO) > 0) { + ErrorMessage errMsg = new ErrorMessage(); + errMsg.setCode(e.getCode()); + errMsg.setMsg(e.getMessage()); + BTPMessage btpMsg = new BTPMessage(); + btpMsg.setSrc(btpAddr); + btpMsg.setDst(msg.getSrc()); + btpMsg.setSvc(msg.getSvc()); + btpMsg.setSn(msg.getSn().negate()); + btpMsg.setPayload(errMsg.toBytes()); + sendMessage(prev, btpMsg); + } + } + + private void sendInternal(BTPAddress link, Internal internal, byte[] payload) { + BMCMessage bmcMsg = new BMCMessage(); + bmcMsg.setType(internal.name()); + bmcMsg.setPayload(payload); + + BTPMessage btpMsg = new BTPMessage(); + btpMsg.setSrc(btpAddr); + btpMsg.setDst(link); + btpMsg.setSvc(INTERNAL_SERVICE); + btpMsg.setSn(BigInteger.ZERO); + btpMsg.setPayload(bmcMsg.toBytes()); + sendMessage(link, btpMsg); + } + + private void sendSack(BTPAddress link, long height, BigInteger seq) { + logger.println("sendSack", "link:", link, "height:", height, "seq:", seq); + SackMessage sackMsg = new SackMessage(); + sackMsg.setHeight(height); + sackMsg.setSeq(seq); + sendInternal(link, Internal.Sack, sackMsg.toBytes()); + } + + private void sendFeeGathering(Address addr, String[] svcs) { + logger.println("sendFeeGathering", "addr:", addr, "svcs:", StringUtil.toString(svcs)); + BTPAddress fa = new BTPAddress(BTPAddress.PROTOCOL_BTP, btpAddr.net(), addr.toString()); + FeeGatheringMessage feeGatheringMsg = new FeeGatheringMessage(); + feeGatheringMsg.setFa(fa); + feeGatheringMsg.setSvcs(svcs); + handleFeeGathering(btpAddr, feeGatheringMsg); + propagateInternal(Internal.FeeGathering, feeGatheringMsg.toBytes()); + } + + private void propagateInternal(Internal internal, byte[] payload) { + for (BTPAddress link : links.keySet()) { + sendInternal(link, internal, payload); + } + } + + @EventLog(indexed = 2) + public void Message(String _next, BigInteger _seq, byte[] _msg) { + } + + @EventLog(indexed = 2) + public void ErrorOnBTPError(String _svc, BigInteger _seq, long _code, String _msg, long _ecode, String _emsg) { + } + + @External + public void handleFragment(String _prev, String _msg, int _idx) { + logger.println("handleFragment", "_prev",_prev,"_idx:",_idx, "len(_msg):"+_msg.length()); + BTPAddress prev = BTPAddress.valueOf(_prev); + Link link = getLink(prev); + Relays relays = link.getRelays(); + if (!relays.containsKey(Context.getOrigin())) { + throw BMCException.unauthorized("not registered relay"); + } + final int INDEX_LAST = 0; + final int INDEX_NEXT = 1; + final int INDEX_OFFSET = 2; + ArrayDB fragments = this.fragments.at(_prev).at(Context.getOrigin()); + if (_idx < 0) { + int last = _idx * -1; + if (fragments.size() == 0) { + fragments.add(Integer.toString(last)); + fragments.add(Integer.toString(last - 1)); + fragments.add(_msg); + } else { + fragments.set(INDEX_LAST, Integer.toString(last)); + fragments.set(INDEX_NEXT, Integer.toString(last - 1)); + fragments.set(INDEX_OFFSET, _msg); + } + } else { + int next = Integer.parseInt(fragments.get(INDEX_NEXT)); + if (next != _idx) { + throw BMCException.unknown("invalid _idx"); + } + int last = Integer.parseInt(fragments.get(INDEX_LAST)); + if (_idx == 0) { + StringBuilder msg = new StringBuilder(); + for(int i = 0; i < last; i++){ + msg.append(fragments.get(i + INDEX_OFFSET)); + } + msg.append(_msg); + logger.println("handleFragment", "handleRelayMessage","fragments:",last+1, "len:"+msg.length()); + handleRelayMessage(_prev, msg.toString()); + } else { + fragments.set(INDEX_NEXT, Integer.toString(_idx - 1)); + int INDEX_MSG = last - _idx + INDEX_OFFSET; + if (INDEX_MSG < fragments.size()) { + fragments.set(INDEX_MSG, _msg); + } else { + fragments.add(_msg); + } + } + } + } + + @External + public void setLinkRotateTerm(String _link, int _block_interval, int _max_agg) { + requireOwnerAccess(); + BTPAddress target = BTPAddress.valueOf(_link); + Link link = getLink(target); + if (_block_interval < 0 || _max_agg < 1) { + throw BMCException.unknown("invalid param"); + } + int oldRotateTerm = link.rotateTerm(); + link.setBlockIntervalDst(_block_interval); + link.setMaxAggregation(_max_agg); + int rotateTerm = link.rotateTerm(); + if (oldRotateTerm == 0 && rotateTerm > 0) { + long currentHeight = Context.getBlockHeight(); + link.setRotateHeight(currentHeight + rotateTerm); + link.setRxHeight(currentHeight); + BMVScoreInterface verifier = getVerifier(target.net()); + link.setRxHeightSrc(verifier.getStatus().getHeight()); + } + putLink(link); + } + + @External + public void setLinkDelayLimit(String _link, int _value) { + requireOwnerAccess(); + BTPAddress target = BTPAddress.valueOf(_link); + Link link = getLink(target); + if (_value < 1) { + throw BMCException.unknown("invalid param"); + } + link.setDelayLimit(_value); + putLink(link); + } + + @External + public void setLinkSackTerm(String _link, int _value) { + requireOwnerAccess(); + BTPAddress target = BTPAddress.valueOf(_link); + Link link = getLink(target); + if (_value < 0) { + throw BMCException.unknown("invalid param"); + } + link.setSackTerm(_value); + link.setSackNext(Context.getBlockHeight()+_value); + putLink(link); + } + + @External + public void addRelay(String _link, Address _addr) { + requireOwnerAccess(); + + BTPAddress target = BTPAddress.valueOf(_link); + Relays relays = getLink(target).getRelays(); + if (relays.containsKey(_addr)) { + throw BMCException.alreadyExistsBMR(); + } + Relay relay = new Relay(); + relay.setAddress(_addr); + relay.setMsgCount(BigInteger.ZERO); + relays.put(_addr, relay); + } + + @External + public void removeRelay(String _link, Address _addr) { + requireOwnerAccess(); + + BTPAddress target = BTPAddress.valueOf(_link); + Relays relays = getLink(target).getRelays(); + if (!relays.containsKey(_addr)) { + throw BMCException.notExistsBMR(); + } + relays.remove(_addr); + } + + private int getServiceCandidateIndex(String svc, Address addr) { + for (int i = 0; i < serviceCandidates.size(); i++) { + ServiceCandidate sc = serviceCandidates.get(i); + if (sc.getSvc().equals(svc) && sc.getAddress().equals(addr)) { + return i; + } + } + return -1; + } + + @External + public void addServiceCandidate(String _svc, Address _addr) { + if (getServiceCandidateIndex(_svc, _addr) >= 0) { + throw BMCException.unknown("already exists ServiceCandidate"); + } + ServiceCandidate sc = new ServiceCandidate(); + sc.setSvc(_svc); + sc.setAddress(_addr); + sc.setOwner(Context.getOrigin()); + serviceCandidates.add(sc); + } + + @External + public void removeServiceCandidate(String _svc, Address _addr) { + requireOwnerAccess(); + int idx = getServiceCandidateIndex(_svc, _addr); + if (idx < 0) { + throw BMCException.unknown("not exists ServiceCandidate"); + } + ServiceCandidate last = serviceCandidates.pop(); + if (idx != serviceCandidates.size()) { + serviceCandidates.set(idx, last); + } + } + + @External(readonly = true) + public ServiceCandidate[] getServiceCandidates() { + int size = this.serviceCandidates.size(); + ServiceCandidate[] serviceCandidates = new ServiceCandidate[size]; + for (int i = 0; i < size; i++) { + serviceCandidates[i] = this.serviceCandidates.get(i); + } + return serviceCandidates; + } + + @External(readonly = true) + public Address[] getRelays(String _link) { + BTPAddress target = BTPAddress.valueOf(_link); + Relays relays = getLink(target).getRelays(); + return ArrayUtil.toAddressArray(relays.keySet()); + } + + @External + public void sendFeeGathering() { + requireOwnerAccess(); + if (services.size() == 0) { + throw BMCException.unknown("services is empty"); + } + Address feeAggregator = getFeeAggregator(); + if (feeAggregator == null) { + throw BMCException.unknown("feeAggregator is null"); + } + String[] svcs = ArrayUtil.toStringArray(services.keySet()); + sendFeeGathering(feeAggregator, svcs); + } + + @External(readonly = true) + public long getFeeGatheringTerm() { + BMCProperties properties = getProperties(); + return properties.getFeeGatheringTerm(); + } + + @External + public void setFeeGatheringTerm(long _value) { + requireOwnerAccess(); + BMCProperties properties = getProperties(); + if (_value < 0) { + throw BMCException.unknown("invalid param"); + } + properties.setFeeGatheringTerm(_value); + properties.setFeeGatheringNext(Context.getBlockHeight()+_value); + setProperties(properties); + } + + @External(readonly = true) + public Address getFeeAggregator() { + BMCProperties properties = getProperties(); + return properties.getFeeAggregator(); + } + + @External + public void setFeeAggregator(Address _addr) { + requireOwnerAccess(); + BMCProperties properties = getProperties(); + properties.setFeeAggregator(_addr); + setProperties(properties); + } + + /* Delegate OwnerManager */ + private void requireOwnerAccess() { + if (!ownerManager.isOwner(Context.getCaller())) { + throw BMCException.unauthorized("require owner access"); + } + } + + @External + public void addOwner(Address _addr) { + try { + ownerManager.addOwner(_addr); + } catch (IllegalStateException e) { + throw BMCException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw BMCException.unknown(e.getMessage()); + } + } + + @External + public void removeOwner(Address _addr) { + try { + ownerManager.removeOwner(_addr); + } catch (IllegalStateException e) { + throw BMCException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw BMCException.unknown(e.getMessage()); + } + } + + @External(readonly = true) + public Address[] getOwners() { + return ownerManager.getOwners(); + } + + @External(readonly = true) + public boolean isOwner(Address _addr) { + return ownerManager.isOwner(_addr); + } + + /* Delegate RelayerManager */ + @Payable + @External + public void registerRelayer(String _desc) { + Address addr = Context.getCaller(); + if (relayers.containsKey(addr)) { + throw BMCException.unknown("already registered relayer"); + } + BigInteger bond = Context.getValue(); + BigInteger relayerMinBond = getRelayerMinBond(); + if (bond == null || bond.compareTo(relayerMinBond) < 0) { + throw BMCException.unknown("require bond at least " + relayerMinBond + " icx"); + } + + Relayer relayer = new Relayer(); + relayer.setAddr(addr); + relayer.setDesc(_desc); + relayer.setSince(Context.getBlockHeight()); + relayer.setSinceExtra(Context.getTransactionIndex()); + relayer.setBond(bond); + relayer.setReward(BigInteger.ZERO); + logger.println("registerRelayer", relayer); + relayers.put(addr, relayer); + + RelayersProperties properties = relayers.getProperties(); + properties.setBond(properties.getBond().add(bond)); + relayers.setProperties(properties); + } + + private void removeRelayerAndRefund(Address _addr, Address _refund) { + if (!relayers.containsKey(_addr)) { + throw BMCException.unknown("not found registered relayer"); + } + Relayer relayer = relayers.remove(_addr); + + RelayersProperties properties = relayers.getProperties(); + BigInteger bond = relayer.getBond(); + Context.transfer(_refund, bond); + properties.setBond(properties.getBond().subtract(bond)); + + BigInteger reward = relayer.getReward(); + if (reward.compareTo(BigInteger.ZERO) > 0) { + Context.transfer(_refund, reward); + properties.setDistributed(properties.getDistributed().subtract(reward)); + } + relayers.setProperties(properties); + } + + @External + public void unregisterRelayer() { + Address _addr = Context.getCaller(); + removeRelayerAndRefund(_addr, _addr); + } + + @External + public void removeRelayer(Address _addr, Address _refund) { + requireOwnerAccess(); + removeRelayerAndRefund(_addr, _refund); + } + + @External(readonly = true) + public Map getRelayers() { + return relayers.toMapWithKeyToString(); + } + + @External + public void distributeRelayerReward() { + logger.println("distributeRelayerReward"); + long currentHeight = Context.getBlockHeight(); + RelayersProperties properties = relayers.getProperties(); + long nextRewardDistribution = properties.getNextRewardDistribution(); + if (nextRewardDistribution <= currentHeight) { + long delayOfDistribution = currentHeight - nextRewardDistribution; + long relayerTerm = properties.getRelayerTerm(); + nextRewardDistribution += relayerTerm; + if (nextRewardDistribution <= currentHeight) { + int omitted = 0; + while(nextRewardDistribution < currentHeight) { + nextRewardDistribution += relayerTerm; + omitted++; + } + logger.println("WARN","rewardDistribution was omitted", omitted, "term:", relayerTerm); + } + long since = nextRewardDistribution - (relayerTerm * 2); + properties.setNextRewardDistribution(nextRewardDistribution); + + BigInteger balance = Context.getBalance(Context.getAddress()); + BigInteger distributed = properties.getDistributed(); + BigInteger bond = properties.getBond(); + BigInteger current = balance.subtract(bond); + BigInteger carryover = properties.getCarryover(); + logger.println("distributeRelayerReward", "since:", since, "delay:",delayOfDistribution, + "balance:", balance, "distributed:", distributed, "bond:", bond, "carryover:", carryover); + if (current.compareTo(distributed) > 0) { + BigInteger budget = current.subtract(distributed); + logger.println("distributeRelayerReward","budget:", budget, "transferred:", budget.subtract(carryover)); + carryover = budget; + Relayer[] filteredRelayers = relayers.getValuesBySinceAndSortAsc(since); + BigInteger sumOfBond = BigInteger.ZERO; + int lenOfRelayers = StrictMath.min(properties.getRelayerRewardRank(), filteredRelayers.length); + for (int i = 0; i < lenOfRelayers; i++) { + sumOfBond = sumOfBond.add(filteredRelayers[i].getBond()); + } + logger.println("distributeRelayerReward","sumOfBond:", sumOfBond, "lenOfRelayers:", lenOfRelayers); + BigInteger sumOfReward = BigInteger.ZERO; + for (int i = 0; i < lenOfRelayers; i++) { + Relayer relayer = filteredRelayers[i]; + double percentage = BigIntegerUtil.floorDivide(relayer.getBond(), sumOfBond, DEFAULT_REWARD_PERCENT_SCALE_FACTOR); + BigInteger reward = BigIntegerUtil.multiply(budget, percentage); + relayer.setReward(relayer.getReward().add(reward)); + logger.println("distributeRelayerReward", "relayer:",relayer.getAddr(), "percentage:",percentage, "reward:", reward); + relayers.put(relayer.getAddr(), relayer); + carryover = carryover.subtract(reward); + sumOfReward = sumOfReward.add(reward); + } + + logger.println("distributeRelayerReward","sumOfReward:", sumOfReward, "carryover:", carryover, "nextRewardDistribution:", nextRewardDistribution); + properties.setDistributed(distributed.add(sumOfReward)); + properties.setCarryover(carryover); + } else { + //reward is zero or negative + logger.println("WARN","transferred reward is zero or negative"); + } + relayers.setProperties(properties); + } + } + + //FIXME fallback is required? + @Payable + public void fallback() { + logger.println("fallback","value:", Context.getValue()); + } + + @External + public void claimRelayerReward() { + Address addr = Context.getCaller(); + if (!relayers.containsKey(addr)) { + throw BMCException.unknown("not found registered relayer"); + } + Relayer relayer = relayers.get(addr); + BigInteger reward = relayer.getReward(); + if (reward.compareTo(BigInteger.ZERO) < 1) { + throw BMCException.unknown("reward is not remained"); + } + Context.transfer(addr, reward); + relayer.setReward(BigInteger.ZERO); + relayers.put(addr, relayer); + RelayersProperties properties = relayers.getProperties(); + properties.setDistributed(properties.getDistributed().subtract(reward)); + relayers.setProperties(properties); + } + + @External(readonly = true) + public BigInteger getRelayerMinBond() { + RelayersProperties properties = relayers.getProperties(); + return properties.getRelayerMinBond(); + } + + @External + public void setRelayerMinBond(BigInteger _value) { + requireOwnerAccess(); + if (_value.compareTo(BigInteger.ZERO) < 0) { + throw BMCException.unknown("minBond must be positive"); + } + RelayersProperties properties = relayers.getProperties(); + properties.setRelayerMinBond(_value); + relayers.setProperties(properties); + } + + @External(readonly = true) + public long getRelayerTerm() { + RelayersProperties properties = relayers.getProperties(); + return properties.getRelayerTerm(); + } + + @External + public void setRelayerTerm(long _value) { + requireOwnerAccess(); + if (_value < 1) { + throw BMCException.unknown("term must be positive"); + } + RelayersProperties properties = relayers.getProperties(); + properties.setRelayerTerm(_value); + relayers.setProperties(properties); + } + + @External(readonly = true) + public int getRelayerRewardRank() { + RelayersProperties properties = relayers.getProperties(); + return properties.getRelayerRewardRank(); + } + + @External + public void setRelayerRewardRank(int _value) { + requireOwnerAccess(); + if (_value < 1) { + throw BMCException.unknown("rewardRank must be positive"); + } + RelayersProperties properties = relayers.getProperties(); + properties.setRelayerRewardRank(_value); + relayers.setProperties(properties); + } + + @External + public void setNextRewardDistribution(long _height) { + requireOwnerAccess(); + RelayersProperties properties = relayers.getProperties(); + properties.setNextRewardDistribution(StrictMath.max(_height, Context.getBlockHeight())); + relayers.setProperties(properties); + } + + @External(readonly = true) + public RelayersProperties getRelayersProperties() { + return relayers.getProperties(); + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ErrorMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ErrorMessage.java new file mode 100644 index 00000000..0b7e65ae --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ErrorMessage.java @@ -0,0 +1,83 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class ErrorMessage { + private long code; + private String msg; + + public long getCode() { + return code; + } + + public void setCode(long code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ErrorMessage{"); + sb.append("code=").append(code); + sb.append(", msg='").append(msg).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, ErrorMessage obj) { + obj.writeObject(writer); + } + + public static ErrorMessage readObject(ObjectReader reader) { + ErrorMessage obj = new ErrorMessage(); + reader.beginList(); + obj.setCode(reader.readLong()); + obj.setMsg(reader.readNullable(String.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.write(this.getCode()); + writer.writeNullable(this.getMsg()); + writer.end(); + } + + public static ErrorMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return ErrorMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + ErrorMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/FeeGatheringMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/FeeGatheringMessage.java new file mode 100644 index 00000000..8e2df489 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/FeeGatheringMessage.java @@ -0,0 +1,109 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class FeeGatheringMessage { + private BTPAddress fa; + private String[] svcs; + + public BTPAddress getFa() { + return fa; + } + + public void setFa(BTPAddress fa) { + this.fa = fa; + } + + public String[] getSvcs() { + return svcs; + } + + public void setSvcs(String[] svcs) { + this.svcs = svcs; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("FeeGatheringMessage{"); + sb.append("fa=").append(fa); + sb.append(", svcs=").append(StringUtil.toString(svcs)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, FeeGatheringMessage obj) { + obj.writeObject(writer); + } + + public static FeeGatheringMessage readObject(ObjectReader reader) { + FeeGatheringMessage obj = new FeeGatheringMessage(); + reader.beginList(); + obj.setFa(reader.readNullable(BTPAddress.class)); + if (reader.beginNullableList()) { + String[] svcs = null; + List svcsList = new ArrayList<>(); + while(reader.hasNext()) { + svcsList.add(reader.readNullable(String.class)); + } + svcs = new String[svcsList.size()]; + for(int i=0; iFor Example::
+ * [ + * "hx..." + * ] + */ + @External(readonly = true) + Address[] getRelays(String _link); + + /** + * Registers candidate for the smart contract for the service. + * Called by the BSH-Owner. + * + * @param _svc String (the name of the service) + * @param _addr Address (the address of the smart contract handling the service) + */ + @External + void addServiceCandidate(String _svc, Address _addr); + + /** + * Unregisters candidate for the smart contract for the service. + * Called by the operator to manage the BTP network. + * + * @param _svc String (the name of the service) + * @param _addr Address (the address of the smart contract handling the service) + */ + @External + void removeServiceCandidate(String _svc, Address _addr); + + /** + * Get registered service candidate + * + * @return A list of service candidates + *
For Example::
+ * [ + * { + * "svc":"the name of the service", + * "address":"cx...", + * "owner":"hx...", + * } + * ] + */ + @External(readonly = true) + ServiceCandidate[] getServiceCandidates(); + + /** + * Optional External method + */ + @External + void sendFeeGathering(); + + @External(readonly = true) + long getFeeGatheringTerm(); + + @External + void setFeeGatheringTerm(long _value); + + @External(readonly = true) + Address getFeeAggregator(); + + @External + void setFeeAggregator(Address _addr); + + /** + * (Payable) Registers the Relayer for the network. + * + * @param _desc String (description of Relayer) + */ + @Payable + @External + void registerRelayer(String _desc); + + /** + * Unregisters the Relayer for the network. + *

+ * _addr Address (the address of Relayer) + */ + @External + void unregisterRelayer(); + + @External + void removeRelayer(Address _addr, Address _refund); + + /** + * Get registered the Relayers. + * + * @return A dictionary with the address of the Relayer as key and information of the Relayer as value. + * + *
For Example::
+ * { + * "hx..." : { + * "description": "description of the Relayer...", + * "bond": "0x10" + * } + * } + */ + @External(readonly = true) + Map getRelayers(); + + /** + * Optional External method + */ + @External + void distributeRelayerReward(); + + /** + * Claim reward of the Relayer + * Called by relayer + * + */ + @External + void claimRelayerReward(); + + /** + * Set minimum bond of the Relayer + * + * @param _value Integer + */ + @External + void setRelayerMinBond(BigInteger _value); + + /** + * Get minimum bond of the Relayer + * + * @return Integer minimum bond of the Relayer + */ + @External(readonly = true) + BigInteger getRelayerMinBond(); + + /** + * Set period of reward calculation of the Relayer + * + * @param _value Integer + */ + @External + void setRelayerTerm(long _value); + + /** + * Get period of reward calculation of the Relayer + * + * @return Integer period of reward calculation of the Relayer + */ + @External(readonly = true) + long getRelayerTerm(); + + @External + void setRelayerRewardRank(int _value); + + @External(readonly = true) + int getRelayerRewardRank(); + + @External + void setNextRewardDistribution(long _height); + + /** + * //FIXME instead of getter pattern + * @return + */ + @External(readonly = true) + RelayersProperties getRelayersProperties(); +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/InitMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/InitMessage.java new file mode 100644 index 00000000..5c21d4b0 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/InitMessage.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class InitMessage { + private BTPAddress[] links; + + public BTPAddress[] getLinks() { + return links; + } + + public void setLinks(BTPAddress[] links) { + this.links = links; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("InitMessage{"); + sb.append("links=").append(StringUtil.toString(links)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, InitMessage obj) { + obj.writeObject(writer); + } + + public static InitMessage readObject(ObjectReader reader) { + InitMessage obj = new InitMessage(); + reader.beginList(); + if (reader.beginNullableList()) { + BTPAddress[] links = null; + List linksList = new ArrayList<>(); + while(reader.hasNext()) { + linksList.add(reader.readNullable(BTPAddress.class)); + } + links = new BTPAddress[linksList.size()]; + for(int i=0; i + private List reachable; + + private double scale() { + if (blockIntervalSrc < 1 || blockIntervalDst < 1) { + return 0; + } else { + return (double)blockIntervalSrc/(double)blockIntervalDst; + } + } + + public int rotateTerm() { + double scale = scale(); + if (scale > 0) { + return (int)StrictMath.ceil((double) maxAggregation / scale); + } else { + return 0; + } + } + + public Relay rotate(long currentHeight, long msgHeight, boolean hasMsg) { + long rotateTerm = rotateTerm(); + if (rotateTerm > 0) { + int rotateCnt; + long baseHeight; + if (hasMsg) { + long guessHeight = rxHeight + (long)StrictMath.ceil((double) (msgHeight - rxHeightSrc) / scale()) - 1; + if (guessHeight > currentHeight) { + guessHeight = currentHeight; + } + + rotateCnt = (int)StrictMath.ceil((double)(guessHeight - rotateHeight)/(double)rotateTerm); + if (rotateCnt < 0) { + rotateCnt = 0; + } + baseHeight = rotateHeight + ((rotateCnt - 1) * rotateTerm); + int skipCnt = (int)StrictMath.ceil((double)(currentHeight - guessHeight)/(double)delayLimit) - 1; + if (skipCnt > 0) { + rotateCnt += skipCnt; + baseHeight = currentHeight; + } + rxHeight = currentHeight; + rxHeightSrc = msgHeight; + } else { + rotateCnt = (int)StrictMath.ceil((double)(currentHeight - rotateHeight)/(double)rotateTerm); + baseHeight = rotateHeight + ((rotateCnt - 1) * rotateTerm); + } + if (rotateCnt > 0) { + rotateHeight = baseHeight + rotateTerm; + relayIdx += rotateCnt; + if (relayIdx >= relays.size()) { + relayIdx = relayIdx % relays.size(); + } + } + return relays.getByIndex(relayIdx); + } else { + return null; + } + } + + public BTPAddress getAddr() { + return addr; + } + + public void setAddr(BTPAddress addr) { + this.addr = addr; + } + + public BigInteger getRxSeq() { + return rxSeq; + } + + public void setRxSeq(BigInteger rxSeq) { + this.rxSeq = rxSeq; + } + + public BigInteger getTxSeq() { + return txSeq; + } + + public void setTxSeq(BigInteger txSeq) { + this.txSeq = txSeq; + } + + public int getBlockIntervalSrc() { + return blockIntervalSrc; + } + + public void setBlockIntervalSrc(int blockIntervalSrc) { + this.blockIntervalSrc = blockIntervalSrc; + } + + public int getBlockIntervalDst() { + return blockIntervalDst; + } + + public void setBlockIntervalDst(int blockIntervalDst) { + this.blockIntervalDst = blockIntervalDst; + } + + public int getMaxAggregation() { + return maxAggregation; + } + + public void setMaxAggregation(int maxAggregation) { + this.maxAggregation = maxAggregation; + } + + public int getDelayLimit() { + return delayLimit; + } + + public void setDelayLimit(int delayLimit) { + this.delayLimit = delayLimit; + } + + public int getRelayIdx() { + return relayIdx; + } + + public void setRelayIdx(int relayIdx) { + this.relayIdx = relayIdx; + } + + public long getRotateHeight() { + return rotateHeight; + } + + public void setRotateHeight(long rotateHeight) { + this.rotateHeight = rotateHeight; + } + + public long getRxHeight() { + return rxHeight; + } + + public void setRxHeight(long rxHeight) { + this.rxHeight = rxHeight; + } + + public long getRxHeightSrc() { + return rxHeightSrc; + } + + public void setRxHeightSrc(long rxHeightSrc) { + this.rxHeightSrc = rxHeightSrc; + } + + public int getSackTerm() { + return sackTerm; + } + + public void setSackTerm(int sackTerm) { + this.sackTerm = sackTerm; + } + + public long getSackNext() { + return sackNext; + } + + public void setSackNext(long sackNext) { + this.sackNext = sackNext; + } + + public long getSackHeight() { + return sackHeight; + } + + public void setSackHeight(long sackHeight) { + this.sackHeight = sackHeight; + } + + public BigInteger getSackSeq() { + return sackSeq; + } + + public void setSackSeq(BigInteger sackSeq) { + this.sackSeq = sackSeq; + } + + public List getReachable() { + return reachable; + } + + public void setReachable(List reachable) { + this.reachable = reachable; + } + + public Relays getRelays() { + return relays; + } + + public void setRelays(Relays relays) { + this.relays = relays; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Link{"); + sb.append("relays=").append(relays); + sb.append(", addr=").append(addr); + sb.append(", rxSeq=").append(rxSeq); + sb.append(", txSeq=").append(txSeq); + sb.append(", blockIntervalSrc=").append(blockIntervalSrc); + sb.append(", blockIntervalDst=").append(blockIntervalDst); + sb.append(", maxAggregation=").append(maxAggregation); + sb.append(", delayLimit=").append(delayLimit); + sb.append(", relayIdx=").append(relayIdx); + sb.append(", rotateHeight=").append(rotateHeight); + sb.append(", rxHeight=").append(rxHeight); + sb.append(", rxHeightSrc=").append(rxHeightSrc); + sb.append(", sackTerm=").append(sackTerm); + sb.append(", sackNext=").append(sackNext); + sb.append(", sackHeight=").append(sackHeight); + sb.append(", sackSeq=").append(sackSeq); + sb.append(", reachable=").append(reachable); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Link obj) { + obj.writeObject(writer); + } + + public static Link readObject(ObjectReader reader) { + Link obj = new Link(); + reader.beginList(); + obj.setAddr(reader.readNullable(BTPAddress.class)); + obj.setRxSeq(reader.readNullable(BigInteger.class)); + obj.setTxSeq(reader.readNullable(BigInteger.class)); + obj.setBlockIntervalSrc(reader.readInt()); + obj.setBlockIntervalDst(reader.readInt()); + obj.setMaxAggregation(reader.readInt()); + obj.setDelayLimit(reader.readInt()); + obj.setRelayIdx(reader.readInt()); + obj.setRotateHeight(reader.readLong()); + obj.setRxHeight(reader.readLong()); + obj.setRxHeightSrc(reader.readLong()); + obj.setSackTerm(reader.readInt()); + obj.setSackNext(reader.readLong()); + obj.setSackHeight(reader.readLong()); + obj.setSackSeq(reader.readNullable(BigInteger.class)); + if (reader.beginNullableList()) { + List reachable = new ArrayList<>(); + while(reader.hasNext()) { + reachable.add(reader.readNullable(BTPAddress.class)); + } + obj.setReachable(reachable); + reader.end(); + } + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(16); + writer.writeNullable(this.getAddr()); + writer.writeNullable(this.getRxSeq()); + writer.writeNullable(this.getTxSeq()); + writer.write(this.getBlockIntervalSrc()); + writer.write(this.getBlockIntervalDst()); + writer.write(this.getMaxAggregation()); + writer.write(this.getDelayLimit()); + writer.write(this.getRelayIdx()); + writer.write(this.getRotateHeight()); + writer.write(this.getRxHeight()); + writer.write(this.getRxHeightSrc()); + writer.write(this.getSackTerm()); + writer.write(this.getSackNext()); + writer.write(this.getSackHeight()); + writer.writeNullable(this.getSackSeq()); + List reachable = this.getReachable(); + if (reachable != null) { + writer.beginNullableList(reachable.size()); + for(BTPAddress v : reachable) { + writer.write(v); + } + writer.end(); + } else { + writer.writeNull(); + } + writer.end(); + } + + public static Link fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return Link.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + Link.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/LinkMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/LinkMessage.java new file mode 100644 index 00000000..d897d336 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/LinkMessage.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class LinkMessage { + private BTPAddress link; + + public BTPAddress getLink() { + return link; + } + + public void setLink(BTPAddress link) { + this.link = link; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("LinkMessage{"); + sb.append("link=").append(link); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, LinkMessage obj) { + obj.writeObject(writer); + } + + public static LinkMessage readObject(ObjectReader reader) { + LinkMessage obj = new LinkMessage(); + reader.beginList(); + obj.setLink(reader.readNullable(BTPAddress.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(1); + writer.writeNullable(this.getLink()); + writer.end(); + } + + public static LinkMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return LinkMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + LinkMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Links.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Links.java new file mode 100644 index 00000000..8304d4ed --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Links.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; + +import java.util.Map; + +public class Links extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Links.class); + + public Links(String id) { + super(id, BTPAddress.class, Link.class); + } + + public Link ensureRelays(Link link) { + if (link != null && link.getRelays() == null) { + String linkId = super.concatId(link.getAddr()); + link.setRelays(new Relays(concatId(linkId, "relays"))); + } + return link; + } + + @Override + public Link getByIndex(int i) { + return ensureRelays(super.getByIndex(i)); + } + + @Override + public Link get(BTPAddress key) { + return ensureRelays(super.get(key)); + } + + @Override + public Link put(BTPAddress key, Link value) { + return ensureRelays(super.put(key, value)); + } + + @Override + public Link remove(BTPAddress key) { + return ensureRelays(super.remove(key)); + } + + @Override + public Map toMap() { + Map map = super.toMap(); + for(Map.Entry entry : map.entrySet()) { + ensureRelays(entry.getValue()); + } + return map; + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relay.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relay.java new file mode 100644 index 00000000..485d9a1a --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relay.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.Address; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Relay { + private Address address; //primary key + private long blockCount; + private BigInteger msgCount; + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public long getBlockCount() { + return blockCount; + } + + public void setBlockCount(long blockCount) { + this.blockCount = blockCount; + } + + public BigInteger getMsgCount() { + return msgCount; + } + + public void setMsgCount(BigInteger msgCount) { + this.msgCount = msgCount; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Relay{"); + sb.append("address=").append(address); + sb.append(", blockCount=").append(blockCount); + sb.append(", msgCount=").append(msgCount); + sb.append('}'); + return sb.toString(); + } + + public static Relay readObject(ObjectReader reader) { + Relay obj = new Relay(); + reader.beginList(); + obj.setAddress(reader.readNullable(Address.class)); + obj.setBlockCount(reader.readLong()); + obj.setMsgCount(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public static void writeObject(ObjectWriter writer, Relay obj) { + obj.writeObject(writer); + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.writeNullable(this.getAddress()); + writer.write(this.getBlockCount()); + writer.writeNullable(this.getMsgCount()); + writer.end(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayer.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayer.java new file mode 100644 index 00000000..f5904c2e --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayer.java @@ -0,0 +1,121 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.Address; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Relayer { + private Address addr; //primary key + private String desc; + private long since; + private int sinceExtra; + private BigInteger bond; + private BigInteger reward; + + public Address getAddr() { + return addr; + } + + public void setAddr(Address addr) { + this.addr = addr; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public long getSince() { + return since; + } + + public void setSince(long since) { + this.since = since; + } + + public int getSinceExtra() { + return sinceExtra; + } + + public void setSinceExtra(int sinceExtra) { + this.sinceExtra = sinceExtra; + } + + public BigInteger getBond() { + return bond; + } + + public void setBond(BigInteger bond) { + this.bond = bond; + } + + public BigInteger getReward() { + return reward; + } + + public void setReward(BigInteger reward) { + this.reward = reward; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Relayer{"); + sb.append("addr=").append(addr); + sb.append(", desc='").append(desc).append('\''); + sb.append(", since=").append(since); + sb.append(", sinceExtra=").append(sinceExtra); + sb.append(", bond=").append(bond); + sb.append(", reward=").append(reward); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Relayer obj) { + obj.writeObject(writer); + } + + public static Relayer readObject(ObjectReader reader) { + Relayer obj = new Relayer(); + reader.beginList(); + obj.setAddr(reader.readNullable(Address.class)); + obj.setDesc(reader.readNullable(String.class)); + obj.setSince(reader.readLong()); + obj.setSinceExtra(reader.readInt()); + obj.setBond(reader.readNullable(BigInteger.class)); + obj.setReward(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(6); + writer.writeNullable(this.getAddr()); + writer.writeNullable(this.getDesc()); + writer.write(this.getSince()); + writer.write(this.getSinceExtra()); + writer.writeNullable(this.getBond()); + writer.writeNullable(this.getReward()); + writer.end(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayers.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayers.java new file mode 100644 index 00000000..793c7302 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relayers.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; +import score.Address; +import score.Context; +import score.VarDB; + +import java.util.List; + +public class Relayers extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Relayers.class); + private final VarDB varDB; + + public Relayers(String id) { + super(id, Address.class, Relayer.class); + varDB = Context.newVarDB(super.concatId("properties"), RelayersProperties.class); + } + + public RelayersProperties getProperties() { + return varDB.getOrDefault(RelayersProperties.DEFAULT); + } + + public void setProperties(RelayersProperties properties) { + varDB.set(properties); + } + + public Relayer[] getValuesBySinceAndSortAsc(long since) { + List list = super.values(); + Relayer[] filteredRelayers = filterSince(list, since); + sortAsc(filteredRelayers); + return filteredRelayers; + } + + public static Relayer[] filterSince(List list, long since) { + int sinceLen=0; + for (Relayer relayer : list) { + if (relayer.getSince() < since) { + sinceLen++; + } + } + Relayer[] array = new Relayer[sinceLen]; + int i=0; + for (Relayer relayer : list) { + if (relayer.getSince() < since) { + array[i++] = relayer; + } + } + return array; + } + + /** + * Compare Relayer for sorting + * bond desc, since asc, sinceExtra asc + * + * @apiNote Not allowed to use java.util.Comparator in javaee + * @apiNote If Relayer implements java.lang.Comparable, + * it makes 'No implementation found for compareTo(Ljava/lang/Object;)I' in Enum classes. + * + * @param o1 the first Relayer to compare + * @param o2 the second Relayer to compare + * @return bond desc, since asc, sinceExtra asc + */ + public static int compare(Relayer o1, Relayer o2) { + int compBond = o2.getBond().compareTo(o1.getBond()); + if (compBond == 0) { + int compSince = Long.compare(o1.getSince(), o2.getSince()); + if (compSince == 0) { + return Integer.compare(o1.getSinceExtra(), o2.getSinceExtra()); + } else { + return compSince; + } + } else { + return compBond; + } + } + + /** + * Sorts array of Relayer + * instead of foundation.icon.score.util.ArrayUtil#sort(java.lang.Comparable[]) + * @see Relayers#compare(Relayer, Relayer) + * + * @param a Array of Relayer + */ + public static void sortAsc(Relayer[] a) { + int len = a.length; + for (int i = 0; i < len; i++) { + Relayer v = a[i]; + for (int j = i + 1; j < len; j++) { + if (compare(v, a[j]) > 0) { + Relayer t = v; + v = a[j]; + a[j] = t; + } + } + a[i] = v; + } + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/RelayersProperties.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/RelayersProperties.java new file mode 100644 index 00000000..1d6822d5 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/RelayersProperties.java @@ -0,0 +1,158 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class RelayersProperties { + public static final RelayersProperties DEFAULT; + + static { + DEFAULT = new RelayersProperties(); + DEFAULT.setRelayerMinBond(BigInteger.ONE); + DEFAULT.setRelayerTerm(43200); + DEFAULT.setNextRewardDistribution(0); + DEFAULT.setRelayerRewardRank(25); + DEFAULT.setDistributed(BigInteger.ZERO); + DEFAULT.setCarryover(BigInteger.ZERO); + DEFAULT.setBond(BigInteger.ZERO); + } + + private BigInteger relayerMinBond; + private long relayerTerm; + private int relayerRewardRank; + private long nextRewardDistribution; + private BigInteger distributed; + private BigInteger carryover; + private BigInteger bond; + + public BigInteger getRelayerMinBond() { + return relayerMinBond; + } + + public void setRelayerMinBond(BigInteger relayerMinBond) { + this.relayerMinBond = relayerMinBond; + } + + public long getRelayerTerm() { + return relayerTerm; + } + + public void setRelayerTerm(long relayerTerm) { + this.relayerTerm = relayerTerm; + } + + public int getRelayerRewardRank() { + return relayerRewardRank; + } + + public void setRelayerRewardRank(int relayerRewardRank) { + this.relayerRewardRank = relayerRewardRank; + } + + public long getNextRewardDistribution() { + return nextRewardDistribution; + } + + public void setNextRewardDistribution(long nextRewardDistribution) { + this.nextRewardDistribution = nextRewardDistribution; + } + + public BigInteger getDistributed() { + return distributed; + } + + public void setDistributed(BigInteger distributed) { + this.distributed = distributed; + } + + public BigInteger getCarryover() { + return carryover; + } + + public void setCarryover(BigInteger carryover) { + this.carryover = carryover; + } + + public BigInteger getBond() { + return bond; + } + + public void setBond(BigInteger bond) { + this.bond = bond; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Properties{"); + sb.append("relayerMinBond=").append(relayerMinBond); + sb.append(", relayerTerm=").append(relayerTerm); + sb.append(", relayerRewardRank=").append(relayerRewardRank); + sb.append(", nextRewardDistribution=").append(nextRewardDistribution); + sb.append(", remained=").append(distributed); + sb.append(", carryover=").append(carryover); + sb.append(", bond=").append(bond); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, RelayersProperties obj) { + obj.writeObject(writer); + } + + public static RelayersProperties readObject(ObjectReader reader) { + RelayersProperties obj = new RelayersProperties(); + reader.beginList(); + obj.setRelayerMinBond(reader.readNullable(BigInteger.class)); + obj.setRelayerTerm(reader.readLong()); + obj.setRelayerRewardRank(reader.readInt()); + obj.setNextRewardDistribution(reader.readLong()); + obj.setDistributed(reader.readNullable(BigInteger.class)); + obj.setCarryover(reader.readNullable(BigInteger.class)); + obj.setBond(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(7); + writer.writeNullable(this.getRelayerMinBond()); + writer.write(this.getRelayerTerm()); + writer.write(this.getRelayerRewardRank()); + writer.write(this.getNextRewardDistribution()); + writer.writeNullable(this.getDistributed()); + writer.writeNullable(this.getCarryover()); + writer.writeNullable(this.getBond()); + writer.end(); + } + + public static RelayersProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return RelayersProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + RelayersProperties.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relays.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relays.java new file mode 100644 index 00000000..2529c03b --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Relays.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; +import score.Address; + +public class Relays extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Relays.class); + + public Relays(String id) { + super(id, Address.class, Relay.class); + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Routes.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Routes.java new file mode 100644 index 00000000..120aa494 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Routes.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; + +public class Routes extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Routes.class); + + public Routes(String id) { + super(id, String.class, BTPAddress.class); + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/SackMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/SackMessage.java new file mode 100644 index 00000000..4306fade --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/SackMessage.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class SackMessage { + private long height; + private BigInteger seq; + + public long getHeight() { + return height; + } + + public void setHeight(long height) { + this.height = height; + } + + public BigInteger getSeq() { + return seq; + } + + public void setSeq(BigInteger seq) { + this.seq = seq; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("SackMessage{"); + sb.append("height=").append(height); + sb.append(", seq=").append(seq); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, SackMessage obj) { + obj.writeObject(writer); + } + + public static SackMessage readObject(ObjectReader reader) { + SackMessage obj = new SackMessage(); + reader.beginList(); + obj.setHeight(reader.readLong()); + obj.setSeq(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.write(this.getHeight()); + writer.writeNullable(this.getSeq()); + writer.end(); + } + + public static SackMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return SackMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + SackMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ServiceCandidate.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ServiceCandidate.java new file mode 100644 index 00000000..4425043f --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/ServiceCandidate.java @@ -0,0 +1,104 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import score.*; + +public class ServiceCandidate { + private String svc; + private Address address; + private Address owner; + + public String getSvc() { + return svc; + } + + public void setSvc(String svc) { + this.svc = svc; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + public Address getOwner() { + return owner; + } + + public void setOwner(Address owner) { + this.owner = owner; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ServiceCandidate{"); + sb.append("svc='").append(svc).append('\''); + sb.append(", address=").append(address); + sb.append(", owner=").append(owner); + sb.append('}'); + return sb.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ServiceCandidate that = (ServiceCandidate) o; + + if (svc != null ? !svc.equals(that.svc) : that.svc != null) return false; + if (address != null ? !address.equals(that.address) : that.address != null) return false; + return owner != null ? owner.equals(that.owner) : that.owner == null; + } + + public static void writeObject(ObjectWriter writer, ServiceCandidate obj) { + obj.writeObject(writer); + } + + public static ServiceCandidate readObject(ObjectReader reader) { + ServiceCandidate obj = new ServiceCandidate(); + reader.beginList(); + obj.setSvc(reader.readNullable(String.class)); + obj.setAddress(reader.readNullable(Address.class)); + obj.setOwner(reader.readNullable(Address.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.writeNullable(this.getSvc()); + writer.writeNullable(this.getAddress()); + writer.writeNullable(this.getOwner()); + writer.end(); + } + + public static ServiceCandidate fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return ServiceCandidate.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + ServiceCandidate.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Services.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Services.java new file mode 100644 index 00000000..e7c64fc4 --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Services.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; +import score.Address; + +public class Services extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Services.class); + + public Services(String id) { + super(id, String.class, Address.class); + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/UnlinkMessage.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/UnlinkMessage.java new file mode 100644 index 00000000..b2cd0f0d --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/UnlinkMessage.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class UnlinkMessage { + private BTPAddress link; + + public BTPAddress getLink() { + return link; + } + + public void setLink(BTPAddress link) { + this.link = link; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("UnlinkMessage{"); + sb.append("link=").append(link); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, UnlinkMessage obj) { + obj.writeObject(writer); + } + + public static UnlinkMessage readObject(ObjectReader reader) { + UnlinkMessage obj = new UnlinkMessage(); + reader.beginList(); + obj.setLink(reader.readNullable(BTPAddress.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(1); + writer.writeNullable(this.getLink()); + writer.end(); + } + + public static UnlinkMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return UnlinkMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + UnlinkMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Verifiers.java b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Verifiers.java new file mode 100644 index 00000000..154ba4cb --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/btp/bmc/Verifiers.java @@ -0,0 +1,30 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.score.data.EnumerableDictDB; +import foundation.icon.score.util.Logger; +import score.Address; + +public class Verifiers extends EnumerableDictDB { + private static final Logger logger = Logger.getLogger(Verifiers.class); + + public Verifiers(String id) { + super(id, String.class, Address.class); + } + +} diff --git a/javascore/bmc/src/main/java/foundation/icon/score/data/EnumerableDictDB.java b/javascore/bmc/src/main/java/foundation/icon/score/data/EnumerableDictDB.java new file mode 100644 index 00000000..6ffaf7bf --- /dev/null +++ b/javascore/bmc/src/main/java/foundation/icon/score/data/EnumerableDictDB.java @@ -0,0 +1,242 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.data; + +import score.Address; +import score.ArrayDB; +import score.Context; +import score.DictDB; +import scorex.util.ArrayList; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +public class EnumerableDictDB { + protected final String id; + private final DictDB indexes; + private final DictDB keys; + private final ArrayDB values; + private final boolean supportedKeyType; + + @SuppressWarnings({"unchecked", "rawtypes"}) + public EnumerableDictDB(String id, Class keyClass, Class valueClass) { + this.id = id; + supportedKeyType = isSupportedKeyType(keyClass); + // key => array index + this.indexes = Context.newDictDB(id, Integer.class); + // array index => key + this.keys = Context.newDictDB(concatId("keys"), keyClass); + // array of valueClass + this.values = Context.newArrayDB(id, (Class) valueClass); + } + + protected String concatId(Object id) { + return concatId(this.id, id); + } + + public int size() { + return values.size(); + } + + private Object ensureKeyType(K key) { + return supportedKeyType ? key : key.toString(); + } + + private Integer getIndex(K key) { + return indexes.get(ensureKeyType(key)); + } + + private void setIndex(K key, Integer i) { + indexes.set(ensureKeyType(key), i); + } + + private K getKey(Integer i) { + return (i != null) ? keys.get(i) : null; + } + + private void setKey(Integer i, K key) { + keys.set(i, key); + } + + @SuppressWarnings("unchecked") + private V getValue(Integer i) { + return (i != null) ? (V) values.get(i) : null; + } + + private V putValue(Integer i, V value) { + V old = getValue(i); + if (old == null) { + values.add(value); + } else { + values.set(i, value); + } + return old; + } + + @SuppressWarnings("unchecked") + private V removeValue(Integer i) { + V old = getValue(i); + if (old != null) { + V last = (V) values.pop(); + if (i != values.size()) { + values.set(i, last); + } + } + return old; + } + + public boolean containsKey(K key) { + return getIndex(key) != null; + } + + public boolean containsValue(V value) { + int size = size(); + for (int i = 0; i < size; i++) { + if (getValue(i).equals(value)) { + return true; + } + } + return false; + } + + public V getByIndex(int i) { + return getValue(i); + } + + public V get(K key) { + Integer i = getIndex(key); + V value = getValue(i); + return value; + } + + public V put(K key, V value) { + Integer i = getIndex(key); + V old = putValue(i, value); + if (old == null) { + i = values.size() - 1; + setIndex(key, i); + setKey(i, key); + } + return old; + } + + public V remove(K key) { + Integer i = getIndex(key); + V old = removeValue(i); + if (old != null) { + setIndex(key, null); + Integer lastIdx = values.size(); + if (i.equals(lastIdx)) { + //remove lastKey + setKey(i, null); + } else { + //update lastKey + K lastKey = getKey(lastIdx); + setIndex(lastKey, i); + setKey(i, lastKey); + } + } + return old; + } + + public void clear() { + int size = size(); + for (int i = 0; i < size; i++) { + K key = keys.get(i); + keys.set(i, null); + indexes.set(ensureKeyType(key), null); + values.removeLast(); + } + } + + public List keySet() { + ArrayList keySet = new ArrayList<>(); + int size = size(); + for (int i = 0; i < size; i++) { + keySet.add(getKey(i)); + } + return keySet; + } + + public List supportedKeySet() { + ArrayList keySet = new ArrayList<>(); + int size = size(); + for (int i = 0; i < size; i++) { + keySet.add(ensureKeyType(getKey(i))); + } + return keySet; + } + + public List values() { + ArrayList values = new ArrayList<>(); + int size = size(); + for (int i = 0; i < size; i++) { + values.add(getValue(i)); + } + return values; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public Map toMap() { + int size = size(); + Map.Entry[] entries = new Map.Entry[size]; + for (int i = 0; i < size; i++) { + entries[i] = Map.entry(getKey(i), getValue(i)); + } + return Map.ofEntries(entries); + } + + public Map toMapWithKeyToString() { + int size = size(); + Map.Entry[] entries = new Map.Entry[size]; + for (int i = 0; i < size; i++) { + K key = getKey(i); + if (key instanceof String){ + entries[i] = Map.entry(key, getValue(i)); + } else { + entries[i] = Map.entry(key.toString(), getValue(i)); + } + } + return Map.ofEntries(entries); + } + + public static String concatId(String id, Object sub) { + return id + "|" + sub.toString(); + } + + static boolean isSupportedKeyType(Class clazz) { + for (Class type : supportedKeyTypes) { + if (type.equals(clazz)) { + return true; + } + } + return false; + } + + static Class[] supportedKeyTypes = new Class[]{ + String.class, + byte[].class, + Address.class, + BigInteger.class, + Byte.class, + Short.class, + Integer.class, + Long.class, + Character.class + }; +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/AssertBMCException.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/AssertBMCException.java new file mode 100644 index 00000000..b09c6414 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/AssertBMCException.java @@ -0,0 +1,75 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.test.AssertBTPException; +import org.junit.jupiter.api.function.Executable; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SuppressWarnings("ThrowableNotThrown") +public class AssertBMCException { + + public static void assertUnknown(Executable executable) { + AssertBTPException.assertBTPException(BMCException.unknown(""), executable); + } + + public static void assertUnauthorized(Executable executable) { + AssertBTPException.assertBTPException(BMCException.unauthorized(), executable); + } + + public static void assertInvalidSn(Executable executable) { + AssertBTPException.assertBTPException(BMCException.invalidSn(), executable); + } + + public static void assertAlreadyExistsBMV(Executable executable) { + AssertBTPException.assertBTPException(BMCException.alreadyExistsBMV(), executable); + } + + public static void assertNotExistsBMV(Executable executable) { + AssertBTPException.assertBTPException(BMCException.notExistsBMV(), executable); + } + + public static void assertAlreadyExistsBSH(Executable executable) { + AssertBTPException.assertBTPException(BMCException.alreadyExistsBSH(), executable); + } + + public static void assertNotExistsBSH(Executable executable) { + AssertBTPException.assertBTPException(BMCException.notExistsBSH(), executable); + } + + public static void assertAlreadyExistsLink(Executable executable) { + AssertBTPException.assertBTPException(BMCException.alreadyExistsLink(), executable); + } + + public static void assertNotExistsLink(Executable executable) { + AssertBTPException.assertBTPException(BMCException.notExistsLink(), executable); + } + + public static void assertAlreadyExistsBMR(Executable executable) { + AssertBTPException.assertBTPException(BMCException.alreadyExistsBMR(), executable); + } + + public static void assertNotExistsBMR(Executable executable) { + AssertBTPException.assertBTPException(BMCException.notExistsBMR(), executable); + } + + public static void assertUnreachable(Executable executable) { + AssertBTPException.assertBTPException(BMCException.unreachable(), executable); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMCIntegrationTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMCIntegrationTest.java new file mode 100644 index 00000000..2af1eae9 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMCIntegrationTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BMC; +import foundation.icon.btp.lib.BMCScoreClient; +import foundation.icon.btp.lib.OwnerManager; +import foundation.icon.btp.lib.OwnerManagerScoreClient; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.client.ScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) +public interface BMCIntegrationTest extends BTPIntegrationTest { + + DefaultScoreClient bmcClient = DefaultScoreClient.of(System.getProperties()); + @ScoreClient + BMC bmc = new BMCScoreClient(bmcClient); + @ScoreClient + ICONSpecific iconSpecific = new ICONSpecificScoreClient(bmcClient); + @ScoreClient + OwnerManager ownerManager = new OwnerManagerScoreClient(bmcClient); + + DefaultScoreClient bmcClientWithTester = new DefaultScoreClient(bmcClient.endpoint(), bmcClient._nid(), tester, bmcClient._address()); + BMC bmcWithTester = new BMCScoreClient(bmcClientWithTester); + ICONSpecific iconSpecificWithTester = new ICONSpecificScoreClient(bmcClientWithTester); + OwnerManager ownerManagerWithTester = new OwnerManagerScoreClient(bmcClientWithTester); + + static Consumer eventLogChecker( + ScoreIntegrationTest.EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + bmcClient._address(), supplier, consumer); + } + + static List btpMessages(TransactionResult txr, Predicate filter) { + return MessageEventLog.eventLogs(txr, bmcClient._address(), filter).stream() + .map(MessageEventLog::getMsg) + .collect(Collectors.toList()); + } + + static List bmcMessages(TransactionResult txr, Predicate nextPredicate) { + return btpMessages(txr, + (el) -> el.getMsg().getSvc().equals(BTPMessageCenter.INTERNAL_SERVICE) && + nextPredicate.test(el.getNext())).stream() + .map((msg) -> BMCMessage.fromBytes(msg.getPayload())) + .collect(Collectors.toList()); + } + + static List internalMessages( + List bmcMessages, + BTPMessageCenter.Internal internal, + Function mapFunc) { + return bmcMessages.stream() + .filter((bmcMsg) -> bmcMsg.getType().equals(internal.name())) + .map((bmcMsg) -> mapFunc.apply(bmcMsg.getPayload())) + .collect(Collectors.toList()); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRManagementTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRManagementTest.java new file mode 100644 index 00000000..c9886f1d --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRManagementTest.java @@ -0,0 +1,127 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.btp.test.MockBMVIntegrationTest; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.AssertRevertedException; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.*; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BMRManagementTest implements BMCIntegrationTest { + static BTPAddress linkBtpAddress = BMCIntegrationTest.Faker.btpLink(); + static String link = linkBtpAddress.toString(); + static String net = linkBtpAddress.net(); + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); + + @BeforeAll + static void beforeAll() { + System.out.println("beforeAll start"); + BMVManagementTest.addVerifier(net, MockBMVIntegrationTest.mockBMVClient._address()); + LinkManagementTest.addLink(link); + System.out.println("beforeAll end"); + } + + @AfterAll + static void afterAll() { + System.out.println("afterAll start"); + LinkManagementTest.clearLink(link); + BMVManagementTest.clearVerifier(net); + System.out.println("afterAll end"); + } + + @Override + public void clearIfExists(TestInfo testInfo) { + clearRelay(link, address); + } + + static boolean isExistsRelay(String link, Address address) { + return ScoreIntegrationTest.indexOf( + iconSpecific.getRelays(link), address) >= 0; + } + + static void addRelay(String link, Address address) { + iconSpecific.addRelay(link, address); + assertTrue(isExistsRelay(link, address)); + } + + static void removeRelay(String link, Address address) { + iconSpecific.removeRelay(link, address); + assertFalse(isExistsRelay(link, address)); + } + + static void clearRelay(String link, Address address) { + if (LinkManagementTest.isExistsLink(link) && isExistsRelay(link, address)) { + System.out.println("clear relay link:" + link + ", address:" + address); + removeRelay(link, address); + } + } + + @Test + void addRelayShouldSuccess() { + addRelay(link, address); + } + + @Test + void addRelayShouldRevertAlreadyExists() { + addRelay(link, address); + + AssertBMCException.assertAlreadyExistsBMR(() -> + addRelay(link, address)); + } + + @Test + void addRelayShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink(() -> + addRelay(BTPIntegrationTest.Faker.btpLink().toString(), address)); + } + + @Test + void removeRelayShouldSuccess() { + addRelay(link, address); + + removeRelay(link, address); + } + + @Test + void removeRelayShouldRevertNotExists() { + AssertBMCException.assertNotExistsBMR(() -> + removeRelay(link, address)); + } + + @Test + void removeRelayShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink(() -> removeRelay( + BTPIntegrationTest.Faker.btpLink().toString(), address)); + } + + @Disabled("readonly call revert test") + @Test + void getRelaysShouldRevertNotExistsLink() { + //noinspection ThrowableNotThrown + AssertRevertedException.assertUserRevertFromJsonrpcError( + BMCException.notExistsLink(), + () -> iconSpecific.getRelays(BTPIntegrationTest.Faker.btpLink().toString()), + null); + } + +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRRotationTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRRotationTest.java new file mode 100644 index 00000000..8dfc310f --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMRRotationTest.java @@ -0,0 +1,241 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BMCScoreClient; +import foundation.icon.btp.lib.BMCStatus; +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.mock.MockBSH; +import foundation.icon.btp.mock.MockRelayMessage; +import foundation.icon.btp.test.MockBMVIntegrationTest; +import foundation.icon.btp.test.MockBSHIntegrationTest; +import foundation.icon.icx.Wallet; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; + +public class BMRRotationTest implements BMCIntegrationTest { + static BTPAddress linkBtpAddress = BMCIntegrationTest.Faker.btpLink(); + static String link = linkBtpAddress.toString(); + static String net = linkBtpAddress.net(); + static BTPAddress btpAddress = BTPAddress.valueOf(bmc.getBtpAddress()); + static String svc = MockBSH.SERVICE; + static int numOfRelays = 3; + static Address[] relayAddresses = new Address[numOfRelays]; + static BMCScoreClient[] relays = new BMCScoreClient[numOfRelays]; + static int blockInterval = BTPMessageCenter.BLOCK_INTERVAL_MSEC; + static int maxAggregation = 10; + static int delayLimit = 3; + static int executeMargin = 2;//pool(1)+execute(1) + static int finalizeMargin = executeMargin + 1; //result(1) + + static BTPMessage btpMessage(BigInteger sn, byte[] payload) { + BTPMessage btpMsg = new BTPMessage(); + btpMsg.setSrc(linkBtpAddress); + btpMsg.setDst(btpAddress); + btpMsg.setSvc(svc); + btpMsg.setSn(sn); + btpMsg.setPayload(payload); + return btpMsg; + } + + @BeforeAll + static void beforeAll() { + System.out.println("beforeAll start"); + BSHManagementTest.addService(svc, MockBSHIntegrationTest.mockBSHClient._address()); + BMVManagementTest.addVerifier(net, MockBMVIntegrationTest.mockBMVClient._address()); + LinkManagementTest.addLink(link); + MockBMVIntegrationTest.mockBMV.setHeight(client._lastBlockHeight().longValue()); + iconSpecific.setLinkRotateTerm(link, blockInterval, maxAggregation); + iconSpecific.setLinkDelayLimit(link, delayLimit); + for (int i = 0; i < numOfRelays; i++) { + Wallet wallet = ScoreIntegrationTest.generateWallet(); + relays[i] = new BMCScoreClient( + bmcClient.endpoint(), + bmcClient._nid(), + wallet, + bmcClient._address()); + Address relayAddress = Address.of(wallet); + relayAddresses[i] = relayAddress; + BMRManagementTest.addRelay(link, relayAddress); + } + System.out.println("beforeAll end"); + } + + @AfterAll + static void afterAll() { + System.out.println("afterAll start"); + //when removeLink, clear all of relays of link, so not required clear relay +// for (int i = 0; i < numOfRelays; i++) { +// BMRManagementTest.clearRelay(link,relayAddresses[i]); +// } + LinkManagementTest.clearLink(link); + BMVManagementTest.clearVerifier(net); + BSHManagementTest.clearService(svc); + System.out.println("afterAll end"); + } + + static int rotateRelayIdx(int idx, int num) { + idx += num; + return idx >= numOfRelays ? 0 : idx; + } + + static int rotate(BMCStatus status, long at) { + long rotateHeight = status.getRotate_height(); + int rotateTerm = status.getRotate_term(); + int rotateCnt = (int)StrictMath.ceil((double)(at - rotateHeight)/(double)rotateTerm); + int relayIdx = status.getRelay_idx(); + if (rotateCnt > 0) { + rotateHeight += ((long) rotateCnt * rotateTerm); + relayIdx = rotateRelayIdx(status.getRelay_idx(), rotateCnt); + } + System.out.println("rotateCnt:"+rotateCnt+ + ", relayIdx:"+relayIdx+ + ", rotateHeight:"+rotateHeight+ + ", at:"+at); + return relayIdx; + } + + @Test + void relayRotationNotContainsBTPMessage() throws Exception { + MockRelayMessage relayMessage = new MockRelayMessage(); + ExecutorService executorService = Executors.newFixedThreadPool(numOfRelays); + + for (int i = 0; i < numOfRelays; i++) { + BMCStatus status = bmc.getStatus(link); + System.out.println(status); + + long sendTxHeight = status.getCur_height(); + int relayIdx = rotate(status, sendTxHeight + executeMargin); + relayMessage.setHeight(sendTxHeight); + String msg = relayMessage.toBase64String(); + + List> tasks = new ArrayList<>(); + for (int j = 0; j < numOfRelays; j++) { + BMCScoreClient relay = relays[j]; + Runnable runable; + if (relayIdx == j) { + runable = () -> relay.handleRelayMessage(link, msg); + } else { + runable = () -> + AssertBMCException.assertUnauthorized( + () -> relay.handleRelayMessage(link, msg)); + } + tasks.add(Executors.callable(runable, null)); + } + List> futures = executorService.invokeAll(tasks); + for(Future future : futures) { + future.get(); + } + //wait to rotateHeight + ScoreIntegrationTest.waitByHeight(bmc.getStatus(link).getRotate_height()); + } + } + + static int guessRotate(BMCStatus status, long msgHeight, long at) { + double scale = (double) status.getBlock_interval_src() / (double) status.getBlock_interval_dst(); + long guessHeight = status.getRx_height() + + (long) StrictMath.ceil( + (double) (msgHeight - status.getRx_height_src()) / scale) - 1; + if (guessHeight > at) { + System.out.println("guessHeight > at,"+guessHeight); + guessHeight = at; + } + long rotateHeight = status.getRotate_height(); + int rotateTerm = status.getRotate_term(); + int rotateCnt = (int)StrictMath.ceil((double)(guessHeight - rotateHeight)/(double)rotateTerm); + if (rotateCnt < 0) { + rotateCnt = 0; + } else { + rotateHeight += ((long) rotateCnt * rotateTerm); + } + int skipCnt = (int)StrictMath.ceil((double)(at - guessHeight)/(double)delayLimit) - 1; + if (skipCnt > 0) { + rotateHeight = at + rotateTerm; + } else { + skipCnt = 0; + } + int relayIdx = status.getRelay_idx(); + if (rotateCnt > 0 || skipCnt > 0) { + relayIdx = rotateRelayIdx(status.getRelay_idx(), rotateCnt + skipCnt); + } + System.out.println("guessHeight:"+guessHeight+ + ", rotateCnt:"+rotateCnt+ + ", skipCnt:"+skipCnt+ + ", relayIdx:"+relayIdx+ + ", rotateHeight:"+rotateHeight+ + ", msgHeight:"+msgHeight+ + ", at:"+at); + return relayIdx; + } + + @Test + void relayRotationContainsBTPMessage() throws Exception { + BigInteger sn = BigInteger.ONE; + byte[] payload = Faker.btpLink().toBytes(); + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(sn, payload)); + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(MessageTest.toBytesArray(btpMessages)); + + ExecutorService executorService = Executors.newFixedThreadPool(numOfRelays); + for (int i = 0; i < numOfRelays; i++) { + BMCStatus status = bmc.getStatus(link); + System.out.println(status); + long msgHeight = status.getCur_height(); + long sendTxHeight = msgHeight + delayLimit; + int relayIdx = guessRotate(status, msgHeight, sendTxHeight+executeMargin); + relayMessage.setHeight(msgHeight); + relayMessage.setLastHeight(relayMessage.getHeight()); + String msg = relayMessage.toBase64String(); + + //wait to msgHeight + delayLimit + ScoreIntegrationTest.waitByHeight(sendTxHeight); + + List> tasks = new ArrayList<>(); + for (int j = 0; j < numOfRelays; j++) { + BMCScoreClient relay = relays[j]; + Runnable runable; + if (relayIdx == j) { + runable = () -> relay.handleRelayMessage(link, msg); + } else { + runable = () -> + AssertBMCException.assertUnauthorized( + () -> relay.handleRelayMessage(link, msg)); + } + tasks.add(Executors.callable(runable, null)); + } + List> futures = executorService.invokeAll(tasks); + for(Future future : futures) { + future.get(); + } + + //reset Link.rxHeight, Link.rxHeightSrc + relayMessage.setHeight(client._lastBlockHeight().longValue()); + relayMessage.setLastHeight(relayMessage.getHeight()); + relays[relayIdx].handleRelayMessage(link, relayMessage.toBase64String()); + } + } + +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMVManagementTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMVManagementTest.java new file mode 100644 index 00000000..50442741 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BMVManagementTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BMVManagementTest implements BMCIntegrationTest { + static BTPAddress btpAddress = BTPIntegrationTest.Faker.btpLink(); + static String net = btpAddress.net(); + static Address address = new Address(btpAddress.account()); + + static boolean isExistsVerifier(String net, Address address) { + return ScoreIntegrationTest.contains( + bmc.getVerifiers(), net, (o) -> address.toString().equals(o)); + } + + static void addVerifier(String net, Address address) { + bmc.addVerifier(net, address); + assertTrue(isExistsVerifier(net, address)); + } + + static boolean isExistsVerifier(String net) { + return bmc.getVerifiers().containsKey(net); + } + + static void removeVerifier(String net) { + bmc.removeVerifier(net); + assertFalse(isExistsVerifier(net)); + } + + static void clearVerifier(String net) { + if(isExistsVerifier(net)) { + System.out.println("clear verifier net:"+net); + removeVerifier(net); + } + } + + @Override + public void clearIfExists(TestInfo testInfo) { + clearVerifier(net); + } + + @Test + void addVerifierShouldSuccess() { + addVerifier(net, address); + } + + @Test + void addVerifierShouldRevertAlreadyExists() { + addVerifier(net, address); + + AssertBMCException.assertAlreadyExistsBMV(() -> addVerifier(net, address)); + } + + @Test + void removeVerifierShouldSuccess() { + addVerifier(net, address); + + removeVerifier(net); + } + + @Test + void removeVerifierShouldRevertNotExists() { + AssertBMCException.assertNotExistsBMV(() -> removeVerifier(net)); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BSHManagementTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BSHManagementTest.java new file mode 100644 index 00000000..ad8a79d8 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/BSHManagementTest.java @@ -0,0 +1,154 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BSHManagementTest implements BMCIntegrationTest { + + static String svc = BTPIntegrationTest.Faker.btpService(); + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT); + + static boolean isExistsServiceCandidate(String svc, Address address, Address owner) { + Predicate predicate = (o) -> o.getSvc().equals(svc) && + o.getAddress().equals(address) && + o.getOwner().equals(owner); + return ScoreIntegrationTest.indexOf( + iconSpecific.getServiceCandidates(), predicate) >= 0; + } + + static void addServiceCandidate(String svc, Address address) { + iconSpecificWithTester.addServiceCandidate(svc, address); + assertTrue(isExistsServiceCandidate(svc, address, Address.of(tester))); + } + + static boolean isExistsServiceCandidate(String svc, Address address) { + Predicate predicate = (o) -> o.getSvc().equals(svc) && + o.getAddress().equals(address); + return ScoreIntegrationTest.indexOf( + iconSpecific.getServiceCandidates(), predicate) >= 0; + } + + static void removeServiceCandidate(String svc, Address address) { + iconSpecific.removeServiceCandidate(svc, address); + assertFalse(isExistsServiceCandidate(svc, address)); + } + + static void clearServiceCandidate(String svc, Address address) { + if(isExistsServiceCandidate(svc, address)) { + System.out.println("clear service candidate svc:"+svc); + removeServiceCandidate(svc, address); + } + } + + static boolean isExistsService(String svc, Address address) { + return ScoreIntegrationTest.contains(bmc.getServices(), svc, + (o) -> address.toString().equals(o)); + } + + static void addService(String svc, Address address) { + bmc.addService(svc, address); + assertTrue(isExistsService(svc, address)); + } + + static boolean isExistsService(String svc) { + return bmc.getServices().containsKey(svc); + } + + static void removeService(String svc) { + bmc.removeService(svc); + assertFalse(isExistsService(svc)); + } + + static void clearService(String svc) { + if(isExistsService(svc)) { + System.out.println("clear service svc:"+svc); + removeService(svc); + } + } + + @Override + public void clearIfExists(TestInfo testInfo) { + clearServiceCandidate(svc, address); + clearService(svc); + } + + @Test + void addServiceCandidateShouldSuccess() { + addServiceCandidate(svc, address); + } + + @Test + void addServiceCandidateShouldRevertAlreadyExists() { + addServiceCandidate(svc, address); + + AssertBMCException.assertUnknown(() -> addServiceCandidate(svc, address)); + } + + @Test + void removeServiceCandidateShouldSuccess() { + addServiceCandidate(svc, address); + + removeServiceCandidate(svc, address); + } + + @Test + void removeServiceCandidateShouldRevertNotExists() { + AssertBMCException.assertUnknown(() -> removeServiceCandidate(svc, address)); + } + + @Test + void addServiceShouldSuccess() { + addService(svc, address); + } + + @Test + void addServiceCandidateAndAddServiceShouldRemoveServiceCandidate() { + addServiceCandidate(svc, address); + addService(svc, address); + + assertFalse(isExistsServiceCandidate(svc, address)); + } + + @Test + void addServiceShouldRevertAlreadyExists() { + addService(svc, address); + + AssertBMCException.assertAlreadyExistsBSH(() -> addService(svc, address)); + } + + @Test + void removeServiceShouldSuccess() { + addService(svc, address); + + removeService(svc); + } + + @Test + void removeServiceShouldRevertNotExists() { + AssertBMCException.assertNotExistsBSH(() -> removeService(svc)); + } +} \ No newline at end of file diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/LinkManagementTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/LinkManagementTest.java new file mode 100644 index 00000000..968c81ae --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/LinkManagementTest.java @@ -0,0 +1,374 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.BMCScoreClient; +import foundation.icon.btp.lib.BMCStatus; +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.btp.test.MockBMVIntegrationTest; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.*; + +public class LinkManagementTest implements BMCIntegrationTest { + + static BTPAddress linkBtpAddress = BTPIntegrationTest.Faker.btpLink(); + static String link = linkBtpAddress.toString(); + static BTPAddress secondLinkBtpAddress = BTPIntegrationTest.Faker.btpLink(); + static String secondLink = secondLinkBtpAddress.toString(); + static String dst = BTPIntegrationTest.Faker.btpLink().toString(); + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); + + static int blockInterval = 1000; + static int maxAgg = 100; + static int delayLimit = 2; + static int sackTerm = 10; + + static Consumer> initMessageChecker(List links) { + return (bmcMessages) -> { + List initMessages = BMCIntegrationTest.internalMessages( + bmcMessages, BTPMessageCenter.Internal.Init, InitMessage::fromBytes); + assertEquals(1, initMessages.size()); + InitMessage initMessage = initMessages.get(0); + assertEquals(links == null ? 0 : links.size(), initMessage.getLinks().length); + assertTrue(links == null || links.containsAll( + Arrays.stream(initMessage.getLinks()) + .map((BTPAddress::toString)) + .collect(Collectors.toList()))); + }; + } + + static Consumer> linkMessageChecker(String link, int size) { + return (bmcMessages) -> { + List linkMessages = BMCIntegrationTest.internalMessages( + bmcMessages, BTPMessageCenter.Internal.Link, LinkMessage::fromBytes); + assertEquals(size, linkMessages.size()); + assertTrue(linkMessages.stream() + .allMatch((linkMsg) -> linkMsg.getLink().toString().equals(link))); + }; + } + + static Consumer> unlinkMessageChecker(String link, int size) { + return (bmcMessages) -> { + List unlinkMessages = BMCIntegrationTest.internalMessages( + bmcMessages, BTPMessageCenter.Internal.Unlink, UnlinkMessage::fromBytes); + assertEquals(size, unlinkMessages.size()); + assertTrue(unlinkMessages.stream() + .allMatch((unlinkMsg) -> unlinkMsg.getLink().toString().equals(link))); + }; + } + + static boolean isExistsLink(String link) { + return ScoreIntegrationTest.indexOf(bmc.getLinks(), link) >= 0; + } + + static void addLink(String link) { + List links = Arrays.asList(bmc.getLinks()); + Consumer transactionResultChecker = (txr) -> { + initMessageChecker(links) + .accept(BMCIntegrationTest.bmcMessages(txr, (next) -> next.equals(link))); + }; + ((BMCScoreClient) bmc).addLink(transactionResultChecker, link); + assertTrue(isExistsLink(link)); + } + + static void removeLink(String link) { + bmc.removeLink(link); + assertFalse(isExistsLink(link)); + } + + static void clearLink(String link) { + if (isExistsLink(link)) { + System.out.println("clear link btpAddress:" + link); + removeLink(link); + } + } + + void setLinkRotateTerm(String link) { + iconSpecific.setLinkRotateTerm(link, blockInterval, maxAgg); + BMCStatus status = bmc.getStatus(link); + assertEquals(blockInterval, status.getBlock_interval_dst()); + assertEquals(maxAgg, status.getMax_agg()); + } + + static boolean isExistsRoute(String dst, String link) { + return ScoreIntegrationTest.contains( + bmc.getRoutes(), dst, (o) -> link.equals(o)); + } + + static boolean isExistsRoute(String dst) { + return bmc.getRoutes().containsKey(dst); + } + + static void addRoute(String dst, String link) { + bmc.addRoute(dst, link); + assertTrue(isExistsRoute(dst, link)); + } + + static void removeRoute(String dst) { + bmc.removeRoute(dst); + assertFalse(isExistsRoute(dst)); + } + + static void clearRoute(String dst) { + if (isExistsRoute(dst)) { + System.out.println("clear route dst:" + dst); + removeRoute(dst); + } + } + + @BeforeAll + static void beforeAll() { + System.out.println("beforeAll start"); + Address mockBMVAddress = MockBMVIntegrationTest.mockBMVClient._address(); + BMVManagementTest.addVerifier( + linkBtpAddress.net(), mockBMVAddress); + BMVManagementTest.addVerifier( + secondLinkBtpAddress.net(), mockBMVAddress); + System.out.println("beforeAll end"); + } + + @AfterAll + static void afterAll() { + System.out.println("afterAll start"); + BMVManagementTest.clearVerifier(linkBtpAddress.net()); + BMVManagementTest.clearVerifier(secondLinkBtpAddress.net()); + System.out.println("afterAll end"); + } + + @Override + public void internalBeforeEach(TestInfo testInfo) { + beforeLinkRequiredTests(testInfo); + } + +// @Override +// public void internalAfterEach(TestInfo testInfo) { +// afterLinkRequiredTests(testInfo); +// } + + @Override + public void clearIfExists(TestInfo testInfo) { + clearRoute(dst); + clearLink(link); + clearLink(secondLink); + } + + @Test + void addLinkShouldSuccess() { + addLink(link); + } + + @Test + void addLinkShouldRevertAlreadyExists() { + addLink(link); + + AssertBMCException.assertAlreadyExistsLink(() -> addLink(link)); + } + + @Test + void addLinkShouldRevertNotExistsBMV() { + AssertBMCException.assertNotExistsBMV( + () -> addLink(BTPIntegrationTest.Faker.btpLink().toString())); + } + + @Test + void removeLinkShouldSuccess() { + addLink(link); + + removeLink(link); + } + + @Test + void removeLinkShouldRevertNotExists() { + AssertBMCException.assertNotExistsLink( + () -> removeLink(link)); + } + + @Test + void removeLinkShouldRevertReferred() { + addLink(link); + addRoute(dst, link); + + AssertBMCException.assertUnknown(() -> removeLink(link)); + } + + @Test + void removeLinkShouldClearRelays() { + addLink(link); + + removeLink(link); + + //check relays of link is empty + addLink(link); + assertEquals(0, iconSpecific.getRelays(link).length); + } + + @Test + void addLinkShouldSendLinkMessageAndRemoveLinkShouldSendUnlinkMessage() { + addLink(link); + + //addLinkShouldSendLinkMessage + String secondLink = secondLinkBtpAddress.toString(); + List links = new ArrayList<>(); + links.add(link); + + Consumer linkMessageCheck = (txr) -> { + initMessageChecker(links) + .accept(BMCIntegrationTest.bmcMessages(txr, (next) -> next.equals(secondLink))); + List copy = new ArrayList<>(links); + linkMessageChecker(secondLink, links.size()) + .accept(BMCIntegrationTest.bmcMessages(txr, copy::remove)); + assertEquals(0, copy.size()); + }; + ((BMCScoreClient) bmc).addLink(linkMessageCheck, secondLink); + assertTrue(isExistsLink(secondLink)); + + //RemoveLinkShouldSendUnlinkMessage + Consumer unlinkMessageCheck = (txr) -> { + List copy = new ArrayList<>(links); + unlinkMessageChecker(secondLink, links.size()) + .accept(BMCIntegrationTest.bmcMessages(txr, copy::remove)); + assertEquals(0, copy.size()); + }; + ((BMCScoreClient) bmc).removeLink(unlinkMessageCheck, secondLink); + assertFalse(isExistsLink(secondLink)); + } + + static boolean isLinkRequiredTests(TestInfo testInfo) { + return !testInfo.getDisplayName().contains("LinkShould"); + } + + void beforeLinkRequiredTests(TestInfo testInfo) { + System.out.println("beforeLinkRequiredTests start on " + testInfo.getDisplayName()); + if (isLinkRequiredTests(testInfo)) { + addLink(link); + } + System.out.println("beforeLinkRequiredTests end on " + testInfo.getDisplayName()); + } + +// void afterLinkRequiredTests(TestInfo testInfo) { +// System.out.println("afterLinkRequiredTests start on "+testInfo.getDisplayName()); +// if (isLinkRequiredTests(testInfo)) { +// clearLink(link); +// } +// System.out.println("afterLinkRequiredTests end on "+testInfo.getDisplayName()); +// } + + @Test + void setLinkRotateTermShouldSuccess() { + setLinkRotateTerm(link); + } + + @Test + void setLinkRotateTermShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink( + () -> setLinkRotateTerm(secondLink)); + } + + @Test + void setLinkRotateTermShouldRevertIllegalArgument() { + int invalidValue = -1; + AssertBMCException.assertUnknown( + () -> iconSpecific.setLinkRotateTerm(link, invalidValue, maxAgg)); + AssertBMCException.assertUnknown( + () -> iconSpecific.setLinkRotateTerm(link, blockInterval, invalidValue)); + } + + @Test + void setLinkDelayLimitShouldSuccess() { + iconSpecific.setLinkDelayLimit(link, delayLimit); + BMCStatus status = bmc.getStatus(link); + assertEquals(delayLimit, status.getDelay_limit()); + } + + @Test + void setLinkDelayLimitShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink( + () -> iconSpecific.setLinkDelayLimit(secondLink, delayLimit)); + } + + @Test + void setLinkDelayLimitShouldRevertIllegalArgument() { + int invalidValue = -1; + AssertBMCException.assertUnknown( + () -> iconSpecific.setLinkDelayLimit(link, invalidValue)); + } + + @Test + void setLinkSackTermShouldSuccess() { + iconSpecific.setLinkSackTerm(link, sackTerm); + BMCStatus status = bmc.getStatus(link); + assertEquals(sackTerm, status.getSack_term()); + } + + @Test + void setLinkSackTermShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink( + () -> iconSpecific.setLinkSackTerm(secondLink, sackTerm)); + } + + @Test + void setLinkSackTermShouldRevertIllegalArgument() { + int invalidValue = -1; + AssertBMCException.assertUnknown( + () -> iconSpecific.setLinkSackTerm(link, invalidValue)); + } + + @Test + void addRouteShouldSuccess() { + addRoute(dst, link); + } + + @Test + void addRouteShouldRevertAlreadyExists() { + addRoute(dst, link); + + AssertBMCException.assertUnknown(() -> addRoute(dst, link)); + } + + @Test + void addRouteShouldRevertNotExistsLink() { + AssertBMCException.assertNotExistsLink( + () -> addRoute(dst, secondLink)); + } + + @Test + void removeRouteShouldSuccess() { + addRoute(dst, link); + + removeRoute(dst); + } + + @Test + void removeRouteShouldRevertNotExists() { + AssertBMCException.assertUnknown(() -> removeRoute(dst)); + } + +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageEventLog.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageEventLog.java new file mode 100644 index 00000000..0d8e4439 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageEventLog.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class MessageEventLog { + static final String SIGNATURE = "Message(str,int,bytes)"; + private String next; + private BigInteger seq; + private BTPMessage msg; + + public MessageEventLog(TransactionResult.EventLog el) { + this.next = el.getIndexed().get(1); + this.seq = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getIndexed().get(2)); + this.msg = BTPMessage.fromBytes(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(0))); + } + + public String getNext() { + return next; + } + + public BigInteger getSeq() { + return seq; + } + + public BTPMessage getMsg() { + return msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BTPMessageEventLog{"); + sb.append("next='").append(next).append('\''); + sb.append(", seq=").append(seq); + sb.append(", msg=").append(msg); + sb.append('}'); + return sb.toString(); + } + + static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + MessageEventLog.SIGNATURE, + address, + MessageEventLog::new, + filter); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageTest.java new file mode 100644 index 00000000..d6366555 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/MessageTest.java @@ -0,0 +1,354 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.btp.lib.*; +import foundation.icon.btp.mock.MockBSH; +import foundation.icon.btp.mock.MockBSHScoreClient; +import foundation.icon.btp.mock.MockRelayMessage; +import foundation.icon.btp.test.*; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.*; + +public class MessageTest implements BMCIntegrationTest { + static BTPAddress linkBtpAddress = BMCIntegrationTest.Faker.btpLink(); + static String link = linkBtpAddress.toString(); + static String net = linkBtpAddress.net(); + static Address relay = Address.of(bmcClient._wallet()); + static BTPAddress btpAddress = BTPAddress.valueOf(bmc.getBtpAddress()); + static String svc = MockBSH.SERVICE; + + @BeforeAll + static void beforeAll() { + System.out.println("beforeAll start"); + BMVManagementTest.addVerifier(net, MockBMVIntegrationTest.mockBMVClient._address()); + LinkManagementTest.addLink(link); + BMRManagementTest.addRelay(link, relay); + + BSHManagementTest.clearService(svc); + BSHManagementTest.addService(svc, MockBSHIntegrationTest.mockBSHClient._address()); + System.out.println("beforeAll end"); + } + + @AfterAll + static void afterAll() { + System.out.println("afterAll start"); + BSHManagementTest.clearService(svc); + + BMRManagementTest.clearRelay(link, relay); + LinkManagementTest.clearLink(link); + BMVManagementTest.clearVerifier(net); + System.out.println("afterAll end"); + } + + static BTPMessage btpMessage(BTPMessageCenter.Internal internal, byte[] payload) { + BMCMessage bmcMsg = new BMCMessage(); + bmcMsg.setType(internal.name()); + bmcMsg.setPayload(payload); + return btpMessage(BTPMessageCenter.INTERNAL_SERVICE, BigInteger.ZERO, bmcMsg.toBytes()); + } + + static BTPMessage btpMessage(String svc, BigInteger sn, byte[] payload) { + BTPMessage btpMsg = new BTPMessage(); + btpMsg.setSrc(linkBtpAddress); + btpMsg.setDst(btpAddress); + btpMsg.setSvc(svc); + btpMsg.setSn(sn); + btpMsg.setPayload(payload); + return btpMsg; + } + + static byte[][] toBytesArray(List btpMessages) { + int len = btpMessages.size(); + byte[][] bytesArray = new byte[len][]; + for (int i = 0; i < len; i++) { + bytesArray[i] = btpMessages.get(i).toBytes(); + } + return bytesArray; + } + + static Consumer> sackMessageChecker(long height, BigInteger seq) { + return (bmcMessages) -> { + List sackMessages = BMCIntegrationTest.internalMessages( + bmcMessages, BTPMessageCenter.Internal.Sack, SackMessage::fromBytes); + assertEquals(1, sackMessages.size()); + SackMessage sackMessage = sackMessages.get(0); + assertEquals(height, sackMessage.getHeight()); + assertEquals(seq, sackMessage.getSeq()); + }; + } + + static Consumer> feeGatheringMessageChecker(BTPAddress fa, List services, int size) { + return (bmcMessages) -> { + List feeGatheringMessages = BMCIntegrationTest.internalMessages( + bmcMessages, BTPMessageCenter.Internal.FeeGathering, FeeGatheringMessage::fromBytes); + assertEquals(size, feeGatheringMessages.size()); + assertTrue(feeGatheringMessages.stream() + .allMatch((feeGatheringMsg) -> feeGatheringMsg.getFa().equals(fa) + && services.size() == feeGatheringMsg.getSvcs().length + && services.containsAll(Arrays.asList(feeGatheringMsg.getSvcs())))); + }; + } + + @Test + void sackMessageShouldUpdateSackHeightAndSackSeq() { + SackMessage sackMessage = new SackMessage(); + sackMessage.setHeight(1); + sackMessage.setSeq(BigInteger.ONE); + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(BTPMessageCenter.Internal.Sack, sackMessage.toBytes())); + + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(toBytesArray(btpMessages)); + bmc.handleRelayMessage(link, relayMessage.toBase64String()); + BMCStatus status = bmc.getStatus(link); + System.out.println(status); + assertEquals(sackMessage.getHeight(), status.getSack_height()); + assertEquals(sackMessage.getSeq(), status.getSack_seq()); + } + + @Test + void handleRelayMessageShouldSendSackMessageToPrev() { + //if sackTerm > 0 && sackNext <= blockHeight + int sackTerm = 2; + iconSpecific.setLinkSackTerm(link, sackTerm); + BigInteger seq = bmc.getStatus(link).getRx_seq(); + long height = 1; + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setHeight(height); + //check SackMessage + Consumer sackMessageCheck = (txr) -> { + sackMessageChecker(height, seq) + .accept(BMCIntegrationTest.bmcMessages(txr, (next) -> next.equals(link))); + }; + ((BMCScoreClient) bmc).handleRelayMessage(sackMessageCheck, link, relayMessage.toBase64String()); + } + + @Test + void handleRelayMessageShouldNotSendSackMessage() { + int sackTerm = 10; + iconSpecific.setLinkSackTerm(link, sackTerm); + long height = 1; + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setHeight(height); + + Consumer notExistsSackMessageCheck = (txr) -> { + assertFalse(BMCIntegrationTest.bmcMessages(txr, (next) -> next.equals(link)).stream() + .anyMatch((bmcMsg) -> bmcMsg.getType().equals(BTPMessageCenter.Internal.Sack.name()))); + }; + ((BMCScoreClient) bmc).handleRelayMessage(notExistsSackMessageCheck, link, relayMessage.toBase64String()); + } + + @Test + void feeGatheringMessageShouldCallHandleFeeGathering() { + //handleRelayMessage -> BSHMock.handleFeeGathering -> EventLog + FeeGatheringMessage feeGatheringMessage = new FeeGatheringMessage(); + BTPAddress fa = new BTPAddress( + BTPAddress.PROTOCOL_BTP, + net, + ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT).toString()); + feeGatheringMessage.setFa(fa); + feeGatheringMessage.setSvcs(new String[]{svc}); + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(BTPMessageCenter.Internal.FeeGathering, feeGatheringMessage.toBytes())); + + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(toBytesArray(btpMessages)); + ((BMCScoreClient) bmc).handleRelayMessage( + MockBSHIntegrationTest.eventLogChecker(HandleFeeGatheringEventLog::eventLogs, (el) -> { + assertEquals(fa.toString(), el.getFa()); + assertEquals(svc, el.getSvc()); + }), + link, relayMessage.toBase64String()); + } + + @Test + void handleRelayMessageShouldSendFeeGatheringMessage() { + //if gatherFeeTerm > 0 && gatherFeeNext <= blockHeight + int gatherFeeTerm = 2; + iconSpecific.setFeeGatheringTerm(gatherFeeTerm); + + Address feeAggregator = ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT); + iconSpecific.setFeeAggregator(feeAggregator); + BTPAddress fa = new BTPAddress(BTPAddress.PROTOCOL_BTP, btpAddress.net(), feeAggregator.toString()); + + @SuppressWarnings({"rawtypes", "unchecked"}) + List services = new ArrayList<>(bmc.getServices().keySet()); + + List links = Arrays.asList(bmc.getLinks()); + Consumer feeGatheringMessageCheck = (txr) -> { + feeGatheringMessageChecker(fa, services, links.size()) + .accept(BMCIntegrationTest.bmcMessages(txr, links::contains)); + }; + ((BMCScoreClient) bmc).handleRelayMessage(feeGatheringMessageCheck, link, new MockRelayMessage().toBase64String()); + } + + @Test + void handleRelayMessageShouldNotSendFeeGatheringMessage() { + //check FeeGathering notExists + int gatherFeeTerm = 10; + iconSpecific.setFeeGatheringTerm(gatherFeeTerm); + + List links = Arrays.asList(bmc.getLinks()); + Consumer notExistsSackMessageCheck = (txr) -> { + assertFalse(BMCIntegrationTest.bmcMessages(txr, links::contains).stream() + .anyMatch((bmcMsg) -> bmcMsg.getType().equals(BTPMessageCenter.Internal.FeeGathering.name()))); + }; + ((BMCScoreClient) bmc).handleRelayMessage(notExistsSackMessageCheck, link, new MockRelayMessage().toBase64String()); + } + + @Test + void handleRelayMessageShouldCallHandleBTPMessage() { + //BMC.handleRelayMessage -> BSHMock.HandleBTPMessage(str,str,int,bytes) + BigInteger sn = BigInteger.ONE; + byte[] payload = Faker.btpLink().toBytes(); + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(svc, sn, payload)); + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(toBytesArray(btpMessages)); + ((BMCScoreClient) bmc).handleRelayMessage( + MockBSHIntegrationTest.eventLogChecker(HandleBTPMessageEventLog::eventLogs, (el) -> { + assertEquals(net, el.getFrom()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(payload, el.getMsg()); + }), + link, + relayMessage.toBase64String()); + } + + @Test + void handleRelayMessageShouldCallHandleBTPError() { + //BMC.handleRelayMessage -> BSHMock.HandleBTPError(str,str,int,int,str) + ErrorMessage errorMessage = new ErrorMessage(); + errorMessage.setCode(1); + errorMessage.setMsg("error"); + BigInteger sn = BigInteger.ONE; + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(svc, sn.negate(), errorMessage.toBytes())); + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(toBytesArray(btpMessages)); + ((BMCScoreClient) bmc).handleRelayMessage( + MockBSHIntegrationTest.eventLogChecker(HandleBTPErrorEventLog::eventLogs, (el) -> { + assertEquals(link, el.getSrc()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertEquals(errorMessage.getCode(), el.getCode()); + assertEquals(errorMessage.getMsg(), el.getMsg()); + }), + link, + relayMessage.toBase64String()); + } + + static String[] fragments(String s, int count) { + int len = s.length(); + if (len < count || count < 1) { + throw new IllegalArgumentException(); + } + int fLen = len / count; + if (len % count != 0) { + fLen++; + } + int begin = 0; + String[] arr = new String[count]; + for (int i = 0; i < count; i++) { + int end = begin + fLen; + if (end < len) { + arr[i] = s.substring(begin, end); + } else { + arr[i] = s.substring(begin); + } + begin = end; + } + return arr; + } + + @Test + void handleFragment() { + //BMC.handleFragment -> BSHMock.HandleBTPMessage(str,str,int,bytes) + BigInteger sn = BigInteger.ONE; + byte[] payload = Faker.btpLink().toBytes(); + List btpMessages = new ArrayList<>(); + btpMessages.add(btpMessage(svc, sn, payload)); + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(toBytesArray(btpMessages)); + String msg = relayMessage.toBase64String(); + int count = 3; + int last = count - 1; + String[] fragments = fragments(msg, count); + for (int i = 0; i < count; i++) { + if (i == 0) { + iconSpecific.handleFragment(link, fragments[i], -1 * last); + } else if (i == last) { + ((ICONSpecificScoreClient) iconSpecific).handleFragment( + MockBSHIntegrationTest.eventLogChecker(HandleBTPMessageEventLog::eventLogs, (el) -> { + assertEquals(net, el.getFrom()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(payload, el.getMsg()); + }), + link, fragments[i], 0); + } else { + iconSpecific.handleFragment(link, fragments[i], last - i); + } + } + } + + @Test + void sendMessageShouldSuccess() { + //BSHMock.sendMessage -> BMC.Message(str,int,bytes) + BigInteger sn = BigInteger.ONE; + byte[] payload = Faker.btpLink().toBytes(); + + BigInteger seq = bmc.getStatus(link).getTx_seq().add(BigInteger.ONE); + ((MockBSHScoreClient) MockBSHIntegrationTest.mockBSH).intercallSendMessage( + BMCIntegrationTest.eventLogChecker(MessageEventLog::eventLogs, (el) -> { + assertEquals(link, el.getNext()); + assertEquals(seq, el.getSeq()); + BTPMessage btpMessage = el.getMsg(); + assertEquals(btpAddress, btpMessage.getSrc()); + assertEquals(linkBtpAddress, btpMessage.getDst()); + assertEquals(svc, btpMessage.getSvc()); + assertEquals(sn, btpMessage.getSn()); + assertArrayEquals(payload, btpMessage.getPayload()); + }), + ((BMCScoreClient) bmc)._address(), + net, svc, sn, payload); + } + + @Test + void sendMessageShouldRevertUnreachable() { + BigInteger sn = BigInteger.ONE; + byte[] payload = Faker.btpLink().toBytes(); + + AssertBMCException.assertUnreachable(() -> MockBSHIntegrationTest.mockBSH.intercallSendMessage( + ((BMCScoreClient) bmc)._address(), + Faker.btpNetwork(), svc, sn, payload)); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/OwnershipTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/OwnershipTest.java new file mode 100644 index 00000000..a37b5a0f --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/OwnershipTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.function.Executable; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OwnershipTest implements BMCIntegrationTest { + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); + static String string = ""; + static String btpAddress = ""; + static int intVal = 0; + static long longVal = 0; + static BigInteger bigInteger = BigInteger.ZERO; + + static boolean isExistsOwner(Address address) { + return ownerManager.isOwner(address); + } + + static void addOwner(Address address) { + ownerManager.addOwner(address); + assertTrue(isExistsOwner(address)); + } + + static void removeOwner(Address address) { + ownerManager.removeOwner(address); + assertFalse(isExistsOwner(address)); + } + + static void clearOwner(Address address) { + if (isExistsOwner(address)) { + System.out.println("clear owner address:"+address); + removeOwner(address); + } + } + + @Override + public void clearIfExists(TestInfo testInfo) { + String testMethod = testInfo.getTestMethod().orElseThrow().getName(); + if (!testMethod.endsWith("RevertUnauthorized")) { + clearOwner(address); + } + } + + @Test + void addOwnerShouldSuccess() { + addOwner(address); + } + + static void assertAlreadyExists(Executable executable) { + AssertBMCException.assertUnknown(executable); + } + + @Test + void addOwnerShouldRevertAlreadyExists() { + addOwner(address); + + assertAlreadyExists(() -> addOwner(address)); + } + + @Test + void removeOwnerShouldSuccess() { + addOwner(address); + + removeOwner(address); + } + + static void assertNotExists(Executable executable) { + AssertBMCException.assertUnknown(executable); + } + + @Test + void removeOwnerShouldRevertNotExists() { + assertNotExists(() -> removeOwner(address)); + } + + static void assertUnauthorized(Executable executable) { + AssertBMCException.assertUnauthorized(executable); + } + + @Test + void addOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> ownerManagerWithTester.addOwner(address)); + } + + @Test + void removeOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> ownerManagerWithTester.removeOwner(address)); + } + + @Test + void addVerifierShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.addVerifier(string, address)); + } + + @Test + void removeVerifierShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.removeVerifier(string)); + } + + @Test + void addServiceShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.addService(string, address)); + } + + @Test + void removeServiceShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.removeService(string)); + } + + @Test + void addLinkShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.addLink(btpAddress)); + } + + @Test + void removeLinkShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.removeLink(btpAddress)); + } + + @Test + void setLinkRotateTermShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setLinkRotateTerm(btpAddress, intVal, intVal)); + } + + @Test + void setLinkDelayLimitShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setLinkDelayLimit(btpAddress, intVal)); + } + + @Test + void setLinkSackTermShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setLinkSackTerm(btpAddress, intVal)); + } + + @Test + void addRouteShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.addRoute(btpAddress, btpAddress)); + } + + @Test + void removeRouteShouldRevertUnauthorized() { + assertUnauthorized(() -> bmcWithTester.removeRoute(btpAddress)); + } + + @Test + void addRelayShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.addRelay(btpAddress, address)); + } + + @Test + void removeRelayShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.removeRelay(btpAddress, address)); + } + + @Test + void setRelayerMinBondShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setRelayerMinBond(bigInteger)); + } + + @Test + void setRelayerTermShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setRelayerTerm(longVal)); + } + + @Test + void setRelayerRewardRankShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setRelayerRewardRank(intVal)); + } + + @Test + void removeRelayerShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.removeRelayer(address, address)); + } + + @Test + void setFeeGatheringTermShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setFeeGatheringTerm(intVal)); + } + + @Test + void setFeeAggregatorShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.setFeeAggregator(address)); + } + + @Test + void sendFeeGatheringShouldRevertUnauthorized() { + assertUnauthorized(() -> iconSpecificWithTester.sendFeeGathering()); + } +} diff --git a/javascore/bmc/src/test/java/foundation/icon/btp/bmc/RelayerManagementTest.java b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/RelayerManagementTest.java new file mode 100644 index 00000000..6fdaa300 --- /dev/null +++ b/javascore/bmc/src/test/java/foundation/icon/btp/bmc/RelayerManagementTest.java @@ -0,0 +1,289 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmc; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import java.math.BigInteger; +import java.util.*; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.*; + +public class RelayerManagementTest implements BMCIntegrationTest { + + static Address address = Address.of(tester); + static BigInteger bond = BigInteger.valueOf(10); + static String desc = "description"; + + static Predicate relayerPredicate(Address address, BigInteger bond, String desc) { + return (o) -> o.getAddr().equals(address) && o.getBond().equals(bond) && o.getDesc().equals(desc); + } + + static boolean isExistsRelayer(Address address, BigInteger bond, String desc) { + return ScoreIntegrationTest.contains(iconSpecific.getRelayers(), + address.toString(), relayerPredicate(address, bond, desc)); + } + + static boolean isExistsRelayer(Address address) { + return iconSpecific.getRelayers().containsKey(address.toString()); + } + + static void registerRelayer(BigInteger bond, String desc) { + ScoreIntegrationTest.balanceCheck(address, bond.negate(), + () -> ((ICONSpecificScoreClient)iconSpecificWithTester).registerRelayer(bond, desc)); + assertTrue(isExistsRelayer(address, bond, desc)); + } + + static void unregisterRelayer() { + Relayer relayer = iconSpecificWithTester.getRelayers().get(address.toString()); + ScoreIntegrationTest.balanceCheck(address, relayer != null ? relayer.getBond().add(relayer.getReward()) : BigInteger.ZERO, + iconSpecificWithTester::unregisterRelayer); + assertFalse(isExistsRelayer(address)); + } + + static void clearRelayer() { + if (isExistsRelayer(address)) { + System.out.println("clear relayer address:"+ address); + unregisterRelayer(); + } + } + + @BeforeAll + static void beforeAll() { + BigInteger value = BigInteger.valueOf(100000000); + ScoreIntegrationTest.balanceCheck(address, value, + () -> bmcClient._transfer(address, value, null)); + } + + @AfterAll + static void afterAll() { + BigInteger value = bmcClientWithTester._balance(address); + ScoreIntegrationTest.balanceCheck(address, value.negate(), + () -> bmcClientWithTester._transfer(Address.of(bmcClient._wallet()), value, null)); + } + + @Override + public void internalBeforeEach(TestInfo testInfo) { + beforeDistributeRelayerTests(testInfo); + } + + @Override + public void internalAfterEach(TestInfo testInfo) { + afterDistributeRelayerTests(testInfo); + } + + @Override + public void clearIfExists(TestInfo testInfo) { + clearRelayer(); + } + + @Test + void registerRelayerShouldSuccess() { + registerRelayer(bond, desc); + } + + @Test + void registerRelayerShouldRevertAlreadyExists() { + registerRelayer(bond, desc); + + AssertBMCException.assertUnknown(() -> registerRelayer(bond, desc)); + } + + @Test + void registerRelayerShouldRevertNotEnoughBond() { + AssertBMCException.assertUnknown(() -> registerRelayer(BigInteger.ZERO, desc)); + } + + @Test + void unregisterRelayerShouldSuccess() { + registerRelayer(bond, desc); + + unregisterRelayer(); + } + + @Test + void unregisterRelayerShouldRevertNotExists() { + AssertBMCException.assertUnknown(RelayerManagementTest::unregisterRelayer); + } + + @Test + void claimRelayerRewardShouldRevertNotExists() { + AssertBMCException.assertUnknown(iconSpecific::claimRelayerReward); + } + + static int relayerTerm = 2; + static int relayerRewardRank = 3; + static Integer[] sortedBonds = new Integer[]{4,3,3,2,1}; + private final List iconSpecificScoreClients = new ArrayList<>(); + private BigInteger sumOfReward = BigInteger.ZERO; + private BigInteger sumOfBond = BigInteger.ZERO; + + ICONSpecificScoreClient newICONSpecificScoreClientWithTransfer(BigInteger value) { + ICONSpecificScoreClient iconSpecificScoreClient = new ICONSpecificScoreClient( + bmcClient.endpoint(), bmcClient._nid(), ScoreIntegrationTest.generateWallet(), bmcClient._address()); + Address address = Address.of(iconSpecificScoreClient._wallet()); + + System.out.println("transfer to relayer address: "+ address+", value: "+value); + bmcClient._transfer(address, value, null); + return iconSpecificScoreClient; + } + + Relayer registerRelayer(ICONSpecificScoreClient iconSpecificScoreClient, BigInteger bond, String desc) { + Address address = Address.of(iconSpecificScoreClient._wallet()); + System.out.println("register relayer address: "+ address+", bond: "+bond+", desc:"+desc); + ScoreIntegrationTest.balanceCheck(bmcClient._address(), bond, + () -> iconSpecificScoreClient.registerRelayer(bond, desc)); + Relayer relayer = iconSpecific.getRelayers().get(address.toString()); + assertTrue(relayer != null && relayerPredicate(address, bond, desc).test(relayer)); + System.out.println("registered relayer "+relayer); + return relayer; + } + + void beforeDistributeRelayerTests(TestInfo testInfo) { + if (isDistributeRelayerTests(testInfo)) { + System.out.println("beforeDistributeRelayerTests start on "+testInfo.getDisplayName()); + assertEquals(0, iconSpecific.getRelayers().size(), "required relayers is empty"); + + iconSpecific.setRelayerTerm(relayerTerm); + iconSpecific.setRelayerRewardRank(relayerRewardRank); + + for (int i = 0; i < sortedBonds.length; i++) { + BigInteger bond = BigInteger.valueOf(sortedBonds[i]); + ICONSpecificScoreClient iconSpecificScoreClient = newICONSpecificScoreClientWithTransfer(bond); + iconSpecificScoreClients.add(iconSpecificScoreClient); + registerRelayer(iconSpecificScoreClient, bond, "Relayer "+i); + + if (i < relayerRewardRank) { + sumOfReward = sumOfReward.add(bond); + } + sumOfBond = sumOfBond.add(bond); + assertEquals(sumOfBond, iconSpecific.getRelayersProperties().getBond()); + } + + iconSpecific.setNextRewardDistribution(0); + + //wait NextRewardDistribution + ScoreIntegrationTest.waitByNumOfBlock(relayerTerm); + System.out.println("beforeDistributeRelayerTests end on "+testInfo.getDisplayName()); + } + } + + void afterDistributeRelayerTests(TestInfo testInfo) { + if (isDistributeRelayerTests(testInfo)) { + System.out.println("afterDistributeRelayerTests start on "+testInfo.getDisplayName()); + Address refund = Address.of(bmcClient._wallet()); + for (Map.Entry entry : iconSpecific.getRelayers().entrySet()) { + Relayer relayer = entry.getValue(); + System.out.println("clear relayer "+ relayer); + ScoreIntegrationTest.balanceCheck(refund, relayer.getBond().add(relayer.getReward()), + () -> iconSpecific.removeRelayer(relayer.getAddr(), refund)); + assertFalse(isExistsRelayer((Address) relayer.getAddr())); + } + iconSpecific.setRelayerTerm(RelayersProperties.DEFAULT.getRelayerTerm()); + iconSpecific.setRelayerRewardRank(RelayersProperties.DEFAULT.getRelayerRewardRank()); + iconSpecific.setNextRewardDistribution(0); + + iconSpecificScoreClients.clear(); + System.out.println("afterDistributeRelayerTests end on "+testInfo.getDisplayName()); + } + } + + static boolean isDistributeRelayerTests(TestInfo testInfo) { + return testInfo.getDisplayName().startsWith("distributeRelayerReward"); + } + + @Test + void distributeRelayerRewardShouldDistributedAndHasCarryOver() { + BigInteger notInTermBond = BigInteger.valueOf(sortedBonds[0]); + ICONSpecificScoreClient notInTermClient = newICONSpecificScoreClientWithTransfer(notInTermBond); + + BigInteger carryOver = iconSpecific.getRelayersProperties().getCarryover(); + boolean carryOverExists = !carryOver.equals(BigInteger.ZERO); + carryOver = carryOverExists ? carryOver : BigInteger.ONE; + BigInteger reward = carryOverExists ? sumOfReward : sumOfReward.add(carryOver); + System.out.println("transfer to BMC for reward "+ reward + + (carryOverExists ? " existsCarryOver: " : " includeCarryOver: ") + carryOver); + Address bmcAddress = bmcClient._address(); + ScoreIntegrationTest.balanceCheck(bmcAddress, reward, + () -> bmcClient._transfer(bmcAddress, reward, null)); + + registerRelayer(notInTermClient, notInTermBond, "notInTerm"); + + System.out.println("distributeRelayerReward properties:"+ iconSpecific.getRelayersProperties()); + iconSpecific.distributeRelayerReward(); + RelayersProperties properties = iconSpecific.getRelayersProperties(); + BigInteger distributed = properties.getDistributed(); + assertEquals(sumOfReward, distributed); + assertEquals(carryOver, properties.getCarryover()); + + Map relayers = iconSpecific.getRelayers(); + for (int i = 0; i < sortedBonds.length; i++) { + ICONSpecificScoreClient iconSpecificScoreClient = iconSpecificScoreClients.get(i); + Relayer relayer = relayers.get(Address.of(iconSpecificScoreClient._wallet()).toString()); + if (i < relayerRewardRank) { + assertEquals(BigInteger.valueOf(sortedBonds[i]), relayer.getReward()); + + System.out.println("claimRelayerRewardShouldSuccess relayerInRank "+relayer); + ScoreIntegrationTest.balanceCheck((Address) relayer.getAddr(), relayer.getReward(), + iconSpecificScoreClient::claimRelayerReward); + + distributed = distributed.subtract(relayer.getReward()); + assertEquals(distributed, iconSpecific.getRelayersProperties().getDistributed()); + } else { + assertEquals(BigInteger.ZERO, relayer.getReward()); + + System.out.println("claimRelayerRewardShouldRevertRewardIsZero relayerNotInRank "+relayer); + AssertBMCException.assertUnknown(iconSpecificScoreClient::claimRelayerReward); + } + } + + Relayer notInTermRelayer = relayers.get(Address.of(notInTermClient._wallet()).toString()); + assertEquals(BigInteger.ZERO, notInTermRelayer.getReward()); + System.out.println("claimRelayerRewardShouldRevertRewardIsZero notInTermRelayer "+notInTermRelayer); + AssertBMCException.assertUnknown(notInTermClient::claimRelayerReward); + + assertEquals(BigInteger.ZERO, iconSpecific.getRelayersProperties().getDistributed()); + } + + @Test + void sortTest() { + List bonds = Arrays.asList(Arrays.copyOf(sortedBonds, sortedBonds.length)); + Collections.shuffle(bonds); + Relayer[] relayers = new Relayer[bonds.size()]; + for (int i = 0; i < bonds.size(); i++) { + Relayer relayer = new Relayer(); + relayer.setDesc("Relayer "+i); + relayer.setBond(BigInteger.valueOf(bonds.get(i))); + relayer.setSince(1); + relayer.setSinceExtra(i); + relayers[i] = relayer; + } + Relayer[] sorted = Arrays.copyOf(relayers, relayers.length); + Relayers.sortAsc(sorted); + System.out.println("sorted: "+Arrays.toString(sorted)); + Relayer[] expected = Arrays.copyOf(relayers, relayers.length); + Arrays.sort(expected, Relayers::compare); + System.out.println("expected: "+Arrays.toString(expected)); + assertArrayEquals(expected, sorted); + } +} diff --git a/javascore/bmv/IconRelayMessageStrucutureForPara.md b/javascore/bmv/IconRelayMessageStrucutureForPara.md new file mode 100644 index 00000000..30830e98 --- /dev/null +++ b/javascore/bmv/IconRelayMessageStrucutureForPara.md @@ -0,0 +1,129 @@ +```python +# block header of Substrate chain +BlockHeader { + parentHash: bytes, + number: CompactU32, + stateRoot: Hash, + extrinsicsRoot: Hash, + digest: bytes +} + +# validator will SCALE encode that message and then sign it. +VoteMessage { + message: { + # Scale enum type, ref https://substrate.dev/docs/en/knowledgebase/advanced/codec#enumerations-tagged-unions + _enum: { + Prevote: { + targetHash: "HASH", + targetNumber: "u32" + }, + Precommit: { + targetHash: "HASH", + targetNumber: "u32" + }, + PrimaryPropose: { + targetHash: "HASH", + targetNumber: "u32" + } + } + }, + round: "u64", + setId: "u64", +} + +ValidatorSignature { + # 64 bytes signature + signature: bytes, + # 32 byte public key of validator + validator: bytes +} + +# validator's signatures to prove that block's confirmed +Votes { + # SCALE codec of VoteMessage + voteMessage: bytes + # list RLP of ValidatorSignature + signatures: [bytes] +} + +# blocks to sync from destination chain +RelayBlockUpdate { + # header of updating block + # SCALE encode of BlockHeader + blockHeader: bytes, + + # only require for last block of list + # signatures of validators with fields that included in vote + # RLP encode of Votes + votes: bytes +} + +# blocks to sync from destination chain +ParaBlockUpdate { + # header of updating block + # SCALE encode of BlockHeader + blockHeader: bytes, + + # only require for last block of list + # signatures of validators with fields that included in vote + # RLP encode of RelayChainData + relayChainData: bytes +} + +StateProof { + # key of storage + key: bytes + # get from api, https://polkadot.js.org/docs/substrate/rpc#getreadproofkeys-vecstoragekey-at-blockhash-readproof + # data of MPT node (branch node, leaf node) to prove that storage, from top to bottom + proofs: [bytes] +} + +# block contain event with proofs +BlockProof { + # SCALE codec of block header + # Header of block contains event + blockHeader: bytes, + + height: "u32" # Merkle tree accumulator height when get witness + witness: [bytes] # list of hash of leaf point to prove that block include in Merkle tree accumulator, low level leaf first +} + +RelayChainData { + # optional, for BMV to synchronize block from relay chain + # list of RLP encode of RelayBlockUpdate + # If it is omitted, an EMPTY_HEADER (0xC0) will be attached + relayBlockUpdates: [bytes] + + # optional, in case of event in previous updated block is missing, it used to prove that block included in source chain + # RLP endcode of BlockProof + # If it is omitted, an EMPTY_HEADER (0xF800) will be attached + blockProof: bytes, + + # required + # needed event: parasInclusion.CandidateIncluded, grandpa.NewAuthorities + # proofs to prove that state storage in merkle patricia trie of relay chain + # a list of RLP encode of StateProof + stateProof: bytes +} + +# message from Relay +RelayMessage { + # optional, for BMV to synchronize block from srouce chain + # list of RLP encode of ParaBlockUpdate + # If it is omitted, an EMPTY_HEADER (0xC0) will be attached + paraBlockUpdates: [bytes], + + # optional, in case of event in previous updated block is missing, it used to prove that block included in source chain + # RLP endcode of BlockProof + # If it is omitted, an EMPTY_HEADER (0xF800) will be attached + blockProof: bytes, + + # optional, in case of have any event to update from source BMC + # proofs to prove that state storage in merkle patricia trie + # a list of RLP encode of StateProof + # This field also can be omitted. If so, 0xC0 will be attached + stateProof: [bytes] +} + +_msg = urlsafe_b64decode(RLP_ENCODE(RelayMessage)) # message from relayer to BMC +``` diff --git a/javascore/bmv/IconRelayMessageStrucutureForSovereign.md b/javascore/bmv/IconRelayMessageStrucutureForSovereign.md new file mode 100644 index 00000000..41c3591f --- /dev/null +++ b/javascore/bmv/IconRelayMessageStrucutureForSovereign.md @@ -0,0 +1,100 @@ +```python +# block header of Substrate chain +BlockHeader { + parentHash: bytes, + number: CompactU32, + stateRoot: Hash, + extrinsicsRoot: Hash, + digest: bytes +} + +# validator will SCALE encode that message and then sign it. +VoteMessage { + message: { + # Scale enum type, ref https://substrate.dev/docs/en/knowledgebase/advanced/codec#enumerations-tagged-unions + _enum: { + Prevote: { + targetHash: "HASH", + targetNumber: "u32" + }, + Precommit: { + targetHash: "HASH", + targetNumber: "u32" + }, + PrimaryPropose: { + targetHash: "HASH", + targetNumber: "u32" + } + } + }, + round: "u64", + setId: "u64", +} + +ValidatorSignature { + # 64 bytes signature + signature: bytes, + # 32 byte public key of validator + validator: bytes +} + +# validator's signatures to prove that block's confirmed +Votes { + # SCALE codec of VoteMessage + voteMessage: bytes + # list RLP of ValidatorSignature + signatures: [bytes] +} + +# blocks to sync from destination chain +BlockUpdate { + # header of updating block + # SCALE encode of BlockHeader + blockHeader: bytes, + + # optional + # signatures of validators with fields that included in vote + # RLP encode of Votes + votes: bytes, +} + +StateProof { + # key of storage + key: bytes + # get from api, https://polkadot.js.org/docs/substrate/rpc#getreadproofkeys-vecstoragekey-at-blockhash-readproof + # data of MPT node (branch node, leaf node) to prove that storage, from top to bottom + # result from api may not in top to bottom order, relay must reorder that + proofs: [bytes] +} + +# block contain event with proofs +BlockProof { + # SCALE codec of block header + # Header of block contains event + blockHeader: bytes, + + height: "u32" # Merkle tree accumulator height when get witness + witness: [bytes] # list of hash of leaf point to prove that block include in Merkle tree accumulator, low level leaf first +} + +# message from Relay +RelayMessage { + # optional, for BMV to synchronize block from srouce chain + # list of RLP encode of BlockUpdate + # If it is omitted, an EMPTY_HEADER (0xC0) will be attached + blockUpdates: [bytes], + + # optional, in case of event in previous updated block is missing, it used to prove that block included in source chain + # RLP endcode of BlockProof + # If it is omitted, an EMPTY_HEADER (0xF800) will be attached + blockProof: bytes, + + # optional, in case of have any event to update from source BMC + # proofs to prove that state storage in merkle patricia trie + # a list of RLP encode of StateProof + # This field also can be omitted. If so, 0xC0 will be attached + stateProof: bytes +} + +_msg = urlsafe_b64decode(RLP_ENCODE(RelayMessage)) # message from relayer to BMC +``` \ No newline at end of file diff --git a/javascore/bmv/Readme.md b/javascore/bmv/Readme.md new file mode 100644 index 00000000..904425af --- /dev/null +++ b/javascore/bmv/Readme.md @@ -0,0 +1,695 @@ +# BMV SCORE + +- [Introduction](#introduction) +- [Requirements](#requirements) +- [Deployment script](#deploymentScript) + - [Deploy script for sovereign chain](#sovereignDeployScript) + - [Deploy script for para chain](#paraDeployScript) +- [Build Event decoder SCORE](#buildEventDecoder) + - [Build with predefined metaData](#buildPredefinedMetadata) + - [Build with custom metaData](#buildCustomMetadata) + - [Build with custom metaData](#buildCustomMetadata) +- [Build BMV SCORE](#buildBMV) + - [Build BMV SCORE of sovereign chain](#buildSovereign) + - [Build BMV SCORE of para chain](#buildPara) +- [Run integration test](#runIntegrationTest) + - [Run sovereign BMV integration test](#runSovereignIntegrationTest) + - [Run para BMV integration test](#runParaIntegrationTest) +- [Deploy BMV SCORE](#deploy) + - [Deploy sovereign BMV](#deploySovereign) + - [Deploy para BMV](#deployPara) + +## Introduction + + + +BMV smart contracts consists of Event decoder score that exists per chain and BMV score (either Sovereign chain type or Relay chain and Parachain type). According to the chain you're trying to connect, to build a complete BMV score, you should build Event decoder score (or use predefined ones) and choose BMV score type. + +Sovereign chain only need one event decoder score. On other hand, parachain BMV need two event decoder SCORE, one for decoder event of para chain and one for decode event of relay chain. + +To ease the deployment process, we have set up typescript for auto build and deploy event decoder, BMV SCORE for both sovereign chain and para chain. Read [Deployment script](#deploymentScript) for more details. +If you want to walk through how to build event decoder for each type of chain, how to build BMV score, run integration test and deploy score, read four last sessions for more details. + +## Requirements + + + +Gradle 6.7.1 +Java OpenJDK 11 +goloop/gochain 0.9.7 or higher + +## Deployment script + + + +### Deploy BMV SCORE of sovereign chain + + + +1. Create `.env` file in `javascore/helper` directory +2. Configure `.env` file above with following parameters: + +``` +# address of BMC SCORE +BMC_ADDRESS= +# network address that BMV verify (eg 0x501.pra) +DST_NET_ADDRESS= + +# size of mta root +MTA_ROOT_SIZE= +# size of mta cache +MTA_CACHE_SIZE= +# allow to verify old mta witness or not +MTA_IS_ALLOW_WITNESS= + +# endpoint of ICON RPC +ICON_ENDPOINT= +# path to keystore file that sign deploy transaction +ICON_KEYSTORE_PATH= +# password of keystore file above +ICON_KEYSTORE_PASSWORD= +# network id of ICON chain +ICON_NID= + +# sovereign chain websocket endpoint +SOVEREIGN_ENDPOINT= +# block height of sovereign chain BMV start to sync +SOVEREIGN_CHAIN_OFFSET= +``` + +3. Install yarn package + +``` +yarn install +``` + +4. Execute sovereign chain deploy script + +``` +yarn deploySovereignBMV +``` + +After building and deployment, script will print out event decoder and sovereign chain BMV SCORE address. + +### Deploy BMV SCORE of para chain + + + +1. Create `.env` file in `javascore/helper` directory +2. Configure `.env` file above with following parameters: + +``` +# address of BMC SCORE +BMC_ADDRESS= +# network address that BMV verify (eg 0x501.pra) +DST_NET_ADDRESS= + +# block height of relay chain BMV start to sync +RELAY_CHAIN_OFFSET= +# block height of para chain BMV start to sync +PARA_CHAIN_OFFSET= +# relay chain websocket endpoint +RELAY_ENDPOINT=wss://wss-relay.testnet.moonbeam.network +# para chain websocket endpoint +PARA_ENDPOINT=wss://moonbeam-alpha.api.onfinality.io/public-ws + +# size of mta root +MTA_ROOT_SIZE= +# size of mta cache +MTA_CACHE_SIZE= +# allow to verify old mta witness or not +MTA_IS_ALLOW_WITNESS= + +# endpoint of ICON RPC +ICON_ENDPOINT= +# path to keystore file that sign deploy transaction +ICON_KEYSTORE_PATH= +# password of keystore file above +ICON_KEYSTORE_PASSWORD= +# network id of ICON chain +ICON_NID= +``` + +3. Install yarn package + +``` +yarn install +``` + +4. Execute sovereign chain deploy script + +``` +yarn deployParaBMV +``` + +After building and deployment, script will print out event decoder and sovereign chain BMV SCORE address. +## Build event decoder SCORE + + + +Each chain has different type of event and data structure, we need meta data file to know what modules and data structure these chain has. Event decoder code will be genereated base on meta data file. + +### Build with predefined metadata + + + +we have predefined meta data for Kusama, Edgeware and Moonriver mainnet. It's stored in `./eventDecoder/KusamaMetaData.json`, `./eventDecoder/EdgewareMetaData.json` and `./eventDecoder/MoonriverMetaData.json`. To build score for these chains use the following command: + +```bash +cd eventDecoder +gradle buildKusamaDecoder +gradle buildEdgewareDecoder +gradle buildMoonriverDecoder +``` + +`KusamaEventDecoder-optimized.jar`, `EdgewareEventDecoder-optimized.jar`, `MoonriverDecoder-optimized.jar` file will be generated in `eventDecoder/build/libs/` + +### Build with custom metadata + + + +In case that you need to build event decoder for other parachains rather than there chain above, you can use typescript helper bellow to get meta data file. + +#### Get metadata with typescript helper + +We have set up yarn project to get metadata through typescript. + +1. Go to helper directory + +```bash +cd javascore/bmv/helper +``` + +2. Install yarn package + +```bash +yarn install +``` + +3. Specify chain endpoint, open file `javascore/bmv/helper/getMetaData.ts`, replace `CHAIN_ENDPOINT` in line 10 with your chain endpoint + +4. Run `yarn getMetaData` in `javascore/bmv/helper` directory + +```bash +cd javascore/bmv/helper + +yarn getMetaData +``` + +#### Build event decoder with your meta data file + +1. Store meta data in `metadata.json` file +2. Get metaData file path +3. execute +```bash +cd eventDecoder +gradle loadMetaData -PmetaDataFilePath=${path of your meta data file} +gradle optimizedJar +``` + +*Note* Substrate base chain may redefine data default type. For example, moonbeam has modify default AccountId (32 bytes) to EthereumAccountId (20 bytes). To adapt that change, we provide `accountIdSize` to specify size of account id + +```bash +cd eventDecoder +gradle loadMetaData -PmetaDataFilePath=${path of your meta data file} -PaccountIdSize=20 +gradle optimizedJar +``` + +`eventDecoder-optimized.jar` file will be generated in `eventDecoder/build/libs/` + +## Build BMV SCORE + + + +Because each chain has thier own module structure and event index, we need to configure event index of target chain before build BMV score +### Build BMV SCORE for sovereign chain + + + +5. Save and execute bellow command to build BMV SCORE + +```bash +cd sovereignChain +gradle optimizedJar +``` + +`SovereignChain-BMV-optimized.jar` file will be generated in `sovereignChain/build/libs/` + +### Build BMV SCORE for para chain + + + +Configure `NewAuthorities`, `EvmEvent`, `CandidateIncluded` event index constant base on your sovereign chain meta data. + +1. Open relay chain and para chain `metadata.json` file +2. Find modules name `Grandpa`, `ParasInclusion` in relay chain metadata and `EVM` module in parachain metadata, get these modules index +3. Get index of `NewAuthorities` event in `Grandpa` module, `CandidateIncluded` event in `ParasInclusion` module and `Log` event of `EVM` module (index of element in array) +4. Edit corresponding parameter in `javascore/bmv/parachain/src/main/java/foundation/icon/btp/lib/Constant.java` line 9, 10, 11, first byte is index of module, second byte is index of event in array + +```java + public static final byte[] NewAuthoritiesEventIndex = new byte[]{ (byte) 0x0a, (byte) 0x00 }; + public static final byte[] EvmEventIndex = new byte[]{ (byte) 0x33, (byte) 0x00 }; + public static final byte[] CandidateIncludedEventIndex = new byte[]{ (byte) 0x35, (byte) 0x01 }; +``` + +5. Save and execute bellow command to build BMV SCORE + +```bash +cd parachain +gradle optimizedJar +``` + +`parachain-BMV-optimized.jar` file will be generated in `parachain/build/libs/` + +## Run integration test + + + +Before running integration test, make sure local icon node is running. Follow https://github.com/icon-project/gochain-local to know how to start your local node. + +We expect that your local ICON node using port 9082 for RPC and default godWallet.json. If you change any default configuration, please update it to `testinteg/conf/env.props` file. +### Sovereign chain intergration test + + + +Data generated for test is base on Edgeware chain, to be albe to test on other chain, plase update test data generator and event decoder score first. To run intergration please build Edgeware event decoder first + +```bash +# build edgeware event decoder score +cd eventDecoder +gradle buildEdgewareDecoder + +# run integration test +cd sovereignChain +gradle integrationTest +``` +### Para chain intergration test + + + +Data generated for test is base on Kusama relay chain and Moonriver parachains, to be albe to test on other chain, please update test data generator and event decoder score first. To run intergration please build Kusma and Moonriver event decoder first + +```bash +# build Kusama and Moonriver event decoder score +cd eventDecoder +gradle buildKusamaDecoder +gradle buildMoonriverDecoder + +# run integration test +cd parachain +gradle integrationTest +``` + +# Deploy BMV score + + + +## Deploy sovereign chain BMV + + + +### Deploy BMC smart contract + +``` +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy bmc.zip \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 13610920001 \ + --content_type application/zip \ + --param _net="0x07.icon" +``` + +- get transaction result and score address: + +``` +goloop rpc --uri http://localhost:9082/api/v3 txresult 0x2a297fc118728494ed284845af620c1c3395a228cfc60983474fd5e04423b468 +``` + +### Deploy event decoder contract, use one of comands below + +*note* score file name can be changed + +``` +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy ./eventDecoder/build/libs/EdgewareEventDecoder-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 13610920010 \ + --content_type application/java +``` + +### Deploy bmv contract + +### Parameter: + +- `mtaOffset`: offset of Merkle Tree Accumulator, block height BMV start to sync block +- `bmc`: address of BMC score +- `net`: network that BMV handle, (Edgeware.frontier) +- `mtaRootSize`: size of MTA roots +- `mtaCacheSize`: size of mta cache +- `mtaIsAllowNewerWitness`: is allow to verify newer witness (client MTA height higher than contract MTA height) +- `lastBlockHash`: hash of previous block (mtaOffset), BMV use this to check previous hash of updating block equal to last block hash +- `encodedValidators`: `Base64(RLP.encode(List validatorPublicKey))`, encoded of validators list +- `eventDecoderAddress`: address of event decoder score +- `currentSetId`: current validator set id +- `newAuthoritiesEventIndex`: two byte index of new authorities event +- `evmEventIndex`: two byte index of evm log event + +```shell +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy ./sovereignChain/build/libs/SovereignChain-BMV-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 3519157719 \ + --content_type application/java \ + --param mtaOffset=0x28 \ + --param bmc=cx4a15a3a924e54ee0329a11ea53021e3a6bed0046 \ + --param net=0x123.edge \ + --param mtaRootSize=0x10 \ + --param mtaCacheSize=0x10 \ + --param mtaIsAllowNewerWitness=0x1 \ + --param lastBlockHash=0xf94a5e262b7192fb951813c50de551761fcc13f2493f41a2f0105cb931cedd89 \ + --param encodedValidators=4aCI3DQX1QWOxLRQPgwS6hoKib4gD-mJIkI9QzQBT6aw7g \ + --param eventDecoderAddress=cx4b577d3b165c14b313294c6babef37fcb3ae7e7d \ + --param currentSetId=0x0 + --param newAuthoritiesEventIndex=0x0a00 + --param evmEventIndex=0x0c00 +``` + +### add BMV address as verifier for network in BMV + +```shell +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx4a15a3a924e54ee0329a11ea53021e3a6bed0046 --method addVerifier \ + --key_store godWallet.json --key_password gochain \ + --param _net=0x123.edge \ + --param _addr=cx47360ef92f8e283653814f7de09154bc02f9f57d \ + --nid 3 --step_limit 3519157719 \ +``` + +### get encode of Merkle tree accumulator + +- MTA encode struct + +``` +mta: { + long height, + long offset, + int rootSize, + int cacheSize, + boolean isAllowNewerWitness, + byte[] lastBlockHash, + List roots, + List caches +} + +result = Base64.encode(RLP.encode(mta)) +``` + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method mta +``` + +### get list of merkle tree accummulator roots +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method mtaRoot +``` + +### get current mta height + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method mtaHeight +``` + +### get current mta caches + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method mtaCaches +``` + +### register link to BMC + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method addLink --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` + +### Configure link + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method setLink --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _block_interval=0x3e8 --param _max_agg=0x10 --param _delay_limit=3 +``` + +### Retrieve properties of link + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method getStatus --param _link=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` + +### Register Relayer and Relay + +- add relayer: + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method addRelayer --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _addr=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd --param _desc="edgeware relayer" +``` + +- add relay: +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method addRelay --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _addr=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd +``` +### retrieve list of registered relayers + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method getRelayers +``` + +- retrieve list of registered relay of link: + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method getRelays --param _link=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` +### call handleRelayMessage of BMC contract + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cxf3e336ff003356e3ee3873c31c929e6c01ef739b --method handleRelayMessage --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _prev=btp://0x123.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _msg= +``` + +## deploy para chain BMV + + +### Deploy BMC smart contract + +``` +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy bmc.zip \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 13610920001 \ + --content_type application/zip \ + --param _net="0x3.icon" +``` + +- get transaction result and score address: + +``` +goloop rpc --uri http://localhost:9082/api/v3 txresult 0x6fd5dd9f2b678bcfd635c9c9886eea13c41ae3dacf55567d3fbdde6adca9ec10 +``` + +### Deploy event decoder contract for parachain and relay chain, use one of comands below + +*note* change score `jar` file name according to relay event decoder and para decoder file. + +``` +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy ./eventDecoder/build/libs/KusamaEventDecoder-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 13610920010 \ + --content_type application/java + +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy ./eventDecoder/build/libs/MoonriverEventDecoder-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 13610920010 \ + --content_type application/java +``` + +### Deploy bmv contract + +### Parameter: + +- `relayMtaOffset`: offset of Merkle Tree Accumulator, block height BMV start to sync block of relay chain +- `paraMtaOffset`: offset of Merkle Tree Accumulator, block height BMV start to sync block of para chain +- `bmc`: address of BMC score +- `net`: network that BMV handle (of parachain), (example 0x501.pra) +- `mtaRootSize`: size of MTA roots use for both parachain and relay chain +- `mtaCacheSize`: size of mta cache use for both parachain and relay chain +- `mtaIsAllowNewerWitness`: is allow to verify newer witness (client MTA height higher than contract MTA height) +- `relayLastBlockHash`: hash of previous block (relayMtaOffset) of relay chain, BMV use this to check previous hash of updating block equal to last block hash +- `paraLastBlockHash`: hash of previous block (paraMtaOffset) of para chain, BMV use this to check previous hash of updating block equal to last block hash +- `encodedValidators`: `Base64(RLP.encode(List validatorPublicKey))`, encoded of validators list of relay chain +- `relayEventDecoderAddress`: address of event decoder score for relay chain +- `paraEventDecoderAddress`: address of event decoder score for para chain +- `relayCurrentSetId`: current validator set id of relay chain +- `paraChainId`: para chain id +- `evmEventIndex`: index of evm log event in para chain +- `newAuthoritiesEventIndex`: index of new authorities event in relay chain +- `candidateIncludedEventIndex`: index of candidate included in relay chain + +### BMV initialize helper + +We has setup yarn project to help read initialize data from para chain and relay chain. + +1. Go to helper directory + +```bash +cd javascore/bmv/helper +``` + +2. Install yarn package + +```bash +yarn install +``` + +3. Create `.env` file and input following parameters: + +- Use https://polkadot.js.org/apps/?#/explorer, choose your chain in top left corner, to monitor current block height of para and relay chain. Choose one block height for offset of chain. + +- Specify relay and para chain offset, replace `RELAY_OFFSET`, `PARA_OFFSET` in line 33, 34 with your relay chain and para endpoint. + +```` +# websocket endpoint of relay chain +RELAY_ENDPOINT= +# websocket endpoint of para chain +PARA_ENDPOINT= + +# block height that BMV start to sync from relay chain +RELAY_CHAIN_OFFSET= +# block height that BMV start to sync from para chain +PARA_CHAIN_OFFSET= +```` + +4. Run `yarn getBMVInitializeParams` in `javascore/bmv/helper` directory + +```bash +cd javascore/bmv/helper + +yarn getBMVInitializeParams +``` + +`BMVInitializeData.json` file will be generated in `javascore/bmv/helper`, this file included initialzed parameter of relay chain and parachain, pass it to command bellow: + +``` +goloop rpc --uri http://localhost:9082/api/v3 sendtx deploy ./parachain/build/libs/parachain-BMV-optimized.jar \ + --key_store godWallet.json --key_password gochain \ + --nid 3 --step_limit 3519157719 \ + --content_type application/java \ + --param relayMtaOffset=0x7f30c0 \ + --param paraMtaOffset=0x186a0 \ + --param bmc=cx1bbc7a1e5d3650b8c2adab22043717f80c5efe60 \ + --param net=0x501.pra \ + --param mtaRootSize=0x10 \ + --param mtaCacheSize=0x10 \ + --param mtaIsAllowNewerWitness=0x1 \ + --param relayLastBlockHash=0x539d6364d029ae47829db726465a44aa555ffcdca9d78cee56afb3becd7d7fb2 \ + --param paraLastBlockHash=0xf0a14ae43ada572f73e2c436bb5317341775ea6a7191f7f99db7f23181916337 \ + --param relayEventDecoderAddress=cx567425fd292e032c79617ebeb38acf2e34b01497 \ + --param paraEventDecoderAddress=cxa6525b38f8bf513fdd55c506f7cf6bc95d0d30a0 \ + --param relayCurrentSetId=0xca1 \ + --param paraChainId=0x7e7 \ + --param encodedValidators=4aCI3DQX1QWOxLRQPgwS6hoKib4gD-mJIkI9QzQBT6aw7g \ + --param evmEventIndex=0x0a00 \ + --param newAuthoritiesEventIndex=0x0a00 \ + --param candidateIncludedEventIndex=0x0a00 +``` + +### add BMV address as verifier for network in BMV + +```shell +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx1bbc7a1e5d3650b8c2adab22043717f80c5efe60 --method addVerifier \ + --key_store godWallet.json --key_password gochain \ + --param _net=0x501.pra \ + --param _addr=cx05f9dfd1446e7e881e35f90949809b507db52b4f \ + --nid 3 --step_limit 3519157719 +``` + +### get encode of Merkle tree accumulator + +- MTA encode struct + +``` +mta: { + long height, + long offset, + int rootSize, + int cacheSize, + boolean isAllowNewerWitness, + byte[] lastBlockHash, + List roots, + List caches +} + +result = Base64.encode(RLP.encode(mta)) +``` + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx633481960fb15437431d83effd085bcd1e288e34 --method paraMta +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method relayMta +``` + +### get list of merkle tree accummulator roots +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method paraMtaRoot +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method relayMtaRoot +``` + +### get current mta height + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method paraMtaHeight +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method relayMtaHeight +``` + +### get current mta caches + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method relayMtaCaches +goloop rpc --uri http://localhost:9082/api/v3 call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method paraMtaCaches +``` + +### register link to BMC + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method addLink --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://moonriver.parachain/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` + +### Configure link + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method setLink --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://0x501.pra/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _block_interval=0x3e8 --param _max_agg=0x10 --param _delay_limit=3 +``` + +### Retrieve properties of link + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method getStatus --param _link=btp://0x501.pra/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` + +### Register Relayer and Relay + +- add relayer: + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method addRelayer --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _addr=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd --param _desc="moonbeam parachain relayer" +``` + +- add relay: +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method addRelay --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _link=btp://0x501.pra/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _addr=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd +``` +### retrieve list of registered relayers + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method getRelayers +``` + +- retrieve list of registered relay of link: + +```bash +goloop rpc --uri http://localhost:9082/api/v3 call --to cx9e8a72eb07ae70bbfb8f5b835a20cb25688a549c --method getRelays --param _link=btp://0x501.pra/08425D9Df219f93d5763c3e85204cb5B4cE33aAa +``` +### call handleRelayMessage of BMC contract + +```bash +goloop rpc --uri http://localhost:9082/api/v3 sendtx call --to cx0fe6bff6d229bf78d365534d0a20ddd9c5683f22 --method handleRelayMessage --key_store godWallet.json --step_limit 10000000000 --nid 3 --key_password gochain --param _prev=btp://0x501.pra/08425D9Df219f93d5763c3e85204cb5B4cE33aAa --param _msg= +``` diff --git a/javascore/bmv/api-0.8.8-SNAPSHOT.jar b/javascore/bmv/api-0.8.8-SNAPSHOT.jar new file mode 100644 index 00000000..9f85cf97 Binary files /dev/null and b/javascore/bmv/api-0.8.8-SNAPSHOT.jar differ diff --git a/javascore/bmv/bmc.zip b/javascore/bmv/bmc.zip new file mode 100644 index 00000000..58fd6676 Binary files /dev/null and b/javascore/bmv/bmc.zip differ diff --git a/javascore/bmv/build.gradle b/javascore/bmv/build.gradle new file mode 100644 index 00000000..0b7148f6 --- /dev/null +++ b/javascore/bmv/build.gradle @@ -0,0 +1,37 @@ +buildscript { + repositories { + mavenCentral() + maven { + url 'https://oss.jfrog.org/artifactory/oss-snapshot-local' + } + } + dependencies { + classpath 'foundation.icon:gradle-javaee-plugin:0.7.8' + } +} + +subprojects { + repositories { + mavenCentral() + maven { + url 'https://oss.jfrog.org/artifactory/oss-snapshot-local' + } + // polkaj public repo + maven { url "https://dl.bintray.com/emerald/polkaj" } + // required for com.github.multiformats:java-multibase library + maven { url 'https://jitpack.io' } + } + + apply plugin: 'java' + apply plugin: 'foundation.icon.javaee' + + java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + // need to add this option to retrieve formal parameter names + compileJava { + options.compilerArgs += ['-parameters'] + } +} diff --git a/javascore/bmv/eventDecoder/EdgewareMetaData.json b/javascore/bmv/eventDecoder/EdgewareMetaData.json new file mode 100644 index 00000000..82d2459a --- /dev/null +++ b/javascore/bmv/eventDecoder/EdgewareMetaData.json @@ -0,0 +1,12190 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "v12": { + "modules": [ + { + "name": "System", + "storage": { + "prefix": "System", + "items": [ + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "AccountInfo", + "linked": false + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " The full account information for a particular account ID." + ] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "plain": "u32" + }, + "fallback": "0x00", + "documentation": [ + " Total extrinsics count for the current block." + ] + }, + { + "name": "BlockWeight", + "modifier": "Default", + "type": { + "plain": "ConsumedWeight" + }, + "fallback": "0x000000000000000000000000000000000000000000000000", + "documentation": [ + " The current weight for the block." + ] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "plain": "u32" + }, + "fallback": "0x00", + "documentation": [ + " Total length (in bytes) for all extrinsics put together, for the current block." + ] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BlockNumber", + "value": "Hash", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " Map of block numbers to block hashes." + ] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "u32", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Extrinsics data for the current block (maps an extrinsic's index to its data)." + ] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "documentation": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "plain": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " Hash of the previous block." + ] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "plain": "DigestOf" + }, + "fallback": "0x00", + "documentation": [ + " Digest of the current block, also part of the block header." + ] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Events deposited for the current block." + ] + }, + { + "name": "EventCount", + "modifier": "Default", + "type": { + "plain": "EventIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " The number of events in the `Events` list." + ] + }, + { + "name": "EventTopics", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "Hash", + "value": "Vec<(BlockNumber,EventIndex)>", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Mapping between a topic (represented by T::Hash) and a vector of indexes", + " of events in the `>` list.", + "", + " All topic vectors have deterministic storage locations depending on the topic. This", + " allows light-clients to leverage the changes trie storage tracking mechanism and", + " in case of changes fetch the list of events of interest.", + "", + " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just", + " the `EventIndex` then in case if the topic has the same contents on the next block", + " no notification will be triggered thus the event might be lost." + ] + }, + { + "name": "LastRuntimeUpgrade", + "modifier": "Optional", + "type": { + "plain": "LastRuntimeUpgradeInfo" + }, + "fallback": "0x00", + "documentation": [ + " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened." + ] + }, + { + "name": "UpgradedToU32RefCount", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not." + ] + }, + { + "name": "ExecutionPhase", + "modifier": "Optional", + "type": { + "plain": "Phase" + }, + "fallback": "0x00", + "documentation": [ + " The execution phase of the block." + ] + } + ] + }, + "calls": [ + { + "name": "fill_block", + "args": [ + { + "name": "_ratio", + "type": "Perbill" + } + ], + "documentation": [ + " A dispatch that will fill the block weight up to the given ratio." + ] + }, + { + "name": "remark", + "args": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "documentation": [ + " Make some on-chain remark.", + "", + " # ", + " - `O(1)`", + " - Base Weight: 0.665 µs, independent of remark length.", + " - No DB operations.", + " # " + ] + }, + { + "name": "set_heap_pages", + "args": [ + { + "name": "pages", + "type": "u64" + } + ], + "documentation": [ + " Set the number of pages in the WebAssembly environment's heap.", + "", + " # ", + " - `O(1)`", + " - 1 storage write.", + " - Base Weight: 1.405 µs", + " - 1 write to HEAP_PAGES", + " # " + ] + }, + { + "name": "set_code", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "documentation": [ + " Set the new runtime code.", + "", + " # ", + " - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`", + " - 1 storage write (codec `O(C)`).", + " - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is expensive).", + " - 1 event.", + " The weight of this function is dependent on the runtime, but generally this is very expensive.", + " We will treat this as a full block.", + " # " + ] + }, + { + "name": "set_code_without_checks", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "documentation": [ + " Set the new runtime code without doing any checks of the given `code`.", + "", + " # ", + " - `O(C)` where `C` length of `code`", + " - 1 storage write (codec `O(C)`).", + " - 1 event.", + " The weight of this function is dependent on the runtime. We will treat this as a full block.", + " # " + ] + }, + { + "name": "set_changes_trie_config", + "args": [ + { + "name": "changes_trie_config", + "type": "Option" + } + ], + "documentation": [ + " Set the new changes trie configuration.", + "", + " # ", + " - `O(1)`", + " - 1 storage write or delete (codec `O(1)`).", + " - 1 call to `deposit_log`: Uses `append` API, so O(1)", + " - Base Weight: 7.218 µs", + " - DB Weight:", + " - Writes: Changes Trie, System Digest", + " # " + ] + }, + { + "name": "set_storage", + "args": [ + { + "name": "items", + "type": "Vec" + } + ], + "documentation": [ + " Set some items of storage.", + "", + " # ", + " - `O(I)` where `I` length of `items`", + " - `I` storage writes (`O(1)`).", + " - Base Weight: 0.568 * i µs", + " - Writes: Number of items", + " # " + ] + }, + { + "name": "kill_storage", + "args": [ + { + "name": "keys", + "type": "Vec" + } + ], + "documentation": [ + " Kill some items from storage.", + "", + " # ", + " - `O(IK)` where `I` length of `keys` and `K` length of one key", + " - `I` storage deletions.", + " - Base Weight: .378 * i µs", + " - Writes: Number of items", + " # " + ] + }, + { + "name": "kill_prefix", + "args": [ + { + "name": "prefix", + "type": "Key" + }, + { + "name": "_subkeys", + "type": "u32" + } + ], + "documentation": [ + " Kill all storage items with a key that starts with the given prefix.", + "", + " **NOTE:** We rely on the Root origin to provide us the number of subkeys under", + " the prefix we are removing to accurately calculate the weight of this function.", + "", + " # ", + " - `O(P)` where `P` amount of keys with prefix `prefix`", + " - `P` storage deletions.", + " - Base Weight: 0.834 * P µs", + " - Writes: Number of subkeys + 1", + " # " + ] + }, + { + "name": "suicide", + "args": [], + "documentation": [ + " Kill the sending account, assuming there are no references outstanding and the composite", + " data is equal to its default value.", + "", + " # ", + " - `O(1)`", + " - 1 storage read and deletion.", + " --------------------", + " Base Weight: 8.626 µs", + " No DB Read or Write operations because caller is already in overlay", + " # " + ] + } + ], + "events": [ + { + "name": "ExtrinsicSuccess", + "args": [ + "DispatchInfo" + ], + "documentation": [ + " An extrinsic completed successfully. \\[info\\]" + ] + }, + { + "name": "ExtrinsicFailed", + "args": [ + "DispatchError", + "DispatchInfo" + ], + "documentation": [ + " An extrinsic failed. \\[error, info\\]" + ] + }, + { + "name": "CodeUpdated", + "args": [], + "documentation": [ + " `:code` was updated." + ] + }, + { + "name": "NewAccount", + "args": [ + "AccountId" + ], + "documentation": [ + " A new \\[account\\] was created." + ] + }, + { + "name": "KilledAccount", + "args": [ + "AccountId" + ], + "documentation": [ + " An \\[account\\] was reaped." + ] + } + ], + "constants": [ + { + "name": "BlockHashCount", + "type": "BlockNumber", + "value": "0x60090000", + "documentation": [ + " The maximum number of blocks to allow in mortal eras." + ] + }, + { + "name": "DbWeight", + "type": "RuntimeDbWeight", + "value": "0x40787d010000000000e1f50500000000", + "documentation": [ + " The weight of runtime database operations the runtime can invoke." + ] + }, + { + "name": "BlockWeights", + "type": "BlockWeights", + "value": "0x00f2052a0100000000204aa9d1010000405973070000000001c06e96a62e010000010098f73e5d010000010000000000000000405973070000000001c0f6e810a30100000100204aa9d1010000010088526a740000004059730700000000000000", + "documentation": [ + " The weight configuration (limits & base values) for each class of extrinsics and block." + ] + }, + { + "name": "SS58Prefix", + "type": "u8", + "value": "0x07", + "documentation": [ + " The designated SS85 prefix of this chain.", + "", + " This replaces the \"ss58Format\" property declared in the chain spec. Reason is", + " that the runtime should know about the prefix in order to make use of it as", + " an identifier of the chain." + ] + } + ], + "errors": [ + { + "name": "InvalidSpecName", + "documentation": [ + " The name of specification does not match between the current runtime", + " and the new runtime." + ] + }, + { + "name": "SpecVersionNeedsToIncrease", + "documentation": [ + " The specification version is not allowed to decrease between the current runtime", + " and the new runtime." + ] + }, + { + "name": "FailedToExtractRuntimeVersion", + "documentation": [ + " Failed to extract the runtime version from the new runtime.", + "", + " Either calling `Core_version` or decoding `RuntimeVersion` failed." + ] + }, + { + "name": "NonDefaultComposite", + "documentation": [ + " Suicide called when the account has non-default composite data." + ] + }, + { + "name": "NonZeroRefCount", + "documentation": [ + " There is a non-zero reference count preventing the account from being purged." + ] + } + ], + "index": 0 + }, + { + "name": "Utility", + "storage": null, + "calls": [ + { + "name": "batch", + "args": [ + { + "name": "calls", + "type": "Vec" + } + ], + "documentation": [ + " Send a batch of dispatch calls.", + "", + " May be called from any origin.", + "", + " - `calls`: The calls to be dispatched from the same origin.", + "", + " If origin is root then call are dispatch without checking origin filter. (This includes", + " bypassing `frame_system::Config::BaseCallFilter`).", + "", + " # ", + " - Complexity: O(C) where C is the number of calls to be batched.", + " # ", + "", + " This will return `Ok` in all circumstances. To determine the success of the batch, an", + " event is deposited. If a call failed and the batch was interrupted, then the", + " `BatchInterrupted` event is deposited, along with the number of successful calls made", + " and the error of the failed call. If all were successful, then the `BatchCompleted`", + " event is deposited." + ] + }, + { + "name": "as_derivative", + "args": [ + { + "name": "index", + "type": "u16" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Send a call through an indexed pseudonym of the sender.", + "", + " Filter from origin are passed along. The call will be dispatched with an origin which", + " use the same filter as the origin of this call.", + "", + " NOTE: If you need to ensure that any account-based filtering is not honored (i.e.", + " because you expect `proxy` to have been used prior in the call stack and you do not want", + " the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`", + " in the Multisig pallet instead.", + "", + " NOTE: Prior to version *12, this was called `as_limited_sub`.", + "", + " The dispatch origin for this call must be _Signed_." + ] + }, + { + "name": "batch_all", + "args": [ + { + "name": "calls", + "type": "Vec" + } + ], + "documentation": [ + " Send a batch of dispatch calls and atomically execute them.", + " The whole transaction will rollback and fail if any of the calls failed.", + "", + " May be called from any origin.", + "", + " - `calls`: The calls to be dispatched from the same origin.", + "", + " If origin is root then call are dispatch without checking origin filter. (This includes", + " bypassing `frame_system::Config::BaseCallFilter`).", + "", + " # ", + " - Complexity: O(C) where C is the number of calls to be batched.", + " # " + ] + } + ], + "events": [ + { + "name": "BatchInterrupted", + "args": [ + "u32", + "DispatchError" + ], + "documentation": [ + " Batch of dispatches did not complete fully. Index of first failing dispatch given, as", + " well as the error. \\[index, error\\]" + ] + }, + { + "name": "BatchCompleted", + "args": [], + "documentation": [ + " Batch of dispatches completed fully with no error." + ] + } + ], + "constants": [], + "errors": [], + "index": 1 + }, + { + "name": "Aura", + "storage": null, + "calls": null, + "events": null, + "constants": [], + "errors": [], + "index": 2 + }, + { + "name": "Timestamp", + "storage": { + "prefix": "Timestamp", + "items": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "plain": "Moment" + }, + "fallback": "0x0000000000000000", + "documentation": [ + " Current time for the current block." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " Did the timestamp get updated in this block?" + ] + } + ] + }, + "calls": [ + { + "name": "set", + "args": [ + { + "name": "now", + "type": "Compact" + } + ], + "documentation": [ + " Set the current time.", + "", + " This call should be invoked exactly once per block. It will panic at the finalization", + " phase, if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by", + " `MinimumPeriod`.", + "", + " The dispatch origin for this call must be `Inherent`.", + "", + " # ", + " - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)", + " - 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in `on_finalize`)", + " - 1 event handler `on_timestamp_set`. Must be `O(1)`.", + " # " + ] + } + ], + "events": null, + "constants": [ + { + "name": "MinimumPeriod", + "type": "Moment", + "value": "0xb80b000000000000", + "documentation": [ + " The minimum period between blocks. Beware that this is different to the *expected* period", + " that the block production apparatus provides. Your chosen consensus system will generally", + " work with this to determine a sensible block time. e.g. For Aura, it will be double this", + " period on default settings." + ] + } + ], + "errors": [], + "index": 3 + }, + { + "name": "Authorship", + "storage": { + "prefix": "Authorship", + "items": [ + { + "name": "Uncles", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Uncles" + ] + }, + { + "name": "Author", + "modifier": "Optional", + "type": { + "plain": "AccountId" + }, + "fallback": "0x00", + "documentation": [ + " Author of current block." + ] + }, + { + "name": "DidSetUncles", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " Whether uncles were already set in this block." + ] + } + ] + }, + "calls": [ + { + "name": "set_uncles", + "args": [ + { + "name": "new_uncles", + "type": "Vec
" + } + ], + "documentation": [ + " Provide a set of uncles." + ] + } + ], + "events": null, + "constants": [], + "errors": [ + { + "name": "InvalidUncleParent", + "documentation": [ + " The uncle parent not in the chain." + ] + }, + { + "name": "UnclesAlreadySet", + "documentation": [ + " Uncles already set in the block." + ] + }, + { + "name": "TooManyUncles", + "documentation": [ + " Too many uncles." + ] + }, + { + "name": "GenesisUncle", + "documentation": [ + " The uncle is genesis." + ] + }, + { + "name": "TooHighUncle", + "documentation": [ + " The uncle is too high in chain." + ] + }, + { + "name": "UncleAlreadyIncluded", + "documentation": [ + " The uncle is already included." + ] + }, + { + "name": "OldUncle", + "documentation": [ + " The uncle isn't recent enough to be included." + ] + } + ], + "index": 4 + }, + { + "name": "Indices", + "storage": { + "prefix": "Indices", + "items": [ + { + "name": "Accounts", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountIndex", + "value": "(AccountId,BalanceOf,bool)", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The lookup from index to account." + ] + } + ] + }, + "calls": [ + { + "name": "claim", + "args": [ + { + "name": "index", + "type": "AccountIndex" + } + ], + "documentation": [ + " Assign an previously unassigned index.", + "", + " Payment: `Deposit` is reserved from the sender account.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `index`: the index to be claimed. This must not be in use.", + "", + " Emits `IndexAssigned` if successful.", + "", + " # ", + " - `O(1)`.", + " - One storage mutation (codec `O(1)`).", + " - One reserve operation.", + " - One event.", + " -------------------", + " - DB Weight: 1 Read/Write (Accounts)", + " # " + ] + }, + { + "name": "transfer", + "args": [ + { + "name": "new", + "type": "AccountId" + }, + { + "name": "index", + "type": "AccountIndex" + } + ], + "documentation": [ + " Assign an index already owned by the sender to another account. The balance reservation", + " is effectively transferred to the new account.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `index`: the index to be re-assigned. This must be owned by the sender.", + " - `new`: the new owner of the index. This function is a no-op if it is equal to sender.", + "", + " Emits `IndexAssigned` if successful.", + "", + " # ", + " - `O(1)`.", + " - One storage mutation (codec `O(1)`).", + " - One transfer operation.", + " - One event.", + " -------------------", + " - DB Weight:", + " - Reads: Indices Accounts, System Account (recipient)", + " - Writes: Indices Accounts, System Account (recipient)", + " # " + ] + }, + { + "name": "free", + "args": [ + { + "name": "index", + "type": "AccountIndex" + } + ], + "documentation": [ + " Free up an index owned by the sender.", + "", + " Payment: Any previous deposit placed for the index is unreserved in the sender account.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must own the index.", + "", + " - `index`: the index to be freed. This must be owned by the sender.", + "", + " Emits `IndexFreed` if successful.", + "", + " # ", + " - `O(1)`.", + " - One storage mutation (codec `O(1)`).", + " - One reserve operation.", + " - One event.", + " -------------------", + " - DB Weight: 1 Read/Write (Accounts)", + " # " + ] + }, + { + "name": "force_transfer", + "args": [ + { + "name": "new", + "type": "AccountId" + }, + { + "name": "index", + "type": "AccountIndex" + }, + { + "name": "freeze", + "type": "bool" + } + ], + "documentation": [ + " Force an index to an account. This doesn't require a deposit. If the index is already", + " held, then any deposit is reimbursed to its current owner.", + "", + " The dispatch origin for this call must be _Root_.", + "", + " - `index`: the index to be (re-)assigned.", + " - `new`: the new owner of the index. This function is a no-op if it is equal to sender.", + " - `freeze`: if set to `true`, will freeze the index so it cannot be transferred.", + "", + " Emits `IndexAssigned` if successful.", + "", + " # ", + " - `O(1)`.", + " - One storage mutation (codec `O(1)`).", + " - Up to one reserve operation.", + " - One event.", + " -------------------", + " - DB Weight:", + " - Reads: Indices Accounts, System Account (original owner)", + " - Writes: Indices Accounts, System Account (original owner)", + " # " + ] + }, + { + "name": "freeze", + "args": [ + { + "name": "index", + "type": "AccountIndex" + } + ], + "documentation": [ + " Freeze an index so it will always point to the sender account. This consumes the deposit.", + "", + " The dispatch origin for this call must be _Signed_ and the signing account must have a", + " non-frozen account `index`.", + "", + " - `index`: the index to be frozen in place.", + "", + " Emits `IndexFrozen` if successful.", + "", + " # ", + " - `O(1)`.", + " - One storage mutation (codec `O(1)`).", + " - Up to one slash operation.", + " - One event.", + " -------------------", + " - DB Weight: 1 Read/Write (Accounts)", + " # " + ] + } + ], + "events": [ + { + "name": "IndexAssigned", + "args": [ + "AccountId", + "AccountIndex" + ], + "documentation": [ + " A account index was assigned. \\[index, who\\]" + ] + }, + { + "name": "IndexFreed", + "args": [ + "AccountIndex" + ], + "documentation": [ + " A account index has been freed up (unassigned). \\[index\\]" + ] + }, + { + "name": "IndexFrozen", + "args": [ + "AccountIndex", + "AccountId" + ], + "documentation": [ + " A account index has been frozen to its current account ID. \\[index, who\\]" + ] + } + ], + "constants": [ + { + "name": "Deposit", + "type": "BalanceOf", + "value": "0x000064a7b3b6e00d0000000000000000", + "documentation": [ + " The deposit needed for reserving an index." + ] + } + ], + "errors": [], + "index": 5 + }, + { + "name": "Balances", + "storage": { + "prefix": "Balances", + "items": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "plain": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "documentation": [ + " The total units issued in the system." + ] + }, + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "AccountData", + "linked": false + } + }, + "fallback": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " The balance of an account.", + "", + " NOTE: This is only used in the case that this module is used to store balances." + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "Vec", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Any liquidity locks on some account balances.", + " NOTE: Should only be accessed when setting, changing and freeing a lock." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "documentation": [ + " Storage version of the pallet.", + "", + " This is set to v2.0.0 for new networks." + ] + } + ] + }, + "calls": [ + { + "name": "transfer", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Transfer some liquid free balance to another account.", + "", + " `transfer` will set the `FreeBalance` of the sender and receiver.", + " It will decrease the total issuance of the system by the `TransferFee`.", + " If the sender's account is below the existential deposit as a result", + " of the transfer, the account will be reaped.", + "", + " The dispatch origin for this call must be `Signed` by the transactor.", + "", + " # ", + " - Dependent on arguments but not critical, given proper implementations for", + " input config types. See related functions below.", + " - It contains a limited number of reads and writes internally and no complex computation.", + "", + " Related functions:", + "", + " - `ensure_can_withdraw` is always called internally but has a bounded complexity.", + " - Transferring balances to accounts that did not exist before will cause", + " `T::OnNewAccount::on_new_account` to be called.", + " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`.", + " - `transfer_keep_alive` works the same way as `transfer`, but has an additional", + " check that the transfer will not kill the origin account.", + " ---------------------------------", + " - Base Weight: 73.64 µs, worst case scenario (account created, account removed)", + " - DB Weight: 1 Read and 1 Write to destination account", + " - Origin account is already in memory, so no DB operations for them.", + " # " + ] + }, + { + "name": "set_balance", + "args": [ + { + "name": "who", + "type": "LookupSource" + }, + { + "name": "new_free", + "type": "Compact" + }, + { + "name": "new_reserved", + "type": "Compact" + } + ], + "documentation": [ + " Set the balances of a given account.", + "", + " This will alter `FreeBalance` and `ReservedBalance` in storage. it will", + " also decrease the total issuance of the system (`TotalIssuance`).", + " If the new free or reserved balance is below the existential deposit,", + " it will reset the account nonce (`frame_system::AccountNonce`).", + "", + " The dispatch origin for this call is `root`.", + "", + " # ", + " - Independent of the arguments.", + " - Contains a limited number of reads and writes.", + " ---------------------", + " - Base Weight:", + " - Creating: 27.56 µs", + " - Killing: 35.11 µs", + " - DB Weight: 1 Read, 1 Write to `who`", + " # " + ] + }, + { + "name": "force_transfer", + "args": [ + { + "name": "source", + "type": "LookupSource" + }, + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Exactly as `transfer`, except the origin must be root and the source account may be", + " specified.", + " # ", + " - Same as transfer, but additional read and write because the source account is", + " not assumed to be in the overlay.", + " # " + ] + }, + { + "name": "transfer_keep_alive", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Same as the [`transfer`] call, but with a check that the transfer will not kill the", + " origin account.", + "", + " 99% of the time you want [`transfer`] instead.", + "", + " [`transfer`]: struct.Module.html#method.transfer", + " # ", + " - Cheaper than transfer because account cannot be killed.", + " - Base Weight: 51.4 µs", + " - DB Weight: 1 Read and 1 Write to dest (sender is in overlay already)", + " #" + ] + } + ], + "events": [ + { + "name": "Endowed", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " An account was created with some free balance. \\[account, free_balance\\]" + ] + }, + { + "name": "DustLost", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " An account was removed whose balance was non-zero but below ExistentialDeposit,", + " resulting in an outright loss. \\[account, balance\\]" + ] + }, + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " Transfer succeeded. \\[from, to, value\\]" + ] + }, + { + "name": "BalanceSet", + "args": [ + "AccountId", + "Balance", + "Balance" + ], + "documentation": [ + " A balance was set by root. \\[who, free, reserved\\]" + ] + }, + { + "name": "Deposit", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " Some amount was deposited (e.g. for transaction fees). \\[who, deposit\\]" + ] + }, + { + "name": "Reserved", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " Some balance was reserved (moved from free to reserved). \\[who, value\\]" + ] + }, + { + "name": "Unreserved", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " Some balance was unreserved (moved from reserved to free). \\[who, value\\]" + ] + }, + { + "name": "ReserveRepatriated", + "args": [ + "AccountId", + "AccountId", + "Balance", + "BalanceStatus" + ], + "documentation": [ + " Some balance was moved from the reserve of the first account to the second account.", + " Final argument indicates the destination balance type.", + " \\[from, to, balance, destination_status\\]" + ] + } + ], + "constants": [ + { + "name": "ExistentialDeposit", + "type": "Balance", + "value": "0x00a0724e180900000000000000000000", + "documentation": [ + " The minimum amount required to keep an account open." + ] + } + ], + "errors": [ + { + "name": "VestingBalance", + "documentation": [ + " Vesting balance too high to send value" + ] + }, + { + "name": "LiquidityRestrictions", + "documentation": [ + " Account liquidity restrictions prevent withdrawal" + ] + }, + { + "name": "Overflow", + "documentation": [ + " Got an overflow after adding" + ] + }, + { + "name": "InsufficientBalance", + "documentation": [ + " Balance too low to send value" + ] + }, + { + "name": "ExistentialDeposit", + "documentation": [ + " Value too low to create account due to existential deposit" + ] + }, + { + "name": "KeepAlive", + "documentation": [ + " Transfer/payment would kill account" + ] + }, + { + "name": "ExistingVestingSchedule", + "documentation": [ + " A vesting schedule already exists for this account" + ] + }, + { + "name": "DeadAccount", + "documentation": [ + " Beneficiary account must pre-exist" + ] + } + ], + "index": 6 + }, + { + "name": "TransactionPayment", + "storage": { + "prefix": "TransactionPayment", + "items": [ + { + "name": "NextFeeMultiplier", + "modifier": "Default", + "type": { + "plain": "Multiplier" + }, + "fallback": "0x000064a7b3b6e00d0000000000000000", + "documentation": [] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "documentation": [] + } + ] + }, + "calls": null, + "events": null, + "constants": [ + { + "name": "TransactionByteFee", + "type": "BalanceOf", + "value": "0x00407a10f35a00000000000000000000", + "documentation": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "WeightToFee", + "type": "Vec", + "value": "0x0401000000000000000000000000000000000000000001", + "documentation": [ + " The polynomial that is applied in order to derive fee from weight." + ] + } + ], + "errors": [], + "index": 7 + }, + { + "name": "Staking", + "storage": { + "prefix": "Staking", + "items": [ + { + "name": "HistoryDepth", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x54000000", + "documentation": [ + " Number of eras to keep in history.", + "", + " Information is kept for eras in `[current_era - history_depth; current_era]`.", + "", + " Must be more than the number of eras delayed by session otherwise. I.e. active era must", + " always be in history. I.e. `active_era > current_era - history_depth` must be", + " guaranteed." + ] + }, + { + "name": "ValidatorCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "documentation": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "documentation": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "Invulnerables", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Any validators that may never be slashed or forcibly kicked. It's a Vec since they're", + " easy to initialize and the performance hit is minimal (we expect no more than four", + " invulnerables) and restricted to testnets." + ] + }, + { + "name": "Bonded", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "AccountId", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Map from all locked \"stash\" accounts to the controller account." + ] + }, + { + "name": "Ledger", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "StakingLedger", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Map from all (unlocked) \"controller\" accounts to the info regarding the staking." + ] + }, + { + "name": "Payee", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "RewardDestination", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Where the reward payment should be made. Keyed by stash." + ] + }, + { + "name": "Validators", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "ValidatorPrefs", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The map from (wannabe) validator stash key to the preferences of that validator." + ] + }, + { + "name": "Nominators", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Nominations", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The map from nominator stash key to the set of stash keys of all validators to nominate." + ] + }, + { + "name": "CurrentEra", + "modifier": "Optional", + "type": { + "plain": "EraIndex" + }, + "fallback": "0x00", + "documentation": [ + " The current era index.", + "", + " This is the latest planned era, depending on how the Session pallet queues the validator", + " set, it might be active or not." + ] + }, + { + "name": "ActiveEra", + "modifier": "Optional", + "type": { + "plain": "ActiveEraInfo" + }, + "fallback": "0x00", + "documentation": [ + " The active era information, it holds index and start.", + "", + " The active era is the era being currently rewarded. Validator set of this era must be", + " equal to [`SessionInterface::validators`]." + ] + }, + { + "name": "ErasStartSessionIndex", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "EraIndex", + "value": "SessionIndex", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The session index at which the era start for the last `HISTORY_DEPTH` eras.", + "", + " Note: This tracks the starting session (i.e. session index when era start being active)", + " for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`." + ] + }, + { + "name": "ErasStakers", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "EraIndex", + "key2": "AccountId", + "value": "Exposure", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x000000", + "documentation": [ + " Exposure of validator at era.", + "", + " This is keyed first by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras.", + " If stakers hasn't been set or has been removed then empty exposure is returned." + ] + }, + { + "name": "ErasStakersClipped", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "EraIndex", + "key2": "AccountId", + "value": "Exposure", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x000000", + "documentation": [ + " Clipped Exposure of validator at era.", + "", + " This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the", + " `T::MaxNominatorRewardedPerValidator` biggest stakers.", + " (Note: the field `total` and `own` of the exposure remains unchanged).", + " This is used to limit the i/o cost for the nominator payout.", + "", + " This is keyed fist by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras.", + " If stakers hasn't been set or has been removed then empty exposure is returned." + ] + }, + { + "name": "ErasValidatorPrefs", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "EraIndex", + "key2": "AccountId", + "value": "ValidatorPrefs", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " Similar to `ErasStakers`, this holds the preferences of validators.", + "", + " This is keyed first by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras." + ] + }, + { + "name": "ErasValidatorReward", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "EraIndex", + "value": "BalanceOf", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The total validator era payout for the last `HISTORY_DEPTH` eras.", + "", + " Eras that haven't finished yet or has been removed doesn't have reward." + ] + }, + { + "name": "ErasRewardPoints", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "EraIndex", + "value": "EraRewardPoints", + "linked": false + } + }, + "fallback": "0x0000000000", + "documentation": [ + " Rewards for the last `HISTORY_DEPTH` eras.", + " If reward hasn't been set or has been removed then 0 reward is returned." + ] + }, + { + "name": "ErasTotalStake", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "EraIndex", + "value": "BalanceOf", + "linked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "documentation": [ + " The total amount staked for the last `HISTORY_DEPTH` eras.", + " If total hasn't been set or has been removed then 0 stake is returned." + ] + }, + { + "name": "ForceEra", + "modifier": "Default", + "type": { + "plain": "Forcing" + }, + "fallback": "0x00", + "documentation": [ + " Mode of era forcing." + ] + }, + { + "name": "SlashRewardFraction", + "modifier": "Default", + "type": { + "plain": "Perbill" + }, + "fallback": "0x00000000", + "documentation": [ + " The percentage of the slash that is distributed to reporters.", + "", + " The rest of the slashed value is handled by the `Slash`." + ] + }, + { + "name": "CanceledSlashPayout", + "modifier": "Default", + "type": { + "plain": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "documentation": [ + " The amount of currency given to reporters of a slash event which was", + " canceled by extraordinary circumstances (e.g. governance)." + ] + }, + { + "name": "UnappliedSlashes", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "EraIndex", + "value": "Vec", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " All unapplied slashes that are queued for later." + ] + }, + { + "name": "BondedEras", + "modifier": "Default", + "type": { + "plain": "Vec<(EraIndex,SessionIndex)>" + }, + "fallback": "0x00", + "documentation": [ + " A mapping from still-bonded eras to the first session index of that era.", + "", + " Must contains information for eras for the range:", + " `[active_era - bounding_duration; active_era]`" + ] + }, + { + "name": "ValidatorSlashInEra", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "EraIndex", + "key2": "AccountId", + "value": "(Perbill,BalanceOf)", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " All slashing events on validators, mapped by era to the highest slash proportion", + " and slash value of the era." + ] + }, + { + "name": "NominatorSlashInEra", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "EraIndex", + "key2": "AccountId", + "value": "BalanceOf", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " All slashing events on nominators, mapped by era to the highest slash value of the era." + ] + }, + { + "name": "SlashingSpans", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "SlashingSpans", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Slashing spans for stash accounts." + ] + }, + { + "name": "SpanSlash", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "(AccountId,SpanIndex)", + "value": "SpanRecord", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " Records information about the maximum slash of a stash within a slashing span,", + " as well as how much reward has been paid out." + ] + }, + { + "name": "EarliestUnappliedSlash", + "modifier": "Optional", + "type": { + "plain": "EraIndex" + }, + "fallback": "0x00", + "documentation": [ + " The earliest era for which we have a pending, unapplied slash." + ] + }, + { + "name": "SnapshotValidators", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Snapshot of validators at the beginning of the current election window. This should only", + " have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`." + ] + }, + { + "name": "SnapshotNominators", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Snapshot of nominators at the beginning of the current election window. This should only", + " have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`." + ] + }, + { + "name": "QueuedElected", + "modifier": "Optional", + "type": { + "plain": "ElectionResult" + }, + "fallback": "0x00", + "documentation": [ + " The next validator set. At the end of an era, if this is available (potentially from the", + " result of an offchain worker), it is immediately used. Otherwise, the on-chain election", + " is executed." + ] + }, + { + "name": "QueuedScore", + "modifier": "Optional", + "type": { + "plain": "ElectionScore" + }, + "fallback": "0x00", + "documentation": [ + " The score of the current [`QueuedElected`]." + ] + }, + { + "name": "EraElectionStatus", + "modifier": "Default", + "type": { + "plain": "ElectionStatus" + }, + "fallback": "0x00", + "documentation": [ + " Flag to control the execution of the offchain election. When `Open(_)`, we accept", + " solutions to be submitted." + ] + }, + { + "name": "IsCurrentSessionFinal", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " True if the current **planned** session is final. Note that this does not take era", + " forcing into account." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x03", + "documentation": [ + " True if network has been upgraded to this version.", + " Storage version of the pallet.", + "", + " This is set to v3.0.0 for new networks." + ] + } + ] + }, + "calls": [ + { + "name": "bond", + "args": [ + { + "name": "controller", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "payee", + "type": "RewardDestination" + } + ], + "documentation": [ + " Take the origin account as a stash and lock up `value` of its balance. `controller` will", + " be the account that controls it.", + "", + " `value` must be more than the `minimum_balance` specified by `T::Currency`.", + "", + " The dispatch origin for this call must be _Signed_ by the stash account.", + "", + " Emits `Bonded`.", + "", + " # ", + " - Independent of the arguments. Moderate complexity.", + " - O(1).", + " - Three extra DB entries.", + "", + " NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned", + " unless the `origin` falls below _existential deposit_ and gets removed as dust.", + " ------------------", + " Weight: O(1)", + " DB Weight:", + " - Read: Bonded, Ledger, [Origin Account], Current Era, History Depth, Locks", + " - Write: Bonded, Payee, [Origin Account], Locks, Ledger", + " # " + ] + }, + { + "name": "bond_extra", + "args": [ + { + "name": "max_additional", + "type": "Compact" + } + ], + "documentation": [ + " Add some extra amount that have appeared in the stash `free_balance` into the balance up", + " for staking.", + "", + " Use this if there are additional funds in your stash account that you wish to bond.", + " Unlike [`bond`] or [`unbond`] this function does not impose any limitation on the amount", + " that can be added.", + "", + " The dispatch origin for this call must be _Signed_ by the stash, not the controller and", + " it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " Emits `Bonded`.", + "", + " # ", + " - Independent of the arguments. Insignificant complexity.", + " - O(1).", + " - One DB entry.", + " ------------", + " DB Weight:", + " - Read: Era Election Status, Bonded, Ledger, [Origin Account], Locks", + " - Write: [Origin Account], Locks, Ledger", + " # " + ] + }, + { + "name": "unbond", + "args": [ + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Schedule a portion of the stash to be unlocked ready for transfer out after the bond", + " period ends. If this leaves an amount actively bonded less than", + " T::Currency::minimum_balance(), then it is increased to the full amount.", + "", + " Once the unlock period is done, you can call `withdraw_unbonded` to actually move", + " the funds out of management ready for transfer.", + "", + " No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`)", + " can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need", + " to be called first to remove some of the chunks (if possible).", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + " And, it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " Emits `Unbonded`.", + "", + " See also [`Call::withdraw_unbonded`].", + "", + " # ", + " - Independent of the arguments. Limited but potentially exploitable complexity.", + " - Contains a limited number of reads.", + " - Each call (requires the remainder of the bonded balance to be above `minimum_balance`)", + " will cause a new entry to be inserted into a vector (`Ledger.unlocking`) kept in storage.", + " The only way to clean the aforementioned storage item is also user-controlled via", + " `withdraw_unbonded`.", + " - One DB entry.", + " ----------", + " Weight: O(1)", + " DB Weight:", + " - Read: EraElectionStatus, Ledger, CurrentEra, Locks, BalanceOf Stash,", + " - Write: Locks, Ledger, BalanceOf Stash,", + " " + ] + }, + { + "name": "withdraw_unbonded", + "args": [ + { + "name": "num_slashing_spans", + "type": "u32" + } + ], + "documentation": [ + " Remove any unlocked chunks from the `unlocking` queue from our management.", + "", + " This essentially frees up that balance to be used by the stash account to do", + " whatever it wants.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + " And, it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " Emits `Withdrawn`.", + "", + " See also [`Call::unbond`].", + "", + " # ", + " - Could be dependent on the `origin` argument and how much `unlocking` chunks exist.", + " It implies `consolidate_unlocked` which loops over `Ledger.unlocking`, which is", + " indirectly user-controlled. See [`unbond`] for more detail.", + " - Contains a limited number of reads, yet the size of which could be large based on `ledger`.", + " - Writes are limited to the `origin` account key.", + " ---------------", + " Complexity O(S) where S is the number of slashing spans to remove", + " Update:", + " - Reads: EraElectionStatus, Ledger, Current Era, Locks, [Origin Account]", + " - Writes: [Origin Account], Locks, Ledger", + " Kill:", + " - Reads: EraElectionStatus, Ledger, Current Era, Bonded, Slashing Spans, [Origin", + " Account], Locks, BalanceOf stash", + " - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators,", + " [Origin Account], Locks, BalanceOf stash.", + " - Writes Each: SpanSlash * S", + " NOTE: Weight annotation is the kill scenario, we refund otherwise.", + " # " + ] + }, + { + "name": "validate", + "args": [ + { + "name": "prefs", + "type": "ValidatorPrefs" + } + ], + "documentation": [ + " Declare the desire to validate for the origin controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + " And, it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " # ", + " - Independent of the arguments. Insignificant complexity.", + " - Contains a limited number of reads.", + " - Writes are limited to the `origin` account key.", + " -----------", + " Weight: O(1)", + " DB Weight:", + " - Read: Era Election Status, Ledger", + " - Write: Nominators, Validators", + " # " + ] + }, + { + "name": "nominate", + "args": [ + { + "name": "targets", + "type": "Vec" + } + ], + "documentation": [ + " Declare the desire to nominate `targets` for the origin controller.", + "", + " Effects will be felt at the beginning of the next era. This can only be called when", + " [`EraElectionStatus`] is `Closed`.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + " And, it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " # ", + " - The transaction's complexity is proportional to the size of `targets` (N)", + " which is capped at CompactAssignments::LIMIT (MAX_NOMINATIONS).", + " - Both the reads and writes follow a similar pattern.", + " ---------", + " Weight: O(N)", + " where N is the number of targets", + " DB Weight:", + " - Reads: Era Election Status, Ledger, Current Era", + " - Writes: Validators, Nominators", + " # " + ] + }, + { + "name": "chill", + "args": [], + "documentation": [ + " Declare no desire to either validate or nominate.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + " And, it can be only called when [`EraElectionStatus`] is `Closed`.", + "", + " # ", + " - Independent of the arguments. Insignificant complexity.", + " - Contains one read.", + " - Writes are limited to the `origin` account key.", + " --------", + " Weight: O(1)", + " DB Weight:", + " - Read: EraElectionStatus, Ledger", + " - Write: Validators, Nominators", + " # " + ] + }, + { + "name": "set_payee", + "args": [ + { + "name": "payee", + "type": "RewardDestination" + } + ], + "documentation": [ + " (Re-)set the payment target for a controller.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + " # ", + " - Independent of the arguments. Insignificant complexity.", + " - Contains a limited number of reads.", + " - Writes are limited to the `origin` account key.", + " ---------", + " - Weight: O(1)", + " - DB Weight:", + " - Read: Ledger", + " - Write: Payee", + " # " + ] + }, + { + "name": "set_controller", + "args": [ + { + "name": "controller", + "type": "LookupSource" + } + ], + "documentation": [ + " (Re-)set the controller of a stash.", + "", + " Effects will be felt at the beginning of the next era.", + "", + " The dispatch origin for this call must be _Signed_ by the stash, not the controller.", + "", + " # ", + " - Independent of the arguments. Insignificant complexity.", + " - Contains a limited number of reads.", + " - Writes are limited to the `origin` account key.", + " ----------", + " Weight: O(1)", + " DB Weight:", + " - Read: Bonded, Ledger New Controller, Ledger Old Controller", + " - Write: Bonded, Ledger New Controller, Ledger Old Controller", + " # " + ] + }, + { + "name": "set_validator_count", + "args": [ + { + "name": "new", + "type": "Compact" + } + ], + "documentation": [ + " Sets the ideal number of validators.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " Weight: O(1)", + " Write: Validator Count", + " # " + ] + }, + { + "name": "increase_validator_count", + "args": [ + { + "name": "additional", + "type": "Compact" + } + ], + "documentation": [ + " Increments the ideal number of validators.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " Same as [`set_validator_count`].", + " # " + ] + }, + { + "name": "scale_validator_count", + "args": [ + { + "name": "factor", + "type": "Percent" + } + ], + "documentation": [ + " Scale up the ideal number of validators by a factor.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " Same as [`set_validator_count`].", + " # " + ] + }, + { + "name": "force_no_eras", + "args": [], + "documentation": [ + " Force there to be no new eras indefinitely.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " - No arguments.", + " - Weight: O(1)", + " - Write: ForceEra", + " # " + ] + }, + { + "name": "force_new_era", + "args": [], + "documentation": [ + " Force there to be a new era at the end of the next session. After this, it will be", + " reset to normal (non-forced) behaviour.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " - No arguments.", + " - Weight: O(1)", + " - Write ForceEra", + " # " + ] + }, + { + "name": "set_invulnerables", + "args": [ + { + "name": "invulnerables", + "type": "Vec" + } + ], + "documentation": [ + " Set the validators who cannot be slashed (if any).", + "", + " The dispatch origin must be Root.", + "", + " # ", + " - O(V)", + " - Write: Invulnerables", + " # " + ] + }, + { + "name": "force_unstake", + "args": [ + { + "name": "stash", + "type": "AccountId" + }, + { + "name": "num_slashing_spans", + "type": "u32" + } + ], + "documentation": [ + " Force a current staker to become completely unstaked, immediately.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " O(S) where S is the number of slashing spans to be removed", + " Reads: Bonded, Slashing Spans, Account, Locks", + " Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Account, Locks", + " Writes Each: SpanSlash * S", + " # " + ] + }, + { + "name": "force_new_era_always", + "args": [], + "documentation": [ + " Force there to be a new era at the end of sessions indefinitely.", + "", + " The dispatch origin must be Root.", + "", + " # ", + " - Weight: O(1)", + " - Write: ForceEra", + " # " + ] + }, + { + "name": "cancel_deferred_slash", + "args": [ + { + "name": "era", + "type": "EraIndex" + }, + { + "name": "slash_indices", + "type": "Vec" + } + ], + "documentation": [ + " Cancel enactment of a deferred slash.", + "", + " Can be called by the `T::SlashCancelOrigin`.", + "", + " Parameters: era and indices of the slashes for that era to kill.", + "", + " # ", + " Complexity: O(U + S)", + " with U unapplied slashes weighted with U=1000", + " and S is the number of slash indices to be canceled.", + " - Read: Unapplied Slashes", + " - Write: Unapplied Slashes", + " # " + ] + }, + { + "name": "payout_stakers", + "args": [ + { + "name": "validator_stash", + "type": "AccountId" + }, + { + "name": "era", + "type": "EraIndex" + } + ], + "documentation": [ + " Pay out all the stakers behind a single validator for a single era.", + "", + " - `validator_stash` is the stash account of the validator. Their nominators, up to", + " `T::MaxNominatorRewardedPerValidator`, will also receive their rewards.", + " - `era` may be any era between `[current_era - history_depth; current_era]`.", + "", + " The origin of this call must be _Signed_. Any account can call this function, even if", + " it is not one of the stakers.", + "", + " This can only be called when [`EraElectionStatus`] is `Closed`.", + "", + " # ", + " - Time complexity: at most O(MaxNominatorRewardedPerValidator).", + " - Contains a limited number of reads and writes.", + " -----------", + " N is the Number of payouts for the validator (including the validator)", + " Weight:", + " - Reward Destination Staked: O(N)", + " - Reward Destination Controller (Creating): O(N)", + " DB Weight:", + " - Read: EraElectionStatus, CurrentEra, HistoryDepth, ErasValidatorReward,", + " ErasStakersClipped, ErasRewardPoints, ErasValidatorPrefs (8 items)", + " - Read Each: Bonded, Ledger, Payee, Locks, System Account (5 items)", + " - Write Each: System Account, Locks, Ledger (3 items)", + "", + " NOTE: weights are assuming that payouts are made to alive stash account (Staked).", + " Paying even a dead controller is cheaper weight-wise. We don't do any refunds here.", + " # " + ] + }, + { + "name": "rebond", + "args": [ + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Rebond a portion of the stash scheduled to be unlocked.", + "", + " The dispatch origin must be signed by the controller, and it can be only called when", + " [`EraElectionStatus`] is `Closed`.", + "", + " # ", + " - Time complexity: O(L), where L is unlocking chunks", + " - Bounded by `MAX_UNLOCKING_CHUNKS`.", + " - Storage changes: Can't increase storage, only decrease it.", + " ---------------", + " - DB Weight:", + " - Reads: EraElectionStatus, Ledger, Locks, [Origin Account]", + " - Writes: [Origin Account], Locks, Ledger", + " # " + ] + }, + { + "name": "set_history_depth", + "args": [ + { + "name": "new_history_depth", + "type": "Compact" + }, + { + "name": "_era_items_deleted", + "type": "Compact" + } + ], + "documentation": [ + " Set `HistoryDepth` value. This function will delete any history information", + " when `HistoryDepth` is reduced.", + "", + " Parameters:", + " - `new_history_depth`: The new history depth you would like to set.", + " - `era_items_deleted`: The number of items that will be deleted by this dispatch.", + " This should report all the storage items that will be deleted by clearing old", + " era history. Needed to report an accurate weight for the dispatch. Trusted by", + " `Root` to report an accurate number.", + "", + " Origin must be root.", + "", + " # ", + " - E: Number of history depths removed, i.e. 10 -> 7 = 3", + " - Weight: O(E)", + " - DB Weight:", + " - Reads: Current Era, History Depth", + " - Writes: History Depth", + " - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs", + " - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake, ErasStartSessionIndex", + " # " + ] + }, + { + "name": "reap_stash", + "args": [ + { + "name": "stash", + "type": "AccountId" + }, + { + "name": "num_slashing_spans", + "type": "u32" + } + ], + "documentation": [ + " Remove all data structure concerning a staker/stash once its balance is zero.", + " This is essentially equivalent to `withdraw_unbonded` except it can be called by anyone", + " and the target `stash` must have no funds left.", + "", + " This can be called from any origin.", + "", + " - `stash`: The stash account to reap. Its balance must be zero.", + "", + " # ", + " Complexity: O(S) where S is the number of slashing spans on the account.", + " DB Weight:", + " - Reads: Stash Account, Bonded, Slashing Spans, Locks", + " - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Stash Account, Locks", + " - Writes Each: SpanSlash * S", + " # " + ] + }, + { + "name": "submit_election_solution", + "args": [ + { + "name": "winners", + "type": "Vec" + }, + { + "name": "compact", + "type": "CompactAssignments" + }, + { + "name": "score", + "type": "ElectionScore" + }, + { + "name": "era", + "type": "EraIndex" + }, + { + "name": "size", + "type": "ElectionSize" + } + ], + "documentation": [ + " Submit an election result to the chain. If the solution:", + "", + " 1. is valid.", + " 2. has a better score than a potentially existing solution on chain.", + "", + " then, it will be _put_ on chain.", + "", + " A solution consists of two pieces of data:", + "", + " 1. `winners`: a flat vector of all the winners of the round.", + " 2. `assignments`: the compact version of an assignment vector that encodes the edge", + " weights.", + "", + " Both of which may be computed using _phragmen_, or any other algorithm.", + "", + " Additionally, the submitter must provide:", + "", + " - The `score` that they claim their solution has.", + "", + " Both validators and nominators will be represented by indices in the solution. The", + " indices should respect the corresponding types ([`ValidatorIndex`] and", + " [`NominatorIndex`]). Moreover, they should be valid when used to index into", + " [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the", + " solution to be rejected. These two storage items are set during the election window and", + " may be used to determine the indices.", + "", + " A solution is valid if:", + "", + " 0. It is submitted when [`EraElectionStatus`] is `Open`.", + " 1. Its claimed score is equal to the score computed on-chain.", + " 2. Presents the correct number of winners.", + " 3. All indexes must be value according to the snapshot vectors. All edge values must", + " also be correct and should not overflow the granularity of the ratio type (i.e. 256", + " or billion).", + " 4. For each edge, all targets are actually nominated by the voter.", + " 5. Has correct self-votes.", + "", + " A solutions score is consisted of 3 parameters:", + "", + " 1. `min { support.total }` for each support of a winner. This value should be maximized.", + " 2. `sum { support.total }` for each support of a winner. This value should be minimized.", + " 3. `sum { support.total^2 }` for each support of a winner. This value should be", + " minimized (to ensure less variance)", + "", + " # ", + " The transaction is assumed to be the longest path, a better solution.", + " - Initial solution is almost the same.", + " - Worse solution is retraced in pre-dispatch-checks which sets its own weight.", + " # " + ] + }, + { + "name": "submit_election_solution_unsigned", + "args": [ + { + "name": "winners", + "type": "Vec" + }, + { + "name": "compact", + "type": "CompactAssignments" + }, + { + "name": "score", + "type": "ElectionScore" + }, + { + "name": "era", + "type": "EraIndex" + }, + { + "name": "size", + "type": "ElectionSize" + } + ], + "documentation": [ + " Unsigned version of `submit_election_solution`.", + "", + " Note that this must pass the [`ValidateUnsigned`] check which only allows transactions", + " from the local node to be included. In other words, only the block author can include a", + " transaction in the block.", + "", + " # ", + " See [`submit_election_solution`].", + " # " + ] + } + ], + "events": [ + { + "name": "EraPayout", + "args": [ + "EraIndex", + "Balance", + "Balance" + ], + "documentation": [ + " The era payout has been set; the first balance is the validator-payout; the second is", + " the remainder from the maximum amount of reward.", + " \\[era_index, validator_payout, remainder\\]" + ] + }, + { + "name": "Reward", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " The staker has been rewarded by this amount. \\[stash, amount\\]" + ] + }, + { + "name": "Slash", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " One validator (and its nominators) has been slashed by the given amount.", + " \\[validator, amount\\]" + ] + }, + { + "name": "OldSlashingReportDiscarded", + "args": [ + "SessionIndex" + ], + "documentation": [ + " An old slashing report from a prior era was discarded because it could", + " not be processed. \\[session_index\\]" + ] + }, + { + "name": "StakingElection", + "args": [ + "ElectionCompute" + ], + "documentation": [ + " A new set of stakers was elected with the given \\[compute\\]." + ] + }, + { + "name": "SolutionStored", + "args": [ + "ElectionCompute" + ], + "documentation": [ + " A new solution for the upcoming election has been stored. \\[compute\\]" + ] + }, + { + "name": "Bonded", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " An account has bonded this amount. \\[stash, amount\\]", + "", + " NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably,", + " it will not be emitted for staking rewards when they are added to stake." + ] + }, + { + "name": "Unbonded", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " An account has unbonded this amount. \\[stash, amount\\]" + ] + }, + { + "name": "Withdrawn", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance`", + " from the unlocking queue. \\[stash, amount\\]" + ] + } + ], + "constants": [ + { + "name": "SessionsPerEra", + "type": "SessionIndex", + "value": "0x06000000", + "documentation": [ + " Number of sessions per era." + ] + }, + { + "name": "BondingDuration", + "type": "EraIndex", + "value": "0x38000000", + "documentation": [ + " Number of eras that staked funds must remain bonded for." + ] + }, + { + "name": "SlashDeferDuration", + "type": "EraIndex", + "value": "0x1c000000", + "documentation": [ + " Number of eras that slashes are deferred by, after computation.", + "", + " This should be less than the bonding duration.", + " Set to 0 if slashes should be applied immediately, without opportunity for", + " intervention." + ] + }, + { + "name": "ElectionLookahead", + "type": "BlockNumber", + "value": "0x96000000", + "documentation": [ + " The number of blocks before the end of the era from which election submissions are allowed.", + "", + " Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will", + " be used.", + "", + " This is bounded by being within the last session. Hence, setting it to a value more than the", + " length of a session will be pointless." + ] + }, + { + "name": "MaxIterations", + "type": "u32", + "value": "0x05000000", + "documentation": [ + " Maximum number of balancing iterations to run in the offchain submission.", + "", + " If set to 0, balance_solution will not be executed at all." + ] + }, + { + "name": "MinSolutionScoreBump", + "type": "Perbill", + "value": "0x20a10700", + "documentation": [ + " The threshold of improvement that should be provided for a new solution to be accepted." + ] + }, + { + "name": "MaxNominatorRewardedPerValidator", + "type": "u32", + "value": "0x80000000", + "documentation": [ + " The maximum number of nominators rewarded for each validator.", + "", + " For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim", + " their reward. This used to limit the i/o cost for the nominator payout." + ] + } + ], + "errors": [ + { + "name": "NotController", + "documentation": [ + " Not a controller account." + ] + }, + { + "name": "NotStash", + "documentation": [ + " Not a stash account." + ] + }, + { + "name": "AlreadyBonded", + "documentation": [ + " Stash is already bonded." + ] + }, + { + "name": "AlreadyPaired", + "documentation": [ + " Controller is already paired." + ] + }, + { + "name": "EmptyTargets", + "documentation": [ + " Targets cannot be empty." + ] + }, + { + "name": "DuplicateIndex", + "documentation": [ + " Duplicate index." + ] + }, + { + "name": "InvalidSlashIndex", + "documentation": [ + " Slash record index out of bounds." + ] + }, + { + "name": "InsufficientValue", + "documentation": [ + " Can not bond with value less than minimum balance." + ] + }, + { + "name": "NoMoreChunks", + "documentation": [ + " Can not schedule more unlock chunks." + ] + }, + { + "name": "NoUnlockChunk", + "documentation": [ + " Can not rebond without unlocking chunks." + ] + }, + { + "name": "FundedTarget", + "documentation": [ + " Attempting to target a stash that still has funds." + ] + }, + { + "name": "InvalidEraToReward", + "documentation": [ + " Invalid era to reward." + ] + }, + { + "name": "InvalidNumberOfNominations", + "documentation": [ + " Invalid number of nominations." + ] + }, + { + "name": "NotSortedAndUnique", + "documentation": [ + " Items are not sorted and unique." + ] + }, + { + "name": "AlreadyClaimed", + "documentation": [ + " Rewards for this era have already been claimed for this validator." + ] + }, + { + "name": "OffchainElectionEarlySubmission", + "documentation": [ + " The submitted result is received out of the open window." + ] + }, + { + "name": "OffchainElectionWeakSubmission", + "documentation": [ + " The submitted result is not as good as the one stored on chain." + ] + }, + { + "name": "SnapshotUnavailable", + "documentation": [ + " The snapshot data of the current window is missing." + ] + }, + { + "name": "OffchainElectionBogusWinnerCount", + "documentation": [ + " Incorrect number of winners were presented." + ] + }, + { + "name": "OffchainElectionBogusWinner", + "documentation": [ + " One of the submitted winners is not an active candidate on chain (index is out of range", + " in snapshot)." + ] + }, + { + "name": "OffchainElectionBogusCompact", + "documentation": [ + " Error while building the assignment type from the compact. This can happen if an index", + " is invalid, or if the weights _overflow_." + ] + }, + { + "name": "OffchainElectionBogusNominator", + "documentation": [ + " One of the submitted nominators is not an active nominator on chain." + ] + }, + { + "name": "OffchainElectionBogusNomination", + "documentation": [ + " One of the submitted nominators has an edge to which they have not voted on chain." + ] + }, + { + "name": "OffchainElectionSlashedNomination", + "documentation": [ + " One of the submitted nominators has an edge which is submitted before the last non-zero", + " slash of the target." + ] + }, + { + "name": "OffchainElectionBogusSelfVote", + "documentation": [ + " A self vote must only be originated from a validator to ONLY themselves." + ] + }, + { + "name": "OffchainElectionBogusEdge", + "documentation": [ + " The submitted result has unknown edges that are not among the presented winners." + ] + }, + { + "name": "OffchainElectionBogusScore", + "documentation": [ + " The claimed score does not match with the one computed from the data." + ] + }, + { + "name": "OffchainElectionBogusElectionSize", + "documentation": [ + " The election size is invalid." + ] + }, + { + "name": "CallNotAllowed", + "documentation": [ + " The call is not allowed at the given time due to restrictions of election period." + ] + }, + { + "name": "IncorrectHistoryDepth", + "documentation": [ + " Incorrect previous history depth input provided." + ] + }, + { + "name": "IncorrectSlashingSpans", + "documentation": [ + " Incorrect number of slashing spans provided." + ] + } + ], + "index": 8 + }, + { + "name": "Session", + "storage": { + "prefix": "Session", + "items": [ + { + "name": "Validators", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The current set of validators." + ] + }, + { + "name": "CurrentIndex", + "modifier": "Default", + "type": { + "plain": "SessionIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " Current index of the session." + ] + }, + { + "name": "QueuedChanged", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " True if the underlying economic identities or weighting behind the validators", + " has changed in the queued validator set." + ] + }, + { + "name": "QueuedKeys", + "modifier": "Default", + "type": { + "plain": "Vec<(ValidatorId,Keys)>" + }, + "fallback": "0x00", + "documentation": [ + " The queued keys for the next session. When the next session begins, these keys", + " will be used to determine the validator's session keys." + ] + }, + { + "name": "DisabledValidators", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Indices of disabled validators.", + "", + " The set is cleared when `on_session_ending` returns a new set of identities." + ] + }, + { + "name": "NextKeys", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ValidatorId", + "value": "Keys", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The next session keys for a validator." + ] + }, + { + "name": "KeyOwner", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "(KeyTypeId,Bytes)", + "value": "ValidatorId", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The owner of a key. The key is the `KeyTypeId` + the encoded key." + ] + } + ] + }, + "calls": [ + { + "name": "set_keys", + "args": [ + { + "name": "keys", + "type": "Keys" + }, + { + "name": "proof", + "type": "Bytes" + } + ], + "documentation": [ + " Sets the session key(s) of the function caller to `keys`.", + " Allows an account to set its session key prior to becoming a validator.", + " This doesn't take effect until the next session.", + "", + " The dispatch origin of this function must be signed.", + "", + " # ", + " - Complexity: `O(1)`", + " Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed.", + " - DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys`", + " - DbWrites: `origin account`, `NextKeys`", + " - DbReads per key id: `KeyOwner`", + " - DbWrites per key id: `KeyOwner`", + " # " + ] + }, + { + "name": "purge_keys", + "args": [], + "documentation": [ + " Removes any session key(s) of the function caller.", + " This doesn't take effect until the next session.", + "", + " The dispatch origin of this function must be signed.", + "", + " # ", + " - Complexity: `O(1)` in number of key types.", + " Actual cost depends on the number of length of `T::Keys::key_ids()` which is fixed.", + " - DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account`", + " - DbWrites: `NextKeys`, `origin account`", + " - DbWrites per key id: `KeyOwnder`", + " # " + ] + } + ], + "events": [ + { + "name": "NewSession", + "args": [ + "SessionIndex" + ], + "documentation": [ + " New session has happened. Note that the argument is the \\[session_index\\], not the block", + " number as the type might suggest." + ] + } + ], + "constants": [], + "errors": [ + { + "name": "InvalidProof", + "documentation": [ + " Invalid ownership proof." + ] + }, + { + "name": "NoAssociatedValidatorId", + "documentation": [ + " No associated validator ID for account." + ] + }, + { + "name": "DuplicatedKey", + "documentation": [ + " Registered duplicate key." + ] + }, + { + "name": "NoKeys", + "documentation": [ + " No keys are associated with this account." + ] + } + ], + "index": 9 + }, + { + "name": "Democracy", + "storage": { + "prefix": "Democracy", + "items": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "plain": "PropIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "plain": "Vec<(PropIndex,Hash,AccountId)>" + }, + "fallback": "0x00", + "documentation": [ + " The public proposals. Unsorted. The second item is the proposal's hash." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "PropIndex", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Those who have locked a deposit.", + "", + " TWOX-NOTE: Safe, as increasing integer keys are safe." + ] + }, + { + "name": "Preimages", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "PreimageStatus", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Map of hashes to the proposal preimage, along with who registered it and their deposit.", + " The block number is the block at which it was deposited." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "plain": "ReferendumIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " The next free referendum index, aka the number of referenda started so far." + ] + }, + { + "name": "LowestUnbaked", + "modifier": "Default", + "type": { + "plain": "ReferendumIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " The lowest referendum index representing an unbaked referendum. Equal to", + " `ReferendumCount` if there isn't a unbaked referendum." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ReferendumIndex", + "value": "ReferendumInfo", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Information concerning any given referendum.", + "", + " TWOX-NOTE: SAFE as indexes are not under an attacker’s control." + ] + }, + { + "name": "VotingOf", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Voting", + "linked": false + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " All votes for a particular voter. We store the balance for the number of votes that we", + " have recorded. The second item is the total amount of delegations, that will be added.", + "", + " TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway." + ] + }, + { + "name": "Locks", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "BlockNumber", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Accounts for which there are locks in action which may be removed at some point in the", + " future. The value is the block number at which the lock expires and may be removed.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "LastTabledWasExternal", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "documentation": [ + " True if the last referendum tabled was submitted externally. False if it was a public", + " proposal." + ] + }, + { + "name": "NextExternal", + "modifier": "Optional", + "type": { + "plain": "(Hash,VoteThreshold)" + }, + "fallback": "0x00", + "documentation": [ + " The referendum to be tabled whenever it would be valid to table an external proposal.", + " This happens when a referendum needs to be tabled and one of two conditions are met:", + " - `LastTabledWasExternal` is `false`; or", + " - `PublicProps` is empty." + ] + }, + { + "name": "Blacklist", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "(BlockNumber,Vec)", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " A record of who vetoed what. Maps proposal hash to a possible existent block number", + " (until when it may not be resubmitted) and who vetoed it." + ] + }, + { + "name": "Cancellations", + "modifier": "Default", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "bool", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Record of all proposals that have been subject to emergency cancellation." + ] + }, + { + "name": "StorageVersion", + "modifier": "Optional", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "documentation": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Propose a sensitive action to be taken.", + "", + " The dispatch origin of this call must be _Signed_ and the sender must", + " have funds to cover the deposit.", + "", + " - `proposal_hash`: The hash of the proposal preimage.", + " - `value`: The amount of deposit (must be at least `MinimumDeposit`).", + "", + " Emits `Proposed`.", + "", + " Weight: `O(p)`" + ] + }, + { + "name": "second", + "args": [ + { + "name": "proposal", + "type": "Compact" + }, + { + "name": "seconds_upper_bound", + "type": "Compact" + } + ], + "documentation": [ + " Signals agreement with a particular proposal.", + "", + " The dispatch origin of this call must be _Signed_ and the sender", + " must have funds to cover the deposit, equal to the original deposit.", + "", + " - `proposal`: The index of the proposal to second.", + " - `seconds_upper_bound`: an upper bound on the current number of seconds on this", + " proposal. Extrinsic is weighted according to this value with no refund.", + "", + " Weight: `O(S)` where S is the number of seconds a proposal already has." + ] + }, + { + "name": "vote", + "args": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "AccountVote" + } + ], + "documentation": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `ref_index`: The index of the referendum to vote for.", + " - `vote`: The vote configuration.", + "", + " Weight: `O(R)` where R is the number of referendums the voter has voted on." + ] + }, + { + "name": "emergency_cancel", + "args": [ + { + "name": "ref_index", + "type": "ReferendumIndex" + } + ], + "documentation": [ + " Schedule an emergency cancellation of a referendum. Cannot happen twice to the same", + " referendum.", + "", + " The dispatch origin of this call must be `CancellationOrigin`.", + "", + " -`ref_index`: The index of the referendum to cancel.", + "", + " Weight: `O(1)`." + ] + }, + { + "name": "external_propose", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [ + " Schedule a referendum to be tabled once it is legal to schedule an external", + " referendum.", + "", + " The dispatch origin of this call must be `ExternalOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Weight: `O(V)` with V number of vetoers in the blacklist of proposal.", + " Decoding vec of length V. Charged as maximum" + ] + }, + { + "name": "external_propose_majority", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [ + " Schedule a majority-carries referendum to be tabled next once it is legal to schedule", + " an external referendum.", + "", + " The dispatch of this call must be `ExternalMajorityOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + " pre-scheduled `external_propose` call.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "external_propose_default", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [ + " Schedule a negative-turnout-bias referendum to be tabled next once it is legal to", + " schedule an external referendum.", + "", + " The dispatch of this call must be `ExternalDefaultOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + " pre-scheduled `external_propose` call.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "fast_track", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "voting_period", + "type": "BlockNumber" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Schedule the currently externally-proposed majority-carries referendum to be tabled", + " immediately. If there is no externally-proposed referendum currently, or if there is one", + " but it is not a majority-carries referendum then it fails.", + "", + " The dispatch of this call must be `FastTrackOrigin`.", + "", + " - `proposal_hash`: The hash of the current external proposal.", + " - `voting_period`: The period that is allowed for voting on this proposal. Increased to", + " `FastTrackVotingPeriod` if too low.", + " - `delay`: The number of block after voting has ended in approval and this should be", + " enacted. This doesn't have a minimum amount.", + "", + " Emits `Started`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "veto_external", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [ + " Veto and blacklist the external proposal hash.", + "", + " The dispatch origin of this call must be `VetoOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.", + "", + " Emits `Vetoed`.", + "", + " Weight: `O(V + log(V))` where V is number of `existing vetoers`" + ] + }, + { + "name": "cancel_referendum", + "args": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a referendum.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " - `ref_index`: The index of the referendum to cancel.", + "", + " # Weight: `O(1)`." + ] + }, + { + "name": "cancel_queued", + "args": [ + { + "name": "which", + "type": "ReferendumIndex" + } + ], + "documentation": [ + " Cancel a proposal queued for enactment.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " - `which`: The index of the referendum to cancel.", + "", + " Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`." + ] + }, + { + "name": "delegate", + "args": [ + { + "name": "to", + "type": "AccountId" + }, + { + "name": "conviction", + "type": "Conviction" + }, + { + "name": "balance", + "type": "BalanceOf" + } + ], + "documentation": [ + " Delegate the voting power (with some given conviction) of the sending account.", + "", + " The balance delegated is locked for as long as it's delegated, and thereafter for the", + " time appropriate for the conviction's lock period.", + "", + " The dispatch origin of this call must be _Signed_, and the signing account must either:", + " - be delegating already; or", + " - have no voting activity (if there is, then it will need to be removed/consolidated", + " through `reap_vote` or `unvote`).", + "", + " - `to`: The account whose voting the `target` account's voting power will follow.", + " - `conviction`: The conviction that will be attached to the delegated votes. When the", + " account is undelegated, the funds will be locked for the corresponding period.", + " - `balance`: The amount of the account's balance to be used in delegating. This must", + " not be more than the account's current balance.", + "", + " Emits `Delegated`.", + "", + " Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "undelegate", + "args": [], + "documentation": [ + " Undelegate the voting power of the sending account.", + "", + " Tokens may be unlocked following once an amount of time consistent with the lock period", + " of the conviction with which the delegation was issued.", + "", + " The dispatch origin of this call must be _Signed_ and the signing account must be", + " currently delegating.", + "", + " Emits `Undelegated`.", + "", + " Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "clear_public_proposals", + "args": [], + "documentation": [ + " Clears all public proposals.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " Weight: `O(1)`." + ] + }, + { + "name": "note_preimage", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "documentation": [ + " Register the preimage for an upcoming proposal. This doesn't require the proposal to be", + " in the dispatch queue but does require a deposit, returned once enacted.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `encoded_proposal`: The preimage of a proposal.", + "", + " Emits `PreimageNoted`.", + "", + " Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_preimage_operational", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "documentation": [ + " Same as `note_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "note_imminent_preimage", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "documentation": [ + " Register the preimage for an upcoming proposal. This requires the proposal to be", + " in the dispatch queue. No deposit is needed. When this call is successful, i.e.", + " the preimage has not been uploaded before and matches some imminent proposal,", + " no fee is paid.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `encoded_proposal`: The preimage of a proposal.", + "", + " Emits `PreimageNoted`.", + "", + " Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_imminent_preimage_operational", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "documentation": [ + " Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "reap_preimage", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "proposal_len_upper_bound", + "type": "Compact" + } + ], + "documentation": [ + " Remove an expired proposal preimage and collect the deposit.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `proposal_hash`: The preimage hash of a proposal.", + " - `proposal_length_upper_bound`: an upper bound on length of the proposal.", + " Extrinsic is weighted according to this value with no refund.", + "", + " This will only work after `VotingPeriod` blocks from the time that the preimage was", + " noted, if it's the same account doing it. If it's a different account, then it'll only", + " work an additional `EnactmentPeriod` later.", + "", + " Emits `PreimageReaped`.", + "", + " Weight: `O(D)` where D is length of proposal." + ] + }, + { + "name": "unlock", + "args": [ + { + "name": "target", + "type": "AccountId" + } + ], + "documentation": [ + " Unlock tokens that have an expired lock.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `target`: The account to remove the lock on.", + "", + " Weight: `O(R)` with R number of vote of target." + ] + }, + { + "name": "remove_vote", + "args": [ + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "documentation": [ + " Remove a vote for a referendum.", + "", + " If:", + " - the referendum was cancelled, or", + " - the referendum is ongoing, or", + " - the referendum has ended such that", + " - the vote of the account was in opposition to the result; or", + " - there was no conviction to the account's vote; or", + " - the account made a split vote", + " ...then the vote is removed cleanly and a following call to `unlock` may result in more", + " funds being available.", + "", + " If, however, the referendum has ended and:", + " - it finished corresponding to the vote of the account, and", + " - the account made a standard vote with conviction, and", + " - the lock period of the conviction is not over", + " ...then the lock will be aggregated into the overall account's lock, which may involve", + " *overlocking* (where the two locks are combined into a single lock that is the maximum", + " of both the amount locked and the time is it locked for).", + "", + " The dispatch origin of this call must be _Signed_, and the signer must have a vote", + " registered for referendum `index`.", + "", + " - `index`: The index of referendum of the vote to be removed.", + "", + " Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "remove_other_vote", + "args": [ + { + "name": "target", + "type": "AccountId" + }, + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "documentation": [ + " Remove a vote for a referendum.", + "", + " If the `target` is equal to the signer, then this function is exactly equivalent to", + " `remove_vote`. If not equal to the signer, then the vote must have expired,", + " either because the referendum was cancelled, because the voter lost the referendum or", + " because the conviction period is over.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `target`: The account of the vote to be removed; this account must have voted for", + " referendum `index`.", + " - `index`: The index of referendum of the vote to be removed.", + "", + " Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "enact_proposal", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "documentation": [ + " Enact a proposal from a referendum. For now we just make the weight be the maximum." + ] + }, + { + "name": "blacklist", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "maybe_ref_index", + "type": "Option" + } + ], + "documentation": [ + " Permanently place a proposal into the blacklist. This prevents it from ever being", + " proposed again.", + "", + " If called on a queued public or external proposal, then this will result in it being", + " removed. If the `ref_index` supplied is an active referendum with the proposal hash,", + " then it will be cancelled.", + "", + " The dispatch origin of this call must be `BlacklistOrigin`.", + "", + " - `proposal_hash`: The proposal hash to blacklist permanently.", + " - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be", + " cancelled.", + "", + " Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a", + " reasonable value)." + ] + }, + { + "name": "cancel_proposal", + "args": [ + { + "name": "prop_index", + "type": "Compact" + } + ], + "documentation": [ + " Remove a proposal.", + "", + " The dispatch origin of this call must be `CancelProposalOrigin`.", + "", + " - `prop_index`: The index of the proposal to cancel.", + "", + " Weight: `O(p)` where `p = PublicProps::::decode_len()`" + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "PropIndex", + "Balance" + ], + "documentation": [ + " A motion has been proposed by a public account. \\[proposal_index, deposit\\]" + ] + }, + { + "name": "Tabled", + "args": [ + "PropIndex", + "Balance", + "Vec" + ], + "documentation": [ + " A public proposal has been tabled for referendum vote. \\[proposal_index, deposit, depositors\\]" + ] + }, + { + "name": "ExternalTabled", + "args": [], + "documentation": [ + " An external proposal has been tabled." + ] + }, + { + "name": "Started", + "args": [ + "ReferendumIndex", + "VoteThreshold" + ], + "documentation": [ + " A referendum has begun. \\[ref_index, threshold\\]" + ] + }, + { + "name": "Passed", + "args": [ + "ReferendumIndex" + ], + "documentation": [ + " A proposal has been approved by referendum. \\[ref_index\\]" + ] + }, + { + "name": "NotPassed", + "args": [ + "ReferendumIndex" + ], + "documentation": [ + " A proposal has been rejected by referendum. \\[ref_index\\]" + ] + }, + { + "name": "Cancelled", + "args": [ + "ReferendumIndex" + ], + "documentation": [ + " A referendum has been cancelled. \\[ref_index\\]" + ] + }, + { + "name": "Executed", + "args": [ + "ReferendumIndex", + "bool" + ], + "documentation": [ + " A proposal has been enacted. \\[ref_index, is_ok\\]" + ] + }, + { + "name": "Delegated", + "args": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " An account has delegated their vote to another account. \\[who, target\\]" + ] + }, + { + "name": "Undelegated", + "args": [ + "AccountId" + ], + "documentation": [ + " An \\[account\\] has cancelled a previous delegation operation." + ] + }, + { + "name": "Vetoed", + "args": [ + "AccountId", + "Hash", + "BlockNumber" + ], + "documentation": [ + " An external proposal has been vetoed. \\[who, proposal_hash, until\\]" + ] + }, + { + "name": "PreimageNoted", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "documentation": [ + " A proposal's preimage was noted, and the deposit taken. \\[proposal_hash, who, deposit\\]" + ] + }, + { + "name": "PreimageUsed", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "documentation": [ + " A proposal preimage was removed and used (the deposit was returned).", + " \\[proposal_hash, provider, deposit\\]" + ] + }, + { + "name": "PreimageInvalid", + "args": [ + "Hash", + "ReferendumIndex" + ], + "documentation": [ + " A proposal could not be executed because its preimage was invalid.", + " \\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageMissing", + "args": [ + "Hash", + "ReferendumIndex" + ], + "documentation": [ + " A proposal could not be executed because its preimage was missing.", + " \\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageReaped", + "args": [ + "Hash", + "AccountId", + "Balance", + "AccountId" + ], + "documentation": [ + " A registered preimage was removed and the deposit collected by the reaper.", + " \\[proposal_hash, provider, deposit, reaper\\]" + ] + }, + { + "name": "Unlocked", + "args": [ + "AccountId" + ], + "documentation": [ + " An \\[account\\] has been unlocked successfully." + ] + }, + { + "name": "Blacklisted", + "args": [ + "Hash" + ], + "documentation": [ + " A proposal \\[hash\\] has been blacklisted permanently." + ] + } + ], + "constants": [ + { + "name": "EnactmentPeriod", + "type": "BlockNumber", + "value": "0x40380000", + "documentation": [ + " The minimum period of locking and the period between a proposal being approved and enacted.", + "", + " It should generally be a little more than the unstake period to ensure that", + " voting stakers have an opportunity to remove themselves from the system in the case where", + " they are on the losing side of a vote." + ] + }, + { + "name": "LaunchPeriod", + "type": "BlockNumber", + "value": "0x80700000", + "documentation": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "VotingPeriod", + "type": "BlockNumber", + "value": "0xc0890100", + "documentation": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "MinimumDeposit", + "type": "BalanceOf", + "value": "0x000010632d5ec76b0500000000000000", + "documentation": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "FastTrackVotingPeriod", + "type": "BlockNumber", + "value": "0x80700000", + "documentation": [ + " Minimum voting period allowed for an emergency referendum." + ] + }, + { + "name": "CooloffPeriod", + "type": "BlockNumber", + "value": "0xc0890100", + "documentation": [ + " Period in blocks where an external proposal may not be re-submitted after being vetoed." + ] + }, + { + "name": "PreimageByteDeposit", + "type": "BalanceOf", + "value": "0x0000c16ff28623000000000000000000", + "documentation": [ + " The amount of balance that must be deposited per byte of preimage stored." + ] + }, + { + "name": "MaxVotes", + "type": "u32", + "value": "0x64000000", + "documentation": [ + " The maximum number of votes for an account." + ] + } + ], + "errors": [ + { + "name": "ValueLow", + "documentation": [ + " Value too low" + ] + }, + { + "name": "ProposalMissing", + "documentation": [ + " Proposal does not exist" + ] + }, + { + "name": "BadIndex", + "documentation": [ + " Unknown index" + ] + }, + { + "name": "AlreadyCanceled", + "documentation": [ + " Cannot cancel the same proposal twice" + ] + }, + { + "name": "DuplicateProposal", + "documentation": [ + " Proposal already made" + ] + }, + { + "name": "ProposalBlacklisted", + "documentation": [ + " Proposal still blacklisted" + ] + }, + { + "name": "NotSimpleMajority", + "documentation": [ + " Next external proposal not simple majority" + ] + }, + { + "name": "InvalidHash", + "documentation": [ + " Invalid hash" + ] + }, + { + "name": "NoProposal", + "documentation": [ + " No external proposal" + ] + }, + { + "name": "AlreadyVetoed", + "documentation": [ + " Identity may not veto a proposal twice" + ] + }, + { + "name": "NotDelegated", + "documentation": [ + " Not delegated" + ] + }, + { + "name": "DuplicatePreimage", + "documentation": [ + " Preimage already noted" + ] + }, + { + "name": "NotImminent", + "documentation": [ + " Not imminent" + ] + }, + { + "name": "TooEarly", + "documentation": [ + " Too early" + ] + }, + { + "name": "Imminent", + "documentation": [ + " Imminent" + ] + }, + { + "name": "PreimageMissing", + "documentation": [ + " Preimage not found" + ] + }, + { + "name": "ReferendumInvalid", + "documentation": [ + " Vote given for invalid referendum" + ] + }, + { + "name": "PreimageInvalid", + "documentation": [ + " Invalid preimage" + ] + }, + { + "name": "NoneWaiting", + "documentation": [ + " No proposals waiting" + ] + }, + { + "name": "NotLocked", + "documentation": [ + " The target account does not have a lock." + ] + }, + { + "name": "NotExpired", + "documentation": [ + " The lock on the account to be unlocked has not yet expired." + ] + }, + { + "name": "NotVoter", + "documentation": [ + " The given account did not vote on the referendum." + ] + }, + { + "name": "NoPermission", + "documentation": [ + " The actor has no permission to conduct the action." + ] + }, + { + "name": "AlreadyDelegating", + "documentation": [ + " The account is already delegating." + ] + }, + { + "name": "Overflow", + "documentation": [ + " An unexpected integer overflow occurred." + ] + }, + { + "name": "Underflow", + "documentation": [ + " An unexpected integer underflow occurred." + ] + }, + { + "name": "InsufficientFunds", + "documentation": [ + " Too high a balance was provided that the account cannot afford." + ] + }, + { + "name": "NotDelegating", + "documentation": [ + " The account is not currently delegating." + ] + }, + { + "name": "VotesExist", + "documentation": [ + " The account currently has votes attached to it and the operation cannot succeed until", + " these are removed, either through `unvote` or `reap_vote`." + ] + }, + { + "name": "InstantNotAllowed", + "documentation": [ + " The instant referendum origin is currently disallowed." + ] + }, + { + "name": "Nonsense", + "documentation": [ + " Delegation to oneself makes no sense." + ] + }, + { + "name": "WrongUpperBound", + "documentation": [ + " Invalid upper bound." + ] + }, + { + "name": "MaxVotesReached", + "documentation": [ + " Maximum number of votes reached." + ] + }, + { + "name": "InvalidWitness", + "documentation": [ + " The provided witness data is wrong." + ] + }, + { + "name": "TooManyProposals", + "documentation": [ + " Maximum number of proposals reached." + ] + } + ], + "index": 10 + }, + { + "name": "Council", + "storage": { + "prefix": "Instance1Collective", + "items": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The hashes of the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Proposal", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Votes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Votes on a given proposal, if it is ongoing." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "documentation": [ + " Proposals so far." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The current members of the collective. This is stored sorted (just by value)." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": "AccountId" + }, + "fallback": "0x00", + "documentation": [ + " The prime member that helps determine the default vote behavior in case of absentations." + ] + } + ] + }, + "calls": [ + { + "name": "set_members", + "args": [ + { + "name": "new_members", + "type": "Vec" + }, + { + "name": "prime", + "type": "Option" + }, + { + "name": "old_count", + "type": "MemberCount" + } + ], + "documentation": [ + " Set the collective's membership.", + "", + " - `new_members`: The new member list. Be nice to the chain and provide it sorted.", + " - `prime`: The prime member whose vote sets the default.", + " - `old_count`: The upper bound for the previous number of members in storage.", + " Used for weight estimation.", + "", + " Requires root origin.", + "", + " NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but", + " the weight estimations rely on it to estimate dispatchable weight.", + "", + " # ", + " ## Weight", + " - `O(MP + N)` where:", + " - `M` old-members-count (code- and governance-bounded)", + " - `N` new-members-count (code- and governance-bounded)", + " - `P` proposals-count (code-bounded)", + " - DB:", + " - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members", + " - 1 storage read (codec `O(P)`) for reading the proposals", + " - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal", + " - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one", + " # " + ] + }, + { + "name": "execute", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "documentation": [ + " Dispatch a proposal from a member using the `Member` origin.", + "", + " Origin must be a member of the collective.", + "", + " # ", + " ## Weight", + " - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`", + " - DB: 1 read (codec `O(M)`) + DB access of `proposal`", + " - 1 event", + " # " + ] + }, + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "documentation": [ + " Add a new proposal to either be voted on or executed directly.", + "", + " Requires the sender to be member.", + "", + " `threshold` determines whether `proposal` is executed directly (`threshold < 2`)", + " or put up for voting.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1)` or `O(B + M + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - branching is influenced by `threshold` where:", + " - `P1` is proposal execution complexity (`threshold < 2`)", + " - `P2` is proposals-count (code-bounded) (`threshold >= 2`)", + " - DB:", + " - 1 storage read `is_member` (codec `O(M)`)", + " - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)", + " - DB accesses influenced by `threshold`:", + " - EITHER storage accesses done by `proposal` (`threshold < 2`)", + " - OR proposal insertion (`threshold <= 2`)", + " - 1 storage mutation `Proposals` (codec `O(P2)`)", + " - 1 storage mutation `ProposalCount` (codec `O(1)`)", + " - 1 storage write `ProposalOf` (codec `O(B)`)", + " - 1 storage write `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "documentation": [ + " Add an aye or nay vote for the sender to the given proposal.", + "", + " Requires the sender to be a member.", + "", + " Transaction fees will be waived if the member is voting on any particular proposal", + " for the first time and the call is successful. Subsequent vote changes will charge a fee.", + " # ", + " ## Weight", + " - `O(M)` where `M` is members-count (code- and governance-bounded)", + " - DB:", + " - 1 storage read `Members` (codec `O(M)`)", + " - 1 storage mutation `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "close", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "proposal_weight_bound", + "type": "Compact" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "documentation": [ + " Close a vote that is either approved, disapproved or whose voting period has ended.", + "", + " May be called by any signed account in order to finish voting and close the proposal.", + "", + " If called before the end of the voting period it will only close the vote if it is", + " has enough votes to be approved or disapproved.", + "", + " If called after the end of the voting period abstentions are counted as rejections", + " unless there is a prime member set and the prime member cast an approval.", + "", + " If the close operation completes successfully with disapproval, the transaction fee will", + " be waived. Otherwise execution of the approved operation will be charged to the caller.", + "", + " + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal.", + " + `length_bound`: The upper bound for the length of the proposal in storage. Checked via", + " `storage::read` so it is `size_of::() == 4` larger than the pure length.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1 + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - `P1` is the complexity of `proposal` preimage.", + " - `P2` is proposal-count (code-bounded)", + " - DB:", + " - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)", + " - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)", + " - any mutations done while executing `proposal` (`P1`)", + " - up to 3 events", + " # " + ] + }, + { + "name": "disapprove_proposal", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "documentation": [ + " Disapprove a proposal, close, and remove it from the system, regardless of its current state.", + "", + " Must be called by the Root origin.", + "", + " Parameters:", + " * `proposal_hash`: The hash of the proposal that should be disapproved.", + "", + " # ", + " Complexity: O(P) where P is the number of max proposals", + " DB Weight:", + " * Reads: Proposals", + " * Writes: Voting, Proposals, ProposalOf", + " # " + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "MemberCount" + ], + "documentation": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given", + " `MemberCount`).", + " \\[account, proposal_index, proposal_hash, threshold\\]" + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "MemberCount", + "MemberCount" + ], + "documentation": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given respectively as `MemberCount`).", + " \\[account, proposal_hash, voted, yes, no\\]" + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "documentation": [ + " A motion was approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "documentation": [ + " A motion was not approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "DispatchResult" + ], + "documentation": [ + " A motion was executed; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "MemberExecuted", + "args": [ + "Hash", + "DispatchResult" + ], + "documentation": [ + " A single member did some action; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "Closed", + "args": [ + "Hash", + "MemberCount", + "MemberCount" + ], + "documentation": [ + " A proposal was closed because its threshold was reached or after its duration was up.", + " \\[proposal_hash, yes, no\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "NotMember", + "documentation": [ + " Account is not a member" + ] + }, + { + "name": "DuplicateProposal", + "documentation": [ + " Duplicate proposals not allowed" + ] + }, + { + "name": "ProposalMissing", + "documentation": [ + " Proposal must exist" + ] + }, + { + "name": "WrongIndex", + "documentation": [ + " Mismatched index" + ] + }, + { + "name": "DuplicateVote", + "documentation": [ + " Duplicate vote ignored" + ] + }, + { + "name": "AlreadyInitialized", + "documentation": [ + " Members are already initialized!" + ] + }, + { + "name": "TooEarly", + "documentation": [ + " The close call was made too early, before the end of the voting." + ] + }, + { + "name": "TooManyProposals", + "documentation": [ + " There can only be a maximum of `MaxProposals` active proposals." + ] + }, + { + "name": "WrongProposalWeight", + "documentation": [ + " The given weight bound for the proposal was too low." + ] + }, + { + "name": "WrongProposalLength", + "documentation": [ + " The given length bound for the proposal was too low." + ] + } + ], + "index": 11 + }, + { + "name": "Elections", + "storage": { + "prefix": "PhragmenElection", + "items": [ + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": "Vec<(AccountId,BalanceOf)>" + }, + "fallback": "0x00", + "documentation": [ + " The current elected membership. Sorted based on account id." + ] + }, + { + "name": "RunnersUp", + "modifier": "Default", + "type": { + "plain": "Vec<(AccountId,BalanceOf)>" + }, + "fallback": "0x00", + "documentation": [ + " The current runners_up. Sorted based on low to high merit (worse to best)." + ] + }, + { + "name": "ElectionRounds", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "documentation": [ + " The total number of vote rounds that have happened, excluding the upcoming one." + ] + }, + { + "name": "Voting", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(BalanceOf,Vec)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "documentation": [ + " Votes and locked stake of a particular voter.", + "", + " TWOX-NOTE: SAFE as `AccountId` is a crypto hash" + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The present candidate list. Sorted based on account-id. A current member or runner-up", + " can never enter this vector and is always implicitly assumed to be a candidate." + ] + } + ] + }, + "calls": [ + { + "name": "vote", + "args": [ + { + "name": "votes", + "type": "Vec" + }, + { + "name": "value", + "type": "Compact" + } + ], + "documentation": [ + " Vote for a set of candidates for the upcoming round of election. This can be called to", + " set the initial votes, or update already existing votes.", + "", + " Upon initial voting, `value` units of `who`'s balance is locked and a bond amount is", + " reserved.", + "", + " The `votes` should:", + " - not be empty.", + " - be less than the number of possible candidates. Note that all current members and", + " runners-up are also automatically candidates for the next round.", + "", + " It is the responsibility of the caller to not place all of their balance into the lock", + " and keep some for further transactions.", + "", + " # ", + " Base weight: 47.93 µs", + " State reads:", + " \t- Candidates.len() + Members.len() + RunnersUp.len()", + " \t- Voting (is_voter)", + " \t- Lock", + " \t- [AccountBalance(who) (unreserve + total_balance)]", + " State writes:", + " \t- Voting", + " \t- Lock", + " \t- [AccountBalance(who) (unreserve -- only when creating a new voter)]", + " # " + ] + }, + { + "name": "remove_voter", + "args": [], + "documentation": [ + " Remove `origin` as a voter. This removes the lock and returns the bond.", + "", + " # ", + " Base weight: 36.8 µs", + " All state access is from do_remove_voter.", + " State reads:", + " \t- Voting", + " \t- [AccountData(who)]", + " State writes:", + " \t- Voting", + " \t- Locks", + " \t- [AccountData(who)]", + " # " + ] + }, + { + "name": "report_defunct_voter", + "args": [ + { + "name": "defunct", + "type": "DefunctVoter" + } + ], + "documentation": [ + " Report `target` for being an defunct voter. In case of a valid report, the reporter is", + " rewarded by the bond amount of `target`. Otherwise, the reporter itself is removed and", + " their bond is slashed.", + "", + " A defunct voter is defined to be:", + " - a voter whose current submitted votes are all invalid. i.e. all of them are no", + " longer a candidate nor an active member or a runner-up.", + "", + "", + " The origin must provide the number of current candidates and votes of the reported target", + " for the purpose of accurate weight calculation.", + "", + " # ", + " No Base weight based on min square analysis.", + " Complexity of candidate_count: 1.755 µs", + " Complexity of vote_count: 18.51 µs", + " State reads:", + " \t- Voting(reporter)", + " \t- Candidate.len()", + " \t- Voting(Target)", + " \t- Candidates, Members, RunnersUp (is_defunct_voter)", + " State writes:", + " \t- Lock(reporter || target)", + " \t- [AccountBalance(reporter)] + AccountBalance(target)", + " \t- Voting(reporter || target)", + " Note: the db access is worse with respect to db, which is when the report is correct.", + " # " + ] + }, + { + "name": "submit_candidacy", + "args": [ + { + "name": "candidate_count", + "type": "Compact" + } + ], + "documentation": [ + " Submit oneself for candidacy.", + "", + " A candidate will either:", + " - Lose at the end of the term and forfeit their deposit.", + " - Win and become a member. Members will eventually get their stash back.", + " - Become a runner-up. Runners-ups are reserved members in case one gets forcefully", + " removed.", + "", + " # ", + " Base weight = 33.33 µs", + " Complexity of candidate_count: 0.375 µs", + " State reads:", + " \t- Candidates", + " \t- Members", + " \t- RunnersUp", + " \t- [AccountBalance(who)]", + " State writes:", + " \t- [AccountBalance(who)]", + " \t- Candidates", + " # " + ] + }, + { + "name": "renounce_candidacy", + "args": [ + { + "name": "renouncing", + "type": "Renouncing" + } + ], + "documentation": [ + " Renounce one's intention to be a candidate for the next election round. 3 potential", + " outcomes exist:", + " - `origin` is a candidate and not elected in any set. In this case, the bond is", + " unreserved, returned and origin is removed as a candidate.", + " - `origin` is a current runner-up. In this case, the bond is unreserved, returned and", + " origin is removed as a runner-up.", + " - `origin` is a current member. In this case, the bond is unreserved and origin is", + " removed as a member, consequently not being a candidate for the next round anymore.", + " Similar to [`remove_voter`], if replacement runners exists, they are immediately used.", + " ", + " If a candidate is renouncing:", + " \tBase weight: 17.28 µs", + " \tComplexity of candidate_count: 0.235 µs", + " \tState reads:", + " \t\t- Candidates", + " \t\t- [AccountBalance(who) (unreserve)]", + " \tState writes:", + " \t\t- Candidates", + " \t\t- [AccountBalance(who) (unreserve)]", + " If member is renouncing:", + " \tBase weight: 46.25 µs", + " \tState reads:", + " \t\t- Members, RunnersUp (remove_and_replace_member),", + " \t\t- [AccountData(who) (unreserve)]", + " \tState writes:", + " \t\t- Members, RunnersUp (remove_and_replace_member),", + " \t\t- [AccountData(who) (unreserve)]", + " If runner is renouncing:", + " \tBase weight: 46.25 µs", + " \tState reads:", + " \t\t- RunnersUp (remove_and_replace_member),", + " \t\t- [AccountData(who) (unreserve)]", + " \tState writes:", + " \t\t- RunnersUp (remove_and_replace_member),", + " \t\t- [AccountData(who) (unreserve)]", + " " + ] + }, + { + "name": "remove_member", + "args": [ + { + "name": "who", + "type": "LookupSource" + }, + { + "name": "has_replacement", + "type": "bool" + } + ], + "documentation": [ + " Remove a particular member from the set. This is effective immediately and the bond of", + " the outgoing member is slashed.", + "", + " If a runner-up is available, then the best runner-up will be removed and replaces the", + " outgoing member. Otherwise, a new phragmen election is started.", + "", + " Note that this does not affect the designated block number of the next election.", + "", + " # ", + " If we have a replacement:", + " \t- Base weight: 50.93 µs", + " \t- State reads:", + " \t\t- RunnersUp.len()", + " \t\t- Members, RunnersUp (remove_and_replace_member)", + " \t- State writes:", + " \t\t- Members, RunnersUp (remove_and_replace_member)", + " Else, since this is a root call and will go into phragmen, we assume full block for now.", + " # " + ] + } + ], + "events": [ + { + "name": "NewTerm", + "args": [ + "Vec<(AccountId,Balance)>" + ], + "documentation": [ + " A new term with \\[new_members\\]. This indicates that enough candidates existed to run the", + " election, not that enough have has been elected. The inner value must be examined for", + " this purpose. A `NewTerm(\\[\\])` indicates that some candidates got their bond slashed and", + " none were elected, whilst `EmptyTerm` means that no candidates existed to begin with." + ] + }, + { + "name": "EmptyTerm", + "args": [], + "documentation": [ + " No (or not enough) candidates existed for this round. This is different from", + " `NewTerm(\\[\\])`. See the description of `NewTerm`." + ] + }, + { + "name": "ElectionError", + "args": [], + "documentation": [ + " Internal error happened while trying to perform election." + ] + }, + { + "name": "MemberKicked", + "args": [ + "AccountId" + ], + "documentation": [ + " A \\[member\\] has been removed. This should always be followed by either `NewTerm` or", + " `EmptyTerm`." + ] + }, + { + "name": "CandidateSlashed", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A candidate was slashed due to failing to obtain a seat as member or runner-up" + ] + }, + { + "name": "SeatHolderSlashed", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A seat holder (member or runner-up) was slashed due to failing to retaining their position." + ] + }, + { + "name": "MemberRenounced", + "args": [ + "AccountId" + ], + "documentation": [ + " A \\[member\\] has renounced their candidacy." + ] + }, + { + "name": "VoterReported", + "args": [ + "AccountId", + "AccountId", + "bool" + ], + "documentation": [ + " A voter was reported with the the report being successful or not.", + " \\[voter, reporter, success\\]" + ] + } + ], + "constants": [ + { + "name": "CandidacyBond", + "type": "BalanceOf", + "value": "0x0000a0dec5adc9353600000000000000", + "documentation": [] + }, + { + "name": "VotingBond", + "type": "BalanceOf", + "value": "0x0000e8890423c78a0000000000000000", + "documentation": [] + }, + { + "name": "DesiredMembers", + "type": "u32", + "value": "0x0d000000", + "documentation": [] + }, + { + "name": "DesiredRunnersUp", + "type": "u32", + "value": "0x07000000", + "documentation": [] + }, + { + "name": "TermDuration", + "type": "BlockNumber", + "value": "0x00270600", + "documentation": [] + }, + { + "name": "ModuleId", + "type": "LockIdentifier", + "value": "0x706872656c656374", + "documentation": [] + } + ], + "errors": [ + { + "name": "UnableToVote", + "documentation": [ + " Cannot vote when no candidates or members exist." + ] + }, + { + "name": "NoVotes", + "documentation": [ + " Must vote for at least one candidate." + ] + }, + { + "name": "TooManyVotes", + "documentation": [ + " Cannot vote more than candidates." + ] + }, + { + "name": "MaximumVotesExceeded", + "documentation": [ + " Cannot vote more than maximum allowed." + ] + }, + { + "name": "LowBalance", + "documentation": [ + " Cannot vote with stake less than minimum balance." + ] + }, + { + "name": "UnableToPayBond", + "documentation": [ + " Voter can not pay voting bond." + ] + }, + { + "name": "MustBeVoter", + "documentation": [ + " Must be a voter." + ] + }, + { + "name": "ReportSelf", + "documentation": [ + " Cannot report self." + ] + }, + { + "name": "DuplicatedCandidate", + "documentation": [ + " Duplicated candidate submission." + ] + }, + { + "name": "MemberSubmit", + "documentation": [ + " Member cannot re-submit candidacy." + ] + }, + { + "name": "RunnerSubmit", + "documentation": [ + " Runner cannot re-submit candidacy." + ] + }, + { + "name": "InsufficientCandidateFunds", + "documentation": [ + " Candidate does not have enough funds." + ] + }, + { + "name": "NotMember", + "documentation": [ + " Not a member." + ] + }, + { + "name": "InvalidCandidateCount", + "documentation": [ + " The provided count of number of candidates is incorrect." + ] + }, + { + "name": "InvalidVoteCount", + "documentation": [ + " The provided count of number of votes is incorrect." + ] + }, + { + "name": "InvalidRenouncing", + "documentation": [ + " The renouncing origin presented a wrong `Renouncing` parameter." + ] + }, + { + "name": "InvalidReplacement", + "documentation": [ + " Prediction regarding replacement after member removal is wrong." + ] + } + ], + "index": 12 + }, + { + "name": "Grandpa", + "storage": { + "prefix": "GrandpaFinality", + "items": [ + { + "name": "State", + "modifier": "Default", + "type": { + "plain": "StoredState" + }, + "fallback": "0x00", + "documentation": [ + " State of the current authority set." + ] + }, + { + "name": "PendingChange", + "modifier": "Optional", + "type": { + "plain": "StoredPendingChange" + }, + "fallback": "0x00", + "documentation": [ + " Pending change: (signaled at, scheduled change)." + ] + }, + { + "name": "NextForced", + "modifier": "Optional", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00", + "documentation": [ + " next block number where we can force a change." + ] + }, + { + "name": "Stalled", + "modifier": "Optional", + "type": { + "plain": "(BlockNumber,BlockNumber)" + }, + "fallback": "0x00", + "documentation": [ + " `true` if we are currently stalled." + ] + }, + { + "name": "CurrentSetId", + "modifier": "Default", + "type": { + "plain": "SetId" + }, + "fallback": "0x0000000000000000", + "documentation": [ + " The number of changes (both in terms of keys and underlying economic responsibilities)", + " in the \"set\" of Grandpa validators from genesis." + ] + }, + { + "name": "SetIdSession", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "SetId", + "value": "SessionIndex", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " A mapping from grandpa set ID to the index of the *most recent* session for which its", + " members were responsible.", + "", + " TWOX-NOTE: `SetId` is not under user control." + ] + } + ] + }, + "calls": [ + { + "name": "report_equivocation", + "args": [ + { + "name": "equivocation_proof", + "type": "GrandpaEquivocationProof" + }, + { + "name": "key_owner_proof", + "type": "KeyOwnerProof" + } + ], + "documentation": [ + " Report voter equivocation/misbehavior. This method will verify the", + " equivocation proof and validate the given key ownership proof", + " against the extracted offender. If both are valid, the offence", + " will be reported." + ] + }, + { + "name": "report_equivocation_unsigned", + "args": [ + { + "name": "equivocation_proof", + "type": "GrandpaEquivocationProof" + }, + { + "name": "key_owner_proof", + "type": "KeyOwnerProof" + } + ], + "documentation": [ + " Report voter equivocation/misbehavior. This method will verify the", + " equivocation proof and validate the given key ownership proof", + " against the extracted offender. If both are valid, the offence", + " will be reported.", + "", + " This extrinsic must be called unsigned and it is expected that only", + " block authors will call it (validated in `ValidateUnsigned`), as such", + " if the block author is defined it will be defined as the equivocation", + " reporter." + ] + }, + { + "name": "note_stalled", + "args": [ + { + "name": "delay", + "type": "BlockNumber" + }, + { + "name": "best_finalized_block_number", + "type": "BlockNumber" + } + ], + "documentation": [ + " Note that the current authority set of the GRANDPA finality gadget has", + " stalled. This will trigger a forced authority set change at the beginning", + " of the next session, to be enacted `delay` blocks after that. The delay", + " should be high enough to safely assume that the block signalling the", + " forced change will not be re-orged (e.g. 1000 blocks). The GRANDPA voters", + " will start the new authority set using the given finalized block as base.", + " Only callable by root." + ] + } + ], + "events": [ + { + "name": "NewAuthorities", + "args": [ + "AuthorityList" + ], + "documentation": [ + " New authority set has been applied. \\[authority_set\\]" + ] + }, + { + "name": "Paused", + "args": [], + "documentation": [ + " Current authority set has been paused." + ] + }, + { + "name": "Resumed", + "args": [], + "documentation": [ + " Current authority set has been resumed." + ] + } + ], + "constants": [], + "errors": [ + { + "name": "PauseFailed", + "documentation": [ + " Attempt to signal GRANDPA pause when the authority set isn't live", + " (either paused or already pending pause)." + ] + }, + { + "name": "ResumeFailed", + "documentation": [ + " Attempt to signal GRANDPA resume when the authority set isn't paused", + " (either live or already pending resume)." + ] + }, + { + "name": "ChangePending", + "documentation": [ + " Attempt to signal GRANDPA change with one already pending." + ] + }, + { + "name": "TooSoon", + "documentation": [ + " Cannot signal forced change so soon after last." + ] + }, + { + "name": "InvalidKeyOwnershipProof", + "documentation": [ + " A key ownership proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "InvalidEquivocationProof", + "documentation": [ + " An equivocation proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "DuplicateOffenceReport", + "documentation": [ + " A given equivocation report is valid but already previously reported." + ] + } + ], + "index": 14 + }, + { + "name": "Treasury", + "storage": { + "prefix": "Treasury", + "items": [ + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": "ProposalIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ProposalIndex", + "value": "TreasuryProposal", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + }, + "calls": [ + { + "name": "propose_spend", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "LookupSource" + } + ], + "documentation": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded.", + "", + " # ", + " - Complexity: O(1)", + " - DbReads: `ProposalCount`, `origin account`", + " - DbWrites: `ProposalCount`, `Proposals`, `origin account`", + " # " + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Reject a proposed spend. The original deposit will be slashed.", + "", + " May only be called from `T::RejectOrigin`.", + "", + " # ", + " - Complexity: O(1)", + " - DbReads: `Proposals`, `rejected proposer account`", + " - DbWrites: `Proposals`, `rejected proposer account`", + " # " + ] + }, + { + "name": "approve_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "documentation": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned.", + "", + " May only be called from `T::ApproveOrigin`.", + "", + " # ", + " - Complexity: O(1).", + " - DbReads: `Proposals`, `Approvals`", + " - DbWrite: `Approvals`", + " # " + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "ProposalIndex" + ], + "documentation": [ + " New proposal. \\[proposal_index\\]" + ] + }, + { + "name": "Spending", + "args": [ + "Balance" + ], + "documentation": [ + " We have ended a spend period and will now allocate funds. \\[budget_remaining\\]" + ] + }, + { + "name": "Awarded", + "args": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "documentation": [ + " Some funds have been allocated. \\[proposal_index, award, beneficiary\\]" + ] + }, + { + "name": "Rejected", + "args": [ + "ProposalIndex", + "Balance" + ], + "documentation": [ + " A proposal was rejected; funds were slashed. \\[proposal_index, slashed\\]" + ] + }, + { + "name": "Burnt", + "args": [ + "Balance" + ], + "documentation": [ + " Some of our funds have been burnt. \\[burn\\]" + ] + }, + { + "name": "Rollover", + "args": [ + "Balance" + ], + "documentation": [ + " Spending has finished; this is the amount that rolls over until next spend.", + " \\[budget_remaining\\]" + ] + }, + { + "name": "Deposit", + "args": [ + "Balance" + ], + "documentation": [ + " Some funds have been deposited. \\[deposit\\]" + ] + } + ], + "constants": [ + { + "name": "ProposalBond", + "type": "Permill", + "value": "0x50c30000", + "documentation": [ + " Fraction of a proposal's value that should be bonded in order to place the proposal.", + " An accepted proposal gets these back. A rejected proposal does not." + ] + }, + { + "name": "ProposalBondMinimum", + "type": "BalanceOf", + "value": "0x0000a0dec5adc9353600000000000000", + "documentation": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "type": "BlockNumber", + "value": "0xc0a80000", + "documentation": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "type": "Permill", + "value": "0x00000000", + "documentation": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "ModuleId", + "type": "ModuleId", + "value": "0x70792f7472737279", + "documentation": [ + " The treasury's module id, used for deriving its sovereign account ID." + ] + } + ], + "errors": [ + { + "name": "InsufficientProposersBalance", + "documentation": [ + " Proposer's balance is too low." + ] + }, + { + "name": "InvalidIndex", + "documentation": [ + " No proposal or bounty at that index." + ] + } + ], + "index": 15 + }, + { + "name": "Contracts", + "storage": { + "prefix": "Contracts", + "items": [ + { + "name": "CurrentSchedule", + "modifier": "Default", + "type": { + "plain": "Schedule" + }, + "fallback": "0x000000000004000000000200000001000080000000100000000010000000010000200000000000080068060000f96a020078750300dd150000af1b0000240b0000a0140000652700007600000081770100cde1020023070000360600007e070000f60500004b180000c927000022070000258386089a070000d20700007c0a0000380700009807000042070000aa07000068090000b3080000be08000002090000db0800000b090000790800006e0800003e09000017090000de08000012090000f80800006e1f0000292200004c1f0000771c0000cc080000ea080000d6080000020900001e0900001609000052090000d7080000d4903b000000000082653b000000000026a93a0000000000e25883000000000068b23a000000000034e63a000000000074b53a000000000022ef8900000000000c693a0000000000906c3a00000000003c4c6d00000000000ef21d000000000010987f00000000000701000000000000b038530000000000fe0200000000000020f8e2220000000080296424000000003441d10900000000e8fd9600000000007051d900000000005618e90700000000980900000000000064b1a30000000000e84eef10000000008408000000000000a256a20a0000000012ac2b0200000000eb0500000000000016154c0b000000005ec5e307000000002009c20a000000004a02000000000000270300000000000098fad61d0000000050020000000000003203000000000000d90a00000000000094b63200000000009510000000000000fcc43400000000001e0d00000000000086df3000000000001a0600000000000078063100000000001906000000000000", + "documentation": [ + " Current cost schedule for contracts." + ] + }, + { + "name": "PristineCode", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "CodeHash", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " A mapping from an original code hash to the original code, untouched by instrumentation." + ] + }, + { + "name": "CodeStorage", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "CodeHash", + "value": "PrefabWasmModule", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " A mapping between an original code hash and instrumented wasm code, ready for execution." + ] + }, + { + "name": "AccountCounter", + "modifier": "Default", + "type": { + "plain": "u64" + }, + "fallback": "0x0000000000000000", + "documentation": [ + " The subtrie counter." + ] + }, + { + "name": "ContractInfoOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "ContractInfo", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The code associated with a given account.", + "", + " TWOX-NOTE: SAFE since `AccountId` is a secure hash." + ] + }, + { + "name": "DeletionQueue", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Evicted contracts that await child trie deletion.", + "", + " Child trie deletion is a heavy operation depending on the amount of storage items", + " stored in said trie. Therefore this operation is performed lazily in `on_initialize`." + ] + } + ] + }, + "calls": [ + { + "name": "update_schedule", + "args": [ + { + "name": "schedule", + "type": "Schedule" + } + ], + "documentation": [ + " Updates the schedule for metering contracts.", + "", + " The schedule must have a greater version than the stored schedule." + ] + }, + { + "name": "put_code", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "documentation": [ + " Stores the given binary Wasm code into the chain's storage and returns its `codehash`.", + " You can instantiate contracts only with stored code." + ] + }, + { + "name": "call", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "data", + "type": "Bytes" + } + ], + "documentation": [ + " Makes a call to an account, optionally transferring some balance.", + "", + " * If the account is a smart-contract account, the associated code will be", + " executed and any value will be transferred.", + " * If the account is a regular account, any value will be transferred.", + " * If no account exists and the call value is not less than `existential_deposit`,", + " a regular account will be created and any value will be transferred." + ] + }, + { + "name": "instantiate", + "args": [ + { + "name": "endowment", + "type": "Compact" + }, + { + "name": "gas_limit", + "type": "Compact" + }, + { + "name": "code_hash", + "type": "CodeHash" + }, + { + "name": "data", + "type": "Bytes" + }, + { + "name": "salt", + "type": "Bytes" + } + ], + "documentation": [ + " Instantiates a new contract from the `code_hash` generated by `put_code`,", + " optionally transferring some balance.", + "", + " The supplied `salt` is used for contract address deriviation. See `fn contract_address`.", + "", + " Instantiation is executed as follows:", + "", + " - The destination address is computed based on the sender, code_hash and the salt.", + " - The smart-contract account is created at the computed address.", + " - The `ctor_code` is executed in the context of the newly-created account. Buffer returned", + " after the execution is saved as the `code` of the account. That code will be invoked", + " upon any call received by this account.", + " - The contract is initialized." + ] + }, + { + "name": "claim_surcharge", + "args": [ + { + "name": "dest", + "type": "AccountId" + }, + { + "name": "aux_sender", + "type": "Option" + } + ], + "documentation": [ + " Allows block producers to claim a small reward for evicting a contract. If a block producer", + " fails to do so, a regular users will be allowed to claim the reward.", + "", + " If contract is not evicted as a result of this call, [`Error::ContractNotEvictable`]", + " is returned and the sender is not eligible for the reward." + ] + } + ], + "events": [ + { + "name": "Instantiated", + "args": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " Contract deployed by address at the specified address. \\[owner, contract\\]" + ] + }, + { + "name": "Evicted", + "args": [ + "AccountId", + "bool" + ], + "documentation": [ + " Contract has been evicted and is now in tombstone state.", + " \\[contract, tombstone\\]", + "", + " # Params", + "", + " - `contract`: `AccountId`: The account ID of the evicted contract.", + " - `tombstone`: `bool`: True if the evicted contract left behind a tombstone." + ] + }, + { + "name": "Restored", + "args": [ + "AccountId", + "AccountId", + "Hash", + "Balance" + ], + "documentation": [ + " Restoration for a contract has been successful.", + " \\[donor, dest, code_hash, rent_allowance\\]", + "", + " # Params", + "", + " - `donor`: `AccountId`: Account ID of the restoring contract", + " - `dest`: `AccountId`: Account ID of the restored contract", + " - `code_hash`: `Hash`: Code hash of the restored contract", + " - `rent_allowance: `Balance`: Rent allowance of the restored contract" + ] + }, + { + "name": "CodeStored", + "args": [ + "Hash" + ], + "documentation": [ + " Code with the specified hash has been stored.", + " \\[code_hash\\]" + ] + }, + { + "name": "ScheduleUpdated", + "args": [ + "u32" + ], + "documentation": [ + " Triggered when the current \\[schedule\\] is updated." + ] + }, + { + "name": "ContractExecution", + "args": [ + "AccountId", + "Bytes" + ], + "documentation": [ + " An event deposited upon execution of a contract from the account.", + " \\[account, data\\]" + ] + } + ], + "constants": [ + { + "name": "SignedClaimHandicap", + "type": "BlockNumber", + "value": "0x02000000", + "documentation": [ + " Number of block delay an extrinsic claim surcharge has.", + "", + " When claim surcharge is called by an extrinsic the rent is checked", + " for current_block - delay" + ] + }, + { + "name": "TombstoneDeposit", + "type": "BalanceOf", + "value": "0x00005f0a3b3c5b4b0000000000000000", + "documentation": [ + " The minimum amount required to generate a tombstone." + ] + }, + { + "name": "DepositPerContract", + "type": "BalanceOf", + "value": "0x00005f0a3b3c5b4b0000000000000000", + "documentation": [ + " The balance every contract needs to deposit to stay alive indefinitely.", + "", + " This is different from the [`Self::TombstoneDeposit`] because this only needs to be", + " deposited while the contract is alive. Costs for additional storage are added to", + " this base cost.", + "", + " This is a simple way to ensure that contracts with empty storage eventually get deleted by", + " making them pay rent. This creates an incentive to remove them early in order to save rent." + ] + }, + { + "name": "DepositPerStorageByte", + "type": "BalanceOf", + "value": "0x0000869eae29d5000000000000000000", + "documentation": [ + " The balance a contract needs to deposit per storage byte to stay alive indefinitely.", + "", + " Let's suppose the deposit is 1,000 BU (balance units)/byte and the rent is 1 BU/byte/day,", + " then a contract with 1,000,000 BU that uses 1,000 bytes of storage would pay no rent.", + " But if the balance reduced to 500,000 BU and the storage stayed the same at 1,000,", + " then it would pay 500 BU/day." + ] + }, + { + "name": "DepositPerStorageItem", + "type": "BalanceOf", + "value": "0x00004f8c34e814020000000000000000", + "documentation": [ + " The balance a contract needs to deposit per storage item to stay alive indefinitely.", + "", + " It works the same as [`Self::DepositPerStorageByte`] but for storage items." + ] + }, + { + "name": "RentFraction", + "type": "Perbill", + "value": "0x0a090000", + "documentation": [ + " The fraction of the deposit that should be used as rent per block.", + "", + " When a contract hasn't enough balance deposited to stay alive indefinitely it needs", + " to pay per block for the storage it consumes that is not covered by the deposit.", + " This determines how high this rent payment is per block as a fraction of the deposit." + ] + }, + { + "name": "SurchargeReward", + "type": "BalanceOf", + "value": "0x00c029f73d5405000000000000000000", + "documentation": [ + " Reward that is received by the party whose touch has led", + " to removal of a contract." + ] + }, + { + "name": "MaxDepth", + "type": "u32", + "value": "0x20000000", + "documentation": [ + " The maximum nesting level of a call/instantiate stack. A reasonable default", + " value is 100." + ] + }, + { + "name": "MaxValueSize", + "type": "u32", + "value": "0x00400000", + "documentation": [ + " The maximum size of a storage value in bytes. A reasonable default is 16 KiB." + ] + }, + { + "name": "DeletionQueueDepth", + "type": "u32", + "value": "0x26010000", + "documentation": [ + " The maximum number of tries that can be queued for deletion." + ] + }, + { + "name": "DeletionWeightLimit", + "type": "Weight", + "value": "0x00d0ed902e000000", + "documentation": [ + " The maximum amount of weight that can be consumed per block for lazy trie removal." + ] + } + ], + "errors": [ + { + "name": "InvalidScheduleVersion", + "documentation": [ + " A new schedule must have a greater version than the current one." + ] + }, + { + "name": "InvalidSurchargeClaim", + "documentation": [ + " An origin must be signed or inherent and auxiliary sender only provided on inherent." + ] + }, + { + "name": "InvalidSourceContract", + "documentation": [ + " Cannot restore from nonexisting or tombstone contract." + ] + }, + { + "name": "InvalidDestinationContract", + "documentation": [ + " Cannot restore to nonexisting or alive contract." + ] + }, + { + "name": "InvalidTombstone", + "documentation": [ + " Tombstones don't match." + ] + }, + { + "name": "InvalidContractOrigin", + "documentation": [ + " An origin TrieId written in the current block." + ] + }, + { + "name": "OutOfGas", + "documentation": [ + " The executed contract exhausted its gas limit." + ] + }, + { + "name": "OutputBufferTooSmall", + "documentation": [ + " The output buffer supplied to a contract API call was too small." + ] + }, + { + "name": "BelowSubsistenceThreshold", + "documentation": [ + " Performing the requested transfer would have brought the contract below", + " the subsistence threshold. No transfer is allowed to do this in order to allow", + " for a tombstone to be created. Use `seal_terminate` to remove a contract without", + " leaving a tombstone behind." + ] + }, + { + "name": "NewContractNotFunded", + "documentation": [ + " The newly created contract is below the subsistence threshold after executing", + " its contructor. No contracts are allowed to exist below that threshold." + ] + }, + { + "name": "TransferFailed", + "documentation": [ + " Performing the requested transfer failed for a reason originating in the", + " chosen currency implementation of the runtime. Most probably the balance is", + " too low or locks are placed on it." + ] + }, + { + "name": "MaxCallDepthReached", + "documentation": [ + " Performing a call was denied because the calling depth reached the limit", + " of what is specified in the schedule." + ] + }, + { + "name": "NotCallable", + "documentation": [ + " The contract that was called is either no contract at all (a plain account)", + " or is a tombstone." + ] + }, + { + "name": "CodeTooLarge", + "documentation": [ + " The code supplied to `put_code` exceeds the limit specified in the current schedule." + ] + }, + { + "name": "CodeNotFound", + "documentation": [ + " No code could be found at the supplied code hash." + ] + }, + { + "name": "OutOfBounds", + "documentation": [ + " A buffer outside of sandbox memory was passed to a contract API function." + ] + }, + { + "name": "DecodingFailed", + "documentation": [ + " Input passed to a contract API function failed to decode as expected type." + ] + }, + { + "name": "ContractTrapped", + "documentation": [ + " Contract trapped during execution." + ] + }, + { + "name": "ValueTooLarge", + "documentation": [ + " The size defined in `T::MaxValueSize` was exceeded." + ] + }, + { + "name": "ReentranceDenied", + "documentation": [ + " The action performed is not allowed while the contract performing it is already", + " on the call stack. Those actions are contract self destruction and restoration", + " of a tombstone." + ] + }, + { + "name": "InputAlreadyRead", + "documentation": [ + " `seal_input` was called twice from the same contract execution context." + ] + }, + { + "name": "RandomSubjectTooLong", + "documentation": [ + " The subject passed to `seal_random` exceeds the limit." + ] + }, + { + "name": "TooManyTopics", + "documentation": [ + " The amount of topics passed to `seal_deposit_events` exceeds the limit." + ] + }, + { + "name": "DuplicateTopics", + "documentation": [ + " The topics passed to `seal_deposit_events` contains at least one duplicate." + ] + }, + { + "name": "NoChainExtension", + "documentation": [ + " The chain does not provide a chain extension. Calling the chain extension results", + " in this error. Note that this usually shouldn't happen as deploying such contracts", + " is rejected." + ] + }, + { + "name": "DeletionQueueFull", + "documentation": [ + " Removal of a contract failed because the deletion queue is full.", + "", + " This can happen when either calling [`Module::claim_surcharge`] or `seal_terminate`.", + " The queue is filled by deleting contracts and emptied by a fixed amount each block.", + " Trying again during another block is the only way to resolve this issue." + ] + }, + { + "name": "ContractNotEvictable", + "documentation": [ + " A contract could not be evicted because it has enough balance to pay rent.", + "", + " This can be returned from [`Module::claim_surcharge`] because the target", + " contract has enough balance to pay for its rent." + ] + } + ], + "index": 16 + }, + { + "name": "Sudo", + "storage": { + "prefix": "Sudo", + "items": [ + { + "name": "Key", + "modifier": "Default", + "type": { + "plain": "AccountId" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [ + " The `AccountId` of the sudo key." + ] + } + ] + }, + "calls": [ + { + "name": "sudo", + "args": [ + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Authenticates the sudo key and dispatches a function call with `Root` origin.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " # ", + " - O(1).", + " - Limited storage reads.", + " - One DB write (event).", + " - Weight of derivative `call` execution + 10,000.", + " # " + ] + }, + { + "name": "sudo_unchecked_weight", + "args": [ + { + "name": "call", + "type": "Call" + }, + { + "name": "_weight", + "type": "Weight" + } + ], + "documentation": [ + " Authenticates the sudo key and dispatches a function call with `Root` origin.", + " This function does not check the weight of the call, and instead allows the", + " Sudo user to specify the weight of the call.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " # ", + " - O(1).", + " - The weight of this call is defined by the caller.", + " # " + ] + }, + { + "name": "set_key", + "args": [ + { + "name": "new", + "type": "LookupSource" + } + ], + "documentation": [ + " Authenticates the current sudo key and sets the given AccountId (`new`) as the new sudo key.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " # ", + " - O(1).", + " - Limited storage reads.", + " - One DB change.", + " # " + ] + }, + { + "name": "sudo_as", + "args": [ + { + "name": "who", + "type": "LookupSource" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Authenticates the sudo key and dispatches a function call with `Signed` origin from", + " a given account.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " # ", + " - O(1).", + " - Limited storage reads.", + " - One DB write (event).", + " - Weight of derivative `call` execution + 10,000.", + " # " + ] + } + ], + "events": [ + { + "name": "Sudid", + "args": [ + "DispatchResult" + ], + "documentation": [ + " A sudo just took place. \\[result\\]" + ] + }, + { + "name": "KeyChanged", + "args": [ + "AccountId" + ], + "documentation": [ + " The \\[sudoer\\] just switched identity; the old key is supplied." + ] + }, + { + "name": "SudoAsDone", + "args": [ + "DispatchResult" + ], + "documentation": [ + " A sudo just took place. \\[result\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "RequireSudo", + "documentation": [ + " Sender must be the Sudo account" + ] + } + ], + "index": 17 + }, + { + "name": "ImOnline", + "storage": { + "prefix": "ImOnline", + "items": [ + { + "name": "HeartbeatAfter", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "documentation": [ + " The block number after which it's ok to send heartbeats in current session.", + "", + " At the beginning of each session we set this to a value that should", + " fall roughly in the middle of the session duration.", + " The idea is to first wait for the validators to produce a block", + " in the current session, so that the heartbeat later on will not be necessary." + ] + }, + { + "name": "Keys", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The current set of keys that may issue a heartbeat." + ] + }, + { + "name": "ReceivedHeartbeats", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "SessionIndex", + "key2": "AuthIndex", + "value": "Bytes", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " For each session index, we keep a mapping of `AuthIndex` to", + " `offchain::OpaqueNetworkState`." + ] + }, + { + "name": "AuthoredBlocks", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "SessionIndex", + "key2": "ValidatorId", + "value": "u32", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00000000", + "documentation": [ + " For each session index, we keep a mapping of `T::ValidatorId` to the", + " number of blocks authored by the given authority." + ] + } + ] + }, + "calls": [ + { + "name": "heartbeat", + "args": [ + { + "name": "heartbeat", + "type": "Heartbeat" + }, + { + "name": "_signature", + "type": "Signature" + } + ], + "documentation": [ + " # ", + " - Complexity: `O(K + E)` where K is length of `Keys` (heartbeat.validators_len)", + " and E is length of `heartbeat.network_state.external_address`", + " - `O(K)`: decoding of length `K`", + " - `O(E)`: decoding/encoding of length `E`", + " - DbReads: pallet_session `Validators`, pallet_session `CurrentIndex`, `Keys`,", + " `ReceivedHeartbeats`", + " - DbWrites: `ReceivedHeartbeats`", + " # " + ] + } + ], + "events": [ + { + "name": "HeartbeatReceived", + "args": [ + "AuthorityId" + ], + "documentation": [ + " A new heartbeat was received from `AuthorityId` \\[authority_id\\]" + ] + }, + { + "name": "AllGood", + "args": [], + "documentation": [ + " At the end of the session, no offence was committed." + ] + }, + { + "name": "SomeOffline", + "args": [ + "Vec" + ], + "documentation": [ + " At the end of the session, at least one validator was found to be \\[offline\\]." + ] + } + ], + "constants": [], + "errors": [ + { + "name": "InvalidKey", + "documentation": [ + " Non existent public key." + ] + }, + { + "name": "DuplicatedHeartbeat", + "documentation": [ + " Duplicated heartbeat." + ] + } + ], + "index": 18 + }, + { + "name": "AuthorityDiscovery", + "storage": null, + "calls": [], + "events": null, + "constants": [], + "errors": [], + "index": 19 + }, + { + "name": "Offences", + "storage": { + "prefix": "Offences", + "items": [ + { + "name": "Reports", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ReportIdOf", + "value": "OffenceDetails", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The primary structure that holds all offence records keyed by report identifiers." + ] + }, + { + "name": "DeferredOffences", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Deferred reports that have been rejected by the offence handler and need to be submitted", + " at a later time." + ] + }, + { + "name": "ConcurrentReportsIndex", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "Kind", + "key2": "OpaqueTimeSlot", + "value": "Vec", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " A vector of reports of the same kind that happened at the same time slot." + ] + }, + { + "name": "ReportsByKindIndex", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "Kind", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Enumerates all reports of a kind along with the time they happened.", + "", + " All reports are sorted by the time of offence.", + "", + " Note that the actual type of this mapping is `Vec`, this is because values of", + " different types are not supported at the moment so we are doing the manual serialization." + ] + } + ] + }, + "calls": [], + "events": [ + { + "name": "Offence", + "args": [ + "Kind", + "OpaqueTimeSlot", + "bool" + ], + "documentation": [ + " There is an offence reported of the given `kind` happened at the `session_index` and", + " (kind-specific) time slot. This event is not deposited for duplicate slashes. last", + " element indicates of the offence was applied (true) or queued (false)", + " \\[kind, timeslot, applied\\]." + ] + } + ], + "constants": [], + "errors": [], + "index": 20 + }, + { + "name": "Historical", + "storage": null, + "calls": null, + "events": null, + "constants": [], + "errors": [], + "index": 21 + }, + { + "name": "RandomnessCollectiveFlip", + "storage": { + "prefix": "RandomnessCollectiveFlip", + "items": [ + { + "name": "RandomMaterial", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Series of block headers from the last 81 blocks that acts as random seed material. This", + " is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of", + " the oldest hash." + ] + } + ] + }, + "calls": [], + "events": null, + "constants": [], + "errors": [], + "index": 22 + }, + { + "name": "Identity", + "storage": { + "prefix": "Identity", + "items": [ + { + "name": "IdentityOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Registration", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Information that is pertinent to identify the entity behind an account.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "SuperOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "(AccountId,Data)", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The super-identity of an alternative \"sub\" identity together with its name, within that", + " context. If the account is not some other account's sub-identity, then just `None`." + ] + }, + { + "name": "SubsOf", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(BalanceOf,Vec)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "documentation": [ + " Alternative \"sub\" identities of this account.", + "", + " The first item is the deposit, the second is a vector of the accounts.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "Registrars", + "modifier": "Default", + "type": { + "plain": "Vec>" + }, + "fallback": "0x00", + "documentation": [ + " The set of registrars. Not expected to get very big as can only be added through a", + " special origin (likely a council motion).", + "", + " The index into this can be cast to `RegistrarIndex` to get a valid value." + ] + } + ] + }, + "calls": [ + { + "name": "add_registrar", + "args": [ + { + "name": "account", + "type": "AccountId" + } + ], + "documentation": [ + " Add a registrar to the system.", + "", + " The dispatch origin for this call must be `T::RegistrarOrigin`.", + "", + " - `account`: the account of the registrar.", + "", + " Emits `RegistrarAdded` if successful.", + "", + " # ", + " - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).", + " - One storage mutation (codec `O(R)`).", + " - One event.", + " # " + ] + }, + { + "name": "set_identity", + "args": [ + { + "name": "info", + "type": "IdentityInfo" + } + ], + "documentation": [ + " Set an account's identity information and reserve the appropriate deposit.", + "", + " If the account already has identity information, the deposit is taken as part payment", + " for the new deposit.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `info`: The identity information.", + "", + " Emits `IdentitySet` if successful.", + "", + " # ", + " - `O(X + X' + R)`", + " - where `X` additional-field-count (deposit-bounded and code-bounded)", + " - where `R` judgements-count (registrar-count-bounded)", + " - One balance reserve operation.", + " - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).", + " - One event.", + " # " + ] + }, + { + "name": "set_subs", + "args": [ + { + "name": "subs", + "type": "Vec<(AccountId,Data)>" + } + ], + "documentation": [ + " Set the sub-accounts of the sender.", + "", + " Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned", + " and an amount `SubAccountDeposit` will be reserved for each item in `subs`.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " identity.", + "", + " - `subs`: The identity's (new) sub-accounts.", + "", + " # ", + " - `O(P + S)`", + " - where `P` old-subs-count (hard- and deposit-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + " - At most one balance operations.", + " - DB:", + " - `P + S` storage mutations (codec complexity `O(1)`)", + " - One storage read (codec complexity `O(P)`).", + " - One storage write (codec complexity `O(S)`).", + " - One storage-exists (`IdentityOf::contains_key`).", + " # " + ] + }, + { + "name": "clear_identity", + "args": [], + "documentation": [ + " Clear an account's identity info and all sub-accounts and return all deposits.", + "", + " Payment: All reserved balances on the account are returned.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " identity.", + "", + " Emits `IdentityCleared` if successful.", + "", + " # ", + " - `O(R + S + X)`", + " - where `R` registrar-count (governance-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + " - where `X` additional-field-count (deposit-bounded and code-bounded).", + " - One balance-unreserve operation.", + " - `2` storage reads and `S + 2` storage deletions.", + " - One event.", + " # " + ] + }, + { + "name": "request_judgement", + "args": [ + { + "name": "reg_index", + "type": "Compact" + }, + { + "name": "max_fee", + "type": "Compact" + } + ], + "documentation": [ + " Request a judgement from a registrar.", + "", + " Payment: At most `max_fee` will be reserved for payment to the registrar if judgement", + " given.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a", + " registered identity.", + "", + " - `reg_index`: The index of the registrar whose judgement is requested.", + " - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:", + "", + " ```nocompile", + " Self::registrars().get(reg_index).unwrap().fee", + " ```", + "", + " Emits `JudgementRequested` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-reserve operation.", + " - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.", + " - One event.", + " # " + ] + }, + { + "name": "cancel_request", + "args": [ + { + "name": "reg_index", + "type": "RegistrarIndex" + } + ], + "documentation": [ + " Cancel a previous request.", + "", + " Payment: A previously reserved deposit is returned on success.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a", + " registered identity.", + "", + " - `reg_index`: The index of the registrar whose judgement is no longer requested.", + "", + " Emits `JudgementUnrequested` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-reserve operation.", + " - One storage mutation `O(R + X)`.", + " - One event", + " # " + ] + }, + { + "name": "set_fee", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "fee", + "type": "Compact" + } + ], + "documentation": [ + " Set the fee required for a judgement to be requested from a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `fee`: the new fee.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 7.315 + R * 0.329 µs (min squares analysis)", + " # " + ] + }, + { + "name": "set_account_id", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "new", + "type": "AccountId" + } + ], + "documentation": [ + " Change the account associated with a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `new`: the new account ID.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 8.823 + R * 0.32 µs (min squares analysis)", + " # " + ] + }, + { + "name": "set_fields", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "fields", + "type": "IdentityFields" + } + ], + "documentation": [ + " Set the field information for a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `fields`: the fields that the registrar concerns themselves with.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 7.464 + R * 0.325 µs (min squares analysis)", + " # " + ] + }, + { + "name": "provide_judgement", + "args": [ + { + "name": "reg_index", + "type": "Compact" + }, + { + "name": "target", + "type": "LookupSource" + }, + { + "name": "judgement", + "type": "IdentityJudgement" + } + ], + "documentation": [ + " Provide a judgement for an account's identity.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `reg_index`.", + "", + " - `reg_index`: the index of the registrar whose judgement is being made.", + " - `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + " - `judgement`: the judgement of the registrar of index `reg_index` about `target`.", + "", + " Emits `JudgementGiven` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-transfer operation.", + " - Up to one account-lookup operation.", + " - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.", + " - One event.", + " # " + ] + }, + { + "name": "kill_identity", + "args": [ + { + "name": "target", + "type": "LookupSource" + } + ], + "documentation": [ + " Remove an account's identity and sub-account information and slash the deposits.", + "", + " Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by", + " `Slash`. Verification request deposits are not returned; they should be cancelled", + " manually using `cancel_request`.", + "", + " The dispatch origin for this call must match `T::ForceOrigin`.", + "", + " - `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + "", + " Emits `IdentityKilled` if successful.", + "", + " # ", + " - `O(R + S + X)`.", + " - One balance-reserve operation.", + " - `S + 2` storage mutations.", + " - One event.", + " # " + ] + }, + { + "name": "add_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + }, + { + "name": "data", + "type": "Data" + } + ], + "documentation": [ + " Add the given account to the sender's subs.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "rename_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + }, + { + "name": "data", + "type": "Data" + } + ], + "documentation": [ + " Alter the associated name of the given sub-account.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "remove_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + } + ], + "documentation": [ + " Remove the given account from the sender's subs.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "quit_sub", + "args": [], + "documentation": [ + " Remove the sender as a sub-account.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender (*not* the original depositor).", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " super-identity.", + "", + " NOTE: This should not normally be used, but is provided in the case that the non-", + " controller of an account is maliciously registered as a sub-account." + ] + } + ], + "events": [ + { + "name": "IdentitySet", + "args": [ + "AccountId" + ], + "documentation": [ + " A name was set or reset (which will remove all judgements). \\[who\\]" + ] + }, + { + "name": "IdentityCleared", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A name was cleared, and the given balance returned. \\[who, deposit\\]" + ] + }, + { + "name": "IdentityKilled", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " A name was removed and the given balance slashed. \\[who, deposit\\]" + ] + }, + { + "name": "JudgementRequested", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "documentation": [ + " A judgement was asked from a registrar. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementUnrequested", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "documentation": [ + " A judgement request was retracted. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementGiven", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "documentation": [ + " A judgement was given by a registrar. \\[target, registrar_index\\]" + ] + }, + { + "name": "RegistrarAdded", + "args": [ + "RegistrarIndex" + ], + "documentation": [ + " A registrar was added. \\[registrar_index\\]" + ] + }, + { + "name": "SubIdentityAdded", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " A sub-identity was added to an identity and the deposit paid. \\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRemoved", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " A sub-identity was removed from an identity and the deposit freed.", + " \\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRevoked", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "documentation": [ + " A sub-identity was cleared, and the given deposit repatriated from the", + " main identity account to the sub-identity account. \\[sub, main, deposit\\]" + ] + } + ], + "constants": [ + { + "name": "BasicDeposit", + "type": "BalanceOf", + "value": "0x0000f444829163450000000000000000", + "documentation": [ + " The amount held on deposit for a registered identity." + ] + }, + { + "name": "FieldDeposit", + "type": "BalanceOf", + "value": "0x00007a22c1c8b1220000000000000000", + "documentation": [ + " The amount held on deposit per additional field for a registered identity." + ] + }, + { + "name": "SubAccountDeposit", + "type": "BalanceOf", + "value": "0x000064a7b3b6e00d0000000000000000", + "documentation": [ + " The amount held on deposit for a registered subaccount. This should account for the fact", + " that one storage item's value will increase by the size of an account ID, and there will be", + " another trie item whose value is the size of an account ID plus 32 bytes." + ] + }, + { + "name": "MaxSubAccounts", + "type": "u32", + "value": "0x64000000", + "documentation": [ + " The maximum number of sub-accounts allowed per identified account." + ] + }, + { + "name": "MaxAdditionalFields", + "type": "u32", + "value": "0x64000000", + "documentation": [ + " Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O", + " required to access an identity, but can be pretty high." + ] + }, + { + "name": "MaxRegistrars", + "type": "u32", + "value": "0x14000000", + "documentation": [ + " Maxmimum number of registrars allowed in the system. Needed to bound the complexity", + " of, e.g., updating judgements." + ] + } + ], + "errors": [ + { + "name": "TooManySubAccounts", + "documentation": [ + " Too many subs-accounts." + ] + }, + { + "name": "NotFound", + "documentation": [ + " Account isn't found." + ] + }, + { + "name": "NotNamed", + "documentation": [ + " Account isn't named." + ] + }, + { + "name": "EmptyIndex", + "documentation": [ + " Empty index." + ] + }, + { + "name": "FeeChanged", + "documentation": [ + " Fee is changed." + ] + }, + { + "name": "NoIdentity", + "documentation": [ + " No identity found." + ] + }, + { + "name": "StickyJudgement", + "documentation": [ + " Sticky judgement." + ] + }, + { + "name": "JudgementGiven", + "documentation": [ + " Judgement given." + ] + }, + { + "name": "InvalidJudgement", + "documentation": [ + " Invalid judgement." + ] + }, + { + "name": "InvalidIndex", + "documentation": [ + " The index is invalid." + ] + }, + { + "name": "InvalidTarget", + "documentation": [ + " The target is invalid." + ] + }, + { + "name": "TooManyFields", + "documentation": [ + " Too many additional fields." + ] + }, + { + "name": "TooManyRegistrars", + "documentation": [ + " Maximum amount of registrars reached. Cannot add any more." + ] + }, + { + "name": "AlreadyClaimed", + "documentation": [ + " Account ID is already named." + ] + }, + { + "name": "NotSub", + "documentation": [ + " Sender is not a sub-account." + ] + }, + { + "name": "NotOwned", + "documentation": [ + " Sub-account isn't owned by sender." + ] + } + ], + "index": 23 + }, + { + "name": "Recovery", + "storage": { + "prefix": "Recovery", + "items": [ + { + "name": "Recoverable", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "RecoveryConfig", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The set of recoverable accounts and their recovery configuration." + ] + }, + { + "name": "ActiveRecoveries", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "AccountId", + "key2": "AccountId", + "value": "ActiveRecovery", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " Active recovery attempts.", + "", + " First account is the account to be recovered, and the second account", + " is the user trying to recover the account." + ] + }, + { + "name": "Proxy", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "AccountId", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The list of allowed proxy accounts.", + "", + " Map from the user who can access it to the recovered account." + ] + } + ] + }, + "calls": [ + { + "name": "as_recovered", + "args": [ + { + "name": "account", + "type": "AccountId" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Send a call through a recovered account.", + "", + " The dispatch origin for this call must be _Signed_ and registered to", + " be able to make calls on behalf of the recovered account.", + "", + " Parameters:", + " - `account`: The recovered account you want to make a call on-behalf-of.", + " - `call`: The call you want to make with the recovered account.", + "", + " # ", + " - The weight of the `call` + 10,000.", + " - One storage lookup to check account is recovered by `who`. O(1)", + " # " + ] + }, + { + "name": "set_recovered", + "args": [ + { + "name": "lost", + "type": "AccountId" + }, + { + "name": "rescuer", + "type": "AccountId" + } + ], + "documentation": [ + " Allow ROOT to bypass the recovery process and set an a rescuer account", + " for a lost account directly.", + "", + " The dispatch origin for this call must be _ROOT_.", + "", + " Parameters:", + " - `lost`: The \"lost account\" to be recovered.", + " - `rescuer`: The \"rescuer account\" which can call as the lost account.", + "", + " # ", + " - One storage write O(1)", + " - One event", + " # " + ] + }, + { + "name": "create_recovery", + "args": [ + { + "name": "friends", + "type": "Vec" + }, + { + "name": "threshold", + "type": "u16" + }, + { + "name": "delay_period", + "type": "BlockNumber" + } + ], + "documentation": [ + " Create a recovery configuration for your account. This makes your account recoverable.", + "", + " Payment: `ConfigDepositBase` + `FriendDepositFactor` * #_of_friends balance", + " will be reserved for storing the recovery configuration. This deposit is returned", + " in full when the user calls `remove_recovery`.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `friends`: A list of friends you trust to vouch for recovery attempts.", + " Should be ordered and contain no duplicate values.", + " - `threshold`: The number of friends that must vouch for a recovery attempt", + " before the account can be recovered. Should be less than or equal to", + " the length of the list of friends.", + " - `delay_period`: The number of blocks after a recovery attempt is initialized", + " that needs to pass before the account can be recovered.", + "", + " # ", + " - Key: F (len of friends)", + " - One storage read to check that account is not already recoverable. O(1).", + " - A check that the friends list is sorted and unique. O(F)", + " - One currency reserve operation. O(X)", + " - One storage write. O(1). Codec O(F).", + " - One event.", + "", + " Total Complexity: O(F + X)", + " # " + ] + }, + { + "name": "initiate_recovery", + "args": [ + { + "name": "account", + "type": "AccountId" + } + ], + "documentation": [ + " Initiate the process for recovering a recoverable account.", + "", + " Payment: `RecoveryDeposit` balance will be reserved for initiating the", + " recovery process. This deposit will always be repatriated to the account", + " trying to be recovered. See `close_recovery`.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `account`: The lost account that you want to recover. This account", + " needs to be recoverable (i.e. have a recovery configuration).", + "", + " # ", + " - One storage read to check that account is recoverable. O(F)", + " - One storage read to check that this recovery process hasn't already started. O(1)", + " - One currency reserve operation. O(X)", + " - One storage read to get the current block number. O(1)", + " - One storage write. O(1).", + " - One event.", + "", + " Total Complexity: O(F + X)", + " # " + ] + }, + { + "name": "vouch_recovery", + "args": [ + { + "name": "lost", + "type": "AccountId" + }, + { + "name": "rescuer", + "type": "AccountId" + } + ], + "documentation": [ + " Allow a \"friend\" of a recoverable account to vouch for an active recovery", + " process for that account.", + "", + " The dispatch origin for this call must be _Signed_ and must be a \"friend\"", + " for the recoverable account.", + "", + " Parameters:", + " - `lost`: The lost account that you want to recover.", + " - `rescuer`: The account trying to rescue the lost account that you", + " want to vouch for.", + "", + " The combination of these two parameters must point to an active recovery", + " process.", + "", + " # ", + " Key: F (len of friends in config), V (len of vouching friends)", + " - One storage read to get the recovery configuration. O(1), Codec O(F)", + " - One storage read to get the active recovery process. O(1), Codec O(V)", + " - One binary search to confirm caller is a friend. O(logF)", + " - One binary search to confirm caller has not already vouched. O(logV)", + " - One storage write. O(1), Codec O(V).", + " - One event.", + "", + " Total Complexity: O(F + logF + V + logV)", + " # " + ] + }, + { + "name": "claim_recovery", + "args": [ + { + "name": "account", + "type": "AccountId" + } + ], + "documentation": [ + " Allow a successful rescuer to claim their recovered account.", + "", + " The dispatch origin for this call must be _Signed_ and must be a \"rescuer\"", + " who has successfully completed the account recovery process: collected", + " `threshold` or more vouches, waited `delay_period` blocks since initiation.", + "", + " Parameters:", + " - `account`: The lost account that you want to claim has been successfully", + " recovered by you.", + "", + " # ", + " Key: F (len of friends in config), V (len of vouching friends)", + " - One storage read to get the recovery configuration. O(1), Codec O(F)", + " - One storage read to get the active recovery process. O(1), Codec O(V)", + " - One storage read to get the current block number. O(1)", + " - One storage write. O(1), Codec O(V).", + " - One event.", + "", + " Total Complexity: O(F + V)", + " # " + ] + }, + { + "name": "close_recovery", + "args": [ + { + "name": "rescuer", + "type": "AccountId" + } + ], + "documentation": [ + " As the controller of a recoverable account, close an active recovery", + " process for your account.", + "", + " Payment: By calling this function, the recoverable account will receive", + " the recovery deposit `RecoveryDeposit` placed by the rescuer.", + "", + " The dispatch origin for this call must be _Signed_ and must be a", + " recoverable account with an active recovery process for it.", + "", + " Parameters:", + " - `rescuer`: The account trying to rescue this recoverable account.", + "", + " # ", + " Key: V (len of vouching friends)", + " - One storage read/remove to get the active recovery process. O(1), Codec O(V)", + " - One balance call to repatriate reserved. O(X)", + " - One event.", + "", + " Total Complexity: O(V + X)", + " # " + ] + }, + { + "name": "remove_recovery", + "args": [], + "documentation": [ + " Remove the recovery process for your account. Recovered accounts are still accessible.", + "", + " NOTE: The user must make sure to call `close_recovery` on all active", + " recovery attempts before calling this function else it will fail.", + "", + " Payment: By calling this function the recoverable account will unreserve", + " their recovery configuration deposit.", + " (`ConfigDepositBase` + `FriendDepositFactor` * #_of_friends)", + "", + " The dispatch origin for this call must be _Signed_ and must be a", + " recoverable account (i.e. has a recovery configuration).", + "", + " # ", + " Key: F (len of friends)", + " - One storage read to get the prefix iterator for active recoveries. O(1)", + " - One storage read/remove to get the recovery configuration. O(1), Codec O(F)", + " - One balance call to unreserved. O(X)", + " - One event.", + "", + " Total Complexity: O(F + X)", + " # " + ] + }, + { + "name": "cancel_recovered", + "args": [ + { + "name": "account", + "type": "AccountId" + } + ], + "documentation": [ + " Cancel the ability to use `as_recovered` for `account`.", + "", + " The dispatch origin for this call must be _Signed_ and registered to", + " be able to make calls on behalf of the recovered account.", + "", + " Parameters:", + " - `account`: The recovered account you are able to call on-behalf-of.", + "", + " # ", + " - One storage mutation to check account is recovered by `who`. O(1)", + " # " + ] + } + ], + "events": [ + { + "name": "RecoveryCreated", + "args": [ + "AccountId" + ], + "documentation": [ + " A recovery process has been set up for an \\[account\\]." + ] + }, + { + "name": "RecoveryInitiated", + "args": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " A recovery process has been initiated for lost account by rescuer account.", + " \\[lost, rescuer\\]" + ] + }, + { + "name": "RecoveryVouched", + "args": [ + "AccountId", + "AccountId", + "AccountId" + ], + "documentation": [ + " A recovery process for lost account by rescuer account has been vouched for by sender.", + " \\[lost, rescuer, sender\\]" + ] + }, + { + "name": "RecoveryClosed", + "args": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " A recovery process for lost account by rescuer account has been closed.", + " \\[lost, rescuer\\]" + ] + }, + { + "name": "AccountRecovered", + "args": [ + "AccountId", + "AccountId" + ], + "documentation": [ + " Lost account has been successfully recovered by rescuer account.", + " \\[lost, rescuer\\]" + ] + }, + { + "name": "RecoveryRemoved", + "args": [ + "AccountId" + ], + "documentation": [ + " A recovery process has been removed for an \\[account\\]." + ] + } + ], + "constants": [ + { + "name": "ConfigDepositBase", + "type": "BalanceOf", + "value": "0x0000f444829163450000000000000000", + "documentation": [ + " The base amount of currency needed to reserve for creating a recovery configuration." + ] + }, + { + "name": "FriendDepositFactor", + "type": "BalanceOf", + "value": "0x0000b2d3595bf0060000000000000000", + "documentation": [ + " The amount of currency needed per additional user when creating a recovery configuration." + ] + }, + { + "name": "MaxFriends", + "type": "u16", + "value": "0x0900", + "documentation": [ + " The maximum amount of friends allowed in a recovery configuration." + ] + }, + { + "name": "RecoveryDeposit", + "type": "BalanceOf", + "value": "0x0000f444829163450000000000000000", + "documentation": [ + " The base amount of currency needed to reserve for starting a recovery." + ] + } + ], + "errors": [ + { + "name": "NotAllowed", + "documentation": [ + " User is not allowed to make a call on behalf of this account" + ] + }, + { + "name": "ZeroThreshold", + "documentation": [ + " Threshold must be greater than zero" + ] + }, + { + "name": "NotEnoughFriends", + "documentation": [ + " Friends list must be greater than zero and threshold" + ] + }, + { + "name": "MaxFriends", + "documentation": [ + " Friends list must be less than max friends" + ] + }, + { + "name": "NotSorted", + "documentation": [ + " Friends list must be sorted and free of duplicates" + ] + }, + { + "name": "NotRecoverable", + "documentation": [ + " This account is not set up for recovery" + ] + }, + { + "name": "AlreadyRecoverable", + "documentation": [ + " This account is already set up for recovery" + ] + }, + { + "name": "AlreadyStarted", + "documentation": [ + " A recovery process has already started for this account" + ] + }, + { + "name": "NotStarted", + "documentation": [ + " A recovery process has not started for this rescuer" + ] + }, + { + "name": "NotFriend", + "documentation": [ + " This account is not a friend who can vouch" + ] + }, + { + "name": "DelayPeriod", + "documentation": [ + " The friend must wait until the delay period to vouch for this recovery" + ] + }, + { + "name": "AlreadyVouched", + "documentation": [ + " This user has already vouched for this recovery" + ] + }, + { + "name": "Threshold", + "documentation": [ + " The threshold for recovering this account has not been met" + ] + }, + { + "name": "StillActive", + "documentation": [ + " There are still active recovery attempts that need to be closed" + ] + }, + { + "name": "Overflow", + "documentation": [ + " There was an overflow in a calculation" + ] + }, + { + "name": "AlreadyProxy", + "documentation": [ + " This account is already set up for recovery" + ] + } + ], + "index": 24 + }, + { + "name": "Vesting", + "storage": { + "prefix": "Vesting", + "items": [ + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "VestingInfo", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Information regarding the vesting of a given account." + ] + } + ] + }, + "calls": [ + { + "name": "vest", + "args": [], + "documentation": [ + " Unlock any vested funds of the sender account.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have funds still", + " locked under this module.", + "", + " Emits either `VestingCompleted` or `VestingUpdated`.", + "", + " # ", + " - `O(1)`.", + " - DbWeight: 2 Reads, 2 Writes", + " - Reads: Vesting Storage, Balances Locks, [Sender Account]", + " - Writes: Vesting Storage, Balances Locks, [Sender Account]", + " # " + ] + }, + { + "name": "vest_other", + "args": [ + { + "name": "target", + "type": "LookupSource" + } + ], + "documentation": [ + " Unlock any vested funds of a `target` account.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `target`: The account whose vested funds should be unlocked. Must have funds still", + " locked under this module.", + "", + " Emits either `VestingCompleted` or `VestingUpdated`.", + "", + " # ", + " - `O(1)`.", + " - DbWeight: 3 Reads, 3 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account", + " - Writes: Vesting Storage, Balances Locks, Target Account", + " # " + ] + }, + { + "name": "vested_transfer", + "args": [ + { + "name": "target", + "type": "LookupSource" + }, + { + "name": "schedule", + "type": "VestingInfo" + } + ], + "documentation": [ + " Create a vested transfer.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `target`: The account that should be transferred the vested funds.", + " - `amount`: The amount of funds to transfer and will be vested.", + " - `schedule`: The vesting schedule attached to the transfer.", + "", + " Emits `VestingCreated`.", + "", + " # ", + " - `O(1)`.", + " - DbWeight: 3 Reads, 3 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account, [Sender Account]", + " - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account]", + " # " + ] + }, + { + "name": "force_vested_transfer", + "args": [ + { + "name": "source", + "type": "LookupSource" + }, + { + "name": "target", + "type": "LookupSource" + }, + { + "name": "schedule", + "type": "VestingInfo" + } + ], + "documentation": [ + " Force a vested transfer.", + "", + " The dispatch origin for this call must be _Root_.", + "", + " - `source`: The account whose funds should be transferred.", + " - `target`: The account that should be transferred the vested funds.", + " - `amount`: The amount of funds to transfer and will be vested.", + " - `schedule`: The vesting schedule attached to the transfer.", + "", + " Emits `VestingCreated`.", + "", + " # ", + " - `O(1)`.", + " - DbWeight: 4 Reads, 4 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account, Source Account", + " - Writes: Vesting Storage, Balances Locks, Target Account, Source Account", + " # " + ] + } + ], + "events": [ + { + "name": "VestingUpdated", + "args": [ + "AccountId", + "Balance" + ], + "documentation": [ + " The amount vested has been updated. This could indicate more funds are available. The", + " balance given is the amount which is left unvested (and thus locked).", + " \\[account, unvested\\]" + ] + }, + { + "name": "VestingCompleted", + "args": [ + "AccountId" + ], + "documentation": [ + " An \\[account\\] has become fully vested. No further vesting can happen." + ] + } + ], + "constants": [ + { + "name": "MinVestedTransfer", + "type": "BalanceOf", + "value": "0x000010632d5ec76b0500000000000000", + "documentation": [ + " The minimum amount to be transferred to create a new vesting schedule." + ] + } + ], + "errors": [ + { + "name": "NotVesting", + "documentation": [ + " The account given is not vesting." + ] + }, + { + "name": "ExistingVestingSchedule", + "documentation": [ + " An existing vesting schedule already exists for this account that cannot be clobbered." + ] + }, + { + "name": "AmountLow", + "documentation": [ + " Amount being transferred is too low to create a vesting schedule." + ] + } + ], + "index": 25 + }, + { + "name": "Scheduler", + "storage": { + "prefix": "Scheduler", + "items": [ + { + "name": "Agenda", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BlockNumber", + "value": "Vec>", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Items to be executed, indexed by the block number that they should be executed on." + ] + }, + { + "name": "Lookup", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "Bytes", + "value": "TaskAddress", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Lookup from identity to the block number and index of the task." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "documentation": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": [ + { + "name": "schedule", + "args": [ + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Anonymously schedule a task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 22.29 + .126 * S µs", + " - DB Weight:", + " - Read: Agenda", + " - Write: Agenda", + " - Will use base weight of 25 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "cancel", + "args": [ + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "index", + "type": "u32" + } + ], + "documentation": [ + " Cancel an anonymously scheduled task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 22.15 + 2.869 * S µs", + " - DB Weight:", + " - Read: Agenda", + " - Write: Agenda, Lookup", + " - Will use base weight of 100 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "schedule_named", + "args": [ + { + "name": "id", + "type": "Bytes" + }, + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Schedule a named task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 29.6 + .159 * S µs", + " - DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + " - Will use base weight of 35 which should be good for more than 30 scheduled calls", + " # " + ] + }, + { + "name": "cancel_named", + "args": [ + { + "name": "id", + "type": "Bytes" + } + ], + "documentation": [ + " Cancel a named scheduled task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 24.91 + 2.907 * S µs", + " - DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + " - Will use base weight of 100 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "schedule_after", + "args": [ + { + "name": "after", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Anonymously schedule a task after a delay.", + "", + " # ", + " Same as [`schedule`].", + " # " + ] + }, + { + "name": "schedule_named_after", + "args": [ + { + "name": "id", + "type": "Bytes" + }, + { + "name": "after", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Schedule a named task after a delay.", + "", + " # ", + " Same as [`schedule_named`].", + " # " + ] + } + ], + "events": [ + { + "name": "Scheduled", + "args": [ + "BlockNumber", + "u32" + ], + "documentation": [ + " Scheduled some task. \\[when, index\\]" + ] + }, + { + "name": "Canceled", + "args": [ + "BlockNumber", + "u32" + ], + "documentation": [ + " Canceled some task. \\[when, index\\]" + ] + }, + { + "name": "Dispatched", + "args": [ + "TaskAddress", + "Option", + "DispatchResult" + ], + "documentation": [ + " Dispatched some task. \\[task, id, result\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "FailedToSchedule", + "documentation": [ + " Failed to schedule a call" + ] + }, + { + "name": "NotFound", + "documentation": [ + " Cannot find the scheduled call." + ] + }, + { + "name": "TargetBlockNumberInPast", + "documentation": [ + " Given target block number is in the past." + ] + }, + { + "name": "RescheduleNoChange", + "documentation": [ + " Reschedule failed because it does not change scheduled time." + ] + } + ], + "index": 26 + }, + { + "name": "Proxy", + "storage": { + "prefix": "Proxy", + "items": [ + { + "name": "Proxies", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "documentation": [ + " The set of account proxies. Maps the account which has delegated to the accounts", + " which are being delegated to, together with the amount held on deposit." + ] + }, + { + "name": "Announcements", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "documentation": [ + " The announcements made by the proxy (key)." + ] + } + ] + }, + "calls": [ + { + "name": "proxy", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "force_proxy_type", + "type": "Option" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Dispatch the given `call` from an account that the sender is authorised for through", + " `add_proxy`.", + "", + " Removes any corresponding announcement(s).", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + " - `call`: The call to be made by the `real` account.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "add_proxy", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Register a proxy account for the sender that is able to make calls on its behalf.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `proxy`: The account that the `caller` would like to make a proxy.", + " - `proxy_type`: The permissions allowed for this proxy account.", + " - `delay`: The announcement period required of the initial proxy. Will generally be", + " zero.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "remove_proxy", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "documentation": [ + " Unregister a proxy account for the sender.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `proxy`: The account that the `caller` would like to remove as a proxy.", + " - `proxy_type`: The permissions currently enabled for the removed proxy account.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "remove_proxies", + "args": [], + "documentation": [ + " Unregister all proxy accounts for the sender.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " WARNING: This may be called on accounts created by `anonymous`, however if done, then", + " the unreserved fees will be inaccessible. **All access to this account will be lost.**", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "anonymous", + "args": [ + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + }, + { + "name": "index", + "type": "u16" + } + ], + "documentation": [ + " Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and", + " initialize it with a proxy of `proxy_type` for `origin` sender.", + "", + " Requires a `Signed` origin.", + "", + " - `proxy_type`: The type of the proxy that the sender will be registered as over the", + " new account. This will almost always be the most permissive `ProxyType` possible to", + " allow for maximum flexibility.", + " - `index`: A disambiguation index, in case this is called multiple times in the same", + " transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just", + " want to use `0`.", + " - `delay`: The announcement period required of the initial proxy. Will generally be", + " zero.", + "", + " Fails with `Duplicate` if this has already been called in this transaction, from the", + " same sender, with the same parameters.", + "", + " Fails if there are insufficient funds to pay for deposit.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # ", + " TODO: Might be over counting 1 read" + ] + }, + { + "name": "kill_anonymous", + "args": [ + { + "name": "spawner", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "height", + "type": "Compact" + }, + { + "name": "ext_index", + "type": "Compact" + } + ], + "documentation": [ + " Removes a previously spawned anonymous proxy.", + "", + " WARNING: **All access to this account will be lost.** Any funds held in it will be", + " inaccessible.", + "", + " Requires a `Signed` origin, and the sender account must have been created by a call to", + " `anonymous` with corresponding parameters.", + "", + " - `spawner`: The account that originally called `anonymous` to create this account.", + " - `index`: The disambiguation index originally passed to `anonymous`. Probably `0`.", + " - `proxy_type`: The proxy type originally passed to `anonymous`.", + " - `height`: The height of the chain when the call to `anonymous` was processed.", + " - `ext_index`: The extrinsic index in which the call to `anonymous` was processed.", + "", + " Fails with `NoPermission` in case the caller is not a previously created anonymous", + " account whose `anonymous` call has corresponding parameters.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "announce", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "documentation": [ + " Publish the hash of a proxy-call that will be made in the future.", + "", + " This must be called some number of blocks before the corresponding `proxy` is attempted", + " if the delay associated with the proxy relationship is greater than zero.", + "", + " No more than `MaxPending` announcements may be made at any one time.", + "", + " This will take a deposit of `AnnouncementDepositFactor` as well as", + " `AnnouncementDepositBase` if there are no other pending announcements.", + "", + " The dispatch origin for this call must be _Signed_ and a proxy of `real`.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `call_hash`: The hash of the call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "remove_announcement", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "documentation": [ + " Remove a given announcement.", + "", + " May be called by a proxy account to remove a call they previously announced and return", + " the deposit.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `call_hash`: The hash of the call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "reject_announcement", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "documentation": [ + " Remove the given announcement of a delegate.", + "", + " May be called by a target (proxied) account to remove a call that one of their delegates", + " (`delegate`) has announced they want to execute. The deposit is returned.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `delegate`: The account that previously announced the call.", + " - `call_hash`: The hash of the call to be made.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "proxy_announced", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "real", + "type": "AccountId" + }, + { + "name": "force_proxy_type", + "type": "Option" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Dispatch the given `call` from an account that the sender is authorised for through", + " `add_proxy`.", + "", + " Removes any corresponding announcement(s).", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + " - `call`: The call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + } + ], + "events": [ + { + "name": "ProxyExecuted", + "args": [ + "DispatchResult" + ], + "documentation": [ + " A proxy was executed correctly, with the given \\[result\\]." + ] + }, + { + "name": "AnonymousCreated", + "args": [ + "AccountId", + "AccountId", + "ProxyType", + "u16" + ], + "documentation": [ + " Anonymous account has been created by new proxy with given", + " disambiguation index and proxy type. \\[anonymous, who, proxy_type, disambiguation_index\\]" + ] + }, + { + "name": "Announced", + "args": [ + "AccountId", + "AccountId", + "Hash" + ], + "documentation": [ + " An announcement was placed to make a call in the future. \\[real, proxy, call_hash\\]" + ] + } + ], + "constants": [ + { + "name": "ProxyDepositBase", + "type": "BalanceOf", + "value": "0x00007f80a935be080000000000000000", + "documentation": [ + " The base amount of currency needed to reserve for creating a proxy." + ] + }, + { + "name": "ProxyDepositFactor", + "type": "BalanceOf", + "value": "0x0000466f825f7a1b0000000000000000", + "documentation": [ + " The amount of currency needed per proxy added." + ] + }, + { + "name": "MaxProxies", + "type": "u16", + "value": "0x2000", + "documentation": [ + " The maximum amount of proxies allowed for a single account." + ] + }, + { + "name": "MaxPending", + "type": "u32", + "value": "0x20000000", + "documentation": [ + " `MaxPending` metadata shadow." + ] + }, + { + "name": "AnnouncementDepositBase", + "type": "BalanceOf", + "value": "0x00007f80a935be080000000000000000", + "documentation": [ + " `AnnouncementDepositBase` metadata shadow." + ] + }, + { + "name": "AnnouncementDepositFactor", + "type": "BalanceOf", + "value": "0x00008cde04bff4360000000000000000", + "documentation": [ + " `AnnouncementDepositFactor` metadata shadow." + ] + } + ], + "errors": [ + { + "name": "TooMany", + "documentation": [ + " There are too many proxies registered or too many announcements pending." + ] + }, + { + "name": "NotFound", + "documentation": [ + " Proxy registration not found." + ] + }, + { + "name": "NotProxy", + "documentation": [ + " Sender is not a proxy of the account to be proxied." + ] + }, + { + "name": "Unproxyable", + "documentation": [ + " A call which is incompatible with the proxy type's filter was attempted." + ] + }, + { + "name": "Duplicate", + "documentation": [ + " Account is already a proxy." + ] + }, + { + "name": "NoPermission", + "documentation": [ + " Call may not be made by proxy because it may escalate its privileges." + ] + }, + { + "name": "Unannounced", + "documentation": [ + " Announcement, if made at all, was made too recently." + ] + }, + { + "name": "NoSelfProxy", + "documentation": [ + " Cannot add self as proxy." + ] + } + ], + "index": 27 + }, + { + "name": "Multisig", + "storage": { + "prefix": "Multisig", + "items": [ + { + "name": "Multisigs", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "AccountId", + "key2": "[u8;32]", + "value": "Multisig", + "key2Hasher": "Blake2_128Concat" + } + }, + "fallback": "0x00", + "documentation": [ + " The set of open multisig operations." + ] + }, + { + "name": "Calls", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "[u8;32]", + "value": "(OpaqueCall,AccountId,BalanceOf)", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [] + } + ] + }, + "calls": [ + { + "name": "as_multi_threshold_1", + "args": [ + { + "name": "other_signatories", + "type": "Vec" + }, + { + "name": "call", + "type": "Call" + } + ], + "documentation": [ + " Immediately dispatch a multi-signature call using a single approval from the caller.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `other_signatories`: The accounts (other than the sender) who are part of the", + " multi-signature, but do not participate in the approval process.", + " - `call`: The call to be executed.", + "", + " Result is equivalent to the dispatched result.", + "", + " # ", + " O(Z + C) where Z is the length of the call and C its execution weight.", + " -------------------------------", + " - DB Weight: None", + " - Plus Call Weight", + " # " + ] + }, + { + "name": "as_multi", + "args": [ + { + "name": "threshold", + "type": "u16" + }, + { + "name": "other_signatories", + "type": "Vec" + }, + { + "name": "maybe_timepoint", + "type": "Option" + }, + { + "name": "call", + "type": "OpaqueCall" + }, + { + "name": "store_call", + "type": "bool" + }, + { + "name": "max_weight", + "type": "Weight" + } + ], + "documentation": [ + " Register approval for a dispatch to be made from a deterministic composite account if", + " approved by a total of `threshold - 1` of `other_signatories`.", + "", + " If there are enough, then dispatch the call.", + "", + " Payment: `DepositBase` will be reserved if this is the first approval, plus", + " `threshold` times `DepositFactor`. It is returned once this dispatch happens or", + " is cancelled.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `threshold`: The total number of approvals for this dispatch before it is executed.", + " - `other_signatories`: The accounts (other than the sender) who can approve this", + " dispatch. May not be empty.", + " - `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is", + " not the first approval, then it must be `Some`, with the timepoint (block number and", + " transaction index) of the first approval transaction.", + " - `call`: The call to be executed.", + "", + " NOTE: Unless this is the final approval, you will generally want to use", + " `approve_as_multi` instead, since it only requires a hash of the call.", + "", + " Result is equivalent to the dispatched result if `threshold` is exactly `1`. Otherwise", + " on success, result is `Ok` and the result from the interior call, if it was executed,", + " may be found in the deposited `MultisigExecuted` event.", + "", + " # ", + " - `O(S + Z + Call)`.", + " - Up to one balance-reserve or unreserve operation.", + " - One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + " - One call encode & hash, both of complexity `O(Z)` where `Z` is tx-len.", + " - One encode & hash, both of complexity `O(S)`.", + " - Up to one binary search and insert (`O(logS + S)`).", + " - I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove.", + " - One event.", + " - The weight of the `call`.", + " - Storage: inserts one item, value size bounded by `MaxSignatories`, with a", + " deposit taken for its lifetime of", + " `DepositBase + threshold * DepositFactor`.", + " -------------------------------", + " - DB Weight:", + " - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`)", + " - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`)", + " - Plus Call Weight", + " # " + ] + }, + { + "name": "approve_as_multi", + "args": [ + { + "name": "threshold", + "type": "u16" + }, + { + "name": "other_signatories", + "type": "Vec" + }, + { + "name": "maybe_timepoint", + "type": "Option" + }, + { + "name": "call_hash", + "type": "[u8;32]" + }, + { + "name": "max_weight", + "type": "Weight" + } + ], + "documentation": [ + " Register approval for a dispatch to be made from a deterministic composite account if", + " approved by a total of `threshold - 1` of `other_signatories`.", + "", + " Payment: `DepositBase` will be reserved if this is the first approval, plus", + " `threshold` times `DepositFactor`. It is returned once this dispatch happens or", + " is cancelled.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `threshold`: The total number of approvals for this dispatch before it is executed.", + " - `other_signatories`: The accounts (other than the sender) who can approve this", + " dispatch. May not be empty.", + " - `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is", + " not the first approval, then it must be `Some`, with the timepoint (block number and", + " transaction index) of the first approval transaction.", + " - `call_hash`: The hash of the call to be executed.", + "", + " NOTE: If this is the final approval, you will want to use `as_multi` instead.", + "", + " # ", + " - `O(S)`.", + " - Up to one balance-reserve or unreserve operation.", + " - One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + " - One encode & hash, both of complexity `O(S)`.", + " - Up to one binary search and insert (`O(logS + S)`).", + " - I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove.", + " - One event.", + " - Storage: inserts one item, value size bounded by `MaxSignatories`, with a", + " deposit taken for its lifetime of", + " `DepositBase + threshold * DepositFactor`.", + " ----------------------------------", + " - DB Weight:", + " - Read: Multisig Storage, [Caller Account]", + " - Write: Multisig Storage, [Caller Account]", + " # " + ] + }, + { + "name": "cancel_as_multi", + "args": [ + { + "name": "threshold", + "type": "u16" + }, + { + "name": "other_signatories", + "type": "Vec" + }, + { + "name": "timepoint", + "type": "Timepoint" + }, + { + "name": "call_hash", + "type": "[u8;32]" + } + ], + "documentation": [ + " Cancel a pre-existing, on-going multisig transaction. Any deposit reserved previously", + " for this operation will be unreserved on success.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `threshold`: The total number of approvals for this dispatch before it is executed.", + " - `other_signatories`: The accounts (other than the sender) who can approve this", + " dispatch. May not be empty.", + " - `timepoint`: The timepoint (block number and transaction index) of the first approval", + " transaction for this dispatch.", + " - `call_hash`: The hash of the call to be executed.", + "", + " # ", + " - `O(S)`.", + " - Up to one balance-reserve or unreserve operation.", + " - One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + " - One encode & hash, both of complexity `O(S)`.", + " - One event.", + " - I/O: 1 read `O(S)`, one remove.", + " - Storage: removes one item.", + " ----------------------------------", + " - DB Weight:", + " - Read: Multisig Storage, [Caller Account], Refund Account, Calls", + " - Write: Multisig Storage, [Caller Account], Refund Account, Calls", + " # " + ] + } + ], + "events": [ + { + "name": "NewMultisig", + "args": [ + "AccountId", + "AccountId", + "CallHash" + ], + "documentation": [ + " A new multisig operation has begun. \\[approving, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigApproval", + "args": [ + "AccountId", + "Timepoint", + "AccountId", + "CallHash" + ], + "documentation": [ + " A multisig operation has been approved by someone.", + " \\[approving, timepoint, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigExecuted", + "args": [ + "AccountId", + "Timepoint", + "AccountId", + "CallHash", + "DispatchResult" + ], + "documentation": [ + " A multisig operation has been executed. \\[approving, timepoint, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigCancelled", + "args": [ + "AccountId", + "Timepoint", + "AccountId", + "CallHash" + ], + "documentation": [ + " A multisig operation has been cancelled. \\[cancelling, timepoint, multisig, call_hash\\]" + ] + } + ], + "constants": [ + { + "name": "DepositBase", + "type": "BalanceOf", + "value": "0x00005f0a3b3c5b4b0000000000000000", + "documentation": [ + " The base amount of currency needed to reserve for creating a multisig execution or to store", + " a dispatch call for later." + ] + }, + { + "name": "DepositFactor", + "type": "BalanceOf", + "value": "0x0000c0d0d335a51a0000000000000000", + "documentation": [ + " The amount of currency needed per unit threshold when creating a multisig execution." + ] + }, + { + "name": "MaxSignatories", + "type": "u16", + "value": "0x6400", + "documentation": [ + " The maximum amount of signatories allowed for a given multisig." + ] + } + ], + "errors": [ + { + "name": "MinimumThreshold", + "documentation": [ + " Threshold must be 2 or greater." + ] + }, + { + "name": "AlreadyApproved", + "documentation": [ + " Call is already approved by this signatory." + ] + }, + { + "name": "NoApprovalsNeeded", + "documentation": [ + " Call doesn't need any (more) approvals." + ] + }, + { + "name": "TooFewSignatories", + "documentation": [ + " There are too few signatories in the list." + ] + }, + { + "name": "TooManySignatories", + "documentation": [ + " There are too many signatories in the list." + ] + }, + { + "name": "SignatoriesOutOfOrder", + "documentation": [ + " The signatories were provided out of order; they should be ordered." + ] + }, + { + "name": "SenderInSignatories", + "documentation": [ + " The sender was contained in the other signatories; it shouldn't be." + ] + }, + { + "name": "NotFound", + "documentation": [ + " Multisig operation not found when attempting to cancel." + ] + }, + { + "name": "NotOwner", + "documentation": [ + " Only the account that originally created the multisig is able to cancel it." + ] + }, + { + "name": "NoTimepoint", + "documentation": [ + " No timepoint was given, yet the multisig operation is already underway." + ] + }, + { + "name": "WrongTimepoint", + "documentation": [ + " A different timepoint was given to the multisig operation that is underway." + ] + }, + { + "name": "UnexpectedTimepoint", + "documentation": [ + " A timepoint was given, yet no multisig operation is underway." + ] + }, + { + "name": "WeightTooLow", + "documentation": [ + " The maximum weight information provided was too low." + ] + }, + { + "name": "AlreadyStored", + "documentation": [ + " The data to be stored is already stored." + ] + } + ], + "index": 28 + }, + { + "name": "Assets", + "storage": { + "prefix": "Assets", + "items": [ + { + "name": "Asset", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AssetId", + "value": "AssetDetails", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Details of an asset." + ] + }, + { + "name": "Account", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Blake2_128Concat", + "key1": "AssetId", + "key2": "AccountId", + "value": "AssetBalance", + "key2Hasher": "Blake2_128Concat" + } + }, + "fallback": "0x000000000000000000000000000000000000", + "documentation": [ + " The number of units of assets held by any given account." + ] + } + ] + }, + "calls": [ + { + "name": "create", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "admin", + "type": "LookupSource" + }, + { + "name": "max_zombies", + "type": "u32" + }, + { + "name": "min_balance", + "type": "TAssetBalance" + } + ], + "documentation": [ + " Issue a new class of fungible assets from a public origin.", + "", + " This new asset class has no assets initially.", + "", + " The origin must be Signed and the sender must have sufficient funds free.", + "", + " Funds of sender are reserved according to the formula:", + " `AssetDepositBase + AssetDepositPerZombie * max_zombies`.", + "", + " Parameters:", + " - `id`: The identifier of the new asset. This must not be currently in use to identify", + " an existing asset.", + " - `owner`: The owner of this class of assets. The owner has full superuser permissions", + " over this asset, but may later change and configure the permissions using `transfer_ownership`", + " and `set_team`.", + " - `max_zombies`: The total number of accounts which may hold assets in this class yet", + " have no existential deposit.", + " - `min_balance`: The minimum balance of this new asset that any single account must", + " have. If an account's balance is reduced below this, then it collapses to zero.", + "", + " Emits `Created` event when successful.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "force_create", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "owner", + "type": "LookupSource" + }, + { + "name": "max_zombies", + "type": "Compact" + }, + { + "name": "min_balance", + "type": "Compact" + } + ], + "documentation": [ + " Issue a new class of fungible assets from a privileged origin.", + "", + " This new asset class has no assets initially.", + "", + " The origin must conform to `ForceOrigin`.", + "", + " Unlike `create`, no funds are reserved.", + "", + " - `id`: The identifier of the new asset. This must not be currently in use to identify", + " an existing asset.", + " - `owner`: The owner of this class of assets. The owner has full superuser permissions", + " over this asset, but may later change and configure the permissions using `transfer_ownership`", + " and `set_team`.", + " - `max_zombies`: The total number of accounts which may hold assets in this class yet", + " have no existential deposit.", + " - `min_balance`: The minimum balance of this new asset that any single account must", + " have. If an account's balance is reduced below this, then it collapses to zero.", + "", + " Emits `ForceCreated` event when successful.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "destroy", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "zombies_witness", + "type": "Compact" + } + ], + "documentation": [ + " Destroy a class of fungible assets owned by the sender.", + "", + " The origin must be Signed and the sender must be the owner of the asset `id`.", + "", + " - `id`: The identifier of the asset to be destroyed. This must identify an existing", + " asset.", + "", + " Emits `Destroyed` event when successful.", + "", + " Weight: `O(z)` where `z` is the number of zombie accounts." + ] + }, + { + "name": "force_destroy", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "zombies_witness", + "type": "Compact" + } + ], + "documentation": [ + " Destroy a class of fungible assets.", + "", + " The origin must conform to `ForceOrigin`.", + "", + " - `id`: The identifier of the asset to be destroyed. This must identify an existing", + " asset.", + "", + " Emits `Destroyed` event when successful.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "mint", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "LookupSource" + }, + { + "name": "amount", + "type": "Compact" + } + ], + "documentation": [ + " Mint assets of a particular class.", + "", + " The origin must be Signed and the sender must be the Issuer of the asset `id`.", + "", + " - `id`: The identifier of the asset to have some amount minted.", + " - `beneficiary`: The account to be credited with the minted assets.", + " - `amount`: The amount of the asset to be minted.", + "", + " Emits `Destroyed` event when successful.", + "", + " Weight: `O(1)`", + " Modes: Pre-existing balance of `beneficiary`; Account pre-existence of `beneficiary`." + ] + }, + { + "name": "burn", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "who", + "type": "LookupSource" + }, + { + "name": "amount", + "type": "Compact" + } + ], + "documentation": [ + " Reduce the balance of `who` by as much as possible up to `amount` assets of `id`.", + "", + " Origin must be Signed and the sender should be the Manager of the asset `id`.", + "", + " Bails with `BalanceZero` if the `who` is already dead.", + "", + " - `id`: The identifier of the asset to have some amount burned.", + " - `who`: The account to be debited from.", + " - `amount`: The maximum amount by which `who`'s balance should be reduced.", + "", + " Emits `Burned` with the actual amount burned. If this takes the balance to below the", + " minimum for the asset, then the amount burned is increased to take it to zero.", + "", + " Weight: `O(1)`", + " Modes: Post-existence of `who`; Pre & post Zombie-status of `who`." + ] + }, + { + "name": "transfer", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "target", + "type": "LookupSource" + }, + { + "name": "amount", + "type": "Compact" + } + ], + "documentation": [ + " Move some assets from the sender account to another.", + "", + " Origin must be Signed.", + "", + " - `id`: The identifier of the asset to have some amount transferred.", + " - `target`: The account to be credited.", + " - `amount`: The amount by which the sender's balance of assets should be reduced and", + " `target`'s balance increased. The amount actually transferred may be slightly greater in", + " the case that the transfer would otherwise take the sender balance above zero but below", + " the minimum balance. Must be greater than zero.", + "", + " Emits `Transferred` with the actual amount transferred. If this takes the source balance", + " to below the minimum for the asset, then the amount transferred is increased to take it", + " to zero.", + "", + " Weight: `O(1)`", + " Modes: Pre-existence of `target`; Post-existence of sender; Prior & post zombie-status", + " of sender; Account pre-existence of `target`." + ] + }, + { + "name": "force_transfer", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "source", + "type": "LookupSource" + }, + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "amount", + "type": "Compact" + } + ], + "documentation": [ + " Move some assets from one account to another.", + "", + " Origin must be Signed and the sender should be the Admin of the asset `id`.", + "", + " - `id`: The identifier of the asset to have some amount transferred.", + " - `source`: The account to be debited.", + " - `dest`: The account to be credited.", + " - `amount`: The amount by which the `source`'s balance of assets should be reduced and", + " `dest`'s balance increased. The amount actually transferred may be slightly greater in", + " the case that the transfer would otherwise take the `source` balance above zero but", + " below the minimum balance. Must be greater than zero.", + "", + " Emits `Transferred` with the actual amount transferred. If this takes the source balance", + " to below the minimum for the asset, then the amount transferred is increased to take it", + " to zero.", + "", + " Weight: `O(1)`", + " Modes: Pre-existence of `dest`; Post-existence of `source`; Prior & post zombie-status", + " of `source`; Account pre-existence of `dest`." + ] + }, + { + "name": "freeze", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "who", + "type": "LookupSource" + } + ], + "documentation": [ + " Disallow further unprivileged transfers from an account.", + "", + " Origin must be Signed and the sender should be the Freezer of the asset `id`.", + "", + " - `id`: The identifier of the asset to be frozen.", + " - `who`: The account to be frozen.", + "", + " Emits `Frozen`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "thaw", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "who", + "type": "LookupSource" + } + ], + "documentation": [ + " Allow unprivileged transfers from an account again.", + "", + " Origin must be Signed and the sender should be the Admin of the asset `id`.", + "", + " - `id`: The identifier of the asset to be frozen.", + " - `who`: The account to be unfrozen.", + "", + " Emits `Thawed`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "transfer_ownership", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "new_owner", + "type": "LookupSource" + } + ], + "documentation": [ + " Change the Owner of an asset.", + "", + " Origin must be Signed and the sender should be the Owner of the asset `id`.", + "", + " - `id`: The identifier of the asset to be frozen.", + " - `owner`: The new Owner of this asset.", + "", + " Emits `OwnerChanged`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "set_team", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "issuer", + "type": "LookupSource" + }, + { + "name": "admin", + "type": "LookupSource" + }, + { + "name": "freezer", + "type": "LookupSource" + } + ], + "documentation": [ + " Change the Issuer, Admin and Freezer of an asset.", + "", + " Origin must be Signed and the sender should be the Owner of the asset `id`.", + "", + " - `id`: The identifier of the asset to be frozen.", + " - `issuer`: The new Issuer of this asset.", + " - `admin`: The new Admin of this asset.", + " - `freezer`: The new Freezer of this asset.", + "", + " Emits `TeamChanged`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "set_max_zombies", + "args": [ + { + "name": "id", + "type": "Compact" + }, + { + "name": "max_zombies", + "type": "Compact" + } + ], + "documentation": [] + } + ], + "events": [ + { + "name": "Created", + "args": [ + "AssetId", + "AccountId", + "AccountId" + ], + "documentation": [ + " Some asset class was created. \\[asset_id, creator, owner\\]" + ] + }, + { + "name": "Issued", + "args": [ + "AssetId", + "AccountId", + "TAssetBalance" + ], + "documentation": [ + " Some assets were issued. \\[asset_id, owner, total_supply\\]" + ] + }, + { + "name": "Transferred", + "args": [ + "AssetId", + "AccountId", + "AccountId", + "TAssetBalance" + ], + "documentation": [ + " Some assets were transferred. \\[asset_id, from, to, amount\\]" + ] + }, + { + "name": "Burned", + "args": [ + "AssetId", + "AccountId", + "TAssetBalance" + ], + "documentation": [ + " Some assets were destroyed. \\[asset_id, owner, balance\\]" + ] + }, + { + "name": "TeamChanged", + "args": [ + "AssetId", + "AccountId", + "AccountId", + "AccountId" + ], + "documentation": [ + " The management team changed \\[asset_id, issuer, admin, freezer\\]" + ] + }, + { + "name": "OwnerChanged", + "args": [ + "AssetId", + "AccountId" + ], + "documentation": [ + " The owner changed \\[asset_id, owner\\]" + ] + }, + { + "name": "Frozen", + "args": [ + "AssetId", + "AccountId" + ], + "documentation": [ + " Some account `who` was frozen. \\[asset_id, who\\]" + ] + }, + { + "name": "Thawed", + "args": [ + "AssetId", + "AccountId" + ], + "documentation": [ + " Some account `who` was thawed. \\[asset_id, who\\]" + ] + }, + { + "name": "Destroyed", + "args": [ + "AssetId" + ], + "documentation": [ + " An asset class was destroyed." + ] + }, + { + "name": "ForceCreated", + "args": [ + "AssetId", + "AccountId" + ], + "documentation": [ + " Some asset class was force-created. \\[asset_id, owner\\]" + ] + }, + { + "name": "MaxZombiesChanged", + "args": [ + "AssetId", + "u32" + ], + "documentation": [ + " The maximum amount of zombies allowed has changed. \\[asset_id, max_zombies\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "AmountZero", + "documentation": [ + " Transfer amount should be non-zero." + ] + }, + { + "name": "BalanceLow", + "documentation": [ + " Account balance must be greater than or equal to the transfer amount." + ] + }, + { + "name": "BalanceZero", + "documentation": [ + " Balance should be non-zero." + ] + }, + { + "name": "NoPermission", + "documentation": [ + " The signing account has no permission to do the operation." + ] + }, + { + "name": "Unknown", + "documentation": [ + " The given asset ID is unknown." + ] + }, + { + "name": "Frozen", + "documentation": [ + " The origin account is frozen." + ] + }, + { + "name": "InUse", + "documentation": [ + " The asset ID is already taken." + ] + }, + { + "name": "TooManyZombies", + "documentation": [ + " Too many zombie accounts in use." + ] + }, + { + "name": "RefsLeft", + "documentation": [ + " Attempt to destroy an asset class when non-zombie, reference-bearing accounts exist." + ] + }, + { + "name": "BadWitness", + "documentation": [ + " Invalid witness data given." + ] + }, + { + "name": "MinBalanceZero", + "documentation": [ + " Minimum balance should be non-zero." + ] + }, + { + "name": "Overflow", + "documentation": [ + " A mint operation lead to an overflow." + ] + }, + { + "name": "NoFreezingAllowed", + "documentation": [ + " The asset does not allow freezing or thawing functionality" + ] + }, + { + "name": "NoBurningAllowed", + "documentation": [ + " The asset does not allow burning functionality." + ] + }, + { + "name": "NoMintingAllowed", + "documentation": [ + " The asset does not allow minting functionality" + ] + } + ], + "index": 29 + }, + { + "name": "TreasuryReward", + "storage": { + "prefix": "TreasuryReward", + "items": [ + { + "name": "MintingInterval", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "documentation": [ + " Interval in number of blocks to reward treasury" + ] + }, + { + "name": "CurrentPayout", + "modifier": "Default", + "type": { + "plain": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "documentation": [ + " Current payout of module" + ] + } + ] + }, + "calls": [ + { + "name": "set_current_payout", + "args": [ + { + "name": "payout", + "type": "BalanceOf" + } + ], + "documentation": [ + " Sets the fixed treasury payout per minting interval." + ] + }, + { + "name": "set_minting_interval", + "args": [ + { + "name": "interval", + "type": "BlockNumber" + } + ], + "documentation": [ + " Sets the treasury minting interval." + ] + } + ], + "events": [ + { + "name": "TreasuryMinting", + "args": [ + "Balance", + "BlockNumber", + "AccountId" + ], + "documentation": [] + } + ], + "constants": [], + "errors": [], + "index": 32 + }, + { + "name": "Ethereum", + "storage": { + "prefix": "Ethereum", + "items": [ + { + "name": "Pending", + "modifier": "Default", + "type": { + "plain": "Vec<(EthTransaction,EthTransactionStatus,EthReceipt)>" + }, + "fallback": "0x00", + "documentation": [ + " Current building block's transactions and receipts." + ] + }, + { + "name": "CurrentBlock", + "modifier": "Optional", + "type": { + "plain": "EthBlock" + }, + "fallback": "0x00", + "documentation": [ + " The current Ethereum block." + ] + }, + { + "name": "CurrentReceipts", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The current Ethereum receipts." + ] + }, + { + "name": "CurrentTransactionStatuses", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " The current transaction statuses." + ] + } + ] + }, + "calls": [ + { + "name": "transact", + "args": [ + { + "name": "transaction", + "type": "EthTransaction" + } + ], + "documentation": [ + " Transact an Ethereum transaction." + ] + } + ], + "events": [ + { + "name": "Executed", + "args": [ + "H160", + "H160", + "H256", + "ExitReason" + ], + "documentation": [ + " An ethereum transaction was successfully executed. [from, to/contract_address, transaction_hash, exit_reason]" + ] + } + ], + "constants": [], + "errors": [], + "index": 33 + }, + { + "name": "EVM", + "storage": { + "prefix": "EVM", + "items": [ + { + "name": "AccountCodes", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "H160", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [] + }, + { + "name": "AccountStorages", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Blake2_128Concat", + "key1": "H160", + "key2": "H256", + "value": "H256", + "key2Hasher": "Blake2_128Concat" + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "documentation": [] + } + ] + }, + "calls": [ + { + "name": "withdraw", + "args": [ + { + "name": "address", + "type": "H160" + }, + { + "name": "value", + "type": "BalanceOf" + } + ], + "documentation": [ + " Withdraw balance from EVM into currency/balances module." + ] + }, + { + "name": "call", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "target", + "type": "H160" + }, + { + "name": "input", + "type": "Bytes" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u32" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "documentation": [ + " Issue an EVM call operation. This is similar to a message call transaction in Ethereum." + ] + }, + { + "name": "create", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "init", + "type": "Bytes" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u32" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "documentation": [ + " Issue an EVM create operation. This is similar to a contract creation transaction in", + " Ethereum." + ] + }, + { + "name": "create2", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "init", + "type": "Bytes" + }, + { + "name": "salt", + "type": "H256" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u32" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "documentation": [ + " Issue an EVM create2 operation." + ] + } + ], + "events": [ + { + "name": "Log", + "args": [ + "EvmLog" + ], + "documentation": [ + " Ethereum events from contracts." + ] + }, + { + "name": "Created", + "args": [ + "H160" + ], + "documentation": [ + " A contract has been created at given \\[address\\]." + ] + }, + { + "name": "CreatedFailed", + "args": [ + "H160" + ], + "documentation": [ + " A \\[contract\\] was attempted to be created, but the execution failed." + ] + }, + { + "name": "Executed", + "args": [ + "H160" + ], + "documentation": [ + " A \\[contract\\] has been executed successfully with states applied." + ] + }, + { + "name": "ExecutedFailed", + "args": [ + "H160" + ], + "documentation": [ + " A \\[contract\\] has been executed with errors. States are reverted with only gas fees applied." + ] + }, + { + "name": "BalanceDeposit", + "args": [ + "AccountId", + "H160", + "U256" + ], + "documentation": [ + " A deposit has been made at a given address. \\[sender, address, value\\]" + ] + }, + { + "name": "BalanceWithdraw", + "args": [ + "AccountId", + "H160", + "U256" + ], + "documentation": [ + " A withdrawal has been made from a given address. \\[sender, address, value\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "BalanceLow", + "documentation": [ + " Not enough balance to perform action" + ] + }, + { + "name": "FeeOverflow", + "documentation": [ + " Calculating total fee overflowed" + ] + }, + { + "name": "PaymentOverflow", + "documentation": [ + " Calculating total payment overflowed" + ] + }, + { + "name": "WithdrawFailed", + "documentation": [ + " Withdraw fee failed" + ] + }, + { + "name": "GasPriceTooLow", + "documentation": [ + " Gas price is too low." + ] + }, + { + "name": "InvalidNonce", + "documentation": [ + " Nonce is invalid" + ] + } + ], + "index": 34 + }, + { + "name": "ChainBridge", + "storage": { + "prefix": "ChainBridge", + "items": [ + { + "name": "ChainNonces", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_256", + "key": "ChainId", + "value": "DepositNonce", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " All whitelisted chains and their respective transaction counts" + ] + }, + { + "name": "RelayerThreshold", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x01000000", + "documentation": [ + " Number of votes required for a proposal to execute" + ] + }, + { + "name": "Relayers", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_256", + "key": "AccountId", + "value": "bool", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Tracks current relayer set" + ] + }, + { + "name": "RelayerCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "documentation": [ + " Number of relayers in set" + ] + }, + { + "name": "Votes", + "modifier": "Optional", + "type": { + "doubleMap": { + "hasher": "Blake2_256", + "key1": "ChainId", + "key2": "(DepositNonce,Proposal)", + "value": "ProposalVotes", + "key2Hasher": "Blake2_256" + } + }, + "fallback": "0x00", + "documentation": [ + " All known proposals.", + " The key is the hash of the call and the deposit ID, to ensure it's unique." + ] + }, + { + "name": "Resources", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_256", + "key": "ResourceId", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Utilized by the bridge software to map resource IDs to actual methods" + ] + } + ] + }, + "calls": [ + { + "name": "set_threshold", + "args": [ + { + "name": "threshold", + "type": "u32" + } + ], + "documentation": [ + " Sets the vote threshold for proposals.", + "", + " This threshold is used to determine how many votes are required", + " before a proposal is executed.", + "", + " # ", + " - O(1) lookup and insert", + " # " + ] + }, + { + "name": "set_resource", + "args": [ + { + "name": "id", + "type": "ResourceId" + }, + { + "name": "method", + "type": "Bytes" + } + ], + "documentation": [ + " Stores a method name on chain under an associated resource ID.", + "", + " # ", + " - O(1) write", + " # " + ] + }, + { + "name": "remove_resource", + "args": [ + { + "name": "id", + "type": "ResourceId" + } + ], + "documentation": [ + " Removes a resource ID from the resource mapping.", + "", + " After this call, bridge transfers with the associated resource ID will", + " be rejected.", + "", + " # ", + " - O(1) removal", + " # " + ] + }, + { + "name": "whitelist_chain", + "args": [ + { + "name": "id", + "type": "ChainId" + } + ], + "documentation": [ + " Enables a chain ID as a source or destination for a bridge transfer.", + "", + " # ", + " - O(1) lookup and insert", + " # " + ] + }, + { + "name": "add_relayer", + "args": [ + { + "name": "v", + "type": "AccountId" + } + ], + "documentation": [ + " Adds a new relayer to the relayer set.", + "", + " # ", + " - O(1) lookup and insert", + " # " + ] + }, + { + "name": "remove_relayer", + "args": [ + { + "name": "v", + "type": "AccountId" + } + ], + "documentation": [ + " Removes an existing relayer from the set.", + "", + " # ", + " - O(1) lookup and removal", + " # " + ] + }, + { + "name": "acknowledge_proposal", + "args": [ + { + "name": "nonce", + "type": "DepositNonce" + }, + { + "name": "src_id", + "type": "ChainId" + }, + { + "name": "r_id", + "type": "ResourceId" + }, + { + "name": "call", + "type": "Proposal" + } + ], + "documentation": [ + " Commits a vote in favour of the provided proposal.", + "", + " If a proposal with the given nonce and source chain ID does not already exist, it will", + " be created with an initial vote in favour from the caller.", + "", + " # ", + " - weight of proposed call, regardless of whether execution is performed", + " # " + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "nonce", + "type": "DepositNonce" + }, + { + "name": "src_id", + "type": "ChainId" + }, + { + "name": "r_id", + "type": "ResourceId" + }, + { + "name": "call", + "type": "Proposal" + } + ], + "documentation": [ + " Commits a vote against a provided proposal.", + "", + " # ", + " - Fixed, since execution of proposal should not be included", + " # " + ] + }, + { + "name": "eval_vote_state", + "args": [ + { + "name": "nonce", + "type": "DepositNonce" + }, + { + "name": "src_id", + "type": "ChainId" + }, + { + "name": "prop", + "type": "Proposal" + } + ], + "documentation": [ + " Evaluate the state of a proposal given the current vote threshold.", + "", + " A proposal with enough votes will be either executed or cancelled, and the status", + " will be updated accordingly.", + "", + " # ", + " - weight of proposed call, regardless of whether execution is performed", + " # " + ] + } + ], + "events": [ + { + "name": "RelayerThresholdChanged", + "args": [ + "u32" + ], + "documentation": [ + " Vote threshold has changed (new_threshold)" + ] + }, + { + "name": "ChainWhitelisted", + "args": [ + "ChainId" + ], + "documentation": [ + " Chain now available for transfers (chain_id)" + ] + }, + { + "name": "RelayerAdded", + "args": [ + "AccountId" + ], + "documentation": [ + " Relayer added to set" + ] + }, + { + "name": "RelayerRemoved", + "args": [ + "AccountId" + ], + "documentation": [ + " Relayer removed from set" + ] + }, + { + "name": "FungibleTransfer", + "args": [ + "ChainId", + "DepositNonce", + "ResourceId", + "U256", + "Bytes" + ], + "documentation": [ + " FunglibleTransfer is for relaying fungibles (dest_id, nonce, resource_id, amount, recipient, metadata)" + ] + }, + { + "name": "NonFungibleTransfer", + "args": [ + "ChainId", + "DepositNonce", + "ResourceId", + "Bytes", + "Bytes", + "Bytes" + ], + "documentation": [ + " NonFungibleTransfer is for relaying NFTS (dest_id, nonce, resource_id, token_id, recipient, metadata)" + ] + }, + { + "name": "GenericTransfer", + "args": [ + "ChainId", + "DepositNonce", + "ResourceId", + "Bytes" + ], + "documentation": [ + " GenericTransfer is for a generic data payload (dest_id, nonce, resource_id, metadata)" + ] + }, + { + "name": "VoteFor", + "args": [ + "ChainId", + "DepositNonce", + "AccountId" + ], + "documentation": [ + " Vote submitted in favour of proposal" + ] + }, + { + "name": "VoteAgainst", + "args": [ + "ChainId", + "DepositNonce", + "AccountId" + ], + "documentation": [ + " Vot submitted against proposal" + ] + }, + { + "name": "ProposalApproved", + "args": [ + "ChainId", + "DepositNonce" + ], + "documentation": [ + " Voting successful for a proposal" + ] + }, + { + "name": "ProposalRejected", + "args": [ + "ChainId", + "DepositNonce" + ], + "documentation": [ + " Voting rejected a proposal" + ] + }, + { + "name": "ProposalSucceeded", + "args": [ + "ChainId", + "DepositNonce" + ], + "documentation": [ + " Execution of call succeeded" + ] + }, + { + "name": "ProposalFailed", + "args": [ + "ChainId", + "DepositNonce" + ], + "documentation": [ + " Execution of call failed" + ] + } + ], + "constants": [ + { + "name": "ChainIdentity", + "type": "ChainId", + "value": "0x05", + "documentation": [] + }, + { + "name": "ProposalLifetime", + "type": "BlockNumber", + "value": "0x64000000", + "documentation": [] + }, + { + "name": "BridgeAccountId", + "type": "AccountId", + "value": "0x6d6f646c63622f62726964670000000000000000000000000000000000000000", + "documentation": [] + } + ], + "errors": [ + { + "name": "ThresholdNotSet", + "documentation": [ + " Relayer threshold not set" + ] + }, + { + "name": "InvalidChainId", + "documentation": [ + " Provided chain Id is not valid" + ] + }, + { + "name": "InvalidThreshold", + "documentation": [ + " Relayer threshold cannot be 0" + ] + }, + { + "name": "ChainNotWhitelisted", + "documentation": [ + " Interactions with this chain is not permitted" + ] + }, + { + "name": "ChainAlreadyWhitelisted", + "documentation": [ + " Chain has already been enabled" + ] + }, + { + "name": "ResourceDoesNotExist", + "documentation": [ + " Resource ID provided isn't mapped to anything" + ] + }, + { + "name": "RelayerAlreadyExists", + "documentation": [ + " Relayer already in set" + ] + }, + { + "name": "RelayerInvalid", + "documentation": [ + " Provided accountId is not a relayer" + ] + }, + { + "name": "MustBeRelayer", + "documentation": [ + " Protected operation, must be performed by relayer" + ] + }, + { + "name": "RelayerAlreadyVoted", + "documentation": [ + " Relayer has already submitted some vote for this proposal" + ] + }, + { + "name": "ProposalAlreadyExists", + "documentation": [ + " A proposal with these parameters has already been submitted" + ] + }, + { + "name": "ProposalDoesNotExist", + "documentation": [ + " No proposal with the ID was found" + ] + }, + { + "name": "ProposalNotComplete", + "documentation": [ + " Cannot complete proposal, needs more votes" + ] + }, + { + "name": "ProposalAlreadyComplete", + "documentation": [ + " Proposal has either failed or succeeded" + ] + }, + { + "name": "ProposalExpired", + "documentation": [ + " Lifetime of proposal has been exceeded" + ] + } + ], + "index": 35 + }, + { + "name": "EdgeBridge", + "storage": null, + "calls": [ + { + "name": "transfer_native", + "args": [ + { + "name": "amount", + "type": "BalanceOf" + }, + { + "name": "recipient", + "type": "Bytes" + }, + { + "name": "dest_id", + "type": "ChainId" + } + ], + "documentation": [ + " Transfers some amount of the native token to some recipient on a (whitelisted) destination chain." + ] + }, + { + "name": "transfer", + "args": [ + { + "name": "to", + "type": "AccountId" + }, + { + "name": "amount", + "type": "BalanceOf" + } + ], + "documentation": [ + " Executes a simple currency transfer using the bridge account as the source" + ] + } + ], + "events": [ + { + "name": "TransferOverBridge", + "args": [ + "AccountId", + "Bytes", + "ChainId", + "Balance" + ], + "documentation": [ + " A bridge transfer event from an Edgeware account to a destination account" + ] + } + ], + "constants": [ + { + "name": "NativeTokenId", + "type": "ResourceId", + "value": "0x0000000000000000000000000000003f7d6469b807c2755c10c2f89fc1a76d01", + "documentation": [] + } + ], + "errors": [], + "index": 36 + }, + { + "name": "Bounties", + "storage": { + "prefix": "Treasury", + "items": [ + { + "name": "BountyCount", + "modifier": "Default", + "type": { + "plain": "BountyIndex" + }, + "fallback": "0x00000000", + "documentation": [ + " Number of bounty proposals that have been made." + ] + }, + { + "name": "Bounties", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BountyIndex", + "value": "Bounty", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Bounties that have been made." + ] + }, + { + "name": "BountyDescriptions", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BountyIndex", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " The description of each bounty." + ] + }, + { + "name": "BountyApprovals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "documentation": [ + " Bounty indices that have been approved but not yet funded." + ] + } + ] + }, + "calls": [ + { + "name": "propose_bounty", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "description", + "type": "Bytes" + } + ], + "documentation": [ + " Propose a new bounty.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Payment: `TipReportDepositBase` will be reserved from the origin account, as well as", + " `DataDepositPerByte` for each byte in `reason`. It will be unreserved upon approval,", + " or slashed when rejected.", + "", + " - `curator`: The curator account whom will manage this bounty.", + " - `fee`: The curator fee.", + " - `value`: The total payment amount of this bounty, curator fee included.", + " - `description`: The description of this bounty." + ] + }, + { + "name": "approve_bounty", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + } + ], + "documentation": [ + " Approve a bounty proposal. At a later time, the bounty will be funded and become active", + " and the original deposit will be returned.", + "", + " May only be called from `T::ApproveOrigin`.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "propose_curator", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + }, + { + "name": "curator", + "type": "LookupSource" + }, + { + "name": "fee", + "type": "Compact" + } + ], + "documentation": [ + " Assign a curator to a funded bounty.", + "", + " May only be called from `T::ApproveOrigin`.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "unassign_curator", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + } + ], + "documentation": [ + " Unassign curator from a bounty.", + "", + " This function can only be called by the `RejectOrigin` a signed origin.", + "", + " If this function is called by the `RejectOrigin`, we assume that the curator is malicious", + " or inactive. As a result, we will slash the curator when possible.", + "", + " If the origin is the curator, we take this as a sign they are unable to do their job and", + " they willingly give up. We could slash them, but for now we allow them to recover their", + " deposit and exit without issue. (We may want to change this if it is abused.)", + "", + " Finally, the origin can be anyone if and only if the curator is \"inactive\". This allows", + " anyone in the community to call out that a curator is not doing their due diligence, and", + " we should pick a new curator. In this case the curator should also be slashed.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "accept_curator", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + } + ], + "documentation": [ + " Accept the curator role for a bounty.", + " A deposit will be reserved from curator and refund upon successful payout.", + "", + " May only be called from the curator.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "award_bounty", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "LookupSource" + } + ], + "documentation": [ + " Award bounty to a beneficiary account. The beneficiary will be able to claim the funds after a delay.", + "", + " The dispatch origin for this call must be the curator of this bounty.", + "", + " - `bounty_id`: Bounty ID to award.", + " - `beneficiary`: The beneficiary account whom will receive the payout.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "claim_bounty", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + } + ], + "documentation": [ + " Claim the payout from an awarded bounty after payout delay.", + "", + " The dispatch origin for this call must be the beneficiary of this bounty.", + "", + " - `bounty_id`: Bounty ID to claim.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "close_bounty", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + } + ], + "documentation": [ + " Cancel a proposed or active bounty. All the funds will be sent to treasury and", + " the curator deposit will be unreserved if possible.", + "", + " Only `T::RejectOrigin` is able to cancel a bounty.", + "", + " - `bounty_id`: Bounty ID to cancel.", + "", + " # ", + " - O(1).", + " # " + ] + }, + { + "name": "extend_bounty_expiry", + "args": [ + { + "name": "bounty_id", + "type": "Compact" + }, + { + "name": "_remark", + "type": "Bytes" + } + ], + "documentation": [ + " Extend the expiry time of an active bounty.", + "", + " The dispatch origin for this call must be the curator of this bounty.", + "", + " - `bounty_id`: Bounty ID to extend.", + " - `remark`: additional information.", + "", + " # ", + " - O(1).", + " # " + ] + } + ], + "events": [ + { + "name": "BountyProposed", + "args": [ + "BountyIndex" + ], + "documentation": [ + " New bounty proposal. \\[index\\]" + ] + }, + { + "name": "BountyRejected", + "args": [ + "BountyIndex", + "Balance" + ], + "documentation": [ + " A bounty proposal was rejected; funds were slashed. \\[index, bond\\]" + ] + }, + { + "name": "BountyBecameActive", + "args": [ + "BountyIndex" + ], + "documentation": [ + " A bounty proposal is funded and became active. \\[index\\]" + ] + }, + { + "name": "BountyAwarded", + "args": [ + "BountyIndex", + "AccountId" + ], + "documentation": [ + " A bounty is awarded to a beneficiary. \\[index, beneficiary\\]" + ] + }, + { + "name": "BountyClaimed", + "args": [ + "BountyIndex", + "Balance", + "AccountId" + ], + "documentation": [ + " A bounty is claimed by beneficiary. \\[index, payout, beneficiary\\]" + ] + }, + { + "name": "BountyCanceled", + "args": [ + "BountyIndex" + ], + "documentation": [ + " A bounty is cancelled. \\[index\\]" + ] + }, + { + "name": "BountyExtended", + "args": [ + "BountyIndex" + ], + "documentation": [ + " A bounty expiry is extended. \\[index\\]" + ] + } + ], + "constants": [ + { + "name": "DataDepositPerByte", + "type": "BalanceOf", + "value": "0x0000c16ff28623000000000000000000", + "documentation": [ + " The amount held on deposit per byte within bounty description." + ] + }, + { + "name": "BountyDepositBase", + "type": "BalanceOf", + "value": "0x0000e8890423c78a0000000000000000", + "documentation": [ + " The amount held on deposit for placing a bounty proposal." + ] + }, + { + "name": "BountyDepositPayoutDelay", + "type": "BlockNumber", + "value": "0xc0890100", + "documentation": [ + " The delay period for which a bounty beneficiary need to wait before claim the payout." + ] + }, + { + "name": "BountyCuratorDeposit", + "type": "Permill", + "value": "0x20a10700", + "documentation": [ + " Percentage of the curator fee that will be reserved upfront as deposit for bounty curator." + ] + }, + { + "name": "BountyValueMinimum", + "type": "BalanceOf", + "value": "0x000010632d5ec76b0500000000000000", + "documentation": [ + " Minimum value for a bounty." + ] + }, + { + "name": "MaximumReasonLength", + "type": "u32", + "value": "0x00400000", + "documentation": [ + " Maximum acceptable reason length." + ] + } + ], + "errors": [ + { + "name": "InsufficientProposersBalance", + "documentation": [ + " Proposer's balance is too low." + ] + }, + { + "name": "InvalidIndex", + "documentation": [ + " No proposal or bounty at that index." + ] + }, + { + "name": "ReasonTooBig", + "documentation": [ + " The reason given is just too big." + ] + }, + { + "name": "UnexpectedStatus", + "documentation": [ + " The bounty status is unexpected." + ] + }, + { + "name": "RequireCurator", + "documentation": [ + " Require bounty curator." + ] + }, + { + "name": "InvalidValue", + "documentation": [ + " Invalid bounty value." + ] + }, + { + "name": "InvalidFee", + "documentation": [ + " Invalid bounty fee." + ] + }, + { + "name": "PendingPayout", + "documentation": [ + " A bounty payout is pending.", + " To cancel the bounty, you must unassign and slash the curator." + ] + }, + { + "name": "Premature", + "documentation": [ + " The bounties cannot be claimed/closed because it's still in the countdown period." + ] + } + ], + "index": 37 + }, + { + "name": "Tips", + "storage": { + "prefix": "Treasury", + "items": [ + { + "name": "Tips", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "Hash", + "value": "OpenTip", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " TipsMap that are not yet completed. Keyed by the hash of `(reason, who)` from the value.", + " This has the insecure enumerable hash function since the key itself is already", + " guaranteed to be a secure hash." + ] + }, + { + "name": "Reasons", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "documentation": [ + " Simple preimage lookup from the reason's hash to the original data. Again, has an", + " insecure enumerable hash since the key is guaranteed to be the result of a secure hash." + ] + } + ] + }, + "calls": [ + { + "name": "report_awesome", + "args": [ + { + "name": "reason", + "type": "Bytes" + }, + { + "name": "who", + "type": "AccountId" + } + ], + "documentation": [ + " Report something `reason` that deserves a tip and claim any eventual the finder's fee.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Payment: `TipReportDepositBase` will be reserved from the origin account, as well as", + " `DataDepositPerByte` for each byte in `reason`.", + "", + " - `reason`: The reason for, or the thing that deserves, the tip; generally this will be", + " a UTF-8-encoded URL.", + " - `who`: The account which should be credited for the tip.", + "", + " Emits `NewTip` if successful.", + "", + " # ", + " - Complexity: `O(R)` where `R` length of `reason`.", + " - encoding and hashing of 'reason'", + " - DbReads: `Reasons`, `Tips`", + " - DbWrites: `Reasons`, `Tips`", + " # " + ] + }, + { + "name": "retract_tip", + "args": [ + { + "name": "hash", + "type": "Hash" + } + ], + "documentation": [ + " Retract a prior tip-report from `report_awesome`, and cancel the process of tipping.", + "", + " If successful, the original deposit will be unreserved.", + "", + " The dispatch origin for this call must be _Signed_ and the tip identified by `hash`", + " must have been reported by the signing account through `report_awesome` (and not", + " through `tip_new`).", + "", + " - `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the original tip `reason` and the beneficiary account ID.", + "", + " Emits `TipRetracted` if successful.", + "", + " # ", + " - Complexity: `O(1)`", + " - Depends on the length of `T::Hash` which is fixed.", + " - DbReads: `Tips`, `origin account`", + " - DbWrites: `Reasons`, `Tips`, `origin account`", + " # " + ] + }, + { + "name": "tip_new", + "args": [ + { + "name": "reason", + "type": "Bytes" + }, + { + "name": "who", + "type": "AccountId" + }, + { + "name": "tip_value", + "type": "Compact" + } + ], + "documentation": [ + " Give a tip for something new; no finder's fee will be taken.", + "", + " The dispatch origin for this call must be _Signed_ and the signing account must be a", + " member of the `Tippers` set.", + "", + " - `reason`: The reason for, or the thing that deserves, the tip; generally this will be", + " a UTF-8-encoded URL.", + " - `who`: The account which should be credited for the tip.", + " - `tip_value`: The amount of tip that the sender would like to give. The median tip", + " value of active tippers will be given to the `who`.", + "", + " Emits `NewTip` if successful.", + "", + " # ", + " - Complexity: `O(R + T)` where `R` length of `reason`, `T` is the number of tippers.", + " - `O(T)`: decoding `Tipper` vec of length `T`", + " `T` is charged as upper bound given by `ContainsLengthBound`.", + " The actual cost depends on the implementation of `T::Tippers`.", + " - `O(R)`: hashing and encoding of reason of length `R`", + " - DbReads: `Tippers`, `Reasons`", + " - DbWrites: `Reasons`, `Tips`", + " # " + ] + }, + { + "name": "tip", + "args": [ + { + "name": "hash", + "type": "Hash" + }, + { + "name": "tip_value", + "type": "Compact" + } + ], + "documentation": [ + " Declare a tip value for an already-open tip.", + "", + " The dispatch origin for this call must be _Signed_ and the signing account must be a", + " member of the `Tippers` set.", + "", + " - `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the hash of the original tip `reason` and the beneficiary", + " account ID.", + " - `tip_value`: The amount of tip that the sender would like to give. The median tip", + " value of active tippers will be given to the `who`.", + "", + " Emits `TipClosing` if the threshold of tippers has been reached and the countdown period", + " has started.", + "", + " # ", + " - Complexity: `O(T)` where `T` is the number of tippers.", + " decoding `Tipper` vec of length `T`, insert tip and check closing,", + " `T` is charged as upper bound given by `ContainsLengthBound`.", + " The actual cost depends on the implementation of `T::Tippers`.", + "", + " Actually weight could be lower as it depends on how many tips are in `OpenTip` but it", + " is weighted as if almost full i.e of length `T-1`.", + " - DbReads: `Tippers`, `Tips`", + " - DbWrites: `Tips`", + " # " + ] + }, + { + "name": "close_tip", + "args": [ + { + "name": "hash", + "type": "Hash" + } + ], + "documentation": [ + " Close and payout a tip.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " The tip identified by `hash` must have finished its countdown period.", + "", + " - `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the original tip `reason` and the beneficiary account ID.", + "", + " # ", + " - Complexity: `O(T)` where `T` is the number of tippers.", + " decoding `Tipper` vec of length `T`.", + " `T` is charged as upper bound given by `ContainsLengthBound`.", + " The actual cost depends on the implementation of `T::Tippers`.", + " - DbReads: `Tips`, `Tippers`, `tip finder`", + " - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder`", + " # " + ] + }, + { + "name": "slash_tip", + "args": [ + { + "name": "hash", + "type": "Hash" + } + ], + "documentation": [ + " Remove and slash an already-open tip.", + "", + " May only be called from `T::RejectOrigin`.", + "", + " As a result, the finder is slashed and the deposits are lost.", + "", + " Emits `TipSlashed` if successful.", + "", + " # ", + " `T` is charged as upper bound given by `ContainsLengthBound`.", + " The actual cost depends on the implementation of `T::Tippers`.", + " # " + ] + } + ], + "events": [ + { + "name": "NewTip", + "args": [ + "Hash" + ], + "documentation": [ + " A new tip suggestion has been opened. \\[tip_hash\\]" + ] + }, + { + "name": "TipClosing", + "args": [ + "Hash" + ], + "documentation": [ + " A tip suggestion has reached threshold and is closing. \\[tip_hash\\]" + ] + }, + { + "name": "TipClosed", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "documentation": [ + " A tip suggestion has been closed. \\[tip_hash, who, payout\\]" + ] + }, + { + "name": "TipRetracted", + "args": [ + "Hash" + ], + "documentation": [ + " A tip suggestion has been retracted. \\[tip_hash\\]" + ] + }, + { + "name": "TipSlashed", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "documentation": [ + " A tip suggestion has been slashed. \\[tip_hash, finder, deposit\\]" + ] + } + ], + "constants": [ + { + "name": "TipCountdown", + "type": "BlockNumber", + "value": "0x40380000", + "documentation": [ + " The period for which a tip remains open after is has achieved threshold tippers." + ] + }, + { + "name": "TipFindersFee", + "type": "Percent", + "value": "0x14", + "documentation": [ + " The amount of the final tip which goes to the original reporter of the tip." + ] + }, + { + "name": "TipReportDepositBase", + "type": "BalanceOf", + "value": "0x000064a7b3b6e00d0000000000000000", + "documentation": [ + " The amount held on deposit for placing a tip report." + ] + }, + { + "name": "DataDepositPerByte", + "type": "BalanceOf", + "value": "0x0000c16ff28623000000000000000000", + "documentation": [ + " The amount held on deposit per byte within the tip report reason." + ] + }, + { + "name": "MaximumReasonLength", + "type": "u32", + "value": "0x00400000", + "documentation": [ + " Maximum acceptable reason length." + ] + } + ], + "errors": [ + { + "name": "ReasonTooBig", + "documentation": [ + " The reason given is just too big." + ] + }, + { + "name": "AlreadyKnown", + "documentation": [ + " The tip was already found/started." + ] + }, + { + "name": "UnknownTip", + "documentation": [ + " The tip hash is unknown." + ] + }, + { + "name": "NotFinder", + "documentation": [ + " The account attempting to retract the tip is not the finder of the tip." + ] + }, + { + "name": "StillOpen", + "documentation": [ + " The tip cannot be claimed/closed because there are not enough tippers yet." + ] + }, + { + "name": "Premature", + "documentation": [ + " The tip cannot be claimed/closed because it's still in the countdown period." + ] + } + ], + "index": 38 + } + ], + "extrinsic": { + "version": 4, + "signedExtensions": [ + "CheckSpecVersion", + "CheckTxVersion", + "CheckGenesis", + "CheckMortality", + "CheckNonce", + "CheckWeight", + "ChargeTransactionPayment" + ] + } + } + } +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/KusamaMetaData.json b/javascore/bmv/eventDecoder/KusamaMetaData.json new file mode 100644 index 00000000..c779261d --- /dev/null +++ b/javascore/bmv/eventDecoder/KusamaMetaData.json @@ -0,0 +1,50776 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "v14": { + "lookup": { + "types": [ + { + "id": 0, + "type": { + "path": [ + "sp_core", + "crypto", + "AccountId32" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 1, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 32, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 2, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "U8" + }, + "docs": [] + } + }, + { + "id": 3, + "type": { + "path": [ + "frame_system", + "AccountInfo" + ], + "params": [ + { + "name": "Index", + "type": 4 + }, + { + "name": "AccountData", + "type": 5 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "nonce", + "type": 4, + "typeName": "Index", + "docs": [] + }, + { + "name": "consumers", + "type": 4, + "typeName": "RefCount", + "docs": [] + }, + { + "name": "providers", + "type": 4, + "typeName": "RefCount", + "docs": [] + }, + { + "name": "sufficients", + "type": 4, + "typeName": "RefCount", + "docs": [] + }, + { + "name": "data", + "type": 5, + "typeName": "AccountData", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 4, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "U32" + }, + "docs": [] + } + }, + { + "id": 5, + "type": { + "path": [ + "pallet_balances", + "AccountData" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "free", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "reserved", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "misc_frozen", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "fee_frozen", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 6, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "U128" + }, + "docs": [] + } + }, + { + "id": 7, + "type": { + "path": [ + "frame_support", + "weights", + "PerDispatchClass" + ], + "params": [ + { + "name": "T", + "type": 8 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "normal", + "type": 8, + "typeName": "T", + "docs": [] + }, + { + "name": "operational", + "type": 8, + "typeName": "T", + "docs": [] + }, + { + "name": "mandatory", + "type": 8, + "typeName": "T", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 8, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "U64" + }, + "docs": [] + } + }, + { + "id": 9, + "type": { + "path": [ + "primitive_types", + "H256" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 10, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 11, + "type": { + "path": [ + "sp_runtime", + "generic", + "digest", + "Digest" + ], + "params": [ + { + "name": "Hash", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "logs", + "type": 12, + "typeName": "Vec>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 12, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 13 + } + }, + "docs": [] + } + }, + { + "id": 13, + "type": { + "path": [ + "sp_runtime", + "generic", + "digest", + "DigestItem" + ], + "params": [ + { + "name": "Hash", + "type": 9 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ChangesTrieRoot", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "PreRuntime", + "fields": [ + { + "name": null, + "type": 14, + "typeName": "ConsensusEngineId", + "docs": [] + }, + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Consensus", + "fields": [ + { + "name": null, + "type": 14, + "typeName": "ConsensusEngineId", + "docs": [] + }, + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Seal", + "fields": [ + { + "name": null, + "type": 14, + "typeName": "ConsensusEngineId", + "docs": [] + }, + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "ChangesTrieSignal", + "fields": [ + { + "name": null, + "type": 15, + "typeName": "ChangesTrieSignal", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "Other", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "RuntimeEnvironmentUpdated", + "fields": [], + "index": 8, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 14, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 4, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 15, + "type": { + "path": [ + "sp_runtime", + "generic", + "digest", + "ChangesTrieSignal" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "NewConfiguration", + "fields": [ + { + "name": null, + "type": 16, + "typeName": "Option", + "docs": [] + } + ], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 16, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 17 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 17, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 17, + "type": { + "path": [ + "sp_core", + "changes_trie", + "ChangesTrieConfiguration" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "digest_interval", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "digest_levels", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 18, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 19 + } + }, + "docs": [] + } + }, + { + "id": 19, + "type": { + "path": [ + "frame_system", + "EventRecord" + ], + "params": [ + { + "name": "E", + "type": 20 + }, + { + "name": "T", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "phase", + "type": 142, + "typeName": "Phase", + "docs": [] + }, + { + "name": "event", + "type": 20, + "typeName": "E", + "docs": [] + }, + { + "name": "topics", + "type": 143, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 20, + "type": { + "path": [ + "kusama_runtime", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "System", + "fields": [ + { + "name": null, + "type": 21, + "typeName": "frame_system::Event", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Indices", + "fields": [ + { + "name": null, + "type": 28, + "typeName": "pallet_indices::Event", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Balances", + "fields": [ + { + "name": null, + "type": 29, + "typeName": "pallet_balances::Event", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Staking", + "fields": [ + { + "name": null, + "type": 31, + "typeName": "pallet_staking::Event", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Offences", + "fields": [ + { + "name": null, + "type": 32, + "typeName": "pallet_offences::Event", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "Session", + "fields": [ + { + "name": null, + "type": 34, + "typeName": "pallet_session::Event", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "Grandpa", + "fields": [ + { + "name": null, + "type": 35, + "typeName": "pallet_grandpa::Event", + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "ImOnline", + "fields": [ + { + "name": null, + "type": 40, + "typeName": "pallet_im_online::Event", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "Democracy", + "fields": [ + { + "name": null, + "type": 49, + "typeName": "pallet_democracy::Event", + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "Council", + "fields": [ + { + "name": null, + "type": 54, + "typeName": "pallet_collective::Event", + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "TechnicalCommittee", + "fields": [ + { + "name": null, + "type": 56, + "typeName": "pallet_collective::Event", + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "PhragmenElection", + "fields": [ + { + "name": null, + "type": 57, + "typeName": "pallet_elections_phragmen::Event", + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "TechnicalMembership", + "fields": [ + { + "name": null, + "type": 60, + "typeName": "pallet_membership::Event", + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "Treasury", + "fields": [ + { + "name": null, + "type": 61, + "typeName": "pallet_treasury::Event", + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "Claims", + "fields": [ + { + "name": null, + "type": 62, + "typeName": "claims::Event", + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "Utility", + "fields": [ + { + "name": null, + "type": 65, + "typeName": "pallet_utility::Event", + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Identity", + "fields": [ + { + "name": null, + "type": 66, + "typeName": "pallet_identity::Event", + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "Society", + "fields": [ + { + "name": null, + "type": 67, + "typeName": "pallet_society::Event", + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "Recovery", + "fields": [ + { + "name": null, + "type": 68, + "typeName": "pallet_recovery::Event", + "docs": [] + } + ], + "index": 27, + "docs": [] + }, + { + "name": "Vesting", + "fields": [ + { + "name": null, + "type": 69, + "typeName": "pallet_vesting::Event", + "docs": [] + } + ], + "index": 28, + "docs": [] + }, + { + "name": "Scheduler", + "fields": [ + { + "name": null, + "type": 70, + "typeName": "pallet_scheduler::Event", + "docs": [] + } + ], + "index": 29, + "docs": [] + }, + { + "name": "Proxy", + "fields": [ + { + "name": null, + "type": 73, + "typeName": "pallet_proxy::Event", + "docs": [] + } + ], + "index": 30, + "docs": [] + }, + { + "name": "Multisig", + "fields": [ + { + "name": null, + "type": 76, + "typeName": "pallet_multisig::Event", + "docs": [] + } + ], + "index": 31, + "docs": [] + }, + { + "name": "Bounties", + "fields": [ + { + "name": null, + "type": 78, + "typeName": "pallet_bounties::Event", + "docs": [] + } + ], + "index": 35, + "docs": [] + }, + { + "name": "Tips", + "fields": [ + { + "name": null, + "type": 79, + "typeName": "pallet_tips::Event", + "docs": [] + } + ], + "index": 36, + "docs": [] + }, + { + "name": "ElectionProviderMultiPhase", + "fields": [ + { + "name": null, + "type": 80, + "typeName": "pallet_election_provider_multi_phase::Event", + "docs": [] + } + ], + "index": 37, + "docs": [] + }, + { + "name": "Gilt", + "fields": [ + { + "name": null, + "type": 83, + "typeName": "pallet_gilt::Event", + "docs": [] + } + ], + "index": 38, + "docs": [] + }, + { + "name": "BagsList", + "fields": [ + { + "name": null, + "type": 84, + "typeName": "pallet_bags_list::Event", + "docs": [] + } + ], + "index": 39, + "docs": [] + }, + { + "name": "ParaInclusion", + "fields": [ + { + "name": null, + "type": 85, + "typeName": "parachains_inclusion::Event", + "docs": [] + } + ], + "index": 53, + "docs": [] + }, + { + "name": "Paras", + "fields": [ + { + "name": null, + "type": 97, + "typeName": "parachains_paras::Event", + "docs": [] + } + ], + "index": 56, + "docs": [] + }, + { + "name": "Ump", + "fields": [ + { + "name": null, + "type": 98, + "typeName": "parachains_ump::Event", + "docs": [] + } + ], + "index": 59, + "docs": [] + }, + { + "name": "Hrmp", + "fields": [ + { + "name": null, + "type": 101, + "typeName": "parachains_hrmp::Event", + "docs": [] + } + ], + "index": 60, + "docs": [] + }, + { + "name": "Registrar", + "fields": [ + { + "name": null, + "type": 103, + "typeName": "paras_registrar::Event", + "docs": [] + } + ], + "index": 70, + "docs": [] + }, + { + "name": "Slots", + "fields": [ + { + "name": null, + "type": 104, + "typeName": "slots::Event", + "docs": [] + } + ], + "index": 71, + "docs": [] + }, + { + "name": "Auctions", + "fields": [ + { + "name": null, + "type": 105, + "typeName": "auctions::Event", + "docs": [] + } + ], + "index": 72, + "docs": [] + }, + { + "name": "Crowdloan", + "fields": [ + { + "name": null, + "type": 106, + "typeName": "crowdloan::Event", + "docs": [] + } + ], + "index": 73, + "docs": [] + }, + { + "name": "XcmPallet", + "fields": [ + { + "name": null, + "type": 107, + "typeName": "pallet_xcm::Event", + "docs": [] + } + ], + "index": 99, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 21, + "type": { + "path": [ + "frame_system", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ExtrinsicSuccess", + "fields": [ + { + "name": null, + "type": 22, + "typeName": "DispatchInfo", + "docs": [] + } + ], + "index": 0, + "docs": [ + "An extrinsic completed successfully. \\[info\\]" + ] + }, + { + "name": "ExtrinsicFailed", + "fields": [ + { + "name": null, + "type": 25, + "typeName": "DispatchError", + "docs": [] + }, + { + "name": null, + "type": 22, + "typeName": "DispatchInfo", + "docs": [] + } + ], + "index": 1, + "docs": [ + "An extrinsic failed. \\[error, info\\]" + ] + }, + { + "name": "CodeUpdated", + "fields": [], + "index": 2, + "docs": [ + "`:code` was updated." + ] + }, + { + "name": "NewAccount", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A new \\[account\\] was created." + ] + }, + { + "name": "KilledAccount", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "An \\[account\\] was reaped." + ] + }, + { + "name": "Remarked", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 5, + "docs": [ + "On on-chain remark happened. \\[origin, remark_hash\\]" + ] + } + ] + } + }, + "docs": [ + "Event for the System pallet." + ] + } + }, + { + "id": 22, + "type": { + "path": [ + "frame_support", + "weights", + "DispatchInfo" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "weight", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "class", + "type": 23, + "typeName": "DispatchClass", + "docs": [] + }, + { + "name": "pays_fee", + "type": 24, + "typeName": "Pays", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 23, + "type": { + "path": [ + "frame_support", + "weights", + "DispatchClass" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Normal", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Operational", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Mandatory", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 24, + "type": { + "path": [ + "frame_support", + "weights", + "Pays" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Yes", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "No", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 25, + "type": { + "path": [ + "sp_runtime", + "DispatchError" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Other", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "CannotLookup", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "BadOrigin", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Module", + "fields": [ + { + "name": "index", + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": "error", + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "ConsumerRemaining", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "NoProviders", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Token", + "fields": [ + { + "name": null, + "type": 26, + "typeName": "TokenError", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Arithmetic", + "fields": [ + { + "name": null, + "type": 27, + "typeName": "ArithmeticError", + "docs": [] + } + ], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 26, + "type": { + "path": [ + "sp_runtime", + "TokenError" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "NoFunds", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "WouldDie", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "BelowMinimum", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "CannotCreate", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "UnknownAsset", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "Frozen", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Unsupported", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 27, + "type": { + "path": [ + "sp_runtime", + "ArithmeticError" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Underflow", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Overflow", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "DivisionByZero", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 28, + "type": { + "path": [ + "pallet_indices", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "IndexAssigned", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A account index was assigned. \\[index, who\\]" + ] + }, + { + "name": "IndexFreed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A account index has been freed up (unassigned). \\[index\\]" + ] + }, + { + "name": "IndexFrozen", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A account index has been frozen to its current account ID. \\[index, who\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 29, + "type": { + "path": [ + "pallet_balances", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Endowed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 0, + "docs": [ + "An account was created with some free balance. \\[account, free_balance\\]" + ] + }, + { + "name": "DustLost", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 1, + "docs": [ + "An account was removed whose balance was non-zero but below ExistentialDeposit,", + "resulting in an outright loss. \\[account, balance\\]" + ] + }, + { + "name": "Transfer", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Transfer succeeded. \\[from, to, value\\]" + ] + }, + { + "name": "BalanceSet", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A balance was set by root. \\[who, free, reserved\\]" + ] + }, + { + "name": "Deposit", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Some amount was deposited (e.g. for transaction fees). \\[who, deposit\\]" + ] + }, + { + "name": "Reserved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Some balance was reserved (moved from free to reserved). \\[who, value\\]" + ] + }, + { + "name": "Unreserved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Some balance was unreserved (moved from reserved to free). \\[who, value\\]" + ] + }, + { + "name": "ReserveRepatriated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "T::Balance", + "docs": [] + }, + { + "name": null, + "type": 30, + "typeName": "Status", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Some balance was moved from the reserve of the first account to the second account.", + "Final argument indicates the destination balance type.", + "\\[from, to, balance, destination_status\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 30, + "type": { + "path": [ + "frame_support", + "traits", + "tokens", + "misc", + "BalanceStatus" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Free", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Reserved", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 31, + "type": { + "path": [ + "pallet_staking", + "pallet", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "EraPaid", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "The era payout has been set; the first balance is the validator-payout; the second is", + "the remainder from the maximum amount of reward.", + "\\[era_index, validator_payout, remainder\\]" + ] + }, + { + "name": "Rewarded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "The nominator has been rewarded by this amount. \\[stash, amount\\]" + ] + }, + { + "name": "Slashed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "One validator (and its nominators) has been slashed by the given amount.", + "\\[validator, amount\\]" + ] + }, + { + "name": "OldSlashingReportDiscarded", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "SessionIndex", + "docs": [] + } + ], + "index": 3, + "docs": [ + "An old slashing report from a prior era was discarded because it could", + "not be processed. \\[session_index\\]" + ] + }, + { + "name": "StakersElected", + "fields": [], + "index": 4, + "docs": [ + "A new set of stakers was elected." + ] + }, + { + "name": "Bonded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 5, + "docs": [ + "An account has bonded this amount. \\[stash, amount\\]", + "", + "NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably,", + "it will not be emitted for staking rewards when they are added to stake." + ] + }, + { + "name": "Unbonded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 6, + "docs": [ + "An account has unbonded this amount. \\[stash, amount\\]" + ] + }, + { + "name": "Withdrawn", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 7, + "docs": [ + "An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance`", + "from the unlocking queue. \\[stash, amount\\]" + ] + }, + { + "name": "Kicked", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 8, + "docs": [ + "A nominator has been kicked from a validator. \\[nominator, stash\\]" + ] + }, + { + "name": "StakingElectionFailed", + "fields": [], + "index": 9, + "docs": [ + "The election failed. No new era is planned." + ] + }, + { + "name": "Chilled", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 10, + "docs": [ + "An account has stopped participating as either a validator or nominator.", + "\\[stash\\]" + ] + }, + { + "name": "PayoutStarted", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 11, + "docs": [ + "The stakers' rewards are getting paid. \\[era_index, validator_stash\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 32, + "type": { + "path": [ + "pallet_offences", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Offence", + "fields": [ + { + "name": null, + "type": 33, + "typeName": "Kind", + "docs": [] + }, + { + "name": null, + "type": 10, + "typeName": "OpaqueTimeSlot", + "docs": [] + } + ], + "index": 0, + "docs": [ + "There is an offence reported of the given `kind` happened at the `session_index` and", + "(kind-specific) time slot. This event is not deposited for duplicate slashes.", + "\\[kind, timeslot\\]." + ] + } + ] + } + }, + "docs": [ + "Events type." + ] + } + }, + { + "id": 33, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 16, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 34, + "type": { + "path": [ + "pallet_session", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "NewSession", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "SessionIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "New session has happened. Note that the argument is the \\[session_index\\], not the", + "block number as the type might suggest." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 35, + "type": { + "path": [ + "pallet_grandpa", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "NewAuthorities", + "fields": [ + { + "name": null, + "type": 36, + "typeName": "AuthorityList", + "docs": [] + } + ], + "index": 0, + "docs": [ + "New authority set has been applied. \\[authority_set\\]" + ] + }, + { + "name": "Paused", + "fields": [], + "index": 1, + "docs": [ + "Current authority set has been paused." + ] + }, + { + "name": "Resumed", + "fields": [], + "index": 2, + "docs": [ + "Current authority set has been resumed." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 36, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 37 + } + }, + "docs": [] + } + }, + { + "id": 37, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 38, + 8 + ] + }, + "docs": [] + } + }, + { + "id": 38, + "type": { + "path": [ + "sp_finality_grandpa", + "app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 39, + "typeName": "ed25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 39, + "type": { + "path": [ + "sp_core", + "ed25519", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 40, + "type": { + "path": [ + "pallet_im_online", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "HeartbeatReceived", + "fields": [ + { + "name": null, + "type": 41, + "typeName": "T::AuthorityId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A new heartbeat was received from `AuthorityId` \\[authority_id\\]" + ] + }, + { + "name": "AllGood", + "fields": [], + "index": 1, + "docs": [ + "At the end of the session, no offence was committed." + ] + }, + { + "name": "SomeOffline", + "fields": [ + { + "name": null, + "type": 43, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "At the end of the session, at least one validator was found to be \\[offline\\]." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 41, + "type": { + "path": [ + "pallet_im_online", + "sr25519", + "app_sr25519", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 42, + "type": { + "path": [ + "sp_core", + "sr25519", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 43, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 44 + } + }, + "docs": [] + } + }, + { + "id": 44, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 45 + ] + }, + "docs": [] + } + }, + { + "id": 45, + "type": { + "path": [ + "pallet_staking", + "Exposure" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "total", + "type": 46, + "typeName": "Balance", + "docs": [] + }, + { + "name": "own", + "type": 46, + "typeName": "Balance", + "docs": [] + }, + { + "name": "others", + "type": 47, + "typeName": "Vec>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 46, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 6 + } + }, + "docs": [] + } + }, + { + "id": 47, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 48 + } + }, + "docs": [] + } + }, + { + "id": 48, + "type": { + "path": [ + "pallet_staking", + "IndividualExposure" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 49, + "type": { + "path": [ + "pallet_democracy", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Proposed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "PropIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A motion has been proposed by a public account. \\[proposal_index, deposit\\]" + ] + }, + { + "name": "Tabled", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "PropIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 50, + "typeName": "Vec", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A public proposal has been tabled for referendum vote. \\[proposal_index, deposit,", + "depositors\\]" + ] + }, + { + "name": "ExternalTabled", + "fields": [], + "index": 2, + "docs": [ + "An external proposal has been tabled." + ] + }, + { + "name": "Started", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + }, + { + "name": null, + "type": 51, + "typeName": "VoteThreshold", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A referendum has begun. \\[ref_index, threshold\\]" + ] + }, + { + "name": "Passed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A proposal has been approved by referendum. \\[ref_index\\]" + ] + }, + { + "name": "NotPassed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A proposal has been rejected by referendum. \\[ref_index\\]" + ] + }, + { + "name": "Cancelled", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A referendum has been cancelled. \\[ref_index\\]" + ] + }, + { + "name": "Executed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 7, + "docs": [ + "A proposal has been enacted. \\[ref_index, result\\]" + ] + }, + { + "name": "Delegated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 8, + "docs": [ + "An account has delegated their vote to another account. \\[who, target\\]" + ] + }, + { + "name": "Undelegated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 9, + "docs": [ + "An \\[account\\] has cancelled a previous delegation operation." + ] + }, + { + "name": "Vetoed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 10, + "docs": [ + "An external proposal has been vetoed. \\[who, proposal_hash, until\\]" + ] + }, + { + "name": "PreimageNoted", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 11, + "docs": [ + "A proposal's preimage was noted, and the deposit taken. \\[proposal_hash, who, deposit\\]" + ] + }, + { + "name": "PreimageUsed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 12, + "docs": [ + "A proposal preimage was removed and used (the deposit was returned).", + "\\[proposal_hash, provider, deposit\\]" + ] + }, + { + "name": "PreimageInvalid", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 13, + "docs": [ + "A proposal could not be executed because its preimage was invalid.", + "\\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageMissing", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 14, + "docs": [ + "A proposal could not be executed because its preimage was missing.", + "\\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageReaped", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 15, + "docs": [ + "A registered preimage was removed and the deposit collected by the reaper.", + "\\[proposal_hash, provider, deposit, reaper\\]" + ] + }, + { + "name": "Blacklisted", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 16, + "docs": [ + "A proposal \\[hash\\] has been blacklisted permanently." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 50, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 0 + } + }, + "docs": [] + } + }, + { + "id": 51, + "type": { + "path": [ + "pallet_democracy", + "vote_threshold", + "VoteThreshold" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "SuperMajorityApprove", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "SuperMajorityAgainst", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "SimpleMajority", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 52, + "type": { + "path": [ + "Result" + ], + "params": [ + { + "name": "T", + "type": 53 + }, + { + "name": "E", + "type": 25 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Ok", + "fields": [ + { + "name": null, + "type": 53, + "typeName": null, + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Err", + "fields": [ + { + "name": null, + "type": 25, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 53, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [] + }, + "docs": [] + } + }, + { + "id": 54, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Proposed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A motion (given hash) has been proposed (by given account) with a threshold (given", + "`MemberCount`).", + "\\[account, proposal_index, proposal_hash, threshold\\]" + ] + }, + { + "name": "Voted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A motion (given hash) has been voted on by given account, leaving", + "a tally (yes votes and no votes given respectively as `MemberCount`).", + "\\[account, proposal_hash, voted, yes, no\\]" + ] + }, + { + "name": "Approved", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A motion was approved by the required threshold.", + "\\[proposal_hash\\]" + ] + }, + { + "name": "Disapproved", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A motion was not approved by the required threshold.", + "\\[proposal_hash\\]" + ] + }, + { + "name": "Executed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A motion was executed; result will be `Ok` if it returned without error.", + "\\[proposal_hash, result\\]" + ] + }, + { + "name": "MemberExecuted", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A single member did some action; result will be `Ok` if it returned without error.", + "\\[proposal_hash, result\\]" + ] + }, + { + "name": "Closed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A proposal was closed because its threshold was reached or after its duration was up.", + "\\[proposal_hash, yes, no\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 55, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "Bool" + }, + "docs": [] + } + }, + { + "id": 56, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Proposed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A motion (given hash) has been proposed (by given account) with a threshold (given", + "`MemberCount`).", + "\\[account, proposal_index, proposal_hash, threshold\\]" + ] + }, + { + "name": "Voted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A motion (given hash) has been voted on by given account, leaving", + "a tally (yes votes and no votes given respectively as `MemberCount`).", + "\\[account, proposal_hash, voted, yes, no\\]" + ] + }, + { + "name": "Approved", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A motion was approved by the required threshold.", + "\\[proposal_hash\\]" + ] + }, + { + "name": "Disapproved", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A motion was not approved by the required threshold.", + "\\[proposal_hash\\]" + ] + }, + { + "name": "Executed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A motion was executed; result will be `Ok` if it returned without error.", + "\\[proposal_hash, result\\]" + ] + }, + { + "name": "MemberExecuted", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A single member did some action; result will be `Ok` if it returned without error.", + "\\[proposal_hash, result\\]" + ] + }, + { + "name": "Closed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A proposal was closed because its threshold was reached or after its duration was up.", + "\\[proposal_hash, yes, no\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 57, + "type": { + "path": [ + "pallet_elections_phragmen", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NewTerm", + "fields": [ + { + "name": null, + "type": 58, + "typeName": "Vec<(::AccountId, BalanceOf)>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A new term with \\[new_members\\]. This indicates that enough candidates existed to run", + "the election, not that enough have has been elected. The inner value must be examined", + "for this purpose. A `NewTerm(\\[\\])` indicates that some candidates got their bond", + "slashed and none were elected, whilst `EmptyTerm` means that no candidates existed to", + "begin with." + ] + }, + { + "name": "EmptyTerm", + "fields": [], + "index": 1, + "docs": [ + "No (or not enough) candidates existed for this round. This is different from", + "`NewTerm(\\[\\])`. See the description of `NewTerm`." + ] + }, + { + "name": "ElectionError", + "fields": [], + "index": 2, + "docs": [ + "Internal error happened while trying to perform election." + ] + }, + { + "name": "MemberKicked", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A \\[member\\] has been removed. This should always be followed by either `NewTerm` or", + "`EmptyTerm`." + ] + }, + { + "name": "Renounced", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Someone has renounced their candidacy." + ] + }, + { + "name": "CandidateSlashed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A \\[candidate\\] was slashed by \\[amount\\] due to failing to obtain a seat as member or", + "runner-up.", + "", + "Note that old members and runners-up are also candidates." + ] + }, + { + "name": "SeatHolderSlashed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A \\[seat holder\\] was slashed by \\[amount\\] by being forcefully removed from the set." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 58, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 59 + } + }, + "docs": [] + } + }, + { + "id": 59, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 60, + "type": { + "path": [ + "pallet_membership", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "MemberAdded", + "fields": [], + "index": 0, + "docs": [ + "The given member was added; see the transaction for who." + ] + }, + { + "name": "MemberRemoved", + "fields": [], + "index": 1, + "docs": [ + "The given member was removed; see the transaction for who." + ] + }, + { + "name": "MembersSwapped", + "fields": [], + "index": 2, + "docs": [ + "Two members were swapped; see the transaction for who." + ] + }, + { + "name": "MembersReset", + "fields": [], + "index": 3, + "docs": [ + "The membership was reset; see the transaction for who the new set is." + ] + }, + { + "name": "KeyChanged", + "fields": [], + "index": 4, + "docs": [ + "One of the members' keys changed." + ] + }, + { + "name": "Dummy", + "fields": [], + "index": 5, + "docs": [ + "Phantom member, never used." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 61, + "type": { + "path": [ + "pallet_treasury", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Proposed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "New proposal. \\[proposal_index\\]" + ] + }, + { + "name": "Spending", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "We have ended a spend period and will now allocate funds. \\[budget_remaining\\]" + ] + }, + { + "name": "Awarded", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Some funds have been allocated. \\[proposal_index, award, beneficiary\\]" + ] + }, + { + "name": "Rejected", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A proposal was rejected; funds were slashed. \\[proposal_index, slashed\\]" + ] + }, + { + "name": "Burnt", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Some of our funds have been burnt. \\[burn\\]" + ] + }, + { + "name": "Rollover", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Spending has finished; this is the amount that rolls over until next spend.", + "\\[budget_remaining\\]" + ] + }, + { + "name": "Deposit", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Some funds have been deposited. \\[deposit\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 62, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Claimed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 63, + "typeName": "EthereumAddress", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Someone claimed some DOTs. `[who, ethereum_address, amount]`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 63, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "EthereumAddress" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 64, + "typeName": "[u8; 20]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 64, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 20, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 65, + "type": { + "path": [ + "pallet_utility", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "BatchInterrupted", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": null, + "type": 25, + "typeName": "DispatchError", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Batch of dispatches did not complete fully. Index of first failing dispatch given, as", + "well as the error. \\[index, error\\]" + ] + }, + { + "name": "BatchCompleted", + "fields": [], + "index": 1, + "docs": [ + "Batch of dispatches completed fully with no error." + ] + }, + { + "name": "ItemCompleted", + "fields": [], + "index": 2, + "docs": [ + "A single item within a Batch of dispatches has completed with no error." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 66, + "type": { + "path": [ + "pallet_identity", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "IdentitySet", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A name was set or reset (which will remove all judgements). \\[who\\]" + ] + }, + { + "name": "IdentityCleared", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A name was cleared, and the given balance returned. \\[who, deposit\\]" + ] + }, + { + "name": "IdentityKilled", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A name was removed and the given balance slashed. \\[who, deposit\\]" + ] + }, + { + "name": "JudgementRequested", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "RegistrarIndex", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A judgement was asked from a registrar. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementUnrequested", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "RegistrarIndex", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A judgement request was retracted. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementGiven", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "RegistrarIndex", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A judgement was given by a registrar. \\[target, registrar_index\\]" + ] + }, + { + "name": "RegistrarAdded", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "RegistrarIndex", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A registrar was added. \\[registrar_index\\]" + ] + }, + { + "name": "SubIdentityAdded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 7, + "docs": [ + "A sub-identity was added to an identity and the deposit paid. \\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRemoved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 8, + "docs": [ + "A sub-identity was removed from an identity and the deposit freed.", + "\\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRevoked", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 9, + "docs": [ + "A sub-identity was cleared, and the given deposit repatriated from the", + "main identity account to the sub-identity account. \\[sub, main, deposit\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 67, + "type": { + "path": [ + "pallet_society", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Founded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "The society is founded by the given identity. \\[founder\\]" + ] + }, + { + "name": "Bid", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A membership bid just happened. The given account is the candidate's ID and their offer", + "is the second. \\[candidate_id, offer\\]" + ] + }, + { + "name": "Vouch", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A membership bid just happened by vouching. The given account is the candidate's ID and", + "their offer is the second. The vouching party is the third. \\[candidate_id, offer,", + "vouching\\]" + ] + }, + { + "name": "AutoUnbid", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A \\[candidate\\] was dropped (due to an excess of bids in the system)." + ] + }, + { + "name": "Unbid", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A \\[candidate\\] was dropped (by their request)." + ] + }, + { + "name": "Unvouch", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A \\[candidate\\] was dropped (by request of who vouched for them)." + ] + }, + { + "name": "Inducted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 50, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A group of candidates have been inducted. The batch's primary is the first value, the", + "batch in full is the second. \\[primary, candidates\\]" + ] + }, + { + "name": "SuspendedMemberJudgement", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 7, + "docs": [ + "A suspended member has been judged. \\[who, judged\\]" + ] + }, + { + "name": "CandidateSuspended", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 8, + "docs": [ + "A \\[candidate\\] has been suspended" + ] + }, + { + "name": "MemberSuspended", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 9, + "docs": [ + "A \\[member\\] has been suspended" + ] + }, + { + "name": "Challenged", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 10, + "docs": [ + "A \\[member\\] has been challenged" + ] + }, + { + "name": "Vote", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 11, + "docs": [ + "A vote has been placed \\[candidate, voter, vote\\]" + ] + }, + { + "name": "DefenderVote", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 12, + "docs": [ + "A vote has been placed for a defending member \\[voter, vote\\]" + ] + }, + { + "name": "NewMaxMembers", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 13, + "docs": [ + "A new \\[max\\] member count has been set" + ] + }, + { + "name": "Unfounded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 14, + "docs": [ + "Society is unfounded. \\[founder\\]" + ] + }, + { + "name": "Deposit", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 15, + "docs": [ + "Some funds were deposited into the society account. \\[value\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 68, + "type": { + "path": [ + "pallet_recovery", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "RecoveryCreated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A recovery process has been set up for an \\[account\\]." + ] + }, + { + "name": "RecoveryInitiated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A recovery process has been initiated for lost account by rescuer account.", + "\\[lost, rescuer\\]" + ] + }, + { + "name": "RecoveryVouched", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A recovery process for lost account by rescuer account has been vouched for by sender.", + "\\[lost, rescuer, sender\\]" + ] + }, + { + "name": "RecoveryClosed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A recovery process for lost account by rescuer account has been closed.", + "\\[lost, rescuer\\]" + ] + }, + { + "name": "AccountRecovered", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Lost account has been successfully recovered by rescuer account.", + "\\[lost, rescuer\\]" + ] + }, + { + "name": "RecoveryRemoved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A recovery process has been removed for an \\[account\\]." + ] + } + ] + } + }, + "docs": [ + "Events type." + ] + } + }, + { + "id": 69, + "type": { + "path": [ + "pallet_vesting", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "VestingUpdated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "The amount vested has been updated. This could indicate a change in funds available.", + "The balance given is the amount which is left unvested (and thus locked).", + "\\[account, unvested\\]" + ] + }, + { + "name": "VestingCompleted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "An \\[account\\] has become fully vested." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 70, + "type": { + "path": [ + "pallet_scheduler", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Scheduled", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Scheduled some task. \\[when, index\\]" + ] + }, + { + "name": "Canceled", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Canceled some task. \\[when, index\\]" + ] + }, + { + "name": "Dispatched", + "fields": [ + { + "name": null, + "type": 71, + "typeName": "TaskAddress", + "docs": [] + }, + { + "name": null, + "type": 72, + "typeName": "Option>", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Dispatched some task. \\[task, id, result\\]" + ] + } + ] + } + }, + "docs": [ + "Events type." + ] + } + }, + { + "id": 71, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 72, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 10 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 10, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 73, + "type": { + "path": [ + "pallet_proxy", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ProxyExecuted", + "fields": [ + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A proxy was executed correctly, with the given \\[result\\]." + ] + }, + { + "name": "AnonymousCreated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": null, + "type": 75, + "typeName": "u16", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Anonymous account has been created by new proxy with given", + "disambiguation index and proxy type. \\[anonymous, who, proxy_type,", + "disambiguation_index\\]" + ] + }, + { + "name": "Announced", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 9, + "typeName": "CallHashOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "An announcement was placed to make a call in the future. \\[real, proxy, call_hash\\]" + ] + }, + { + "name": "ProxyAdded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A proxy was added. \\[delegator, delegatee, proxy_type, delay\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 74, + "type": { + "path": [ + "kusama_runtime", + "ProxyType" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Any", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "NonTransfer", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Governance", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Staking", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "IdentityJudgement", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "CancelProxy", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Auction", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 75, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "U16" + }, + "docs": [] + } + }, + { + "id": 76, + "type": { + "path": [ + "pallet_multisig", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NewMultisig", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 1, + "typeName": "CallHash", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A new multisig operation has begun. \\[approving, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigApproval", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 77, + "typeName": "Timepoint", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 1, + "typeName": "CallHash", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A multisig operation has been approved by someone.", + "\\[approving, timepoint, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigExecuted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 77, + "typeName": "Timepoint", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 1, + "typeName": "CallHash", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A multisig operation has been executed. \\[approving, timepoint, multisig, call_hash\\]" + ] + }, + { + "name": "MultisigCancelled", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 77, + "typeName": "Timepoint", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 1, + "typeName": "CallHash", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A multisig operation has been cancelled. \\[cancelling, timepoint, multisig, call_hash\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 77, + "type": { + "path": [ + "pallet_multisig", + "Timepoint" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "height", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 78, + "type": { + "path": [ + "pallet_bounties", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "BountyProposed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "New bounty proposal. \\[index\\]" + ] + }, + { + "name": "BountyRejected", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A bounty proposal was rejected; funds were slashed. \\[index, bond\\]" + ] + }, + { + "name": "BountyBecameActive", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A bounty proposal is funded and became active. \\[index\\]" + ] + }, + { + "name": "BountyAwarded", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A bounty is awarded to a beneficiary. \\[index, beneficiary\\]" + ] + }, + { + "name": "BountyClaimed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A bounty is claimed by beneficiary. \\[index, payout, beneficiary\\]" + ] + }, + { + "name": "BountyCanceled", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A bounty is cancelled. \\[index\\]" + ] + }, + { + "name": "BountyExtended", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 6, + "docs": [ + "A bounty expiry is extended. \\[index\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 79, + "type": { + "path": [ + "pallet_tips", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NewTip", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A new tip suggestion has been opened. \\[tip_hash\\]" + ] + }, + { + "name": "TipClosing", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A tip suggestion has reached threshold and is closing. \\[tip_hash\\]" + ] + }, + { + "name": "TipClosed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A tip suggestion has been closed. \\[tip_hash, who, payout\\]" + ] + }, + { + "name": "TipRetracted", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A tip suggestion has been retracted. \\[tip_hash\\]" + ] + }, + { + "name": "TipSlashed", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A tip suggestion has been slashed. \\[tip_hash, finder, deposit\\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 80, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "SolutionStored", + "fields": [ + { + "name": null, + "type": 81, + "typeName": "ElectionCompute", + "docs": [] + }, + { + "name": null, + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A solution was stored with the given compute.", + "", + "If the solution is signed, this means that it hasn't yet been processed. If the", + "solution is unsigned, this means that it has also been processed.", + "", + "The `bool` is `true` when a previous solution was ejected to make room for this one." + ] + }, + { + "name": "ElectionFinalized", + "fields": [ + { + "name": null, + "type": 82, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [ + "The election has been finalized, with `Some` of the given computation, or else if the", + "election failed, `None`." + ] + }, + { + "name": "Rewarded", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "An account has been rewarded for their signed submission being finalized." + ] + }, + { + "name": "Slashed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 3, + "docs": [ + "An account has been slashed for submitting an invalid signed submission." + ] + }, + { + "name": "SignedPhaseStarted", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "The signed phase of the given round has started." + ] + }, + { + "name": "UnsignedPhaseStarted", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 5, + "docs": [ + "The unsigned phase of the given round has started." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 81, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "ElectionCompute" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "OnChain", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Signed", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Unsigned", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Fallback", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "Emergency", + "fields": [], + "index": 4, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 82, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 81 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 81, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 83, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "BidPlaced", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A bid was successfully placed.", + "\\[ who, amount, duration \\]" + ] + }, + { + "name": "BidRetracted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A bid was successfully removed (before being accepted as a gilt).", + "\\[ who, amount, duration \\]" + ] + }, + { + "name": "GiltIssued", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ActiveIndex", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A bid was accepted as a gilt. The balance may not be released until expiry.", + "\\[ index, expiry, who, amount \\]" + ] + }, + { + "name": "GiltThawed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "ActiveIndex", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 3, + "docs": [ + "An expired gilt has been thawed.", + "\\[ index, who, original_amount, additional_amount \\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 84, + "type": { + "path": [ + "pallet_bags_list", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Rebagged", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "VoteWeight", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "VoteWeight", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Moved an account from one bag to another. \\[who, from, to\\]." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 85, + "type": { + "path": [ + "polkadot_runtime_parachains", + "inclusion", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "CandidateBacked", + "fields": [ + { + "name": null, + "type": 86, + "typeName": "CandidateReceipt", + "docs": [] + }, + { + "name": null, + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": null, + "type": 95, + "typeName": "CoreIndex", + "docs": [] + }, + { + "name": null, + "type": 96, + "typeName": "GroupIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A candidate was backed. `[candidate, head_data]`" + ] + }, + { + "name": "CandidateIncluded", + "fields": [ + { + "name": null, + "type": 86, + "typeName": "CandidateReceipt", + "docs": [] + }, + { + "name": null, + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": null, + "type": 95, + "typeName": "CoreIndex", + "docs": [] + }, + { + "name": null, + "type": 96, + "typeName": "GroupIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A candidate was included. `[candidate, head_data]`" + ] + }, + { + "name": "CandidateTimedOut", + "fields": [ + { + "name": null, + "type": 86, + "typeName": "CandidateReceipt", + "docs": [] + }, + { + "name": null, + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": null, + "type": 95, + "typeName": "CoreIndex", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A candidate timed out. `[candidate, head_data]`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 86, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CandidateReceipt" + ], + "params": [ + { + "name": "H", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "descriptor", + "type": 87, + "typeName": "CandidateDescriptor", + "docs": [] + }, + { + "name": "commitments_hash", + "type": 9, + "typeName": "Hash", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 87, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CandidateDescriptor" + ], + "params": [ + { + "name": "H", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "para_id", + "type": 88, + "typeName": "Id", + "docs": [] + }, + { + "name": "relay_parent", + "type": 9, + "typeName": "H", + "docs": [] + }, + { + "name": "collator", + "type": 89, + "typeName": "CollatorId", + "docs": [] + }, + { + "name": "persisted_validation_data_hash", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "pov_hash", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "erasure_root", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "signature", + "type": 90, + "typeName": "CollatorSignature", + "docs": [] + }, + { + "name": "para_head", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "validation_code_hash", + "type": 93, + "typeName": "ValidationCodeHash", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 88, + "type": { + "path": [ + "polkadot_parachain", + "primitives", + "Id" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 89, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "collator_app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 90, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "collator_app", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 91, + "typeName": "sr25519::Signature", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 91, + "type": { + "path": [ + "sp_core", + "sr25519", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 92, + "typeName": "[u8; 64]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 92, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 64, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 93, + "type": { + "path": [ + "polkadot_parachain", + "primitives", + "ValidationCodeHash" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 94, + "type": { + "path": [ + "polkadot_parachain", + "primitives", + "HeadData" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 95, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CoreIndex" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 96, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "GroupIndex" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 97, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "CurrentCodeUpdated", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Current code has been updated for a Para. `para_id`" + ] + }, + { + "name": "CurrentHeadUpdated", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Current head has been updated for a Para. `para_id`" + ] + }, + { + "name": "CodeUpgradeScheduled", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "A code upgrade has been scheduled for a Para. `para_id`" + ] + }, + { + "name": "NewHeadNoted", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "A new head has been noted for a Para. `para_id`" + ] + }, + { + "name": "ActionQueued", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "SessionIndex", + "docs": [] + } + ], + "index": 4, + "docs": [ + "A para has been queued to execute pending actions. `para_id`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 98, + "type": { + "path": [ + "polkadot_runtime_parachains", + "ump", + "pallet", + "Event" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidFormat", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "MessageId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Upward message is invalid XCM.", + "\\[ id \\]" + ] + }, + { + "name": "UnsupportedVersion", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "MessageId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Upward message is unsupported version of XCM.", + "\\[ id \\]" + ] + }, + { + "name": "ExecutedUpward", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "MessageId", + "docs": [] + }, + { + "name": null, + "type": 99, + "typeName": "Outcome", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Upward message executed with the given outcome.", + "\\[ id, outcome \\]" + ] + }, + { + "name": "WeightExhausted", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "MessageId", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 3, + "docs": [ + "The weight limit for handling downward messages was reached.", + "\\[ id, remaining, required \\]" + ] + }, + { + "name": "UpwardMessagesReceived", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Some downward messages have been received and will be processed.", + "\\[ para, count, size \\]" + ] + }, + { + "name": "OverweightEnqueued", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 1, + "typeName": "MessageId", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "OverweightIndex", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 5, + "docs": [ + "The weight budget was exceeded for an individual downward message.", + "", + "This message can be later dispatched manually using `service_overweight` dispatchable", + "using the assigned `overweight_index`.", + "", + "\\[ para, id, overweight_index, required \\]" + ] + }, + { + "name": "OverweightServiced", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "OverweightIndex", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Downward message from the overweight queue was executed with the given actual weight", + "used.", + "", + "\\[ overweight_index, used \\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 99, + "type": { + "path": [ + "xcm", + "v2", + "traits", + "Outcome" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Complete", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Incomplete", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": null, + "type": 100, + "typeName": "Error", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Error", + "fields": [ + { + "name": null, + "type": 100, + "typeName": "Error", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 100, + "type": { + "path": [ + "xcm", + "v2", + "traits", + "Error" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Overflow", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Unimplemented", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "UntrustedReserveLocation", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "UntrustedTeleportLocation", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "MultiLocationFull", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "MultiLocationNotInvertible", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "BadOrigin", + "fields": [], + "index": 6, + "docs": [] + }, + { + "name": "InvalidLocation", + "fields": [], + "index": 7, + "docs": [] + }, + { + "name": "AssetNotFound", + "fields": [], + "index": 8, + "docs": [] + }, + { + "name": "FailedToTransactAsset", + "fields": [], + "index": 9, + "docs": [] + }, + { + "name": "NotWithdrawable", + "fields": [], + "index": 10, + "docs": [] + }, + { + "name": "LocationCannotHold", + "fields": [], + "index": 11, + "docs": [] + }, + { + "name": "ExceedsMaxMessageSize", + "fields": [], + "index": 12, + "docs": [] + }, + { + "name": "DestinationUnsupported", + "fields": [], + "index": 13, + "docs": [] + }, + { + "name": "Transport", + "fields": [], + "index": 14, + "docs": [] + }, + { + "name": "Unroutable", + "fields": [], + "index": 15, + "docs": [] + }, + { + "name": "UnknownClaim", + "fields": [], + "index": 16, + "docs": [] + }, + { + "name": "FailedToDecode", + "fields": [], + "index": 17, + "docs": [] + }, + { + "name": "TooMuchWeightRequired", + "fields": [], + "index": 18, + "docs": [] + }, + { + "name": "NotHoldingFees", + "fields": [], + "index": 19, + "docs": [] + }, + { + "name": "TooExpensive", + "fields": [], + "index": 20, + "docs": [] + }, + { + "name": "Trap", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "u64", + "docs": [] + } + ], + "index": 21, + "docs": [] + }, + { + "name": "UnhandledXcmVersion", + "fields": [], + "index": 22, + "docs": [] + }, + { + "name": "WeightLimitReached", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 23, + "docs": [] + }, + { + "name": "Barrier", + "fields": [], + "index": 24, + "docs": [] + }, + { + "name": "WeightNotComputable", + "fields": [], + "index": 25, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 101, + "type": { + "path": [ + "polkadot_runtime_parachains", + "hrmp", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "OpenChannelRequested", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Open HRMP channel requested.", + "`[sender, recipient, proposed_max_capacity, proposed_max_message_size]`" + ] + }, + { + "name": "OpenChannelCanceled", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 102, + "typeName": "HrmpChannelId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "An HRMP channel request sent by the receiver was canceled by either party.", + "`[by_parachain, channel_id]`" + ] + }, + { + "name": "OpenChannelAccepted", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Open HRMP channel accepted. `[sender, recipient]`" + ] + }, + { + "name": "ChannelClosed", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 102, + "typeName": "HrmpChannelId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "HRMP channel closed. `[by_parachain, channel_id]`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 102, + "type": { + "path": [ + "polkadot_parachain", + "primitives", + "HrmpChannelId" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "sender", + "type": 88, + "typeName": "Id", + "docs": [] + }, + { + "name": "recipient", + "type": 88, + "typeName": "Id", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 103, + "type": { + "path": [ + "polkadot_runtime_common", + "paras_registrar", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Registered", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Deregistered", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Reserved", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 104, + "type": { + "path": [ + "polkadot_runtime_common", + "slots", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NewLeasePeriod", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A new `[lease_period]` is beginning." + ] + }, + { + "name": "Leased", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A para has won the right to a continuous set of lease periods as a parachain.", + "First balance is any extra amount reserved on top of the para's existing deposit.", + "Second balance is the total amount reserved.", + "`[parachain_id, leaser, period_begin, period_count, extra_reserved, total_amount]`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 105, + "type": { + "path": [ + "polkadot_runtime_common", + "auctions", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "AuctionStarted", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "AuctionIndex", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [ + "An auction started. Provides its index and the block number where it will begin to", + "close and the first lease period of the quadruplet that is auctioned.", + "`[auction_index, lease_period, ending]`" + ] + }, + { + "name": "AuctionClosed", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "AuctionIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "An auction ended. All funds become unreserved. `[auction_index]`" + ] + }, + { + "name": "Reserved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Funds were reserved for a winning bid. First balance is the extra amount reserved.", + "Second is the total. `[bidder, extra_reserved, total_amount]`" + ] + }, + { + "name": "Unreserved", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Funds were unreserved since bidder is no longer active. `[bidder, amount]`" + ] + }, + { + "name": "ReserveConfiscated", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Someone attempted to lease the same slot twice for a parachain. The amount is held in reserve", + "but no parachain slot has been leased.", + "`[parachain_id, leaser, amount]`" + ] + }, + { + "name": "BidAccepted", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + } + ], + "index": 5, + "docs": [ + "A new bid has been accepted as the current winner.", + "`[who, para_id, amount, first_slot, last_slot]`" + ] + }, + { + "name": "WinningOffset", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "AuctionIndex", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 6, + "docs": [ + "The winning offset was chosen for an auction. This will map into the `Winning` storage map.", + "`[auction_index, block_number]`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 106, + "type": { + "path": [ + "polkadot_runtime_common", + "crowdloan", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Created", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Create a new crowdloaning campaign. `[fund_index]`" + ] + }, + { + "name": "Contributed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Contributed to a crowd sale. `[who, fund_index, amount]`" + ] + }, + { + "name": "Withdrew", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Withdrew full balance of a contributor. `[who, fund_index, amount]`" + ] + }, + { + "name": "PartiallyRefunded", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "The loans in a fund have been partially dissolved, i.e. there are some left", + "over child keys that still need to be killed. `[fund_index]`" + ] + }, + { + "name": "AllRefunded", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "All loans in a fund have been refunded. `[fund_index]`" + ] + }, + { + "name": "Dissolved", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Fund is dissolved. `[fund_index]`" + ] + }, + { + "name": "HandleBidResult", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 52, + "typeName": "DispatchResult", + "docs": [] + } + ], + "index": 6, + "docs": [ + "The result of trying to submit a new bid to the Slots pallet." + ] + }, + { + "name": "Edited", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 7, + "docs": [ + "The configuration to a crowdloan has been edited. `[fund_index]`" + ] + }, + { + "name": "MemoUpdated", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 8, + "docs": [ + "A memo has been updated. `[who, fund_index, memo]`" + ] + }, + { + "name": "AddedToNewRaise", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 9, + "docs": [ + "A parachain has been moved to `NewRaise`" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 107, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "Event" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Attempted", + "fields": [ + { + "name": null, + "type": 99, + "typeName": "xcm::latest::Outcome", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Execution of an XCM message was attempted.", + "", + "\\[ outcome \\]" + ] + }, + { + "name": "Sent", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A XCM message was sent.", + "", + "\\[ origin, destination, message \\]" + ] + }, + { + "name": "UnexpectedResponse", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Query response received which does not match a registered query. This may be because a", + "matching query was never registered, it may be because it is a duplicate response, or", + "because the query timed out.", + "", + "\\[ origin location, id \\]" + ] + }, + { + "name": "ResponseReady", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 126, + "typeName": "Response", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Query response has been received and is ready for taking with `take_response`. There is", + "no registered notification call.", + "", + "\\[ id, response \\]" + ] + }, + { + "name": "Notified", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Query response has been received and query is removed. The registered notification has", + "been dispatched and executed successfully.", + "", + "\\[ id, pallet index, call index \\]" + ] + }, + { + "name": "NotifyOverweight", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Query response has been received and query is removed. The registered notification could", + "not be dispatched because the dispatch weight is greater than the maximum weight", + "originally budgeted by this runtime for the query result.", + "", + "\\[ id, pallet index, call index, actual weight, max budgeted weight \\]" + ] + }, + { + "name": "NotifyDispatchError", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Query response has been received and query is removed. There was a general error with", + "dispatching the notification call.", + "", + "\\[ id, pallet index, call index \\]" + ] + }, + { + "name": "NotifyDecodeFailed", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Query response has been received and query is removed. The dispatch was unable to be", + "decoded into a `Call`; this might be due to dispatch function having a signature which", + "is not `(origin, QueryId, Response)`.", + "", + "\\[ id, pallet index, call index \\]" + ] + }, + { + "name": "InvalidResponder", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 135, + "typeName": "Option", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Expected query response has been received but the origin location of the response does", + "not match that expected. The query remains registered for a later, valid, response to", + "be received and acted upon.", + "", + "\\[ origin location, id, expected location \\]" + ] + }, + { + "name": "InvalidResponderVersion", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Expected query response has been received but the expected origin location placed in", + "storate by this runtime previously cannot be decoded. The query remains registered.", + "", + "This is unexpected (since a location placed in storage in a previously executing", + "runtime should be readable prior to query timeout) and dangerous since the possibly", + "valid response will be dropped. Manual governance intervention is probably going to be", + "needed.", + "", + "\\[ origin location, id \\]" + ] + }, + { + "name": "ResponseTaken", + "fields": [ + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Received query response has been read and removed.", + "", + "\\[ id \\]" + ] + }, + { + "name": "AssetsTrapped", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "H256", + "docs": [] + }, + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 136, + "typeName": "VersionedMultiAssets", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Some assets have been placed in an asset trap.", + "", + "\\[ hash, origin, assets \\]" + ] + }, + { + "name": "VersionChangeNotified", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "XcmVersion", + "docs": [] + } + ], + "index": 12, + "docs": [ + "An XCM version change notification message has been attempted to be sent.", + "", + "\\[ destination, result \\]" + ] + }, + { + "name": "SupportedVersionChanged", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "XcmVersion", + "docs": [] + } + ], + "index": 13, + "docs": [ + "The supported version of a location has been changed. This might be through an", + "automatic notification or a manual intervention.", + "", + "\\[ location, XCM version \\]" + ] + }, + { + "name": "NotifyTargetSendFail", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + }, + { + "name": null, + "type": 100, + "typeName": "XcmError", + "docs": [] + } + ], + "index": 14, + "docs": [ + "A given location which had a version change subscription was dropped owing to an error", + "sending the notification to it.", + "", + "\\[ location, query ID, error \\]" + ] + }, + { + "name": "NotifyTargetMigrationFail", + "fields": [ + { + "name": null, + "type": 141, + "typeName": "VersionedMultiLocation", + "docs": [] + }, + { + "name": null, + "type": 8, + "typeName": "QueryId", + "docs": [] + } + ], + "index": 15, + "docs": [ + "A given location which had a version change subscription was dropped owing to an error", + "migrating the location to our new XCM format.", + "", + "\\[ location, query ID \\]" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tThe [event](https://substrate.dev/docs/en/knowledgebase/runtime/events) emitted\n\t\t\tby this pallet.\n\t\t\t" + ] + } + }, + { + "id": 108, + "type": { + "path": [ + "xcm", + "v1", + "multilocation", + "MultiLocation" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "parents", + "type": 2, + "typeName": "u8", + "docs": [] + }, + { + "name": "interior", + "type": 109, + "typeName": "Junctions", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 109, + "type": { + "path": [ + "xcm", + "v1", + "multilocation", + "Junctions" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Here", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "X1", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "X2", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "X3", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "X4", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "X5", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "X6", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "X7", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "X8", + "fields": [ + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 110, + "typeName": "Junction", + "docs": [] + } + ], + "index": 8, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 110, + "type": { + "path": [ + "xcm", + "v1", + "junction", + "Junction" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Parachain", + "fields": [ + { + "name": null, + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "AccountId32", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "id", + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "AccountIndex64", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "index", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "AccountKey20", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "key", + "type": 64, + "typeName": "[u8; 20]", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "PalletInstance", + "fields": [ + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "GeneralIndex", + "fields": [ + { + "name": null, + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "GeneralKey", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "OnlyChild", + "fields": [], + "index": 7, + "docs": [] + }, + { + "name": "Plurality", + "fields": [ + { + "name": "id", + "type": 114, + "typeName": "BodyId", + "docs": [] + }, + { + "name": "part", + "type": 115, + "typeName": "BodyPart", + "docs": [] + } + ], + "index": 8, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 111, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 4 + } + }, + "docs": [] + } + }, + { + "id": 112, + "type": { + "path": [ + "xcm", + "v0", + "junction", + "NetworkId" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Any", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Named", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Polkadot", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Kusama", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 113, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 8 + } + }, + "docs": [] + } + }, + { + "id": 114, + "type": { + "path": [ + "xcm", + "v0", + "junction", + "BodyId" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Unit", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Named", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Index", + "fields": [ + { + "name": null, + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Executive", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "Technical", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "Legislative", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Judicial", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 115, + "type": { + "path": [ + "xcm", + "v0", + "junction", + "BodyPart" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Voice", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Members", + "fields": [ + { + "name": "count", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Fraction", + "fields": [ + { + "name": "nom", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "denom", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "AtLeastProportion", + "fields": [ + { + "name": "nom", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "denom", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "MoreThanProportion", + "fields": [ + { + "name": "nom", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "denom", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 116, + "type": { + "path": [ + "xcm", + "v2", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 117, + "typeName": "Vec>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 117, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 118 + } + }, + "docs": [] + } + }, + { + "id": 118, + "type": { + "path": [ + "xcm", + "v2", + "Instruction" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposited", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "ReceiveTeleportedAsset", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "response", + "type": 126, + "typeName": "Response", + "docs": [] + }, + { + "name": "max_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 130, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "ClearOrigin", + "fields": [], + "index": 10, + "docs": [] + }, + { + "name": "DescendOrigin", + "fields": [ + { + "name": null, + "type": 109, + "typeName": "InteriorMultiLocation", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "ReportError", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 12, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "receive", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "reserve", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 121, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight_limit", + "type": 134, + "typeName": "WeightLimit", + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "RefundSurplus", + "fields": [], + "index": 20, + "docs": [] + }, + { + "name": "SetErrorHandler", + "fields": [ + { + "name": null, + "type": 116, + "typeName": "Xcm", + "docs": [] + } + ], + "index": 21, + "docs": [] + }, + { + "name": "SetAppendix", + "fields": [ + { + "name": null, + "type": 116, + "typeName": "Xcm", + "docs": [] + } + ], + "index": 22, + "docs": [] + }, + { + "name": "ClearError", + "fields": [], + "index": 23, + "docs": [] + }, + { + "name": "ClaimAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "ticket", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Trap", + "fields": [ + { + "name": null, + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "SubscribeVersion", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "UnsubscribeVersion", + "fields": [], + "index": 27, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 119, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "MultiAssets" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 120, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 120, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 121 + } + }, + "docs": [] + } + }, + { + "id": 121, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "MultiAsset" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 122, + "typeName": "AssetId", + "docs": [] + }, + { + "name": "fun", + "type": 123, + "typeName": "Fungibility", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 122, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "AssetId" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Concrete", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Abstract", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 123, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "Fungibility" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Fungible", + "fields": [ + { + "name": null, + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "NonFungible", + "fields": [ + { + "name": null, + "type": 124, + "typeName": "AssetInstance", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 124, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "AssetInstance" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Undefined", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Index", + "fields": [ + { + "name": null, + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Array4", + "fields": [ + { + "name": null, + "type": 14, + "typeName": "[u8; 4]", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Array8", + "fields": [ + { + "name": null, + "type": 125, + "typeName": "[u8; 8]", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Array16", + "fields": [ + { + "name": null, + "type": 33, + "typeName": "[u8; 16]", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Array32", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Blob", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 125, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 8, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 126, + "type": { + "path": [ + "xcm", + "v2", + "Response" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Null", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Assets", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "ExecutionResult", + "fields": [ + { + "name": null, + "type": 127, + "typeName": "Option<(u32, Error)>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Version", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "super::Version", + "docs": [] + } + ], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 127, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 128 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 128, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 128, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 100 + ] + }, + "docs": [] + } + }, + { + "id": 129, + "type": { + "path": [ + "xcm", + "v0", + "OriginKind" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Native", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "SovereignAccount", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Superuser", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Xcm", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 130, + "type": { + "path": [ + "xcm", + "double_encoded", + "DoubleEncoded" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "encoded", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 131, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "MultiAssetFilter" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Definite", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Wild", + "fields": [ + { + "name": null, + "type": 132, + "typeName": "WildMultiAsset", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 132, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "WildMultiAsset" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "All", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "AllOf", + "fields": [ + { + "name": "id", + "type": 122, + "typeName": "AssetId", + "docs": [] + }, + { + "name": "fun", + "type": 133, + "typeName": "WildFungibility", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 133, + "type": { + "path": [ + "xcm", + "v1", + "multiasset", + "WildFungibility" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Fungible", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "NonFungible", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 134, + "type": { + "path": [ + "xcm", + "v2", + "WeightLimit" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Unlimited", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Limited", + "fields": [ + { + "name": null, + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 135, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 108 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 108, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 136, + "type": { + "path": [ + "xcm", + "VersionedMultiAssets" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [ + { + "name": null, + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "v1::MultiAssets", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 137, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 138 + } + }, + "docs": [] + } + }, + { + "id": 138, + "type": { + "path": [ + "xcm", + "v0", + "multi_asset", + "MultiAsset" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "All", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "AllFungible", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "AllNonFungible", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "AllAbstractFungible", + "fields": [ + { + "name": "id", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "AllAbstractNonFungible", + "fields": [ + { + "name": "class", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "AllConcreteFungible", + "fields": [ + { + "name": "id", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "AllConcreteNonFungible", + "fields": [ + { + "name": "class", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "AbstractFungible", + "fields": [ + { + "name": "id", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "amount", + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "AbstractNonFungible", + "fields": [ + { + "name": "class", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "instance", + "type": 124, + "typeName": "AssetInstance", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "ConcreteFungible", + "fields": [ + { + "name": "id", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "amount", + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "ConcreteNonFungible", + "fields": [ + { + "name": "class", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "instance", + "type": 124, + "typeName": "AssetInstance", + "docs": [] + } + ], + "index": 11, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 139, + "type": { + "path": [ + "xcm", + "v0", + "multi_location", + "MultiLocation" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Null", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "X1", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "X2", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "X3", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "X4", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "X5", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "X6", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "X7", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "X8", + "fields": [ + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + }, + { + "name": null, + "type": 140, + "typeName": "Junction", + "docs": [] + } + ], + "index": 8, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 140, + "type": { + "path": [ + "xcm", + "v0", + "junction", + "Junction" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Parent", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Parachain", + "fields": [ + { + "name": null, + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "AccountId32", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "id", + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "AccountIndex64", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "index", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "AccountKey20", + "fields": [ + { + "name": "network", + "type": 112, + "typeName": "NetworkId", + "docs": [] + }, + { + "name": "key", + "type": 64, + "typeName": "[u8; 20]", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "PalletInstance", + "fields": [ + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "GeneralIndex", + "fields": [ + { + "name": null, + "type": 46, + "typeName": "u128", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "GeneralKey", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "OnlyChild", + "fields": [], + "index": 8, + "docs": [] + }, + { + "name": "Plurality", + "fields": [ + { + "name": "id", + "type": 114, + "typeName": "BodyId", + "docs": [] + }, + { + "name": "part", + "type": 115, + "typeName": "BodyPart", + "docs": [] + } + ], + "index": 9, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 141, + "type": { + "path": [ + "xcm", + "VersionedMultiLocation" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [ + { + "name": null, + "type": 139, + "typeName": "v0::MultiLocation", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "v1::MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 142, + "type": { + "path": [ + "frame_system", + "Phase" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "ApplyExtrinsic", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Finalization", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Initialization", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 143, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 9 + } + }, + "docs": [] + } + }, + { + "id": 144, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 71 + } + }, + "docs": [] + } + }, + { + "id": 145, + "type": { + "path": [ + "frame_system", + "LastRuntimeUpgradeInfo" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "spec_version", + "type": 111, + "typeName": "codec::Compact", + "docs": [] + }, + { + "name": "spec_name", + "type": 146, + "typeName": "sp_runtime::RuntimeString", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 146, + "type": { + "path": [], + "params": [], + "def": { + "primitive": "Str" + }, + "docs": [] + } + }, + { + "id": 147, + "type": { + "path": [ + "frame_system", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "fill_block", + "fields": [ + { + "name": "ratio", + "type": 148, + "typeName": "Perbill", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A dispatch that will fill the block weight up to the given ratio." + ] + }, + { + "name": "remark", + "fields": [ + { + "name": "remark", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Make some on-chain remark.", + "", + "# ", + "- `O(1)`", + "# " + ] + }, + { + "name": "set_heap_pages", + "fields": [ + { + "name": "pages", + "type": 8, + "typeName": "u64", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Set the number of pages in the WebAssembly environment's heap.", + "", + "# ", + "- `O(1)`", + "- 1 storage write.", + "- Base Weight: 1.405 µs", + "- 1 write to HEAP_PAGES", + "- 1 digest item", + "# " + ] + }, + { + "name": "set_code", + "fields": [ + { + "name": "code", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Set the new runtime code.", + "", + "# ", + "- `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`", + "- 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is", + " expensive).", + "- 1 storage write (codec `O(C)`).", + "- 1 digest item.", + "- 1 event.", + "The weight of this function is dependent on the runtime, but generally this is very", + "expensive. We will treat this as a full block.", + "# " + ] + }, + { + "name": "set_code_without_checks", + "fields": [ + { + "name": "code", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Set the new runtime code without doing any checks of the given `code`.", + "", + "# ", + "- `O(C)` where `C` length of `code`", + "- 1 storage write (codec `O(C)`).", + "- 1 digest item.", + "- 1 event.", + "The weight of this function is dependent on the runtime. We will treat this as a full", + "block. # " + ] + }, + { + "name": "set_changes_trie_config", + "fields": [ + { + "name": "changes_trie_config", + "type": 16, + "typeName": "Option", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Set the new changes trie configuration.", + "", + "# ", + "- `O(1)`", + "- 1 storage write or delete (codec `O(1)`).", + "- 1 call to `deposit_log`: Uses `append` API, so O(1)", + "- Base Weight: 7.218 µs", + "- DB Weight:", + " - Writes: Changes Trie, System Digest", + "# " + ] + }, + { + "name": "set_storage", + "fields": [ + { + "name": "items", + "type": 149, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Set some items of storage.", + "", + "# ", + "- `O(I)` where `I` length of `items`", + "- `I` storage writes (`O(1)`).", + "- Base Weight: 0.568 * i µs", + "- Writes: Number of items", + "# " + ] + }, + { + "name": "kill_storage", + "fields": [ + { + "name": "keys", + "type": 151, + "typeName": "Vec", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Kill some items from storage.", + "", + "# ", + "- `O(IK)` where `I` length of `keys` and `K` length of one key", + "- `I` storage deletions.", + "- Base Weight: .378 * i µs", + "- Writes: Number of items", + "# " + ] + }, + { + "name": "kill_prefix", + "fields": [ + { + "name": "prefix", + "type": 10, + "typeName": "Key", + "docs": [] + }, + { + "name": "subkeys", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Kill all storage items with a key that starts with the given prefix.", + "", + "**NOTE:** We rely on the Root origin to provide us the number of subkeys under", + "the prefix we are removing to accurately calculate the weight of this function.", + "", + "# ", + "- `O(P)` where `P` amount of keys with prefix `prefix`", + "- `P` storage deletions.", + "- Base Weight: 0.834 * P µs", + "- Writes: Number of subkeys + 1", + "# " + ] + }, + { + "name": "remark_with_event", + "fields": [ + { + "name": "remark", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Make some on-chain remark and emit event.", + "", + "# ", + "- `O(b)` where b is the length of the remark.", + "- 1 event.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 148, + "type": { + "path": [ + "sp_arithmetic", + "per_things", + "Perbill" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 149, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 150 + } + }, + "docs": [] + } + }, + { + "id": 150, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 10, + 10 + ] + }, + "docs": [] + } + }, + { + "id": 151, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 10 + } + }, + "docs": [] + } + }, + { + "id": 152, + "type": { + "path": [ + "frame_system", + "limits", + "BlockWeights" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "base_block", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "max_block", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "per_class", + "type": 153, + "typeName": "PerDispatchClass", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 153, + "type": { + "path": [ + "frame_support", + "weights", + "PerDispatchClass" + ], + "params": [ + { + "name": "T", + "type": 154 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "normal", + "type": 154, + "typeName": "T", + "docs": [] + }, + { + "name": "operational", + "type": 154, + "typeName": "T", + "docs": [] + }, + { + "name": "mandatory", + "type": 154, + "typeName": "T", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 154, + "type": { + "path": [ + "frame_system", + "limits", + "WeightsPerClass" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "base_extrinsic", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "max_extrinsic", + "type": 155, + "typeName": "Option", + "docs": [] + }, + { + "name": "max_total", + "type": 155, + "typeName": "Option", + "docs": [] + }, + { + "name": "reserved", + "type": 155, + "typeName": "Option", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 155, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 8 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 8, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 156, + "type": { + "path": [ + "frame_system", + "limits", + "BlockLength" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "max", + "type": 157, + "typeName": "PerDispatchClass", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 157, + "type": { + "path": [ + "frame_support", + "weights", + "PerDispatchClass" + ], + "params": [ + { + "name": "T", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "normal", + "type": 4, + "typeName": "T", + "docs": [] + }, + { + "name": "operational", + "type": 4, + "typeName": "T", + "docs": [] + }, + { + "name": "mandatory", + "type": 4, + "typeName": "T", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 158, + "type": { + "path": [ + "frame_support", + "weights", + "RuntimeDbWeight" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "read", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "write", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 159, + "type": { + "path": [ + "sp_version", + "RuntimeVersion" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "spec_name", + "type": 146, + "typeName": "RuntimeString", + "docs": [] + }, + { + "name": "impl_name", + "type": 146, + "typeName": "RuntimeString", + "docs": [] + }, + { + "name": "authoring_version", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "spec_version", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "impl_version", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "apis", + "type": 160, + "typeName": "ApisVec", + "docs": [] + }, + { + "name": "transaction_version", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 160, + "type": { + "path": [ + "Cow" + ], + "params": [ + { + "name": "T", + "type": 161 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 161, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 161, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 162 + } + }, + "docs": [] + } + }, + { + "id": 162, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 125, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 163, + "type": { + "path": [ + "frame_system", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidSpecName", + "fields": [], + "index": 0, + "docs": [ + "The name of specification does not match between the current runtime", + "and the new runtime." + ] + }, + { + "name": "SpecVersionNeedsToIncrease", + "fields": [], + "index": 1, + "docs": [ + "The specification version is not allowed to decrease between the current runtime", + "and the new runtime." + ] + }, + { + "name": "FailedToExtractRuntimeVersion", + "fields": [], + "index": 2, + "docs": [ + "Failed to extract the runtime version from the new runtime.", + "", + "Either calling `Core_version` or decoding `RuntimeVersion` failed." + ] + }, + { + "name": "NonDefaultComposite", + "fields": [], + "index": 3, + "docs": [ + "Suicide called when the account has non-default composite data." + ] + }, + { + "name": "NonZeroRefCount", + "fields": [], + "index": 4, + "docs": [ + "There is a non-zero reference count preventing the account from being purged." + ] + } + ] + } + }, + "docs": [ + "Error for the System pallet" + ] + } + }, + { + "id": 164, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 165 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 167, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 165, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 166, + 8 + ] + }, + "docs": [] + } + }, + { + "id": 166, + "type": { + "path": [ + "sp_consensus_babe", + "app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 167, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 165 + } + }, + "docs": [] + } + }, + { + "id": 168, + "type": { + "path": [ + "sp_consensus_slots", + "Slot" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 8, + "typeName": "u64", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 169, + "type": { + "path": [ + "sp_consensus_babe", + "digests", + "NextConfigDescriptor" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1", + "fields": [ + { + "name": "c", + "type": 170, + "typeName": "(u64, u64)", + "docs": [] + }, + { + "name": "allowed_slots", + "type": 171, + "typeName": "AllowedSlots", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 170, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 8, + 8 + ] + }, + "docs": [] + } + }, + { + "id": 171, + "type": { + "path": [ + "sp_consensus_babe", + "AllowedSlots" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "PrimarySlots", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "PrimaryAndSecondaryPlainSlots", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "PrimaryAndSecondaryVRFSlots", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 172, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 1 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 173, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 173, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 1 + } + }, + "docs": [] + } + }, + { + "id": 174, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 1 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 175, + "type": { + "path": [ + "sp_consensus_babe", + "BabeEpochConfiguration" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "c", + "type": 170, + "typeName": "(u64, u64)", + "docs": [] + }, + { + "name": "allowed_slots", + "type": 171, + "typeName": "AllowedSlots", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 176, + "type": { + "path": [ + "pallet_babe", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "report_equivocation", + "fields": [ + { + "name": "equivocation_proof", + "type": 177, + "typeName": "Box>", + "docs": [] + }, + { + "name": "key_owner_proof", + "type": 180, + "typeName": "T::KeyOwnerProof", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Report authority equivocation/misbehavior. This method will verify", + "the equivocation proof and validate the given key ownership proof", + "against the extracted offender. If both are valid, the offence will", + "be reported." + ] + }, + { + "name": "report_equivocation_unsigned", + "fields": [ + { + "name": "equivocation_proof", + "type": 177, + "typeName": "Box>", + "docs": [] + }, + { + "name": "key_owner_proof", + "type": 180, + "typeName": "T::KeyOwnerProof", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Report authority equivocation/misbehavior. This method will verify", + "the equivocation proof and validate the given key ownership proof", + "against the extracted offender. If both are valid, the offence will", + "be reported.", + "This extrinsic must be called unsigned and it is expected that only", + "block authors will call it (validated in `ValidateUnsigned`), as such", + "if the block author is defined it will be defined as the equivocation", + "reporter." + ] + }, + { + "name": "plan_config_change", + "fields": [ + { + "name": "config", + "type": 169, + "typeName": "NextConfigDescriptor", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Plan an epoch config change. The epoch config change is recorded and will be enacted on", + "the next call to `enact_epoch_change`. The config will be activated one epoch after.", + "Multiple calls to this method will replace any existing planned config change that had", + "not been enacted yet." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 177, + "type": { + "path": [ + "sp_consensus_slots", + "EquivocationProof" + ], + "params": [ + { + "name": "Header", + "type": 178 + }, + { + "name": "Id", + "type": 166 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "offender", + "type": 166, + "typeName": "Id", + "docs": [] + }, + { + "name": "slot", + "type": 168, + "typeName": "Slot", + "docs": [] + }, + { + "name": "first_header", + "type": 178, + "typeName": "Header", + "docs": [] + }, + { + "name": "second_header", + "type": 178, + "typeName": "Header", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 178, + "type": { + "path": [ + "sp_runtime", + "generic", + "header", + "Header" + ], + "params": [ + { + "name": "Number", + "type": 4 + }, + { + "name": "Hash", + "type": 179 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "parent_hash", + "type": 9, + "typeName": "Hash::Output", + "docs": [] + }, + { + "name": "number", + "type": 111, + "typeName": "Number", + "docs": [] + }, + { + "name": "state_root", + "type": 9, + "typeName": "Hash::Output", + "docs": [] + }, + { + "name": "extrinsics_root", + "type": 9, + "typeName": "Hash::Output", + "docs": [] + }, + { + "name": "digest", + "type": 11, + "typeName": "Digest", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 179, + "type": { + "path": [ + "sp_runtime", + "traits", + "BlakeTwo256" + ], + "params": [], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 180, + "type": { + "path": [ + "sp_session", + "MembershipProof" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "session", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + }, + { + "name": "trie_nodes", + "type": 151, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "validator_count", + "type": 4, + "typeName": "ValidatorCount", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 181, + "type": { + "path": [ + "pallet_babe", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidEquivocationProof", + "fields": [], + "index": 0, + "docs": [ + "An equivocation proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "InvalidKeyOwnershipProof", + "fields": [], + "index": 1, + "docs": [ + "A key ownership proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "DuplicateOffenceReport", + "fields": [], + "index": 2, + "docs": [ + "A given equivocation report is valid but already previously reported." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 182, + "type": { + "path": [ + "pallet_timestamp", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set", + "fields": [ + { + "name": "now", + "type": 113, + "typeName": "T::Moment", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Set the current time.", + "", + "This call should be invoked exactly once per block. It will panic at the finalization", + "phase, if this call hasn't been invoked by that time.", + "", + "The timestamp should be greater than the previous one by the amount specified by", + "`MinimumPeriod`.", + "", + "The dispatch origin for this call must be `Inherent`.", + "", + "# ", + "- `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)", + "- 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in", + " `on_finalize`)", + "- 1 event handler `on_timestamp_set`. Must be `O(1)`.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 183, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 6, + 55 + ] + }, + "docs": [] + } + }, + { + "id": 184, + "type": { + "path": [ + "pallet_indices", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "claim", + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Assign an previously unassigned index.", + "", + "Payment: `Deposit` is reserved from the sender account.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `index`: the index to be claimed. This must not be in use.", + "", + "Emits `IndexAssigned` if successful.", + "", + "# ", + "- `O(1)`.", + "- One storage mutation (codec `O(1)`).", + "- One reserve operation.", + "- One event.", + "-------------------", + "- DB Weight: 1 Read/Write (Accounts)", + "# " + ] + }, + { + "name": "transfer", + "fields": [ + { + "name": "new", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Assign an index already owned by the sender to another account. The balance reservation", + "is effectively transferred to the new account.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `index`: the index to be re-assigned. This must be owned by the sender.", + "- `new`: the new owner of the index. This function is a no-op if it is equal to sender.", + "", + "Emits `IndexAssigned` if successful.", + "", + "# ", + "- `O(1)`.", + "- One storage mutation (codec `O(1)`).", + "- One transfer operation.", + "- One event.", + "-------------------", + "- DB Weight:", + " - Reads: Indices Accounts, System Account (recipient)", + " - Writes: Indices Accounts, System Account (recipient)", + "# " + ] + }, + { + "name": "free", + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Free up an index owned by the sender.", + "", + "Payment: Any previous deposit placed for the index is unreserved in the sender account.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must own the index.", + "", + "- `index`: the index to be freed. This must be owned by the sender.", + "", + "Emits `IndexFreed` if successful.", + "", + "# ", + "- `O(1)`.", + "- One storage mutation (codec `O(1)`).", + "- One reserve operation.", + "- One event.", + "-------------------", + "- DB Weight: 1 Read/Write (Accounts)", + "# " + ] + }, + { + "name": "force_transfer", + "fields": [ + { + "name": "new", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + }, + { + "name": "freeze", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Force an index to an account. This doesn't require a deposit. If the index is already", + "held, then any deposit is reimbursed to its current owner.", + "", + "The dispatch origin for this call must be _Root_.", + "", + "- `index`: the index to be (re-)assigned.", + "- `new`: the new owner of the index. This function is a no-op if it is equal to sender.", + "- `freeze`: if set to `true`, will freeze the index so it cannot be transferred.", + "", + "Emits `IndexAssigned` if successful.", + "", + "# ", + "- `O(1)`.", + "- One storage mutation (codec `O(1)`).", + "- Up to one reserve operation.", + "- One event.", + "-------------------", + "- DB Weight:", + " - Reads: Indices Accounts, System Account (original owner)", + " - Writes: Indices Accounts, System Account (original owner)", + "# " + ] + }, + { + "name": "freeze", + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "T::AccountIndex", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Freeze an index so it will always point to the sender account. This consumes the", + "deposit.", + "", + "The dispatch origin for this call must be _Signed_ and the signing account must have a", + "non-frozen account `index`.", + "", + "- `index`: the index to be frozen in place.", + "", + "Emits `IndexFrozen` if successful.", + "", + "# ", + "- `O(1)`.", + "- One storage mutation (codec `O(1)`).", + "- Up to one slash operation.", + "- One event.", + "-------------------", + "- DB Weight: 1 Read/Write (Accounts)", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 185, + "type": { + "path": [ + "pallet_indices", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotAssigned", + "fields": [], + "index": 0, + "docs": [ + "The index was not already assigned." + ] + }, + { + "name": "NotOwner", + "fields": [], + "index": 1, + "docs": [ + "The index is assigned to another account." + ] + }, + { + "name": "InUse", + "fields": [], + "index": 2, + "docs": [ + "The index was not available." + ] + }, + { + "name": "NotTransfer", + "fields": [], + "index": 3, + "docs": [ + "The source and destination accounts are identical." + ] + }, + { + "name": "Permanent", + "fields": [], + "index": 4, + "docs": [ + "The index is permanent and may not be freed/changed." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 186, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 187 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 189, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 187, + "type": { + "path": [ + "pallet_balances", + "BalanceLock" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 125, + "typeName": "LockIdentifier", + "docs": [] + }, + { + "name": "amount", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "reasons", + "type": 188, + "typeName": "Reasons", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 188, + "type": { + "path": [ + "pallet_balances", + "Reasons" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Fee", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Misc", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "All", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 189, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 187 + } + }, + "docs": [] + } + }, + { + "id": 190, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 191 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 192, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 191, + "type": { + "path": [ + "pallet_balances", + "ReserveData" + ], + "params": [ + { + "name": "ReserveIdentifier", + "type": 125 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 125, + "typeName": "ReserveIdentifier", + "docs": [] + }, + { + "name": "amount", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 192, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 191 + } + }, + "docs": [] + } + }, + { + "id": 193, + "type": { + "path": [ + "pallet_balances", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1_0_0", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "V2_0_0", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 194, + "type": { + "path": [ + "pallet_balances", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "transfer", + "fields": [ + { + "name": "dest", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Transfer some liquid free balance to another account.", + "", + "`transfer` will set the `FreeBalance` of the sender and receiver.", + "It will decrease the total issuance of the system by the `TransferFee`.", + "If the sender's account is below the existential deposit as a result", + "of the transfer, the account will be reaped.", + "", + "The dispatch origin for this call must be `Signed` by the transactor.", + "", + "# ", + "- Dependent on arguments but not critical, given proper implementations for input config", + " types. See related functions below.", + "- It contains a limited number of reads and writes internally and no complex", + " computation.", + "", + "Related functions:", + "", + " - `ensure_can_withdraw` is always called internally but has a bounded complexity.", + " - Transferring balances to accounts that did not exist before will cause", + " `T::OnNewAccount::on_new_account` to be called.", + " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`.", + " - `transfer_keep_alive` works the same way as `transfer`, but has an additional check", + " that the transfer will not kill the origin account.", + "---------------------------------", + "- Base Weight: 73.64 µs, worst case scenario (account created, account removed)", + "- DB Weight: 1 Read and 1 Write to destination account", + "- Origin account is already in memory, so no DB operations for them.", + "# " + ] + }, + { + "name": "set_balance", + "fields": [ + { + "name": "who", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "new_free", + "type": 46, + "typeName": "T::Balance", + "docs": [] + }, + { + "name": "new_reserved", + "type": 46, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Set the balances of a given account.", + "", + "This will alter `FreeBalance` and `ReservedBalance` in storage. it will", + "also decrease the total issuance of the system (`TotalIssuance`).", + "If the new free or reserved balance is below the existential deposit,", + "it will reset the account nonce (`frame_system::AccountNonce`).", + "", + "The dispatch origin for this call is `root`.", + "", + "# ", + "- Independent of the arguments.", + "- Contains a limited number of reads and writes.", + "---------------------", + "- Base Weight:", + " - Creating: 27.56 µs", + " - Killing: 35.11 µs", + "- DB Weight: 1 Read, 1 Write to `who`", + "# " + ] + }, + { + "name": "force_transfer", + "fields": [ + { + "name": "source", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "dest", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Exactly as `transfer`, except the origin must be root and the source account may be", + "specified.", + "# ", + "- Same as transfer, but additional read and write because the source account is not", + " assumed to be in the overlay.", + "# " + ] + }, + { + "name": "transfer_keep_alive", + "fields": [ + { + "name": "dest", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Same as the [`transfer`] call, but with a check that the transfer will not kill the", + "origin account.", + "", + "99% of the time you want [`transfer`] instead.", + "", + "[`transfer`]: struct.Pallet.html#method.transfer", + "# ", + "- Cheaper than transfer because account cannot be killed.", + "- Base Weight: 51.4 µs", + "- DB Weight: 1 Read and 1 Write to dest (sender is in overlay already)", + "#" + ] + }, + { + "name": "transfer_all", + "fields": [ + { + "name": "dest", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "keep_alive", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Transfer the entire transferable balance from the caller account.", + "", + "NOTE: This function only attempts to transfer _transferable_ balances. This means that", + "any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be", + "transferred by this function. To ensure that this function results in a killed account,", + "you might need to prepare the account by removing any reference counters, storage", + "deposits, etc...", + "", + "The dispatch origin of this call must be Signed.", + "", + "- `dest`: The recipient of the transfer.", + "- `keep_alive`: A boolean to determine if the `transfer_all` operation should send all", + " of the funds the account has, causing the sender account to be killed (false), or", + " transfer everything except at least the existential deposit, which will guarantee to", + " keep the sender account alive (true). # ", + "- O(1). Just like transfer, but reading the user's transferable balance first.", + " #" + ] + }, + { + "name": "force_unreserve", + "fields": [ + { + "name": "who", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "amount", + "type": 6, + "typeName": "T::Balance", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Unreserve some balance from a user by force.", + "", + "Can only be called by ROOT." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 195, + "type": { + "path": [ + "sp_runtime", + "multiaddress", + "MultiAddress" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "AccountIndex", + "type": 53 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Id", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Index", + "fields": [ + { + "name": null, + "type": 196, + "typeName": "AccountIndex", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Raw", + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Address32", + "fields": [ + { + "name": null, + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Address20", + "fields": [ + { + "name": null, + "type": 64, + "typeName": "[u8; 20]", + "docs": [] + } + ], + "index": 4, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 196, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 53 + } + }, + "docs": [] + } + }, + { + "id": 197, + "type": { + "path": [ + "pallet_balances", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "VestingBalance", + "fields": [], + "index": 0, + "docs": [ + "Vesting balance too high to send value" + ] + }, + { + "name": "LiquidityRestrictions", + "fields": [], + "index": 1, + "docs": [ + "Account liquidity restrictions prevent withdrawal" + ] + }, + { + "name": "InsufficientBalance", + "fields": [], + "index": 2, + "docs": [ + "Balance too low to send value" + ] + }, + { + "name": "ExistentialDeposit", + "fields": [], + "index": 3, + "docs": [ + "Value too low to create account due to existential deposit" + ] + }, + { + "name": "KeepAlive", + "fields": [], + "index": 4, + "docs": [ + "Transfer/payment would kill account" + ] + }, + { + "name": "ExistingVestingSchedule", + "fields": [], + "index": 5, + "docs": [ + "A vesting schedule already exists for this account" + ] + }, + { + "name": "DeadAccount", + "fields": [], + "index": 6, + "docs": [ + "Beneficiary account must pre-exist" + ] + }, + { + "name": "TooManyReserves", + "fields": [], + "index": 7, + "docs": [ + "Number of named reserves exceed MaxReserves" + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 198, + "type": { + "path": [ + "sp_arithmetic", + "fixed_point", + "FixedU128" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 6, + "typeName": "u128", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 199, + "type": { + "path": [ + "pallet_transaction_payment", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1Ancient", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "V2", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 200, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 201 + } + }, + "docs": [] + } + }, + { + "id": 201, + "type": { + "path": [ + "frame_support", + "weights", + "WeightToFeeCoefficient" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "coeff_integer", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "coeff_frac", + "type": 148, + "typeName": "Perbill", + "docs": [] + }, + { + "name": "negative", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "degree", + "type": 2, + "typeName": "u8", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 202, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 203 + } + }, + "docs": [] + } + }, + { + "id": 203, + "type": { + "path": [ + "pallet_authorship", + "UncleEntryItem" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Hash", + "type": 9 + }, + { + "name": "Author", + "type": 0 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InclusionHeight", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Uncle", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": null, + "type": 204, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 204, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 0 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 0, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 205, + "type": { + "path": [ + "pallet_authorship", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set_uncles", + "fields": [ + { + "name": "new_uncles", + "type": 206, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Provide a set of uncles." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 206, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 178 + } + }, + "docs": [] + } + }, + { + "id": 207, + "type": { + "path": [ + "pallet_authorship", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidUncleParent", + "fields": [], + "index": 0, + "docs": [ + "The uncle parent not in the chain." + ] + }, + { + "name": "UnclesAlreadySet", + "fields": [], + "index": 1, + "docs": [ + "Uncles already set in the block." + ] + }, + { + "name": "TooManyUncles", + "fields": [], + "index": 2, + "docs": [ + "Too many uncles." + ] + }, + { + "name": "GenesisUncle", + "fields": [], + "index": 3, + "docs": [ + "The uncle is genesis." + ] + }, + { + "name": "TooHighUncle", + "fields": [], + "index": 4, + "docs": [ + "The uncle is too high in chain." + ] + }, + { + "name": "UncleAlreadyIncluded", + "fields": [], + "index": 5, + "docs": [ + "The uncle is already included." + ] + }, + { + "name": "OldUncle", + "fields": [], + "index": 6, + "docs": [ + "The uncle isn't recent enough to be included." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 208, + "type": { + "path": [ + "pallet_staking", + "StakingLedger" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "stash", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "total", + "type": 46, + "typeName": "Balance", + "docs": [] + }, + { + "name": "active", + "type": 46, + "typeName": "Balance", + "docs": [] + }, + { + "name": "unlocking", + "type": 209, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "claimed_rewards", + "type": 211, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 209, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 210 + } + }, + "docs": [] + } + }, + { + "id": 210, + "type": { + "path": [ + "pallet_staking", + "UnlockChunk" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "value", + "type": 46, + "typeName": "Balance", + "docs": [] + }, + { + "name": "era", + "type": 111, + "typeName": "EraIndex", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 211, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 4 + } + }, + "docs": [] + } + }, + { + "id": 212, + "type": { + "path": [ + "pallet_staking", + "RewardDestination" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Staked", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Stash", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Controller", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Account", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "None", + "fields": [], + "index": 4, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 213, + "type": { + "path": [ + "pallet_staking", + "ValidatorPrefs" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "commission", + "type": 214, + "typeName": "Perbill", + "docs": [] + }, + { + "name": "blocked", + "type": 55, + "typeName": "bool", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 214, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 148 + } + }, + "docs": [] + } + }, + { + "id": 215, + "type": { + "path": [ + "pallet_staking", + "Nominations" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "targets", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "submitted_in", + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "suppressed", + "type": 55, + "typeName": "bool", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 216, + "type": { + "path": [ + "pallet_staking", + "ActiveEraInfo" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "start", + "type": 155, + "typeName": "Option", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 217, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 0 + ] + }, + "docs": [] + } + }, + { + "id": 218, + "type": { + "path": [ + "pallet_staking", + "EraRewardPoints" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "total", + "type": 4, + "typeName": "RewardPoint", + "docs": [] + }, + { + "name": "individual", + "type": 219, + "typeName": "BTreeMap", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 219, + "type": { + "path": [ + "BTreeMap" + ], + "params": [ + { + "name": "K", + "type": 0 + }, + { + "name": "V", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 220, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 220, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 221 + } + }, + "docs": [] + } + }, + { + "id": 221, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 222, + "type": { + "path": [ + "pallet_staking", + "Forcing" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "NotForcing", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "ForceNew", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "ForceNone", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "ForceAlways", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 223, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 224 + } + }, + "docs": [] + } + }, + { + "id": 224, + "type": { + "path": [ + "pallet_staking", + "UnappliedSlash" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "validator", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "own", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "others", + "type": 58, + "typeName": "Vec<(AccountId, Balance)>", + "docs": [] + }, + { + "name": "reporters", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "payout", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 225, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 148, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 226, + "type": { + "path": [ + "pallet_staking", + "slashing", + "SlashingSpans" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "span_index", + "type": 4, + "typeName": "SpanIndex", + "docs": [] + }, + { + "name": "last_start", + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "last_nonzero_slash", + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "prior", + "type": 211, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 227, + "type": { + "path": [ + "pallet_staking", + "slashing", + "SpanRecord" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "slashed", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "paid_out", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 228, + "type": { + "path": [ + "pallet_staking", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1_0_0Ancient", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "V2_0_0", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "V3_0_0", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "V4_0_0", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "V5_0_0", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "V6_0_0", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "V7_0_0", + "fields": [], + "index": 6, + "docs": [] + }, + { + "name": "V8_0_0", + "fields": [], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 229, + "type": { + "path": [ + "sp_arithmetic", + "per_things", + "Percent" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 2, + "typeName": "u8", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 230, + "type": { + "path": [ + "pallet_staking", + "pallet", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "bond", + "fields": [ + { + "name": "controller", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "payee", + "type": 212, + "typeName": "RewardDestination", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Take the origin account as a stash and lock up `value` of its balance. `controller` will", + "be the account that controls it.", + "", + "`value` must be more than the `minimum_balance` specified by `T::Currency`.", + "", + "The dispatch origin for this call must be _Signed_ by the stash account.", + "", + "Emits `Bonded`.", + "# ", + "- Independent of the arguments. Moderate complexity.", + "- O(1).", + "- Three extra DB entries.", + "", + "NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned", + "unless the `origin` falls below _existential deposit_ and gets removed as dust.", + "------------------", + "# " + ] + }, + { + "name": "bond_extra", + "fields": [ + { + "name": "max_additional", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Add some extra amount that have appeared in the stash `free_balance` into the balance up", + "for staking.", + "", + "The dispatch origin for this call must be _Signed_ by the stash, not the controller.", + "", + "Use this if there are additional funds in your stash account that you wish to bond.", + "Unlike [`bond`](Self::bond) or [`unbond`](Self::unbond) this function does not impose", + "any limitation on the amount that can be added.", + "", + "Emits `Bonded`.", + "", + "# ", + "- Independent of the arguments. Insignificant complexity.", + "- O(1).", + "# " + ] + }, + { + "name": "unbond", + "fields": [ + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Schedule a portion of the stash to be unlocked ready for transfer out after the bond", + "period ends. If this leaves an amount actively bonded less than", + "T::Currency::minimum_balance(), then it is increased to the full amount.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + "Once the unlock period is done, you can call `withdraw_unbonded` to actually move", + "the funds out of management ready for transfer.", + "", + "No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`)", + "can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need", + "to be called first to remove some of the chunks (if possible).", + "", + "If a user encounters the `InsufficientBond` error when calling this extrinsic,", + "they should call `chill` first in order to free up their bonded funds.", + "", + "Emits `Unbonded`.", + "", + "See also [`Call::withdraw_unbonded`]." + ] + }, + { + "name": "withdraw_unbonded", + "fields": [ + { + "name": "num_slashing_spans", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Remove any unlocked chunks from the `unlocking` queue from our management.", + "", + "This essentially frees up that balance to be used by the stash account to do", + "whatever it wants.", + "", + "The dispatch origin for this call must be _Signed_ by the controller.", + "", + "Emits `Withdrawn`.", + "", + "See also [`Call::unbond`].", + "", + "# ", + "Complexity O(S) where S is the number of slashing spans to remove", + "NOTE: Weight annotation is the kill scenario, we refund otherwise.", + "# " + ] + }, + { + "name": "validate", + "fields": [ + { + "name": "prefs", + "type": 213, + "typeName": "ValidatorPrefs", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Declare the desire to validate for the origin controller.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash." + ] + }, + { + "name": "nominate", + "fields": [ + { + "name": "targets", + "type": 231, + "typeName": "Vec<::Source>", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Declare the desire to nominate `targets` for the origin controller.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + "# ", + "- The transaction's complexity is proportional to the size of `targets` (N)", + "which is capped at CompactAssignments::LIMIT (MAX_NOMINATIONS).", + "- Both the reads and writes follow a similar pattern.", + "# " + ] + }, + { + "name": "chill", + "fields": [], + "index": 6, + "docs": [ + "Declare no desire to either validate or nominate.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + "# ", + "- Independent of the arguments. Insignificant complexity.", + "- Contains one read.", + "- Writes are limited to the `origin` account key.", + "# " + ] + }, + { + "name": "set_payee", + "fields": [ + { + "name": "payee", + "type": 212, + "typeName": "RewardDestination", + "docs": [] + } + ], + "index": 7, + "docs": [ + "(Re-)set the payment target for a controller.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + "# ", + "- Independent of the arguments. Insignificant complexity.", + "- Contains a limited number of reads.", + "- Writes are limited to the `origin` account key.", + "---------", + "- Weight: O(1)", + "- DB Weight:", + " - Read: Ledger", + " - Write: Payee", + "# " + ] + }, + { + "name": "set_controller", + "fields": [ + { + "name": "controller", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 8, + "docs": [ + "(Re-)set the controller of a stash.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the stash, not the controller.", + "", + "# ", + "- Independent of the arguments. Insignificant complexity.", + "- Contains a limited number of reads.", + "- Writes are limited to the `origin` account key.", + "----------", + "Weight: O(1)", + "DB Weight:", + "- Read: Bonded, Ledger New Controller, Ledger Old Controller", + "- Write: Bonded, Ledger New Controller, Ledger Old Controller", + "# " + ] + }, + { + "name": "set_validator_count", + "fields": [ + { + "name": "new", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Sets the ideal number of validators.", + "", + "The dispatch origin must be Root.", + "", + "# ", + "Weight: O(1)", + "Write: Validator Count", + "# " + ] + }, + { + "name": "increase_validator_count", + "fields": [ + { + "name": "additional", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Increments the ideal number of validators.", + "", + "The dispatch origin must be Root.", + "", + "# ", + "Same as [`Self::set_validator_count`].", + "# " + ] + }, + { + "name": "scale_validator_count", + "fields": [ + { + "name": "factor", + "type": 229, + "typeName": "Percent", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Scale up the ideal number of validators by a factor.", + "", + "The dispatch origin must be Root.", + "", + "# ", + "Same as [`Self::set_validator_count`].", + "# " + ] + }, + { + "name": "force_no_eras", + "fields": [], + "index": 12, + "docs": [ + "Force there to be no new eras indefinitely.", + "", + "The dispatch origin must be Root.", + "", + "# Warning", + "", + "The election process starts multiple blocks before the end of the era.", + "Thus the election process may be ongoing when this is called. In this case the", + "election will continue until the next era is triggered.", + "", + "# ", + "- No arguments.", + "- Weight: O(1)", + "- Write: ForceEra", + "# " + ] + }, + { + "name": "force_new_era", + "fields": [], + "index": 13, + "docs": [ + "Force there to be a new era at the end of the next session. After this, it will be", + "reset to normal (non-forced) behaviour.", + "", + "The dispatch origin must be Root.", + "", + "# Warning", + "", + "The election process starts multiple blocks before the end of the era.", + "If this is called just before a new era is triggered, the election process may not", + "have enough blocks to get a result.", + "", + "# ", + "- No arguments.", + "- Weight: O(1)", + "- Write ForceEra", + "# " + ] + }, + { + "name": "set_invulnerables", + "fields": [ + { + "name": "invulnerables", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ], + "index": 14, + "docs": [ + "Set the validators who cannot be slashed (if any).", + "", + "The dispatch origin must be Root.", + "", + "# ", + "- O(V)", + "- Write: Invulnerables", + "# " + ] + }, + { + "name": "force_unstake", + "fields": [ + { + "name": "stash", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "num_slashing_spans", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 15, + "docs": [ + "Force a current staker to become completely unstaked, immediately.", + "", + "The dispatch origin must be Root.", + "", + "# ", + "O(S) where S is the number of slashing spans to be removed", + "Reads: Bonded, Slashing Spans, Account, Locks", + "Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators,", + "Account, Locks Writes Each: SpanSlash * S", + "# " + ] + }, + { + "name": "force_new_era_always", + "fields": [], + "index": 16, + "docs": [ + "Force there to be a new era at the end of sessions indefinitely.", + "", + "The dispatch origin must be Root.", + "", + "# Warning", + "", + "The election process starts multiple blocks before the end of the era.", + "If this is called just before a new era is triggered, the election process may not", + "have enough blocks to get a result.", + "", + "# ", + "- Weight: O(1)", + "- Write: ForceEra", + "# " + ] + }, + { + "name": "cancel_deferred_slash", + "fields": [ + { + "name": "era", + "type": 4, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "slash_indices", + "type": 211, + "typeName": "Vec", + "docs": [] + } + ], + "index": 17, + "docs": [ + "Cancel enactment of a deferred slash.", + "", + "Can be called by the `T::SlashCancelOrigin`.", + "", + "Parameters: era and indices of the slashes for that era to kill.", + "", + "# ", + "Complexity: O(U + S)", + "with U unapplied slashes weighted with U=1000", + "and S is the number of slash indices to be canceled.", + "- Read: Unapplied Slashes", + "- Write: Unapplied Slashes", + "# " + ] + }, + { + "name": "payout_stakers", + "fields": [ + { + "name": "validator_stash", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "era", + "type": 4, + "typeName": "EraIndex", + "docs": [] + } + ], + "index": 18, + "docs": [ + "Pay out all the stakers behind a single validator for a single era.", + "", + "- `validator_stash` is the stash account of the validator. Their nominators, up to", + " `T::MaxNominatorRewardedPerValidator`, will also receive their rewards.", + "- `era` may be any era between `[current_era - history_depth; current_era]`.", + "", + "The origin of this call must be _Signed_. Any account can call this function, even if", + "it is not one of the stakers.", + "", + "# ", + "- Time complexity: at most O(MaxNominatorRewardedPerValidator).", + "- Contains a limited number of reads and writes.", + "-----------", + "N is the Number of payouts for the validator (including the validator)", + "Weight:", + "- Reward Destination Staked: O(N)", + "- Reward Destination Controller (Creating): O(N)", + "", + " NOTE: weights are assuming that payouts are made to alive stash account (Staked).", + " Paying even a dead controller is cheaper weight-wise. We don't do any refunds here.", + "# " + ] + }, + { + "name": "rebond", + "fields": [ + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 19, + "docs": [ + "Rebond a portion of the stash scheduled to be unlocked.", + "", + "The dispatch origin must be signed by the controller.", + "", + "# ", + "- Time complexity: O(L), where L is unlocking chunks", + "- Bounded by `MAX_UNLOCKING_CHUNKS`.", + "- Storage changes: Can't increase storage, only decrease it.", + "# " + ] + }, + { + "name": "set_history_depth", + "fields": [ + { + "name": "new_history_depth", + "type": 111, + "typeName": "EraIndex", + "docs": [] + }, + { + "name": "era_items_deleted", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 20, + "docs": [ + "Set `HistoryDepth` value. This function will delete any history information", + "when `HistoryDepth` is reduced.", + "", + "Parameters:", + "- `new_history_depth`: The new history depth you would like to set.", + "- `era_items_deleted`: The number of items that will be deleted by this dispatch. This", + " should report all the storage items that will be deleted by clearing old era history.", + " Needed to report an accurate weight for the dispatch. Trusted by `Root` to report an", + " accurate number.", + "", + "Origin must be root.", + "", + "# ", + "- E: Number of history depths removed, i.e. 10 -> 7 = 3", + "- Weight: O(E)", + "- DB Weight:", + " - Reads: Current Era, History Depth", + " - Writes: History Depth", + " - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs", + " - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake,", + " ErasStartSessionIndex", + "# " + ] + }, + { + "name": "reap_stash", + "fields": [ + { + "name": "stash", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "num_slashing_spans", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 21, + "docs": [ + "Remove all data structure concerning a staker/stash once its balance is at the minimum.", + "This is essentially equivalent to `withdraw_unbonded` except it can be called by anyone", + "and the target `stash` must have no funds left beyond the ED.", + "", + "This can be called from any origin.", + "", + "- `stash`: The stash account to reap. Its balance must be zero.", + "", + "# ", + "Complexity: O(S) where S is the number of slashing spans on the account.", + "DB Weight:", + "- Reads: Stash Account, Bonded, Slashing Spans, Locks", + "- Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators,", + " Stash Account, Locks", + "- Writes Each: SpanSlash * S", + "# " + ] + }, + { + "name": "kick", + "fields": [ + { + "name": "who", + "type": 231, + "typeName": "Vec<::Source>", + "docs": [] + } + ], + "index": 22, + "docs": [ + "Remove the given nominations from the calling validator.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_ by the controller, not the stash.", + "", + "- `who`: A list of nominator stash accounts who are nominating this validator which", + " should no longer be nominating this validator.", + "", + "Note: Making this call only makes sense if you first set the validator preferences to", + "block any further nominations." + ] + }, + { + "name": "set_staking_limits", + "fields": [ + { + "name": "min_nominator_bond", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "min_validator_bond", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "max_nominator_count", + "type": 232, + "typeName": "Option", + "docs": [] + }, + { + "name": "max_validator_count", + "type": 232, + "typeName": "Option", + "docs": [] + }, + { + "name": "threshold", + "type": 233, + "typeName": "Option", + "docs": [] + } + ], + "index": 23, + "docs": [ + "Update the various staking limits this pallet.", + "", + "* `min_nominator_bond`: The minimum active bond needed to be a nominator.", + "* `min_validator_bond`: The minimum active bond needed to be a validator.", + "* `max_nominator_count`: The max number of users who can be a nominator at once. When", + " set to `None`, no limit is enforced.", + "* `max_validator_count`: The max number of users who can be a validator at once. When", + " set to `None`, no limit is enforced.", + "", + "Origin must be Root to call this function.", + "", + "NOTE: Existing nominators and validators will not be affected by this update.", + "to kick people under the new limits, `chill_other` should be called." + ] + }, + { + "name": "chill_other", + "fields": [ + { + "name": "controller", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 24, + "docs": [ + "Declare a `controller` to stop participating as either a validator or nominator.", + "", + "Effects will be felt at the beginning of the next era.", + "", + "The dispatch origin for this call must be _Signed_, but can be called by anyone.", + "", + "If the caller is the same as the controller being targeted, then no further checks are", + "enforced, and this function behaves just like `chill`.", + "", + "If the caller is different than the controller being targeted, the following conditions", + "must be met:", + "* A `ChillThreshold` must be set and checked which defines how close to the max", + " nominators or validators we must reach before users can start chilling one-another.", + "* A `MaxNominatorCount` and `MaxValidatorCount` must be set which is used to determine", + " how close we are to the threshold.", + "* A `MinNominatorBond` and `MinValidatorBond` must be set and checked, which determines", + " if this is a person that should be chilled because they have not met the threshold", + " bond required.", + "", + "This can be helpful if bond requirements are updated, and we need to remove old users", + "who do not satisfy these requirements." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 231, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 195 + } + }, + "docs": [] + } + }, + { + "id": 232, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 4, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 233, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 229 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 229, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 234, + "type": { + "path": [ + "pallet_staking", + "pallet", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotController", + "fields": [], + "index": 0, + "docs": [ + "Not a controller account." + ] + }, + { + "name": "NotStash", + "fields": [], + "index": 1, + "docs": [ + "Not a stash account." + ] + }, + { + "name": "AlreadyBonded", + "fields": [], + "index": 2, + "docs": [ + "Stash is already bonded." + ] + }, + { + "name": "AlreadyPaired", + "fields": [], + "index": 3, + "docs": [ + "Controller is already paired." + ] + }, + { + "name": "EmptyTargets", + "fields": [], + "index": 4, + "docs": [ + "Targets cannot be empty." + ] + }, + { + "name": "DuplicateIndex", + "fields": [], + "index": 5, + "docs": [ + "Duplicate index." + ] + }, + { + "name": "InvalidSlashIndex", + "fields": [], + "index": 6, + "docs": [ + "Slash record index out of bounds." + ] + }, + { + "name": "InsufficientBond", + "fields": [], + "index": 7, + "docs": [ + "Can not bond with value less than minimum required." + ] + }, + { + "name": "NoMoreChunks", + "fields": [], + "index": 8, + "docs": [ + "Can not schedule more unlock chunks." + ] + }, + { + "name": "NoUnlockChunk", + "fields": [], + "index": 9, + "docs": [ + "Can not rebond without unlocking chunks." + ] + }, + { + "name": "FundedTarget", + "fields": [], + "index": 10, + "docs": [ + "Attempting to target a stash that still has funds." + ] + }, + { + "name": "InvalidEraToReward", + "fields": [], + "index": 11, + "docs": [ + "Invalid era to reward." + ] + }, + { + "name": "InvalidNumberOfNominations", + "fields": [], + "index": 12, + "docs": [ + "Invalid number of nominations." + ] + }, + { + "name": "NotSortedAndUnique", + "fields": [], + "index": 13, + "docs": [ + "Items are not sorted and unique." + ] + }, + { + "name": "AlreadyClaimed", + "fields": [], + "index": 14, + "docs": [ + "Rewards for this era have already been claimed for this validator." + ] + }, + { + "name": "IncorrectHistoryDepth", + "fields": [], + "index": 15, + "docs": [ + "Incorrect previous history depth input provided." + ] + }, + { + "name": "IncorrectSlashingSpans", + "fields": [], + "index": 16, + "docs": [ + "Incorrect number of slashing spans provided." + ] + }, + { + "name": "BadState", + "fields": [], + "index": 17, + "docs": [ + "Internal state has become somehow corrupted and the operation cannot continue." + ] + }, + { + "name": "TooManyTargets", + "fields": [], + "index": 18, + "docs": [ + "Too many nomination targets supplied." + ] + }, + { + "name": "BadTarget", + "fields": [], + "index": 19, + "docs": [ + "A nomination target was supplied that was blocked or otherwise not a validator." + ] + }, + { + "name": "CannotChillOther", + "fields": [], + "index": 20, + "docs": [ + "The user has enough bond and thus cannot be chilled forcefully by an external person." + ] + }, + { + "name": "TooManyNominators", + "fields": [], + "index": 21, + "docs": [ + "There are too many nominators in the system. Governance needs to adjust the staking", + "settings to keep things safe for the runtime." + ] + }, + { + "name": "TooManyValidators", + "fields": [], + "index": 22, + "docs": [ + "There are too many validators in the system. Governance needs to adjust the staking", + "settings to keep things safe for the runtime." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 235, + "type": { + "path": [ + "sp_staking", + "offence", + "OffenceDetails" + ], + "params": [ + { + "name": "Reporter", + "type": 0 + }, + { + "name": "Offender", + "type": 44 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "offender", + "type": 44, + "typeName": "Offender", + "docs": [] + }, + { + "name": "reporters", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 236, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 33, + 10 + ] + }, + "docs": [] + } + }, + { + "id": 237, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 238 + } + }, + "docs": [] + } + }, + { + "id": 238, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 239 + ] + }, + "docs": [] + } + }, + { + "id": 239, + "type": { + "path": [ + "kusama_runtime", + "SessionKeys" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "grandpa", + "type": 38, + "typeName": "::Public", + "docs": [] + }, + { + "name": "babe", + "type": 166, + "typeName": "::Public", + "docs": [] + }, + { + "name": "im_online", + "type": 41, + "typeName": "::Public", + "docs": [] + }, + { + "name": "para_validator", + "type": 240, + "typeName": "::Public", + "docs": [] + }, + { + "name": "para_assignment", + "type": 241, + "typeName": "::Public", + "docs": [] + }, + { + "name": "authority_discovery", + "type": 242, + "typeName": "::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 240, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "validator_app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 241, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "assignment_app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 242, + "type": { + "path": [ + "sp_authority_discovery", + "app", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 243, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 244, + 10 + ] + }, + "docs": [] + } + }, + { + "id": 244, + "type": { + "path": [ + "sp_core", + "crypto", + "KeyTypeId" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 14, + "typeName": "[u8; 4]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 245, + "type": { + "path": [ + "pallet_session", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set_keys", + "fields": [ + { + "name": "keys", + "type": 239, + "typeName": "T::Keys", + "docs": [] + }, + { + "name": "proof", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Sets the session key(s) of the function caller to `keys`.", + "Allows an account to set its session key prior to becoming a validator.", + "This doesn't take effect until the next session.", + "", + "The dispatch origin of this function must be signed.", + "", + "# ", + "- Complexity: `O(1)`. Actual cost depends on the number of length of", + " `T::Keys::key_ids()` which is fixed.", + "- DbReads: `origin account`, `T::ValidatorIdOf`, `NextKeys`", + "- DbWrites: `origin account`, `NextKeys`", + "- DbReads per key id: `KeyOwner`", + "- DbWrites per key id: `KeyOwner`", + "# " + ] + }, + { + "name": "purge_keys", + "fields": [], + "index": 1, + "docs": [ + "Removes any session key(s) of the function caller.", + "This doesn't take effect until the next session.", + "", + "The dispatch origin of this function must be signed.", + "", + "# ", + "- Complexity: `O(1)` in number of key types. Actual cost depends on the number of length", + " of `T::Keys::key_ids()` which is fixed.", + "- DbReads: `T::ValidatorIdOf`, `NextKeys`, `origin account`", + "- DbWrites: `NextKeys`, `origin account`", + "- DbWrites per key id: `KeyOwner`", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 246, + "type": { + "path": [ + "pallet_session", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidProof", + "fields": [], + "index": 0, + "docs": [ + "Invalid ownership proof." + ] + }, + { + "name": "NoAssociatedValidatorId", + "fields": [], + "index": 1, + "docs": [ + "No associated validator ID for account." + ] + }, + { + "name": "DuplicatedKey", + "fields": [], + "index": 2, + "docs": [ + "Registered duplicate key." + ] + }, + { + "name": "NoKeys", + "fields": [], + "index": 3, + "docs": [ + "No keys are associated with this account." + ] + }, + { + "name": "NoAccount", + "fields": [], + "index": 4, + "docs": [ + "Key setting account is not live, so it's impossible to associate keys." + ] + } + ] + } + }, + "docs": [ + "Error for the session pallet." + ] + } + }, + { + "id": 247, + "type": { + "path": [ + "pallet_grandpa", + "StoredState" + ], + "params": [ + { + "name": "N", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Live", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "PendingPause", + "fields": [ + { + "name": "scheduled_at", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "N", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Paused", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "PendingResume", + "fields": [ + { + "name": "scheduled_at", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "N", + "docs": [] + } + ], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 248, + "type": { + "path": [ + "pallet_grandpa", + "StoredPendingChange" + ], + "params": [ + { + "name": "N", + "type": 4 + }, + { + "name": "Limit", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "scheduled_at", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "next_authorities", + "type": 249, + "typeName": "BoundedAuthorityList", + "docs": [] + }, + { + "name": "forced", + "type": 232, + "typeName": "Option", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 249, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 37 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 36, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 250, + "type": { + "path": [ + "pallet_grandpa", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "report_equivocation", + "fields": [ + { + "name": "equivocation_proof", + "type": 251, + "typeName": "Box>", + "docs": [] + }, + { + "name": "key_owner_proof", + "type": 180, + "typeName": "T::KeyOwnerProof", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Report voter equivocation/misbehavior. This method will verify the", + "equivocation proof and validate the given key ownership proof", + "against the extracted offender. If both are valid, the offence", + "will be reported." + ] + }, + { + "name": "report_equivocation_unsigned", + "fields": [ + { + "name": "equivocation_proof", + "type": 251, + "typeName": "Box>", + "docs": [] + }, + { + "name": "key_owner_proof", + "type": 180, + "typeName": "T::KeyOwnerProof", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Report voter equivocation/misbehavior. This method will verify the", + "equivocation proof and validate the given key ownership proof", + "against the extracted offender. If both are valid, the offence", + "will be reported.", + "", + "This extrinsic must be called unsigned and it is expected that only", + "block authors will call it (validated in `ValidateUnsigned`), as such", + "if the block author is defined it will be defined as the equivocation", + "reporter." + ] + }, + { + "name": "note_stalled", + "fields": [ + { + "name": "delay", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "best_finalized_block_number", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Note that the current authority set of the GRANDPA finality gadget has", + "stalled. This will trigger a forced authority set change at the beginning", + "of the next session, to be enacted `delay` blocks after that. The delay", + "should be high enough to safely assume that the block signalling the", + "forced change will not be re-orged (e.g. 1000 blocks). The GRANDPA voters", + "will start the new authority set using the given finalized block as base.", + "Only callable by root." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 251, + "type": { + "path": [ + "sp_finality_grandpa", + "EquivocationProof" + ], + "params": [ + { + "name": "H", + "type": 9 + }, + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "set_id", + "type": 8, + "typeName": "SetId", + "docs": [] + }, + { + "name": "equivocation", + "type": 252, + "typeName": "Equivocation", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 252, + "type": { + "path": [ + "sp_finality_grandpa", + "Equivocation" + ], + "params": [ + { + "name": "H", + "type": 9 + }, + { + "name": "N", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Prevote", + "fields": [ + { + "name": null, + "type": 253, + "typeName": "grandpa::Equivocation,\nAuthoritySignature>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Precommit", + "fields": [ + { + "name": null, + "type": 258, + "typeName": "grandpa::Equivocation,\nAuthoritySignature>", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 253, + "type": { + "path": [ + "finality_grandpa", + "Equivocation" + ], + "params": [ + { + "name": "Id", + "type": 38 + }, + { + "name": "V", + "type": 254 + }, + { + "name": "S", + "type": 255 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "round_number", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "identity", + "type": 38, + "typeName": "Id", + "docs": [] + }, + { + "name": "first", + "type": 257, + "typeName": "(V, S)", + "docs": [] + }, + { + "name": "second", + "type": 257, + "typeName": "(V, S)", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 254, + "type": { + "path": [ + "finality_grandpa", + "Prevote" + ], + "params": [ + { + "name": "H", + "type": 9 + }, + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "target_hash", + "type": 9, + "typeName": "H", + "docs": [] + }, + { + "name": "target_number", + "type": 4, + "typeName": "N", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 255, + "type": { + "path": [ + "sp_finality_grandpa", + "app", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 256, + "typeName": "ed25519::Signature", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 256, + "type": { + "path": [ + "sp_core", + "ed25519", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 92, + "typeName": "[u8; 64]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 257, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 254, + 255 + ] + }, + "docs": [] + } + }, + { + "id": 258, + "type": { + "path": [ + "finality_grandpa", + "Equivocation" + ], + "params": [ + { + "name": "Id", + "type": 38 + }, + { + "name": "V", + "type": 259 + }, + { + "name": "S", + "type": 255 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "round_number", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "identity", + "type": 38, + "typeName": "Id", + "docs": [] + }, + { + "name": "first", + "type": 260, + "typeName": "(V, S)", + "docs": [] + }, + { + "name": "second", + "type": 260, + "typeName": "(V, S)", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 259, + "type": { + "path": [ + "finality_grandpa", + "Precommit" + ], + "params": [ + { + "name": "H", + "type": 9 + }, + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "target_hash", + "type": 9, + "typeName": "H", + "docs": [] + }, + { + "name": "target_number", + "type": 4, + "typeName": "N", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 260, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 259, + 255 + ] + }, + "docs": [] + } + }, + { + "id": 261, + "type": { + "path": [ + "pallet_grandpa", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "PauseFailed", + "fields": [], + "index": 0, + "docs": [ + "Attempt to signal GRANDPA pause when the authority set isn't live", + "(either paused or already pending pause)." + ] + }, + { + "name": "ResumeFailed", + "fields": [], + "index": 1, + "docs": [ + "Attempt to signal GRANDPA resume when the authority set isn't paused", + "(either live or already pending resume)." + ] + }, + { + "name": "ChangePending", + "fields": [], + "index": 2, + "docs": [ + "Attempt to signal GRANDPA change with one already pending." + ] + }, + { + "name": "TooSoon", + "fields": [], + "index": 3, + "docs": [ + "Cannot signal forced change so soon after last." + ] + }, + { + "name": "InvalidKeyOwnershipProof", + "fields": [], + "index": 4, + "docs": [ + "A key ownership proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "InvalidEquivocationProof", + "fields": [], + "index": 5, + "docs": [ + "An equivocation proof provided as part of an equivocation report is invalid." + ] + }, + { + "name": "DuplicateOffenceReport", + "fields": [], + "index": 6, + "docs": [ + "A given equivocation report is valid but already previously reported." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 262, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 41 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 263, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 263, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 41 + } + }, + "docs": [] + } + }, + { + "id": 264, + "type": { + "path": [ + "frame_support", + "traits", + "misc", + "WrapperOpaque" + ], + "params": [ + { + "name": "T", + "type": 265 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 111, + "typeName": null, + "docs": [] + }, + { + "name": null, + "type": 265, + "typeName": "T", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 265, + "type": { + "path": [ + "pallet_im_online", + "BoundedOpaqueNetworkState" + ], + "params": [ + { + "name": "PeerIdEncodingLimit", + "type": null + }, + { + "name": "MultiAddrEncodingLimit", + "type": null + }, + { + "name": "AddressesLimit", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "peer_id", + "type": 266, + "typeName": "WeakBoundedVec", + "docs": [] + }, + { + "name": "external_addresses", + "type": 267, + "typeName": "WeakBoundedVec, AddressesLimit\n>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 266, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 2 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 267, + "type": { + "path": [ + "frame_support", + "storage", + "weak_bounded_vec", + "WeakBoundedVec" + ], + "params": [ + { + "name": "T", + "type": 266 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 268, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 268, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 266 + } + }, + "docs": [] + } + }, + { + "id": 269, + "type": { + "path": [ + "pallet_im_online", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "heartbeat", + "fields": [ + { + "name": "heartbeat", + "type": 270, + "typeName": "Heartbeat", + "docs": [] + }, + { + "name": "signature", + "type": 275, + "typeName": "::Signature", + "docs": [] + } + ], + "index": 0, + "docs": [ + "# ", + "- Complexity: `O(K + E)` where K is length of `Keys` (heartbeat.validators_len) and E is", + " length of `heartbeat.network_state.external_address`", + " - `O(K)`: decoding of length `K`", + " - `O(E)`: decoding/encoding of length `E`", + "- DbReads: pallet_session `Validators`, pallet_session `CurrentIndex`, `Keys`,", + " `ReceivedHeartbeats`", + "- DbWrites: `ReceivedHeartbeats`", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 270, + "type": { + "path": [ + "pallet_im_online", + "Heartbeat" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "block_number", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "network_state", + "type": 271, + "typeName": "OpaqueNetworkState", + "docs": [] + }, + { + "name": "session_index", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + }, + { + "name": "authority_index", + "type": 4, + "typeName": "AuthIndex", + "docs": [] + }, + { + "name": "validators_len", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 271, + "type": { + "path": [ + "sp_core", + "offchain", + "OpaqueNetworkState" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "peer_id", + "type": 272, + "typeName": "OpaquePeerId", + "docs": [] + }, + { + "name": "external_addresses", + "type": 273, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 272, + "type": { + "path": [ + "sp_core", + "OpaquePeerId" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 273, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 274 + } + }, + "docs": [] + } + }, + { + "id": 274, + "type": { + "path": [ + "sp_core", + "offchain", + "OpaqueMultiaddr" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 275, + "type": { + "path": [ + "pallet_im_online", + "sr25519", + "app_sr25519", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 91, + "typeName": "sr25519::Signature", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 276, + "type": { + "path": [ + "pallet_im_online", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidKey", + "fields": [], + "index": 0, + "docs": [ + "Non existent public key." + ] + }, + { + "name": "DuplicatedHeartbeat", + "fields": [], + "index": 1, + "docs": [ + "Duplicated heartbeat." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 277, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 278 + } + }, + "docs": [] + } + }, + { + "id": 278, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 9, + 0 + ] + }, + "docs": [] + } + }, + { + "id": 279, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 50, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 280, + "type": { + "path": [ + "pallet_democracy", + "PreimageStatus" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Missing", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Available", + "fields": [ + { + "name": "data", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "provider", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "since", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "expiry", + "type": 232, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 281, + "type": { + "path": [ + "pallet_democracy", + "types", + "ReferendumInfo" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Hash", + "type": 9 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Ongoing", + "fields": [ + { + "name": null, + "type": 282, + "typeName": "ReferendumStatus", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Finished", + "fields": [ + { + "name": "approved", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "end", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 282, + "type": { + "path": [ + "pallet_democracy", + "types", + "ReferendumStatus" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Hash", + "type": 9 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "end", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "proposal_hash", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "threshold", + "type": 51, + "typeName": "VoteThreshold", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "tally", + "type": 283, + "typeName": "Tally", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 283, + "type": { + "path": [ + "pallet_democracy", + "types", + "Tally" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "ayes", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "nays", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "turnout", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 284, + "type": { + "path": [ + "pallet_democracy", + "vote", + "Voting" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Direct", + "fields": [ + { + "name": "votes", + "type": 285, + "typeName": "Vec<(ReferendumIndex, AccountVote)>", + "docs": [] + }, + { + "name": "delegations", + "type": 289, + "typeName": "Delegations", + "docs": [] + }, + { + "name": "prior", + "type": 290, + "typeName": "PriorLock", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Delegating", + "fields": [ + { + "name": "balance", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "target", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "conviction", + "type": 291, + "typeName": "Conviction", + "docs": [] + }, + { + "name": "delegations", + "type": 289, + "typeName": "Delegations", + "docs": [] + }, + { + "name": "prior", + "type": 290, + "typeName": "PriorLock", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 285, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 286 + } + }, + "docs": [] + } + }, + { + "id": 286, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 287 + ] + }, + "docs": [] + } + }, + { + "id": 287, + "type": { + "path": [ + "pallet_democracy", + "vote", + "AccountVote" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Standard", + "fields": [ + { + "name": "vote", + "type": 288, + "typeName": "Vote", + "docs": [] + }, + { + "name": "balance", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Split", + "fields": [ + { + "name": "aye", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "nay", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 288, + "type": { + "path": [ + "pallet_democracy", + "vote", + "Vote" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 289, + "type": { + "path": [ + "pallet_democracy", + "types", + "Delegations" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "votes", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "capital", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 290, + "type": { + "path": [ + "pallet_democracy", + "vote", + "PriorLock" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 291, + "type": { + "path": [ + "pallet_democracy", + "conviction", + "Conviction" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Locked1x", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Locked2x", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Locked3x", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "Locked4x", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "Locked5x", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Locked6x", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 292, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 9, + 51 + ] + }, + "docs": [] + } + }, + { + "id": 293, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 50 + ] + }, + "docs": [] + } + }, + { + "id": 294, + "type": { + "path": [ + "pallet_democracy", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1", + "fields": [], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 295, + "type": { + "path": [ + "pallet_democracy", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "propose", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Propose a sensitive action to be taken.", + "", + "The dispatch origin of this call must be _Signed_ and the sender must", + "have funds to cover the deposit.", + "", + "- `proposal_hash`: The hash of the proposal preimage.", + "- `value`: The amount of deposit (must be at least `MinimumDeposit`).", + "", + "Emits `Proposed`.", + "", + "Weight: `O(p)`" + ] + }, + { + "name": "second", + "fields": [ + { + "name": "proposal", + "type": 111, + "typeName": "PropIndex", + "docs": [] + }, + { + "name": "seconds_upper_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Signals agreement with a particular proposal.", + "", + "The dispatch origin of this call must be _Signed_ and the sender", + "must have funds to cover the deposit, equal to the original deposit.", + "", + "- `proposal`: The index of the proposal to second.", + "- `seconds_upper_bound`: an upper bound on the current number of seconds on this", + " proposal. Extrinsic is weighted according to this value with no refund.", + "", + "Weight: `O(S)` where S is the number of seconds a proposal already has." + ] + }, + { + "name": "vote", + "fields": [ + { + "name": "ref_index", + "type": 111, + "typeName": "ReferendumIndex", + "docs": [] + }, + { + "name": "vote", + "type": 287, + "typeName": "AccountVote>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + "otherwise it is a vote to keep the status quo.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `ref_index`: The index of the referendum to vote for.", + "- `vote`: The vote configuration.", + "", + "Weight: `O(R)` where R is the number of referendums the voter has voted on." + ] + }, + { + "name": "emergency_cancel", + "fields": [ + { + "name": "ref_index", + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Schedule an emergency cancellation of a referendum. Cannot happen twice to the same", + "referendum.", + "", + "The dispatch origin of this call must be `CancellationOrigin`.", + "", + "-`ref_index`: The index of the referendum to cancel.", + "", + "Weight: `O(1)`." + ] + }, + { + "name": "external_propose", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Schedule a referendum to be tabled once it is legal to schedule an external", + "referendum.", + "", + "The dispatch origin of this call must be `ExternalOrigin`.", + "", + "- `proposal_hash`: The preimage hash of the proposal.", + "", + "Weight: `O(V)` with V number of vetoers in the blacklist of proposal.", + " Decoding vec of length V. Charged as maximum" + ] + }, + { + "name": "external_propose_majority", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Schedule a majority-carries referendum to be tabled next once it is legal to schedule", + "an external referendum.", + "", + "The dispatch of this call must be `ExternalMajorityOrigin`.", + "", + "- `proposal_hash`: The preimage hash of the proposal.", + "", + "Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + "pre-scheduled `external_propose` call.", + "", + "Weight: `O(1)`" + ] + }, + { + "name": "external_propose_default", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Schedule a negative-turnout-bias referendum to be tabled next once it is legal to", + "schedule an external referendum.", + "", + "The dispatch of this call must be `ExternalDefaultOrigin`.", + "", + "- `proposal_hash`: The preimage hash of the proposal.", + "", + "Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + "pre-scheduled `external_propose` call.", + "", + "Weight: `O(1)`" + ] + }, + { + "name": "fast_track", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "voting_period", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Schedule the currently externally-proposed majority-carries referendum to be tabled", + "immediately. If there is no externally-proposed referendum currently, or if there is one", + "but it is not a majority-carries referendum then it fails.", + "", + "The dispatch of this call must be `FastTrackOrigin`.", + "", + "- `proposal_hash`: The hash of the current external proposal.", + "- `voting_period`: The period that is allowed for voting on this proposal. Increased to", + " `FastTrackVotingPeriod` if too low.", + "- `delay`: The number of block after voting has ended in approval and this should be", + " enacted. This doesn't have a minimum amount.", + "", + "Emits `Started`.", + "", + "Weight: `O(1)`" + ] + }, + { + "name": "veto_external", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Veto and blacklist the external proposal hash.", + "", + "The dispatch origin of this call must be `VetoOrigin`.", + "", + "- `proposal_hash`: The preimage hash of the proposal to veto and blacklist.", + "", + "Emits `Vetoed`.", + "", + "Weight: `O(V + log(V))` where V is number of `existing vetoers`" + ] + }, + { + "name": "cancel_referendum", + "fields": [ + { + "name": "ref_index", + "type": 111, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Remove a referendum.", + "", + "The dispatch origin of this call must be _Root_.", + "", + "- `ref_index`: The index of the referendum to cancel.", + "", + "# Weight: `O(1)`." + ] + }, + { + "name": "cancel_queued", + "fields": [ + { + "name": "which", + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Cancel a proposal queued for enactment.", + "", + "The dispatch origin of this call must be _Root_.", + "", + "- `which`: The index of the referendum to cancel.", + "", + "Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`." + ] + }, + { + "name": "delegate", + "fields": [ + { + "name": "to", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "conviction", + "type": 291, + "typeName": "Conviction", + "docs": [] + }, + { + "name": "balance", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Delegate the voting power (with some given conviction) of the sending account.", + "", + "The balance delegated is locked for as long as it's delegated, and thereafter for the", + "time appropriate for the conviction's lock period.", + "", + "The dispatch origin of this call must be _Signed_, and the signing account must either:", + " - be delegating already; or", + " - have no voting activity (if there is, then it will need to be removed/consolidated", + " through `reap_vote` or `unvote`).", + "", + "- `to`: The account whose voting the `target` account's voting power will follow.", + "- `conviction`: The conviction that will be attached to the delegated votes. When the", + " account is undelegated, the funds will be locked for the corresponding period.", + "- `balance`: The amount of the account's balance to be used in delegating. This must not", + " be more than the account's current balance.", + "", + "Emits `Delegated`.", + "", + "Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "undelegate", + "fields": [], + "index": 12, + "docs": [ + "Undelegate the voting power of the sending account.", + "", + "Tokens may be unlocked following once an amount of time consistent with the lock period", + "of the conviction with which the delegation was issued.", + "", + "The dispatch origin of this call must be _Signed_ and the signing account must be", + "currently delegating.", + "", + "Emits `Undelegated`.", + "", + "Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "clear_public_proposals", + "fields": [], + "index": 13, + "docs": [ + "Clears all public proposals.", + "", + "The dispatch origin of this call must be _Root_.", + "", + "Weight: `O(1)`." + ] + }, + { + "name": "note_preimage", + "fields": [ + { + "name": "encoded_proposal", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 14, + "docs": [ + "Register the preimage for an upcoming proposal. This doesn't require the proposal to be", + "in the dispatch queue but does require a deposit, returned once enacted.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `encoded_proposal`: The preimage of a proposal.", + "", + "Emits `PreimageNoted`.", + "", + "Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_preimage_operational", + "fields": [ + { + "name": "encoded_proposal", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 15, + "docs": [ + "Same as `note_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "note_imminent_preimage", + "fields": [ + { + "name": "encoded_proposal", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 16, + "docs": [ + "Register the preimage for an upcoming proposal. This requires the proposal to be", + "in the dispatch queue. No deposit is needed. When this call is successful, i.e.", + "the preimage has not been uploaded before and matches some imminent proposal,", + "no fee is paid.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `encoded_proposal`: The preimage of a proposal.", + "", + "Emits `PreimageNoted`.", + "", + "Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_imminent_preimage_operational", + "fields": [ + { + "name": "encoded_proposal", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 17, + "docs": [ + "Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "reap_preimage", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "proposal_len_upper_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 18, + "docs": [ + "Remove an expired proposal preimage and collect the deposit.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `proposal_hash`: The preimage hash of a proposal.", + "- `proposal_length_upper_bound`: an upper bound on length of the proposal. Extrinsic is", + " weighted according to this value with no refund.", + "", + "This will only work after `VotingPeriod` blocks from the time that the preimage was", + "noted, if it's the same account doing it. If it's a different account, then it'll only", + "work an additional `EnactmentPeriod` later.", + "", + "Emits `PreimageReaped`.", + "", + "Weight: `O(D)` where D is length of proposal." + ] + }, + { + "name": "unlock", + "fields": [ + { + "name": "target", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 19, + "docs": [ + "Unlock tokens that have an expired lock.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `target`: The account to remove the lock on.", + "", + "Weight: `O(R)` with R number of vote of target." + ] + }, + { + "name": "remove_vote", + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 20, + "docs": [ + "Remove a vote for a referendum.", + "", + "If:", + "- the referendum was cancelled, or", + "- the referendum is ongoing, or", + "- the referendum has ended such that", + " - the vote of the account was in opposition to the result; or", + " - there was no conviction to the account's vote; or", + " - the account made a split vote", + "...then the vote is removed cleanly and a following call to `unlock` may result in more", + "funds being available.", + "", + "If, however, the referendum has ended and:", + "- it finished corresponding to the vote of the account, and", + "- the account made a standard vote with conviction, and", + "- the lock period of the conviction is not over", + "...then the lock will be aggregated into the overall account's lock, which may involve", + "*overlocking* (where the two locks are combined into a single lock that is the maximum", + "of both the amount locked and the time is it locked for).", + "", + "The dispatch origin of this call must be _Signed_, and the signer must have a vote", + "registered for referendum `index`.", + "", + "- `index`: The index of referendum of the vote to be removed.", + "", + "Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "remove_other_vote", + "fields": [ + { + "name": "target", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 21, + "docs": [ + "Remove a vote for a referendum.", + "", + "If the `target` is equal to the signer, then this function is exactly equivalent to", + "`remove_vote`. If not equal to the signer, then the vote must have expired,", + "either because the referendum was cancelled, because the voter lost the referendum or", + "because the conviction period is over.", + "", + "The dispatch origin of this call must be _Signed_.", + "", + "- `target`: The account of the vote to be removed; this account must have voted for", + " referendum `index`.", + "- `index`: The index of referendum of the vote to be removed.", + "", + "Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "enact_proposal", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "ReferendumIndex", + "docs": [] + } + ], + "index": 22, + "docs": [ + "Enact a proposal from a referendum. For now we just make the weight be the maximum." + ] + }, + { + "name": "blacklist", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "maybe_ref_index", + "type": 232, + "typeName": "Option", + "docs": [] + } + ], + "index": 23, + "docs": [ + "Permanently place a proposal into the blacklist. This prevents it from ever being", + "proposed again.", + "", + "If called on a queued public or external proposal, then this will result in it being", + "removed. If the `ref_index` supplied is an active referendum with the proposal hash,", + "then it will be cancelled.", + "", + "The dispatch origin of this call must be `BlacklistOrigin`.", + "", + "- `proposal_hash`: The proposal hash to blacklist permanently.", + "- `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be", + "cancelled.", + "", + "Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a", + " reasonable value)." + ] + }, + { + "name": "cancel_proposal", + "fields": [ + { + "name": "prop_index", + "type": 111, + "typeName": "PropIndex", + "docs": [] + } + ], + "index": 24, + "docs": [ + "Remove a proposal.", + "", + "The dispatch origin of this call must be `CancelProposalOrigin`.", + "", + "- `prop_index`: The index of the proposal to cancel.", + "", + "Weight: `O(p)` where `p = PublicProps::::decode_len()`" + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 296, + "type": { + "path": [ + "pallet_democracy", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ValueLow", + "fields": [], + "index": 0, + "docs": [ + "Value too low" + ] + }, + { + "name": "ProposalMissing", + "fields": [], + "index": 1, + "docs": [ + "Proposal does not exist" + ] + }, + { + "name": "AlreadyCanceled", + "fields": [], + "index": 2, + "docs": [ + "Cannot cancel the same proposal twice" + ] + }, + { + "name": "DuplicateProposal", + "fields": [], + "index": 3, + "docs": [ + "Proposal already made" + ] + }, + { + "name": "ProposalBlacklisted", + "fields": [], + "index": 4, + "docs": [ + "Proposal still blacklisted" + ] + }, + { + "name": "NotSimpleMajority", + "fields": [], + "index": 5, + "docs": [ + "Next external proposal not simple majority" + ] + }, + { + "name": "InvalidHash", + "fields": [], + "index": 6, + "docs": [ + "Invalid hash" + ] + }, + { + "name": "NoProposal", + "fields": [], + "index": 7, + "docs": [ + "No external proposal" + ] + }, + { + "name": "AlreadyVetoed", + "fields": [], + "index": 8, + "docs": [ + "Identity may not veto a proposal twice" + ] + }, + { + "name": "DuplicatePreimage", + "fields": [], + "index": 9, + "docs": [ + "Preimage already noted" + ] + }, + { + "name": "NotImminent", + "fields": [], + "index": 10, + "docs": [ + "Not imminent" + ] + }, + { + "name": "TooEarly", + "fields": [], + "index": 11, + "docs": [ + "Too early" + ] + }, + { + "name": "Imminent", + "fields": [], + "index": 12, + "docs": [ + "Imminent" + ] + }, + { + "name": "PreimageMissing", + "fields": [], + "index": 13, + "docs": [ + "Preimage not found" + ] + }, + { + "name": "ReferendumInvalid", + "fields": [], + "index": 14, + "docs": [ + "Vote given for invalid referendum" + ] + }, + { + "name": "PreimageInvalid", + "fields": [], + "index": 15, + "docs": [ + "Invalid preimage" + ] + }, + { + "name": "NoneWaiting", + "fields": [], + "index": 16, + "docs": [ + "No proposals waiting" + ] + }, + { + "name": "NotVoter", + "fields": [], + "index": 17, + "docs": [ + "The given account did not vote on the referendum." + ] + }, + { + "name": "NoPermission", + "fields": [], + "index": 18, + "docs": [ + "The actor has no permission to conduct the action." + ] + }, + { + "name": "AlreadyDelegating", + "fields": [], + "index": 19, + "docs": [ + "The account is already delegating." + ] + }, + { + "name": "InsufficientFunds", + "fields": [], + "index": 20, + "docs": [ + "Too high a balance was provided that the account cannot afford." + ] + }, + { + "name": "NotDelegating", + "fields": [], + "index": 21, + "docs": [ + "The account is not currently delegating." + ] + }, + { + "name": "VotesExist", + "fields": [], + "index": 22, + "docs": [ + "The account currently has votes attached to it and the operation cannot succeed until", + "these are removed, either through `unvote` or `reap_vote`." + ] + }, + { + "name": "InstantNotAllowed", + "fields": [], + "index": 23, + "docs": [ + "The instant referendum origin is currently disallowed." + ] + }, + { + "name": "Nonsense", + "fields": [], + "index": 24, + "docs": [ + "Delegation to oneself makes no sense." + ] + }, + { + "name": "WrongUpperBound", + "fields": [], + "index": 25, + "docs": [ + "Invalid upper bound." + ] + }, + { + "name": "MaxVotesReached", + "fields": [], + "index": 26, + "docs": [ + "Maximum number of votes reached." + ] + }, + { + "name": "TooManyProposals", + "fields": [], + "index": 27, + "docs": [ + "Maximum number of proposals reached." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 297, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 9 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 143, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 298, + "type": { + "path": [ + "kusama_runtime", + "Call" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "System", + "fields": [ + { + "name": null, + "type": 147, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Babe", + "fields": [ + { + "name": null, + "type": 176, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Timestamp", + "fields": [ + { + "name": null, + "type": 182, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Indices", + "fields": [ + { + "name": null, + "type": 184, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Balances", + "fields": [ + { + "name": null, + "type": 194, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Authorship", + "fields": [ + { + "name": null, + "type": 205, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Staking", + "fields": [ + { + "name": null, + "type": 230, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Session", + "fields": [ + { + "name": null, + "type": 245, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "Grandpa", + "fields": [ + { + "name": null, + "type": 250, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "ImOnline", + "fields": [ + { + "name": null, + "type": 269, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "Democracy", + "fields": [ + { + "name": null, + "type": 295, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "Council", + "fields": [ + { + "name": null, + "type": 299, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "TechnicalCommittee", + "fields": [ + { + "name": null, + "type": 300, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "PhragmenElection", + "fields": [ + { + "name": null, + "type": 301, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "TechnicalMembership", + "fields": [ + { + "name": null, + "type": 303, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "Treasury", + "fields": [ + { + "name": null, + "type": 304, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "Claims", + "fields": [ + { + "name": null, + "type": 305, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "Utility", + "fields": [ + { + "name": null, + "type": 312, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Identity", + "fields": [ + { + "name": null, + "type": 314, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "Society", + "fields": [ + { + "name": null, + "type": 354, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "Recovery", + "fields": [ + { + "name": null, + "type": 356, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 27, + "docs": [] + }, + { + "name": "Vesting", + "fields": [ + { + "name": null, + "type": 357, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 28, + "docs": [] + }, + { + "name": "Scheduler", + "fields": [ + { + "name": null, + "type": 359, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 29, + "docs": [] + }, + { + "name": "Proxy", + "fields": [ + { + "name": null, + "type": 361, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 30, + "docs": [] + }, + { + "name": "Multisig", + "fields": [ + { + "name": null, + "type": 363, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 31, + "docs": [] + }, + { + "name": "Bounties", + "fields": [ + { + "name": null, + "type": 365, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 35, + "docs": [] + }, + { + "name": "Tips", + "fields": [ + { + "name": null, + "type": 366, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 36, + "docs": [] + }, + { + "name": "ElectionProviderMultiPhase", + "fields": [ + { + "name": null, + "type": 367, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 37, + "docs": [] + }, + { + "name": "Gilt", + "fields": [ + { + "name": null, + "type": 450, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 38, + "docs": [] + }, + { + "name": "BagsList", + "fields": [ + { + "name": null, + "type": 453, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 39, + "docs": [] + }, + { + "name": "Configuration", + "fields": [ + { + "name": null, + "type": 454, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 51, + "docs": [] + }, + { + "name": "ParasShared", + "fields": [ + { + "name": null, + "type": 455, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 52, + "docs": [] + }, + { + "name": "ParaInclusion", + "fields": [ + { + "name": null, + "type": 456, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 53, + "docs": [] + }, + { + "name": "ParaInherent", + "fields": [ + { + "name": null, + "type": 457, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 54, + "docs": [] + }, + { + "name": "Paras", + "fields": [ + { + "name": null, + "type": 484, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 56, + "docs": [] + }, + { + "name": "Initializer", + "fields": [ + { + "name": null, + "type": 485, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 57, + "docs": [] + }, + { + "name": "Dmp", + "fields": [ + { + "name": null, + "type": 486, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 58, + "docs": [] + }, + { + "name": "Ump", + "fields": [ + { + "name": null, + "type": 487, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 59, + "docs": [] + }, + { + "name": "Hrmp", + "fields": [ + { + "name": null, + "type": 488, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 60, + "docs": [] + }, + { + "name": "Registrar", + "fields": [ + { + "name": null, + "type": 489, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 70, + "docs": [] + }, + { + "name": "Slots", + "fields": [ + { + "name": null, + "type": 490, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 71, + "docs": [] + }, + { + "name": "Auctions", + "fields": [ + { + "name": null, + "type": 491, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 72, + "docs": [] + }, + { + "name": "Crowdloan", + "fields": [ + { + "name": null, + "type": 493, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 73, + "docs": [] + }, + { + "name": "XcmPallet", + "fields": [ + { + "name": null, + "type": 501, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::dispatch\n::CallableCallFor", + "docs": [] + } + ], + "index": 99, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 299, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set_members", + "fields": [ + { + "name": "new_members", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "prime", + "type": 204, + "typeName": "Option", + "docs": [] + }, + { + "name": "old_count", + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Set the collective's membership.", + "", + "- `new_members`: The new member list. Be nice to the chain and provide it sorted.", + "- `prime`: The prime member whose vote sets the default.", + "- `old_count`: The upper bound for the previous number of members in storage. Used for", + " weight estimation.", + "", + "Requires root origin.", + "", + "NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but", + " the weight estimations rely on it to estimate dispatchable weight.", + "", + "# ", + "## Weight", + "- `O(MP + N)` where:", + " - `M` old-members-count (code- and governance-bounded)", + " - `N` new-members-count (code- and governance-bounded)", + " - `P` proposals-count (code-bounded)", + "- DB:", + " - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the", + " members", + " - 1 storage read (codec `O(P)`) for reading the proposals", + " - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal", + " - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one", + "# " + ] + }, + { + "name": "execute", + "fields": [ + { + "name": "proposal", + "type": 298, + "typeName": "Box<>::Proposal>", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Dispatch a proposal from a member using the `Member` origin.", + "", + "Origin must be a member of the collective.", + "", + "# ", + "## Weight", + "- `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching", + " `proposal`", + "- DB: 1 read (codec `O(M)`) + DB access of `proposal`", + "- 1 event", + "# " + ] + }, + { + "name": "propose", + "fields": [ + { + "name": "threshold", + "type": 111, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": "proposal", + "type": 298, + "typeName": "Box<>::Proposal>", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Add a new proposal to either be voted on or executed directly.", + "", + "Requires the sender to be member.", + "", + "`threshold` determines whether `proposal` is executed directly (`threshold < 2`)", + "or put up for voting.", + "", + "# ", + "## Weight", + "- `O(B + M + P1)` or `O(B + M + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - branching is influenced by `threshold` where:", + " - `P1` is proposal execution complexity (`threshold < 2`)", + " - `P2` is proposals-count (code-bounded) (`threshold >= 2`)", + "- DB:", + " - 1 storage read `is_member` (codec `O(M)`)", + " - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)", + " - DB accesses influenced by `threshold`:", + " - EITHER storage accesses done by `proposal` (`threshold < 2`)", + " - OR proposal insertion (`threshold <= 2`)", + " - 1 storage mutation `Proposals` (codec `O(P2)`)", + " - 1 storage mutation `ProposalCount` (codec `O(1)`)", + " - 1 storage write `ProposalOf` (codec `O(B)`)", + " - 1 storage write `Voting` (codec `O(M)`)", + " - 1 event", + "# " + ] + }, + { + "name": "vote", + "fields": [ + { + "name": "proposal", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "index", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": "approve", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Add an aye or nay vote for the sender to the given proposal.", + "", + "Requires the sender to be a member.", + "", + "Transaction fees will be waived if the member is voting on any particular proposal", + "for the first time and the call is successful. Subsequent vote changes will charge a", + "fee.", + "# ", + "## Weight", + "- `O(M)` where `M` is members-count (code- and governance-bounded)", + "- DB:", + " - 1 storage read `Members` (codec `O(M)`)", + " - 1 storage mutation `Voting` (codec `O(M)`)", + "- 1 event", + "# " + ] + }, + { + "name": "close", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "index", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": "proposal_weight_bound", + "type": 113, + "typeName": "Weight", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Close a vote that is either approved, disapproved or whose voting period has ended.", + "", + "May be called by any signed account in order to finish voting and close the proposal.", + "", + "If called before the end of the voting period it will only close the vote if it is", + "has enough votes to be approved or disapproved.", + "", + "If called after the end of the voting period abstentions are counted as rejections", + "unless there is a prime member set and the prime member cast an approval.", + "", + "If the close operation completes successfully with disapproval, the transaction fee will", + "be waived. Otherwise execution of the approved operation will be charged to the caller.", + "", + "+ `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed", + "proposal.", + "+ `length_bound`: The upper bound for the length of the proposal in storage. Checked via", + "`storage::read` so it is `size_of::() == 4` larger than the pure length.", + "", + "# ", + "## Weight", + "- `O(B + M + P1 + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - `P1` is the complexity of `proposal` preimage.", + " - `P2` is proposal-count (code-bounded)", + "- DB:", + " - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)", + " - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec", + " `O(P2)`)", + " - any mutations done while executing `proposal` (`P1`)", + "- up to 3 events", + "# " + ] + }, + { + "name": "disapprove_proposal", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Disapprove a proposal, close, and remove it from the system, regardless of its current", + "state.", + "", + "Must be called by the Root origin.", + "", + "Parameters:", + "* `proposal_hash`: The hash of the proposal that should be disapproved.", + "", + "# ", + "Complexity: O(P) where P is the number of max proposals", + "DB Weight:", + "* Reads: Proposals", + "* Writes: Voting, Proposals, ProposalOf", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 300, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set_members", + "fields": [ + { + "name": "new_members", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "prime", + "type": 204, + "typeName": "Option", + "docs": [] + }, + { + "name": "old_count", + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Set the collective's membership.", + "", + "- `new_members`: The new member list. Be nice to the chain and provide it sorted.", + "- `prime`: The prime member whose vote sets the default.", + "- `old_count`: The upper bound for the previous number of members in storage. Used for", + " weight estimation.", + "", + "Requires root origin.", + "", + "NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but", + " the weight estimations rely on it to estimate dispatchable weight.", + "", + "# ", + "## Weight", + "- `O(MP + N)` where:", + " - `M` old-members-count (code- and governance-bounded)", + " - `N` new-members-count (code- and governance-bounded)", + " - `P` proposals-count (code-bounded)", + "- DB:", + " - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the", + " members", + " - 1 storage read (codec `O(P)`) for reading the proposals", + " - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal", + " - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one", + "# " + ] + }, + { + "name": "execute", + "fields": [ + { + "name": "proposal", + "type": 298, + "typeName": "Box<>::Proposal>", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Dispatch a proposal from a member using the `Member` origin.", + "", + "Origin must be a member of the collective.", + "", + "# ", + "## Weight", + "- `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching", + " `proposal`", + "- DB: 1 read (codec `O(M)`) + DB access of `proposal`", + "- 1 event", + "# " + ] + }, + { + "name": "propose", + "fields": [ + { + "name": "threshold", + "type": 111, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": "proposal", + "type": 298, + "typeName": "Box<>::Proposal>", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Add a new proposal to either be voted on or executed directly.", + "", + "Requires the sender to be member.", + "", + "`threshold` determines whether `proposal` is executed directly (`threshold < 2`)", + "or put up for voting.", + "", + "# ", + "## Weight", + "- `O(B + M + P1)` or `O(B + M + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - branching is influenced by `threshold` where:", + " - `P1` is proposal execution complexity (`threshold < 2`)", + " - `P2` is proposals-count (code-bounded) (`threshold >= 2`)", + "- DB:", + " - 1 storage read `is_member` (codec `O(M)`)", + " - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)", + " - DB accesses influenced by `threshold`:", + " - EITHER storage accesses done by `proposal` (`threshold < 2`)", + " - OR proposal insertion (`threshold <= 2`)", + " - 1 storage mutation `Proposals` (codec `O(P2)`)", + " - 1 storage mutation `ProposalCount` (codec `O(1)`)", + " - 1 storage write `ProposalOf` (codec `O(B)`)", + " - 1 storage write `Voting` (codec `O(M)`)", + " - 1 event", + "# " + ] + }, + { + "name": "vote", + "fields": [ + { + "name": "proposal", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "index", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": "approve", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Add an aye or nay vote for the sender to the given proposal.", + "", + "Requires the sender to be a member.", + "", + "Transaction fees will be waived if the member is voting on any particular proposal", + "for the first time and the call is successful. Subsequent vote changes will charge a", + "fee.", + "# ", + "## Weight", + "- `O(M)` where `M` is members-count (code- and governance-bounded)", + "- DB:", + " - 1 storage read `Members` (codec `O(M)`)", + " - 1 storage mutation `Voting` (codec `O(M)`)", + "- 1 event", + "# " + ] + }, + { + "name": "close", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "index", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": "proposal_weight_bound", + "type": 113, + "typeName": "Weight", + "docs": [] + }, + { + "name": "length_bound", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Close a vote that is either approved, disapproved or whose voting period has ended.", + "", + "May be called by any signed account in order to finish voting and close the proposal.", + "", + "If called before the end of the voting period it will only close the vote if it is", + "has enough votes to be approved or disapproved.", + "", + "If called after the end of the voting period abstentions are counted as rejections", + "unless there is a prime member set and the prime member cast an approval.", + "", + "If the close operation completes successfully with disapproval, the transaction fee will", + "be waived. Otherwise execution of the approved operation will be charged to the caller.", + "", + "+ `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed", + "proposal.", + "+ `length_bound`: The upper bound for the length of the proposal in storage. Checked via", + "`storage::read` so it is `size_of::() == 4` larger than the pure length.", + "", + "# ", + "## Weight", + "- `O(B + M + P1 + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - `P1` is the complexity of `proposal` preimage.", + " - `P2` is proposal-count (code-bounded)", + "- DB:", + " - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)", + " - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec", + " `O(P2)`)", + " - any mutations done while executing `proposal` (`P1`)", + "- up to 3 events", + "# " + ] + }, + { + "name": "disapprove_proposal", + "fields": [ + { + "name": "proposal_hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Disapprove a proposal, close, and remove it from the system, regardless of its current", + "state.", + "", + "Must be called by the Root origin.", + "", + "Parameters:", + "* `proposal_hash`: The hash of the proposal that should be disapproved.", + "", + "# ", + "Complexity: O(P) where P is the number of max proposals", + "DB Weight:", + "* Reads: Proposals", + "* Writes: Voting, Proposals, ProposalOf", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 301, + "type": { + "path": [ + "pallet_elections_phragmen", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "vote", + "fields": [ + { + "name": "votes", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Vote for a set of candidates for the upcoming round of election. This can be called to", + "set the initial votes, or update already existing votes.", + "", + "Upon initial voting, `value` units of `who`'s balance is locked and a deposit amount is", + "reserved. The deposit is based on the number of votes and can be updated over time.", + "", + "The `votes` should:", + " - not be empty.", + " - be less than the number of possible candidates. Note that all current members and", + " runners-up are also automatically candidates for the next round.", + "", + "If `value` is more than `who`'s total balance, then the maximum of the two is used.", + "", + "The dispatch origin of this call must be signed.", + "", + "### Warning", + "", + "It is the responsibility of the caller to **NOT** place all of their balance into the", + "lock and keep some for further operations.", + "", + "# ", + "We assume the maximum weight among all 3 cases: vote_equal, vote_more and vote_less.", + "# " + ] + }, + { + "name": "remove_voter", + "fields": [], + "index": 1, + "docs": [ + "Remove `origin` as a voter.", + "", + "This removes the lock and returns the deposit.", + "", + "The dispatch origin of this call must be signed and be a voter." + ] + }, + { + "name": "submit_candidacy", + "fields": [ + { + "name": "candidate_count", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Submit oneself for candidacy. A fixed amount of deposit is recorded.", + "", + "All candidates are wiped at the end of the term. They either become a member/runner-up,", + "or leave the system while their deposit is slashed.", + "", + "The dispatch origin of this call must be signed.", + "", + "### Warning", + "", + "Even if a candidate ends up being a member, they must call [`Call::renounce_candidacy`]", + "to get their deposit back. Losing the spot in an election will always lead to a slash.", + "", + "# ", + "The number of current candidates must be provided as witness data.", + "# " + ] + }, + { + "name": "renounce_candidacy", + "fields": [ + { + "name": "renouncing", + "type": 302, + "typeName": "Renouncing", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Renounce one's intention to be a candidate for the next election round. 3 potential", + "outcomes exist:", + "", + "- `origin` is a candidate and not elected in any set. In this case, the deposit is", + " unreserved, returned and origin is removed as a candidate.", + "- `origin` is a current runner-up. In this case, the deposit is unreserved, returned and", + " origin is removed as a runner-up.", + "- `origin` is a current member. In this case, the deposit is unreserved and origin is", + " removed as a member, consequently not being a candidate for the next round anymore.", + " Similar to [`remove_member`](Self::remove_member), if replacement runners exists, they", + " are immediately used. If the prime is renouncing, then no prime will exist until the", + " next round.", + "", + "The dispatch origin of this call must be signed, and have one of the above roles.", + "", + "# ", + "The type of renouncing must be provided as witness data.", + "# " + ] + }, + { + "name": "remove_member", + "fields": [ + { + "name": "who", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "has_replacement", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Remove a particular member from the set. This is effective immediately and the bond of", + "the outgoing member is slashed.", + "", + "If a runner-up is available, then the best runner-up will be removed and replaces the", + "outgoing member. Otherwise, a new phragmen election is started.", + "", + "The dispatch origin of this call must be root.", + "", + "Note that this does not affect the designated block number of the next election.", + "", + "# ", + "If we have a replacement, we use a small weight. Else, since this is a root call and", + "will go into phragmen, we assume full block for now.", + "# " + ] + }, + { + "name": "clean_defunct_voters", + "fields": [ + { + "name": "num_voters", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "num_defunct", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Clean all voters who are defunct (i.e. they do not serve any purpose at all). The", + "deposit of the removed voters are returned.", + "", + "This is an root function to be used only for cleaning the state.", + "", + "The dispatch origin of this call must be root.", + "", + "# ", + "The total number of voters and those that are defunct must be provided as witness data.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 302, + "type": { + "path": [ + "pallet_elections_phragmen", + "Renouncing" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Member", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "RunnerUp", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Candidate", + "fields": [ + { + "name": null, + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 303, + "type": { + "path": [ + "pallet_membership", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "add_member", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Add a member `who` to the set.", + "", + "May only be called from `T::AddOrigin`." + ] + }, + { + "name": "remove_member", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Remove a member `who` from the set.", + "", + "May only be called from `T::RemoveOrigin`." + ] + }, + { + "name": "swap_member", + "fields": [ + { + "name": "remove", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "add", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Swap out one member `remove` for another `add`.", + "", + "May only be called from `T::SwapOrigin`.", + "", + "Prime membership is *not* passed from `remove` to `add`, if extant." + ] + }, + { + "name": "reset_members", + "fields": [ + { + "name": "members", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Change the membership to a new set, disregarding the existing membership. Be nice and", + "pass `members` pre-sorted.", + "", + "May only be called from `T::ResetOrigin`." + ] + }, + { + "name": "change_key", + "fields": [ + { + "name": "new", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Swap out the sending member for some other key `new`.", + "", + "May only be called from `Signed` origin of a current member.", + "", + "Prime membership is passed from the origin account to `new`, if extant." + ] + }, + { + "name": "set_prime", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Set the prime member. Must be a current member.", + "", + "May only be called from `T::PrimeOrigin`." + ] + }, + { + "name": "clear_prime", + "fields": [], + "index": 6, + "docs": [ + "Remove the prime member if it exists.", + "", + "May only be called from `T::PrimeOrigin`." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 304, + "type": { + "path": [ + "pallet_treasury", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "propose_spend", + "fields": [ + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "beneficiary", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Put forward a suggestion for spending. A deposit proportional to the value", + "is reserved and slashed if the proposal is rejected. It is returned once the", + "proposal is awarded.", + "", + "# ", + "- Complexity: O(1)", + "- DbReads: `ProposalCount`, `origin account`", + "- DbWrites: `ProposalCount`, `Proposals`, `origin account`", + "# " + ] + }, + { + "name": "reject_proposal", + "fields": [ + { + "name": "proposal_id", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Reject a proposed spend. The original deposit will be slashed.", + "", + "May only be called from `T::RejectOrigin`.", + "", + "# ", + "- Complexity: O(1)", + "- DbReads: `Proposals`, `rejected proposer account`", + "- DbWrites: `Proposals`, `rejected proposer account`", + "# " + ] + }, + { + "name": "approve_proposal", + "fields": [ + { + "name": "proposal_id", + "type": 111, + "typeName": "ProposalIndex", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + "and the original deposit will be returned.", + "", + "May only be called from `T::ApproveOrigin`.", + "", + "# ", + "- Complexity: O(1).", + "- DbReads: `Proposals`, `Approvals`", + "- DbWrite: `Approvals`", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 305, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "claim", + "fields": [ + { + "name": "dest", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "ethereum_signature", + "type": 306, + "typeName": "EcdsaSignature", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Make a claim to collect your DOTs.", + "", + "The dispatch origin for this call must be _None_.", + "", + "Unsigned Validation:", + "A call to claim is deemed valid if the signature provided matches", + "the expected signed message of:", + "", + "> Ethereum Signed Message:", + "> (configured prefix string)(address)", + "", + "and `address` matches the `dest` account.", + "", + "Parameters:", + "- `dest`: The destination account to payout the claim.", + "- `ethereum_signature`: The signature of an ethereum signed message", + " matching the format described above.", + "", + "", + "The weight of this call is invariant over the input parameters.", + "Weight includes logic to validate unsigned `claim` call.", + "", + "Total Complexity: O(1)", + "" + ] + }, + { + "name": "mint_claim", + "fields": [ + { + "name": "who", + "type": 63, + "typeName": "EthereumAddress", + "docs": [] + }, + { + "name": "value", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "vesting_schedule", + "type": 308, + "typeName": "Option<(BalanceOf, BalanceOf, T::BlockNumber)>", + "docs": [] + }, + { + "name": "statement", + "type": 310, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Mint a new claim to collect DOTs.", + "", + "The dispatch origin for this call must be _Root_.", + "", + "Parameters:", + "- `who`: The Ethereum address allowed to collect this claim.", + "- `value`: The number of DOTs that will be claimed.", + "- `vesting_schedule`: An optional vesting schedule for these DOTs.", + "", + "", + "The weight of this call is invariant over the input parameters.", + "We assume worst case that both vesting and statement is being inserted.", + "", + "Total Complexity: O(1)", + "" + ] + }, + { + "name": "claim_attest", + "fields": [ + { + "name": "dest", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "ethereum_signature", + "type": 306, + "typeName": "EcdsaSignature", + "docs": [] + }, + { + "name": "statement", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Make a claim to collect your DOTs by signing a statement.", + "", + "The dispatch origin for this call must be _None_.", + "", + "Unsigned Validation:", + "A call to `claim_attest` is deemed valid if the signature provided matches", + "the expected signed message of:", + "", + "> Ethereum Signed Message:", + "> (configured prefix string)(address)(statement)", + "", + "and `address` matches the `dest` account; the `statement` must match that which is", + "expected according to your purchase arrangement.", + "", + "Parameters:", + "- `dest`: The destination account to payout the claim.", + "- `ethereum_signature`: The signature of an ethereum signed message", + " matching the format described above.", + "- `statement`: The identity of the statement which is being attested to in the signature.", + "", + "", + "The weight of this call is invariant over the input parameters.", + "Weight includes logic to validate unsigned `claim_attest` call.", + "", + "Total Complexity: O(1)", + "" + ] + }, + { + "name": "attest", + "fields": [ + { + "name": "statement", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Attest to a statement, needed to finalize the claims process.", + "", + "WARNING: Insecure unless your chain includes `PrevalidateAttests` as a `SignedExtension`.", + "", + "Unsigned Validation:", + "A call to attest is deemed valid if the sender has a `Preclaim` registered", + "and provides a `statement` which is expected for the account.", + "", + "Parameters:", + "- `statement`: The identity of the statement which is being attested to in the signature.", + "", + "", + "The weight of this call is invariant over the input parameters.", + "Weight includes logic to do pre-validation on `attest` call.", + "", + "Total Complexity: O(1)", + "" + ] + }, + { + "name": "move_claim", + "fields": [ + { + "name": "old", + "type": 63, + "typeName": "EthereumAddress", + "docs": [] + }, + { + "name": "new", + "type": 63, + "typeName": "EthereumAddress", + "docs": [] + }, + { + "name": "maybe_preclaim", + "type": 204, + "typeName": "Option", + "docs": [] + } + ], + "index": 4, + "docs": [] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 306, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "EcdsaSignature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 307, + "typeName": "[u8; 65]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 307, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 65, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 308, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 309 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 309, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 309, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 6, + 6, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 310, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 311 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 311, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 311, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "StatementKind" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Regular", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Saft", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 312, + "type": { + "path": [ + "pallet_utility", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "batch", + "fields": [ + { + "name": "calls", + "type": 313, + "typeName": "Vec<::Call>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Send a batch of dispatch calls.", + "", + "May be called from any origin.", + "", + "- `calls`: The calls to be dispatched from the same origin. The number of call must not", + " exceed the constant: `batched_calls_limit` (available in constant metadata).", + "", + "If origin is root then call are dispatch without checking origin filter. (This includes", + "bypassing `frame_system::Config::BaseCallFilter`).", + "", + "# ", + "- Complexity: O(C) where C is the number of calls to be batched.", + "# ", + "", + "This will return `Ok` in all circumstances. To determine the success of the batch, an", + "event is deposited. If a call failed and the batch was interrupted, then the", + "`BatchInterrupted` event is deposited, along with the number of successful calls made", + "and the error of the failed call. If all were successful, then the `BatchCompleted`", + "event is deposited." + ] + }, + { + "name": "as_derivative", + "fields": [ + { + "name": "index", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Send a call through an indexed pseudonym of the sender.", + "", + "Filter from origin are passed along. The call will be dispatched with an origin which", + "use the same filter as the origin of this call.", + "", + "NOTE: If you need to ensure that any account-based filtering is not honored (i.e.", + "because you expect `proxy` to have been used prior in the call stack and you do not want", + "the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`", + "in the Multisig pallet instead.", + "", + "NOTE: Prior to version *12, this was called `as_limited_sub`.", + "", + "The dispatch origin for this call must be _Signed_." + ] + }, + { + "name": "batch_all", + "fields": [ + { + "name": "calls", + "type": 313, + "typeName": "Vec<::Call>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Send a batch of dispatch calls and atomically execute them.", + "The whole transaction will rollback and fail if any of the calls failed.", + "", + "May be called from any origin.", + "", + "- `calls`: The calls to be dispatched from the same origin. The number of call must not", + " exceed the constant: `batched_calls_limit` (available in constant metadata).", + "", + "If origin is root then call are dispatch without checking origin filter. (This includes", + "bypassing `frame_system::Config::BaseCallFilter`).", + "", + "# ", + "- Complexity: O(C) where C is the number of calls to be batched.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 313, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 298 + } + }, + "docs": [] + } + }, + { + "id": 314, + "type": { + "path": [ + "pallet_identity", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "add_registrar", + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Add a registrar to the system.", + "", + "The dispatch origin for this call must be `T::RegistrarOrigin`.", + "", + "- `account`: the account of the registrar.", + "", + "Emits `RegistrarAdded` if successful.", + "", + "# ", + "- `O(R)` where `R` registrar-count (governance-bounded and code-bounded).", + "- One storage mutation (codec `O(R)`).", + "- One event.", + "# " + ] + }, + { + "name": "set_identity", + "fields": [ + { + "name": "info", + "type": 315, + "typeName": "Box>", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Set an account's identity information and reserve the appropriate deposit.", + "", + "If the account already has identity information, the deposit is taken as part payment", + "for the new deposit.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `info`: The identity information.", + "", + "Emits `IdentitySet` if successful.", + "", + "# ", + "- `O(X + X' + R)`", + " - where `X` additional-field-count (deposit-bounded and code-bounded)", + " - where `R` judgements-count (registrar-count-bounded)", + "- One balance reserve operation.", + "- One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).", + "- One event.", + "# " + ] + }, + { + "name": "set_subs", + "fields": [ + { + "name": "subs", + "type": 349, + "typeName": "Vec<(T::AccountId, Data)>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Set the sub-accounts of the sender.", + "", + "Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned", + "and an amount `SubAccountDeposit` will be reserved for each item in `subs`.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "identity.", + "", + "- `subs`: The identity's (new) sub-accounts.", + "", + "# ", + "- `O(P + S)`", + " - where `P` old-subs-count (hard- and deposit-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + "- At most one balance operations.", + "- DB:", + " - `P + S` storage mutations (codec complexity `O(1)`)", + " - One storage read (codec complexity `O(P)`).", + " - One storage write (codec complexity `O(S)`).", + " - One storage-exists (`IdentityOf::contains_key`).", + "# " + ] + }, + { + "name": "clear_identity", + "fields": [], + "index": 3, + "docs": [ + "Clear an account's identity info and all sub-accounts and return all deposits.", + "", + "Payment: All reserved balances on the account are returned.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "identity.", + "", + "Emits `IdentityCleared` if successful.", + "", + "# ", + "- `O(R + S + X)`", + " - where `R` registrar-count (governance-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + " - where `X` additional-field-count (deposit-bounded and code-bounded).", + "- One balance-unreserve operation.", + "- `2` storage reads and `S + 2` storage deletions.", + "- One event.", + "# " + ] + }, + { + "name": "request_judgement", + "fields": [ + { + "name": "reg_index", + "type": 111, + "typeName": "RegistrarIndex", + "docs": [] + }, + { + "name": "max_fee", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Request a judgement from a registrar.", + "", + "Payment: At most `max_fee` will be reserved for payment to the registrar if judgement", + "given.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a", + "registered identity.", + "", + "- `reg_index`: The index of the registrar whose judgement is requested.", + "- `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:", + "", + "```nocompile", + "Self::registrars().get(reg_index).unwrap().fee", + "```", + "", + "Emits `JudgementRequested` if successful.", + "", + "# ", + "- `O(R + X)`.", + "- One balance-reserve operation.", + "- Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.", + "- One event.", + "# " + ] + }, + { + "name": "cancel_request", + "fields": [ + { + "name": "reg_index", + "type": 4, + "typeName": "RegistrarIndex", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Cancel a previous request.", + "", + "Payment: A previously reserved deposit is returned on success.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a", + "registered identity.", + "", + "- `reg_index`: The index of the registrar whose judgement is no longer requested.", + "", + "Emits `JudgementUnrequested` if successful.", + "", + "# ", + "- `O(R + X)`.", + "- One balance-reserve operation.", + "- One storage mutation `O(R + X)`.", + "- One event", + "# " + ] + }, + { + "name": "set_fee", + "fields": [ + { + "name": "index", + "type": 111, + "typeName": "RegistrarIndex", + "docs": [] + }, + { + "name": "fee", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Set the fee required for a judgement to be requested from a registrar.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must be the account", + "of the registrar whose index is `index`.", + "", + "- `index`: the index of the registrar whose fee is to be set.", + "- `fee`: the new fee.", + "", + "# ", + "- `O(R)`.", + "- One storage mutation `O(R)`.", + "- Benchmark: 7.315 + R * 0.329 µs (min squares analysis)", + "# " + ] + }, + { + "name": "set_account_id", + "fields": [ + { + "name": "index", + "type": 111, + "typeName": "RegistrarIndex", + "docs": [] + }, + { + "name": "new", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Change the account associated with a registrar.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must be the account", + "of the registrar whose index is `index`.", + "", + "- `index`: the index of the registrar whose fee is to be set.", + "- `new`: the new account ID.", + "", + "# ", + "- `O(R)`.", + "- One storage mutation `O(R)`.", + "- Benchmark: 8.823 + R * 0.32 µs (min squares analysis)", + "# " + ] + }, + { + "name": "set_fields", + "fields": [ + { + "name": "index", + "type": 111, + "typeName": "RegistrarIndex", + "docs": [] + }, + { + "name": "fields", + "type": 351, + "typeName": "IdentityFields", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Set the field information for a registrar.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must be the account", + "of the registrar whose index is `index`.", + "", + "- `index`: the index of the registrar whose fee is to be set.", + "- `fields`: the fields that the registrar concerns themselves with.", + "", + "# ", + "- `O(R)`.", + "- One storage mutation `O(R)`.", + "- Benchmark: 7.464 + R * 0.325 µs (min squares analysis)", + "# " + ] + }, + { + "name": "provide_judgement", + "fields": [ + { + "name": "reg_index", + "type": 111, + "typeName": "RegistrarIndex", + "docs": [] + }, + { + "name": "target", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "judgement", + "type": 353, + "typeName": "Judgement>", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Provide a judgement for an account's identity.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must be the account", + "of the registrar whose index is `reg_index`.", + "", + "- `reg_index`: the index of the registrar whose judgement is being made.", + "- `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + "- `judgement`: the judgement of the registrar of index `reg_index` about `target`.", + "", + "Emits `JudgementGiven` if successful.", + "", + "# ", + "- `O(R + X)`.", + "- One balance-transfer operation.", + "- Up to one account-lookup operation.", + "- Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.", + "- One event.", + "# " + ] + }, + { + "name": "kill_identity", + "fields": [ + { + "name": "target", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Remove an account's identity and sub-account information and slash the deposits.", + "", + "Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by", + "`Slash`. Verification request deposits are not returned; they should be cancelled", + "manually using `cancel_request`.", + "", + "The dispatch origin for this call must match `T::ForceOrigin`.", + "", + "- `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + "", + "Emits `IdentityKilled` if successful.", + "", + "# ", + "- `O(R + S + X)`.", + "- One balance-reserve operation.", + "- `S + 2` storage mutations.", + "- One event.", + "# " + ] + }, + { + "name": "add_sub", + "fields": [ + { + "name": "sub", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "data", + "type": 318, + "typeName": "Data", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Add the given account to the sender's subs.", + "", + "Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + "to the sender.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "sub identity of `sub`." + ] + }, + { + "name": "rename_sub", + "fields": [ + { + "name": "sub", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "data", + "type": 318, + "typeName": "Data", + "docs": [] + } + ], + "index": 12, + "docs": [ + "Alter the associated name of the given sub-account.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "sub identity of `sub`." + ] + }, + { + "name": "remove_sub", + "fields": [ + { + "name": "sub", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 13, + "docs": [ + "Remove the given account from the sender's subs.", + "", + "Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + "to the sender.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "sub identity of `sub`." + ] + }, + { + "name": "quit_sub", + "fields": [], + "index": 14, + "docs": [ + "Remove the sender as a sub-account.", + "", + "Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + "to the sender (*not* the original depositor).", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have a registered", + "super-identity.", + "", + "NOTE: This should not normally be used, but is provided in the case that the non-", + "controller of an account is maliciously registered as a sub-account." + ] + } + ] + } + }, + "docs": [ + "Identity pallet declaration." + ] + } + }, + { + "id": 315, + "type": { + "path": [ + "pallet_identity", + "types", + "IdentityInfo" + ], + "params": [ + { + "name": "FieldLimit", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "additional", + "type": 316, + "typeName": "BoundedVec<(Data, Data), FieldLimit>", + "docs": [] + }, + { + "name": "display", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "legal", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "web", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "riot", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "email", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "pgp_fingerprint", + "type": 348, + "typeName": "Option<[u8; 20]>", + "docs": [] + }, + { + "name": "image", + "type": 318, + "typeName": "Data", + "docs": [] + }, + { + "name": "twitter", + "type": 318, + "typeName": "Data", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 316, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 317 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 347, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 317, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 318, + 318 + ] + }, + "docs": [] + } + }, + { + "id": 318, + "type": { + "path": [ + "pallet_identity", + "types", + "Data" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Raw0", + "fields": [ + { + "name": null, + "type": 319, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Raw1", + "fields": [ + { + "name": null, + "type": 320, + "typeName": null, + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Raw2", + "fields": [ + { + "name": null, + "type": 321, + "typeName": null, + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Raw3", + "fields": [ + { + "name": null, + "type": 322, + "typeName": null, + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Raw4", + "fields": [ + { + "name": null, + "type": 14, + "typeName": null, + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Raw5", + "fields": [ + { + "name": null, + "type": 323, + "typeName": null, + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Raw6", + "fields": [ + { + "name": null, + "type": 324, + "typeName": null, + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "Raw7", + "fields": [ + { + "name": null, + "type": 325, + "typeName": null, + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "Raw8", + "fields": [ + { + "name": null, + "type": 125, + "typeName": null, + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "Raw9", + "fields": [ + { + "name": null, + "type": 326, + "typeName": null, + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "Raw10", + "fields": [ + { + "name": null, + "type": 327, + "typeName": null, + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "Raw11", + "fields": [ + { + "name": null, + "type": 328, + "typeName": null, + "docs": [] + } + ], + "index": 12, + "docs": [] + }, + { + "name": "Raw12", + "fields": [ + { + "name": null, + "type": 329, + "typeName": null, + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "Raw13", + "fields": [ + { + "name": null, + "type": 330, + "typeName": null, + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "Raw14", + "fields": [ + { + "name": null, + "type": 331, + "typeName": null, + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "Raw15", + "fields": [ + { + "name": null, + "type": 332, + "typeName": null, + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "Raw16", + "fields": [ + { + "name": null, + "type": 33, + "typeName": null, + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "Raw17", + "fields": [ + { + "name": null, + "type": 333, + "typeName": null, + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "Raw18", + "fields": [ + { + "name": null, + "type": 334, + "typeName": null, + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "Raw19", + "fields": [ + { + "name": null, + "type": 335, + "typeName": null, + "docs": [] + } + ], + "index": 20, + "docs": [] + }, + { + "name": "Raw20", + "fields": [ + { + "name": null, + "type": 64, + "typeName": null, + "docs": [] + } + ], + "index": 21, + "docs": [] + }, + { + "name": "Raw21", + "fields": [ + { + "name": null, + "type": 336, + "typeName": null, + "docs": [] + } + ], + "index": 22, + "docs": [] + }, + { + "name": "Raw22", + "fields": [ + { + "name": null, + "type": 337, + "typeName": null, + "docs": [] + } + ], + "index": 23, + "docs": [] + }, + { + "name": "Raw23", + "fields": [ + { + "name": null, + "type": 338, + "typeName": null, + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Raw24", + "fields": [ + { + "name": null, + "type": 339, + "typeName": null, + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "Raw25", + "fields": [ + { + "name": null, + "type": 340, + "typeName": null, + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "Raw26", + "fields": [ + { + "name": null, + "type": 341, + "typeName": null, + "docs": [] + } + ], + "index": 27, + "docs": [] + }, + { + "name": "Raw27", + "fields": [ + { + "name": null, + "type": 342, + "typeName": null, + "docs": [] + } + ], + "index": 28, + "docs": [] + }, + { + "name": "Raw28", + "fields": [ + { + "name": null, + "type": 343, + "typeName": null, + "docs": [] + } + ], + "index": 29, + "docs": [] + }, + { + "name": "Raw29", + "fields": [ + { + "name": null, + "type": 344, + "typeName": null, + "docs": [] + } + ], + "index": 30, + "docs": [] + }, + { + "name": "Raw30", + "fields": [ + { + "name": null, + "type": 345, + "typeName": null, + "docs": [] + } + ], + "index": 31, + "docs": [] + }, + { + "name": "Raw31", + "fields": [ + { + "name": null, + "type": 346, + "typeName": null, + "docs": [] + } + ], + "index": 32, + "docs": [] + }, + { + "name": "Raw32", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 33, + "docs": [] + }, + { + "name": "BlakeTwo256", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 34, + "docs": [] + }, + { + "name": "Sha256", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 35, + "docs": [] + }, + { + "name": "Keccak256", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 36, + "docs": [] + }, + { + "name": "ShaThree256", + "fields": [ + { + "name": null, + "type": 1, + "typeName": null, + "docs": [] + } + ], + "index": 37, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 319, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 0, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 320, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 1, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 321, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 2, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 322, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 3, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 323, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 5, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 324, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 6, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 325, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 7, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 326, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 9, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 327, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 10, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 328, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 11, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 329, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 12, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 330, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 13, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 331, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 14, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 332, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 15, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 333, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 17, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 334, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 18, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 335, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 19, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 336, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 21, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 337, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 22, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 338, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 23, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 339, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 24, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 340, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 25, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 341, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 26, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 342, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 27, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 343, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 28, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 344, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 29, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 345, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 30, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 346, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 31, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 347, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 317 + } + }, + "docs": [] + } + }, + { + "id": 348, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 64 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 64, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 349, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 350 + } + }, + "docs": [] + } + }, + { + "id": 350, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 318 + ] + }, + "docs": [] + } + }, + { + "id": 351, + "type": { + "path": [ + "pallet_identity", + "types", + "BitFlags" + ], + "params": [ + { + "name": "T", + "type": 352 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 8, + "typeName": "IdentityField", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 352, + "type": { + "path": [ + "pallet_identity", + "types", + "IdentityField" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Display", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Legal", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "Web", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "Riot", + "fields": [], + "index": 8, + "docs": [] + }, + { + "name": "Email", + "fields": [], + "index": 16, + "docs": [] + }, + { + "name": "PgpFingerprint", + "fields": [], + "index": 32, + "docs": [] + }, + { + "name": "Image", + "fields": [], + "index": 64, + "docs": [] + }, + { + "name": "Twitter", + "fields": [], + "index": 128, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 353, + "type": { + "path": [ + "pallet_identity", + "types", + "Judgement" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Unknown", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "FeePaid", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Reasonable", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "KnownGood", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "OutOfDate", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "LowQuality", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "Erroneous", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 354, + "type": { + "path": [ + "pallet_society", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "bid", + "fields": [ + { + "name": "value", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "A user outside of the society can make a bid for entry.", + "", + "Payment: `CandidateDeposit` will be reserved for making a bid. It is returned", + "when the bid becomes a member, or if the bid calls `unbid`.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `value`: A one time payment the bid would like to receive when joining the society.", + "", + "# ", + "Key: B (len of bids), C (len of candidates), M (len of members), X (balance reserve)", + "- Storage Reads:", + "\t- One storage read to check for suspended candidate. O(1)", + "\t- One storage read to check for suspended member. O(1)", + "\t- One storage read to retrieve all current bids. O(B)", + "\t- One storage read to retrieve all current candidates. O(C)", + "\t- One storage read to retrieve all members. O(M)", + "- Storage Writes:", + "\t- One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization", + " w/ read)", + "\t- Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1)", + "- Notable Computation:", + "\t- O(B + C + log M) search to check user is not already a part of society.", + "\t- O(log B) search to insert the new bid sorted.", + "- External Pallet Operations:", + "\t- One balance reserve operation. O(X)", + "\t- Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT.", + "- Events:", + "\t- One event for new bid.", + "\t- Up to one event for AutoUnbid if bid.len() > MAX_BID_COUNT.", + "", + "Total Complexity: O(M + B + C + logM + logB + X)", + "# " + ] + }, + { + "name": "unbid", + "fields": [ + { + "name": "pos", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "A bidder can remove their bid for entry into society.", + "By doing so, they will have their candidate deposit returned or", + "they will unvouch their voucher.", + "", + "Payment: The bid deposit is unreserved if the user made a bid.", + "", + "The dispatch origin for this call must be _Signed_ and a bidder.", + "", + "Parameters:", + "- `pos`: Position in the `Bids` vector of the bid who wants to unbid.", + "", + "# ", + "Key: B (len of bids), X (balance unreserve)", + "- One storage read and write to retrieve and update the bids. O(B)", + "- Either one unreserve balance action O(X) or one vouching storage removal. O(1)", + "- One event.", + "", + "Total Complexity: O(B + X)", + "# " + ] + }, + { + "name": "vouch", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "value", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "tip", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "As a member, vouch for someone to join society by placing a bid on their behalf.", + "", + "There is no deposit required to vouch for a new bid, but a member can only vouch for", + "one bid at a time. If the bid becomes a suspended candidate and ultimately rejected by", + "the suspension judgement origin, the member will be banned from vouching again.", + "", + "As a vouching member, you can claim a tip if the candidate is accepted. This tip will", + "be paid as a portion of the reward the member will receive for joining the society.", + "", + "The dispatch origin for this call must be _Signed_ and a member.", + "", + "Parameters:", + "- `who`: The user who you would like to vouch for.", + "- `value`: The total reward to be paid between you and the candidate if they become", + "a member in the society.", + "- `tip`: Your cut of the total `value` payout when the candidate is inducted into", + "the society. Tips larger than `value` will be saturated upon payout.", + "", + "# ", + "Key: B (len of bids), C (len of candidates), M (len of members)", + "- Storage Reads:", + "\t- One storage read to retrieve all members. O(M)", + "\t- One storage read to check member is not already vouching. O(1)", + "\t- One storage read to check for suspended candidate. O(1)", + "\t- One storage read to check for suspended member. O(1)", + "\t- One storage read to retrieve all current bids. O(B)", + "\t- One storage read to retrieve all current candidates. O(C)", + "- Storage Writes:", + "\t- One storage write to insert vouching status to the member. O(1)", + "\t- One storage mutate to add a new bid to the vector O(B) (TODO: possible optimization", + " w/ read)", + "\t- Up to one storage removal if bid.len() > MAX_BID_COUNT. O(1)", + "- Notable Computation:", + "\t- O(log M) search to check sender is a member.", + "\t- O(B + C + log M) search to check user is not already a part of society.", + "\t- O(log B) search to insert the new bid sorted.", + "- External Pallet Operations:", + "\t- One balance reserve operation. O(X)", + "\t- Up to one balance unreserve operation if bids.len() > MAX_BID_COUNT.", + "- Events:", + "\t- One event for vouch.", + "\t- Up to one event for AutoUnbid if bid.len() > MAX_BID_COUNT.", + "", + "Total Complexity: O(M + B + C + logM + logB + X)", + "# " + ] + }, + { + "name": "unvouch", + "fields": [ + { + "name": "pos", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 3, + "docs": [ + "As a vouching member, unvouch a bid. This only works while vouched user is", + "only a bidder (and not a candidate).", + "", + "The dispatch origin for this call must be _Signed_ and a vouching member.", + "", + "Parameters:", + "- `pos`: Position in the `Bids` vector of the bid who should be unvouched.", + "", + "# ", + "Key: B (len of bids)", + "- One storage read O(1) to check the signer is a vouching member.", + "- One storage mutate to retrieve and update the bids. O(B)", + "- One vouching storage removal. O(1)", + "- One event.", + "", + "Total Complexity: O(B)", + "# " + ] + }, + { + "name": "vote", + "fields": [ + { + "name": "candidate", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "approve", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 4, + "docs": [ + "As a member, vote on a candidate.", + "", + "The dispatch origin for this call must be _Signed_ and a member.", + "", + "Parameters:", + "- `candidate`: The candidate that the member would like to bid on.", + "- `approve`: A boolean which says if the candidate should be approved (`true`) or", + " rejected (`false`).", + "", + "# ", + "Key: C (len of candidates), M (len of members)", + "- One storage read O(M) and O(log M) search to check user is a member.", + "- One account lookup.", + "- One storage read O(C) and O(C) search to check that user is a candidate.", + "- One storage write to add vote to votes. O(1)", + "- One event.", + "", + "Total Complexity: O(M + logM + C)", + "# " + ] + }, + { + "name": "defender_vote", + "fields": [ + { + "name": "approve", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 5, + "docs": [ + "As a member, vote on the defender.", + "", + "The dispatch origin for this call must be _Signed_ and a member.", + "", + "Parameters:", + "- `approve`: A boolean which says if the candidate should be", + "approved (`true`) or rejected (`false`).", + "", + "# ", + "- Key: M (len of members)", + "- One storage read O(M) and O(log M) search to check user is a member.", + "- One storage write to add vote to votes. O(1)", + "- One event.", + "", + "Total Complexity: O(M + logM)", + "# " + ] + }, + { + "name": "payout", + "fields": [], + "index": 6, + "docs": [ + "Transfer the first matured payout for the sender and remove it from the records.", + "", + "NOTE: This extrinsic needs to be called multiple times to claim multiple matured", + "payouts.", + "", + "Payment: The member will receive a payment equal to their first matured", + "payout to their free balance.", + "", + "The dispatch origin for this call must be _Signed_ and a member with", + "payouts remaining.", + "", + "# ", + "Key: M (len of members), P (number of payouts for a particular member)", + "- One storage read O(M) and O(log M) search to check signer is a member.", + "- One storage read O(P) to get all payouts for a member.", + "- One storage read O(1) to get the current block number.", + "- One currency transfer call. O(X)", + "- One storage write or removal to update the member's payouts. O(P)", + "", + "Total Complexity: O(M + logM + P + X)", + "# " + ] + }, + { + "name": "found", + "fields": [ + { + "name": "founder", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "max_members", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "rules", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Found the society.", + "", + "This is done as a discrete action in order to allow for the", + "pallet to be included into a running chain and can only be done once.", + "", + "The dispatch origin for this call must be from the _FounderSetOrigin_.", + "", + "Parameters:", + "- `founder` - The first member and head of the newly founded society.", + "- `max_members` - The initial max number of members for the society.", + "- `rules` - The rules of this society concerning membership.", + "", + "# ", + "- Two storage mutates to set `Head` and `Founder`. O(1)", + "- One storage write to add the first member to society. O(1)", + "- One event.", + "", + "Total Complexity: O(1)", + "# " + ] + }, + { + "name": "unfound", + "fields": [], + "index": 8, + "docs": [ + "Annul the founding of the society.", + "", + "The dispatch origin for this call must be Signed, and the signing account must be both", + "the `Founder` and the `Head`. This implies that it may only be done when there is one", + "member.", + "", + "# ", + "- Two storage reads O(1).", + "- Four storage removals O(1).", + "- One event.", + "", + "Total Complexity: O(1)", + "# " + ] + }, + { + "name": "judge_suspended_member", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "forgive", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Allow suspension judgement origin to make judgement on a suspended member.", + "", + "If a suspended member is forgiven, we simply add them back as a member, not affecting", + "any of the existing storage items for that member.", + "", + "If a suspended member is rejected, remove all associated storage items, including", + "their payouts, and remove any vouched bids they currently have.", + "", + "The dispatch origin for this call must be from the _SuspensionJudgementOrigin_.", + "", + "Parameters:", + "- `who` - The suspended member to be judged.", + "- `forgive` - A boolean representing whether the suspension judgement origin forgives", + " (`true`) or rejects (`false`) a suspended member.", + "", + "# ", + "Key: B (len of bids), M (len of members)", + "- One storage read to check `who` is a suspended member. O(1)", + "- Up to one storage write O(M) with O(log M) binary search to add a member back to", + " society.", + "- Up to 3 storage removals O(1) to clean up a removed member.", + "- Up to one storage write O(B) with O(B) search to remove vouched bid from bids.", + "- Up to one additional event if unvouch takes place.", + "- One storage removal. O(1)", + "- One event for the judgement.", + "", + "Total Complexity: O(M + logM + B)", + "# " + ] + }, + { + "name": "judge_suspended_candidate", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "judgement", + "type": 355, + "typeName": "Judgement", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Allow suspended judgement origin to make judgement on a suspended candidate.", + "", + "If the judgement is `Approve`, we add them to society as a member with the appropriate", + "payment for joining society.", + "", + "If the judgement is `Reject`, we either slash the deposit of the bid, giving it back", + "to the society treasury, or we ban the voucher from vouching again.", + "", + "If the judgement is `Rebid`, we put the candidate back in the bid pool and let them go", + "through the induction process again.", + "", + "The dispatch origin for this call must be from the _SuspensionJudgementOrigin_.", + "", + "Parameters:", + "- `who` - The suspended candidate to be judged.", + "- `judgement` - `Approve`, `Reject`, or `Rebid`.", + "", + "# ", + "Key: B (len of bids), M (len of members), X (balance action)", + "- One storage read to check `who` is a suspended candidate.", + "- One storage removal of the suspended candidate.", + "- Approve Logic", + "\t- One storage read to get the available pot to pay users with. O(1)", + "\t- One storage write to update the available pot. O(1)", + "\t- One storage read to get the current block number. O(1)", + "\t- One storage read to get all members. O(M)", + "\t- Up to one unreserve currency action.", + "\t- Up to two new storage writes to payouts.", + "\t- Up to one storage write with O(log M) binary search to add a member to society.", + "- Reject Logic", + "\t- Up to one repatriate reserved currency action. O(X)", + "\t- Up to one storage write to ban the vouching member from vouching again.", + "- Rebid Logic", + "\t- Storage mutate with O(log B) binary search to place the user back into bids.", + "- Up to one additional event if unvouch takes place.", + "- One storage removal.", + "- One event for the judgement.", + "", + "Total Complexity: O(M + logM + B + X)", + "# " + ] + }, + { + "name": "set_max_members", + "fields": [ + { + "name": "max", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Allows root origin to change the maximum number of members in society.", + "Max membership count must be greater than 1.", + "", + "The dispatch origin for this call must be from _ROOT_.", + "", + "Parameters:", + "- `max` - The maximum number of members for the society.", + "", + "# ", + "- One storage write to update the max. O(1)", + "- One event.", + "", + "Total Complexity: O(1)", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 355, + "type": { + "path": [ + "pallet_society", + "Judgement" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Rebid", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Reject", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Approve", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 356, + "type": { + "path": [ + "pallet_recovery", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "as_recovered", + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Send a call through a recovered account.", + "", + "The dispatch origin for this call must be _Signed_ and registered to", + "be able to make calls on behalf of the recovered account.", + "", + "Parameters:", + "- `account`: The recovered account you want to make a call on-behalf-of.", + "- `call`: The call you want to make with the recovered account.", + "", + "# ", + "- The weight of the `call` + 10,000.", + "- One storage lookup to check account is recovered by `who`. O(1)", + "# " + ] + }, + { + "name": "set_recovered", + "fields": [ + { + "name": "lost", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "rescuer", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Allow ROOT to bypass the recovery process and set an a rescuer account", + "for a lost account directly.", + "", + "The dispatch origin for this call must be _ROOT_.", + "", + "Parameters:", + "- `lost`: The \"lost account\" to be recovered.", + "- `rescuer`: The \"rescuer account\" which can call as the lost account.", + "", + "# ", + "- One storage write O(1)", + "- One event", + "# " + ] + }, + { + "name": "create_recovery", + "fields": [ + { + "name": "friends", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "threshold", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "delay_period", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Create a recovery configuration for your account. This makes your account recoverable.", + "", + "Payment: `ConfigDepositBase` + `FriendDepositFactor` * #_of_friends balance", + "will be reserved for storing the recovery configuration. This deposit is returned", + "in full when the user calls `remove_recovery`.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `friends`: A list of friends you trust to vouch for recovery attempts. Should be", + " ordered and contain no duplicate values.", + "- `threshold`: The number of friends that must vouch for a recovery attempt before the", + " account can be recovered. Should be less than or equal to the length of the list of", + " friends.", + "- `delay_period`: The number of blocks after a recovery attempt is initialized that", + " needs to pass before the account can be recovered.", + "", + "# ", + "- Key: F (len of friends)", + "- One storage read to check that account is not already recoverable. O(1).", + "- A check that the friends list is sorted and unique. O(F)", + "- One currency reserve operation. O(X)", + "- One storage write. O(1). Codec O(F).", + "- One event.", + "", + "Total Complexity: O(F + X)", + "# " + ] + }, + { + "name": "initiate_recovery", + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Initiate the process for recovering a recoverable account.", + "", + "Payment: `RecoveryDeposit` balance will be reserved for initiating the", + "recovery process. This deposit will always be repatriated to the account", + "trying to be recovered. See `close_recovery`.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `account`: The lost account that you want to recover. This account needs to be", + " recoverable (i.e. have a recovery configuration).", + "", + "# ", + "- One storage read to check that account is recoverable. O(F)", + "- One storage read to check that this recovery process hasn't already started. O(1)", + "- One currency reserve operation. O(X)", + "- One storage read to get the current block number. O(1)", + "- One storage write. O(1).", + "- One event.", + "", + "Total Complexity: O(F + X)", + "# " + ] + }, + { + "name": "vouch_recovery", + "fields": [ + { + "name": "lost", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "rescuer", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Allow a \"friend\" of a recoverable account to vouch for an active recovery", + "process for that account.", + "", + "The dispatch origin for this call must be _Signed_ and must be a \"friend\"", + "for the recoverable account.", + "", + "Parameters:", + "- `lost`: The lost account that you want to recover.", + "- `rescuer`: The account trying to rescue the lost account that you want to vouch for.", + "", + "The combination of these two parameters must point to an active recovery", + "process.", + "", + "# ", + "Key: F (len of friends in config), V (len of vouching friends)", + "- One storage read to get the recovery configuration. O(1), Codec O(F)", + "- One storage read to get the active recovery process. O(1), Codec O(V)", + "- One binary search to confirm caller is a friend. O(logF)", + "- One binary search to confirm caller has not already vouched. O(logV)", + "- One storage write. O(1), Codec O(V).", + "- One event.", + "", + "Total Complexity: O(F + logF + V + logV)", + "# " + ] + }, + { + "name": "claim_recovery", + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Allow a successful rescuer to claim their recovered account.", + "", + "The dispatch origin for this call must be _Signed_ and must be a \"rescuer\"", + "who has successfully completed the account recovery process: collected", + "`threshold` or more vouches, waited `delay_period` blocks since initiation.", + "", + "Parameters:", + "- `account`: The lost account that you want to claim has been successfully recovered by", + " you.", + "", + "# ", + "Key: F (len of friends in config), V (len of vouching friends)", + "- One storage read to get the recovery configuration. O(1), Codec O(F)", + "- One storage read to get the active recovery process. O(1), Codec O(V)", + "- One storage read to get the current block number. O(1)", + "- One storage write. O(1), Codec O(V).", + "- One event.", + "", + "Total Complexity: O(F + V)", + "# " + ] + }, + { + "name": "close_recovery", + "fields": [ + { + "name": "rescuer", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 6, + "docs": [ + "As the controller of a recoverable account, close an active recovery", + "process for your account.", + "", + "Payment: By calling this function, the recoverable account will receive", + "the recovery deposit `RecoveryDeposit` placed by the rescuer.", + "", + "The dispatch origin for this call must be _Signed_ and must be a", + "recoverable account with an active recovery process for it.", + "", + "Parameters:", + "- `rescuer`: The account trying to rescue this recoverable account.", + "", + "# ", + "Key: V (len of vouching friends)", + "- One storage read/remove to get the active recovery process. O(1), Codec O(V)", + "- One balance call to repatriate reserved. O(X)", + "- One event.", + "", + "Total Complexity: O(V + X)", + "# " + ] + }, + { + "name": "remove_recovery", + "fields": [], + "index": 7, + "docs": [ + "Remove the recovery process for your account. Recovered accounts are still accessible.", + "", + "NOTE: The user must make sure to call `close_recovery` on all active", + "recovery attempts before calling this function else it will fail.", + "", + "Payment: By calling this function the recoverable account will unreserve", + "their recovery configuration deposit.", + "(`ConfigDepositBase` + `FriendDepositFactor` * #_of_friends)", + "", + "The dispatch origin for this call must be _Signed_ and must be a", + "recoverable account (i.e. has a recovery configuration).", + "", + "# ", + "Key: F (len of friends)", + "- One storage read to get the prefix iterator for active recoveries. O(1)", + "- One storage read/remove to get the recovery configuration. O(1), Codec O(F)", + "- One balance call to unreserved. O(X)", + "- One event.", + "", + "Total Complexity: O(F + X)", + "# " + ] + }, + { + "name": "cancel_recovered", + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Cancel the ability to use `as_recovered` for `account`.", + "", + "The dispatch origin for this call must be _Signed_ and registered to", + "be able to make calls on behalf of the recovered account.", + "", + "Parameters:", + "- `account`: The recovered account you are able to call on-behalf-of.", + "", + "# ", + "- One storage mutation to check account is recovered by `who`. O(1)", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 357, + "type": { + "path": [ + "pallet_vesting", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "vest", + "fields": [], + "index": 0, + "docs": [ + "Unlock any vested funds of the sender account.", + "", + "The dispatch origin for this call must be _Signed_ and the sender must have funds still", + "locked under this pallet.", + "", + "Emits either `VestingCompleted` or `VestingUpdated`.", + "", + "# ", + "- `O(1)`.", + "- DbWeight: 2 Reads, 2 Writes", + " - Reads: Vesting Storage, Balances Locks, [Sender Account]", + " - Writes: Vesting Storage, Balances Locks, [Sender Account]", + "# " + ] + }, + { + "name": "vest_other", + "fields": [ + { + "name": "target", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Unlock any vested funds of a `target` account.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `target`: The account whose vested funds should be unlocked. Must have funds still", + "locked under this pallet.", + "", + "Emits either `VestingCompleted` or `VestingUpdated`.", + "", + "# ", + "- `O(1)`.", + "- DbWeight: 3 Reads, 3 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account", + " - Writes: Vesting Storage, Balances Locks, Target Account", + "# " + ] + }, + { + "name": "vested_transfer", + "fields": [ + { + "name": "target", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "schedule", + "type": 358, + "typeName": "VestingInfo, T::BlockNumber>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Create a vested transfer.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `target`: The account receiving the vested funds.", + "- `schedule`: The vesting schedule attached to the transfer.", + "", + "Emits `VestingCreated`.", + "", + "NOTE: This will unlock all schedules through the current block.", + "", + "# ", + "- `O(1)`.", + "- DbWeight: 3 Reads, 3 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account, [Sender Account]", + " - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account]", + "# " + ] + }, + { + "name": "force_vested_transfer", + "fields": [ + { + "name": "source", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "target", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "schedule", + "type": 358, + "typeName": "VestingInfo, T::BlockNumber>", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Force a vested transfer.", + "", + "The dispatch origin for this call must be _Root_.", + "", + "- `source`: The account whose funds should be transferred.", + "- `target`: The account that should be transferred the vested funds.", + "- `schedule`: The vesting schedule attached to the transfer.", + "", + "Emits `VestingCreated`.", + "", + "NOTE: This will unlock all schedules through the current block.", + "", + "# ", + "- `O(1)`.", + "- DbWeight: 4 Reads, 4 Writes", + " - Reads: Vesting Storage, Balances Locks, Target Account, Source Account", + " - Writes: Vesting Storage, Balances Locks, Target Account, Source Account", + "# " + ] + }, + { + "name": "merge_schedules", + "fields": [ + { + "name": "schedule1_index", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "schedule2_index", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Merge two vesting schedules together, creating a new vesting schedule that unlocks over", + "the highest possible start and end blocks. If both schedules have already started the", + "current block will be used as the schedule start; with the caveat that if one schedule", + "is finished by the current block, the other will be treated as the new merged schedule,", + "unmodified.", + "", + "NOTE: If `schedule1_index == schedule2_index` this is a no-op.", + "NOTE: This will unlock all schedules through the current block prior to merging.", + "NOTE: If both schedules have ended by the current block, no new schedule will be created", + "and both will be removed.", + "", + "Merged schedule attributes:", + "- `starting_block`: `MAX(schedule1.starting_block, scheduled2.starting_block,", + " current_block)`.", + "- `ending_block`: `MAX(schedule1.ending_block, schedule2.ending_block)`.", + "- `locked`: `schedule1.locked_at(current_block) + schedule2.locked_at(current_block)`.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `schedule1_index`: index of the first schedule to merge.", + "- `schedule2_index`: index of the second schedule to merge." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 358, + "type": { + "path": [ + "pallet_vesting", + "vesting_info", + "VestingInfo" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "locked", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "per_block", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "starting_block", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 359, + "type": { + "path": [ + "pallet_scheduler", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "schedule", + "fields": [ + { + "name": "when", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "maybe_periodic", + "type": 360, + "typeName": "Option>", + "docs": [] + }, + { + "name": "priority", + "type": 2, + "typeName": "schedule::Priority", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Anonymously schedule a task.", + "", + "# ", + "- S = Number of already scheduled calls", + "- Base Weight: 22.29 + .126 * S µs", + "- DB Weight:", + " - Read: Agenda", + " - Write: Agenda", + "- Will use base weight of 25 which should be good for up to 30 scheduled calls", + "# " + ] + }, + { + "name": "cancel", + "fields": [ + { + "name": "when", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Cancel an anonymously scheduled task.", + "", + "# ", + "- S = Number of already scheduled calls", + "- Base Weight: 22.15 + 2.869 * S µs", + "- DB Weight:", + " - Read: Agenda", + " - Write: Agenda, Lookup", + "- Will use base weight of 100 which should be good for up to 30 scheduled calls", + "# " + ] + }, + { + "name": "schedule_named", + "fields": [ + { + "name": "id", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "when", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "maybe_periodic", + "type": 360, + "typeName": "Option>", + "docs": [] + }, + { + "name": "priority", + "type": 2, + "typeName": "schedule::Priority", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Schedule a named task.", + "", + "# ", + "- S = Number of already scheduled calls", + "- Base Weight: 29.6 + .159 * S µs", + "- DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + "- Will use base weight of 35 which should be good for more than 30 scheduled calls", + "# " + ] + }, + { + "name": "cancel_named", + "fields": [ + { + "name": "id", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Cancel a named scheduled task.", + "", + "# ", + "- S = Number of already scheduled calls", + "- Base Weight: 24.91 + 2.907 * S µs", + "- DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + "- Will use base weight of 100 which should be good for up to 30 scheduled calls", + "# " + ] + }, + { + "name": "schedule_after", + "fields": [ + { + "name": "after", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "maybe_periodic", + "type": 360, + "typeName": "Option>", + "docs": [] + }, + { + "name": "priority", + "type": 2, + "typeName": "schedule::Priority", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Anonymously schedule a task after a delay.", + "", + "# ", + "Same as [`schedule`].", + "# " + ] + }, + { + "name": "schedule_named_after", + "fields": [ + { + "name": "id", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "after", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "maybe_periodic", + "type": 360, + "typeName": "Option>", + "docs": [] + }, + { + "name": "priority", + "type": 2, + "typeName": "schedule::Priority", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Schedule a named task after a delay.", + "", + "# ", + "Same as [`schedule_named`](Self::schedule_named).", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 360, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 71 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 71, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 361, + "type": { + "path": [ + "pallet_proxy", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "proxy", + "fields": [ + { + "name": "real", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "force_proxy_type", + "type": 362, + "typeName": "Option", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Dispatch the given `call` from an account that the sender is authorised for through", + "`add_proxy`.", + "", + "Removes any corresponding announcement(s).", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `real`: The account that the proxy will make a call on behalf of.", + "- `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + "- `call`: The call to be made by the `real` account.", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# " + ] + }, + { + "name": "add_proxy", + "fields": [ + { + "name": "delegate", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "proxy_type", + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Register a proxy account for the sender that is able to make calls on its behalf.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `proxy`: The account that the `caller` would like to make a proxy.", + "- `proxy_type`: The permissions allowed for this proxy account.", + "- `delay`: The announcement period required of the initial proxy. Will generally be", + "zero.", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# " + ] + }, + { + "name": "remove_proxy", + "fields": [ + { + "name": "delegate", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "proxy_type", + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Unregister a proxy account for the sender.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `proxy`: The account that the `caller` would like to remove as a proxy.", + "- `proxy_type`: The permissions currently enabled for the removed proxy account.", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# " + ] + }, + { + "name": "remove_proxies", + "fields": [], + "index": 3, + "docs": [ + "Unregister all proxy accounts for the sender.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "WARNING: This may be called on accounts created by `anonymous`, however if done, then", + "the unreserved fees will be inaccessible. **All access to this account will be lost.**", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# " + ] + }, + { + "name": "anonymous", + "fields": [ + { + "name": "proxy_type", + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "index", + "type": 75, + "typeName": "u16", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and", + "initialize it with a proxy of `proxy_type` for `origin` sender.", + "", + "Requires a `Signed` origin.", + "", + "- `proxy_type`: The type of the proxy that the sender will be registered as over the", + "new account. This will almost always be the most permissive `ProxyType` possible to", + "allow for maximum flexibility.", + "- `index`: A disambiguation index, in case this is called multiple times in the same", + "transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just", + "want to use `0`.", + "- `delay`: The announcement period required of the initial proxy. Will generally be", + "zero.", + "", + "Fails with `Duplicate` if this has already been called in this transaction, from the", + "same sender, with the same parameters.", + "", + "Fails if there are insufficient funds to pay for deposit.", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# ", + "TODO: Might be over counting 1 read" + ] + }, + { + "name": "kill_anonymous", + "fields": [ + { + "name": "spawner", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "proxy_type", + "type": 74, + "typeName": "T::ProxyType", + "docs": [] + }, + { + "name": "index", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "height", + "type": 111, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "ext_index", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Removes a previously spawned anonymous proxy.", + "", + "WARNING: **All access to this account will be lost.** Any funds held in it will be", + "inaccessible.", + "", + "Requires a `Signed` origin, and the sender account must have been created by a call to", + "`anonymous` with corresponding parameters.", + "", + "- `spawner`: The account that originally called `anonymous` to create this account.", + "- `index`: The disambiguation index originally passed to `anonymous`. Probably `0`.", + "- `proxy_type`: The proxy type originally passed to `anonymous`.", + "- `height`: The height of the chain when the call to `anonymous` was processed.", + "- `ext_index`: The extrinsic index in which the call to `anonymous` was processed.", + "", + "Fails with `NoPermission` in case the caller is not a previously created anonymous", + "account whose `anonymous` call has corresponding parameters.", + "", + "# ", + "Weight is a function of the number of proxies the user has (P).", + "# " + ] + }, + { + "name": "announce", + "fields": [ + { + "name": "real", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "call_hash", + "type": 9, + "typeName": "CallHashOf", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Publish the hash of a proxy-call that will be made in the future.", + "", + "This must be called some number of blocks before the corresponding `proxy` is attempted", + "if the delay associated with the proxy relationship is greater than zero.", + "", + "No more than `MaxPending` announcements may be made at any one time.", + "", + "This will take a deposit of `AnnouncementDepositFactor` as well as", + "`AnnouncementDepositBase` if there are no other pending announcements.", + "", + "The dispatch origin for this call must be _Signed_ and a proxy of `real`.", + "", + "Parameters:", + "- `real`: The account that the proxy will make a call on behalf of.", + "- `call_hash`: The hash of the call to be made by the `real` account.", + "", + "# ", + "Weight is a function of:", + "- A: the number of announcements made.", + "- P: the number of proxies the user has.", + "# " + ] + }, + { + "name": "remove_announcement", + "fields": [ + { + "name": "real", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "call_hash", + "type": 9, + "typeName": "CallHashOf", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Remove a given announcement.", + "", + "May be called by a proxy account to remove a call they previously announced and return", + "the deposit.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `real`: The account that the proxy will make a call on behalf of.", + "- `call_hash`: The hash of the call to be made by the `real` account.", + "", + "# ", + "Weight is a function of:", + "- A: the number of announcements made.", + "- P: the number of proxies the user has.", + "# " + ] + }, + { + "name": "reject_announcement", + "fields": [ + { + "name": "delegate", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "call_hash", + "type": 9, + "typeName": "CallHashOf", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Remove the given announcement of a delegate.", + "", + "May be called by a target (proxied) account to remove a call that one of their delegates", + "(`delegate`) has announced they want to execute. The deposit is returned.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `delegate`: The account that previously announced the call.", + "- `call_hash`: The hash of the call to be made.", + "", + "# ", + "Weight is a function of:", + "- A: the number of announcements made.", + "- P: the number of proxies the user has.", + "# " + ] + }, + { + "name": "proxy_announced", + "fields": [ + { + "name": "delegate", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "real", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "force_proxy_type", + "type": 362, + "typeName": "Option", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Dispatch the given `call` from an account that the sender is authorized for through", + "`add_proxy`.", + "", + "Removes any corresponding announcement(s).", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Parameters:", + "- `real`: The account that the proxy will make a call on behalf of.", + "- `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + "- `call`: The call to be made by the `real` account.", + "", + "# ", + "Weight is a function of:", + "- A: the number of announcements made.", + "- P: the number of proxies the user has.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 362, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 74 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 74, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 363, + "type": { + "path": [ + "pallet_multisig", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "as_multi_threshold_1", + "fields": [ + { + "name": "other_signatories", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Box<::Call>", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Immediately dispatch a multi-signature call using a single approval from the caller.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `other_signatories`: The accounts (other than the sender) who are part of the", + "multi-signature, but do not participate in the approval process.", + "- `call`: The call to be executed.", + "", + "Result is equivalent to the dispatched result.", + "", + "# ", + "O(Z + C) where Z is the length of the call and C its execution weight.", + "-------------------------------", + "- DB Weight: None", + "- Plus Call Weight", + "# " + ] + }, + { + "name": "as_multi", + "fields": [ + { + "name": "threshold", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "other_signatories", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "maybe_timepoint", + "type": 364, + "typeName": "Option>", + "docs": [] + }, + { + "name": "call", + "type": 10, + "typeName": "OpaqueCall", + "docs": [] + }, + { + "name": "store_call", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "max_weight", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Register approval for a dispatch to be made from a deterministic composite account if", + "approved by a total of `threshold - 1` of `other_signatories`.", + "", + "If there are enough, then dispatch the call.", + "", + "Payment: `DepositBase` will be reserved if this is the first approval, plus", + "`threshold` times `DepositFactor`. It is returned once this dispatch happens or", + "is cancelled.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `threshold`: The total number of approvals for this dispatch before it is executed.", + "- `other_signatories`: The accounts (other than the sender) who can approve this", + "dispatch. May not be empty.", + "- `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is", + "not the first approval, then it must be `Some`, with the timepoint (block number and", + "transaction index) of the first approval transaction.", + "- `call`: The call to be executed.", + "", + "NOTE: Unless this is the final approval, you will generally want to use", + "`approve_as_multi` instead, since it only requires a hash of the call.", + "", + "Result is equivalent to the dispatched result if `threshold` is exactly `1`. Otherwise", + "on success, result is `Ok` and the result from the interior call, if it was executed,", + "may be found in the deposited `MultisigExecuted` event.", + "", + "# ", + "- `O(S + Z + Call)`.", + "- Up to one balance-reserve or unreserve operation.", + "- One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + "- One call encode & hash, both of complexity `O(Z)` where `Z` is tx-len.", + "- One encode & hash, both of complexity `O(S)`.", + "- Up to one binary search and insert (`O(logS + S)`).", + "- I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove.", + "- One event.", + "- The weight of the `call`.", + "- Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit", + " taken for its lifetime of `DepositBase + threshold * DepositFactor`.", + "-------------------------------", + "- DB Weight:", + " - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`)", + " - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`)", + "- Plus Call Weight", + "# " + ] + }, + { + "name": "approve_as_multi", + "fields": [ + { + "name": "threshold", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "other_signatories", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "maybe_timepoint", + "type": 364, + "typeName": "Option>", + "docs": [] + }, + { + "name": "call_hash", + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + }, + { + "name": "max_weight", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Register approval for a dispatch to be made from a deterministic composite account if", + "approved by a total of `threshold - 1` of `other_signatories`.", + "", + "Payment: `DepositBase` will be reserved if this is the first approval, plus", + "`threshold` times `DepositFactor`. It is returned once this dispatch happens or", + "is cancelled.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `threshold`: The total number of approvals for this dispatch before it is executed.", + "- `other_signatories`: The accounts (other than the sender) who can approve this", + "dispatch. May not be empty.", + "- `maybe_timepoint`: If this is the first approval, then this must be `None`. If it is", + "not the first approval, then it must be `Some`, with the timepoint (block number and", + "transaction index) of the first approval transaction.", + "- `call_hash`: The hash of the call to be executed.", + "", + "NOTE: If this is the final approval, you will want to use `as_multi` instead.", + "", + "# ", + "- `O(S)`.", + "- Up to one balance-reserve or unreserve operation.", + "- One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + "- One encode & hash, both of complexity `O(S)`.", + "- Up to one binary search and insert (`O(logS + S)`).", + "- I/O: 1 read `O(S)`, up to 1 mutate `O(S)`. Up to one remove.", + "- One event.", + "- Storage: inserts one item, value size bounded by `MaxSignatories`, with a deposit", + " taken for its lifetime of `DepositBase + threshold * DepositFactor`.", + "----------------------------------", + "- DB Weight:", + " - Read: Multisig Storage, [Caller Account]", + " - Write: Multisig Storage, [Caller Account]", + "# " + ] + }, + { + "name": "cancel_as_multi", + "fields": [ + { + "name": "threshold", + "type": 75, + "typeName": "u16", + "docs": [] + }, + { + "name": "other_signatories", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "timepoint", + "type": 77, + "typeName": "Timepoint", + "docs": [] + }, + { + "name": "call_hash", + "type": 1, + "typeName": "[u8; 32]", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Cancel a pre-existing, on-going multisig transaction. Any deposit reserved previously", + "for this operation will be unreserved on success.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "- `threshold`: The total number of approvals for this dispatch before it is executed.", + "- `other_signatories`: The accounts (other than the sender) who can approve this", + "dispatch. May not be empty.", + "- `timepoint`: The timepoint (block number and transaction index) of the first approval", + "transaction for this dispatch.", + "- `call_hash`: The hash of the call to be executed.", + "", + "# ", + "- `O(S)`.", + "- Up to one balance-reserve or unreserve operation.", + "- One passthrough operation, one insert, both `O(S)` where `S` is the number of", + " signatories. `S` is capped by `MaxSignatories`, with weight being proportional.", + "- One encode & hash, both of complexity `O(S)`.", + "- One event.", + "- I/O: 1 read `O(S)`, one remove.", + "- Storage: removes one item.", + "----------------------------------", + "- DB Weight:", + " - Read: Multisig Storage, [Caller Account], Refund Account, Calls", + " - Write: Multisig Storage, [Caller Account], Refund Account, Calls", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 364, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 77 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 77, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 365, + "type": { + "path": [ + "pallet_bounties", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "propose_bounty", + "fields": [ + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "description", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Propose a new bounty.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Payment: `TipReportDepositBase` will be reserved from the origin account, as well as", + "`DataDepositPerByte` for each byte in `reason`. It will be unreserved upon approval,", + "or slashed when rejected.", + "", + "- `curator`: The curator account whom will manage this bounty.", + "- `fee`: The curator fee.", + "- `value`: The total payment amount of this bounty, curator fee included.", + "- `description`: The description of this bounty." + ] + }, + { + "name": "approve_bounty", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Approve a bounty proposal. At a later time, the bounty will be funded and become active", + "and the original deposit will be returned.", + "", + "May only be called from `T::ApproveOrigin`.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "propose_curator", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": "curator", + "type": 195, + "typeName": "::Source", + "docs": [] + }, + { + "name": "fee", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Assign a curator to a funded bounty.", + "", + "May only be called from `T::ApproveOrigin`.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "unassign_curator", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Unassign curator from a bounty.", + "", + "This function can only be called by the `RejectOrigin` a signed origin.", + "", + "If this function is called by the `RejectOrigin`, we assume that the curator is", + "malicious or inactive. As a result, we will slash the curator when possible.", + "", + "If the origin is the curator, we take this as a sign they are unable to do their job and", + "they willingly give up. We could slash them, but for now we allow them to recover their", + "deposit and exit without issue. (We may want to change this if it is abused.)", + "", + "Finally, the origin can be anyone if and only if the curator is \"inactive\". This allows", + "anyone in the community to call out that a curator is not doing their due diligence, and", + "we should pick a new curator. In this case the curator should also be slashed.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "accept_curator", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Accept the curator role for a bounty.", + "A deposit will be reserved from curator and refund upon successful payout.", + "", + "May only be called from the curator.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "award_bounty", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": "beneficiary", + "type": 195, + "typeName": "::Source", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Award bounty to a beneficiary account. The beneficiary will be able to claim the funds", + "after a delay.", + "", + "The dispatch origin for this call must be the curator of this bounty.", + "", + "- `bounty_id`: Bounty ID to award.", + "- `beneficiary`: The beneficiary account whom will receive the payout.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "claim_bounty", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Claim the payout from an awarded bounty after payout delay.", + "", + "The dispatch origin for this call must be the beneficiary of this bounty.", + "", + "- `bounty_id`: Bounty ID to claim.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "close_bounty", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Cancel a proposed or active bounty. All the funds will be sent to treasury and", + "the curator deposit will be unreserved if possible.", + "", + "Only `T::RejectOrigin` is able to cancel a bounty.", + "", + "- `bounty_id`: Bounty ID to cancel.", + "", + "# ", + "- O(1).", + "# " + ] + }, + { + "name": "extend_bounty_expiry", + "fields": [ + { + "name": "bounty_id", + "type": 111, + "typeName": "BountyIndex", + "docs": [] + }, + { + "name": "remark", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Extend the expiry time of an active bounty.", + "", + "The dispatch origin for this call must be the curator of this bounty.", + "", + "- `bounty_id`: Bounty ID to extend.", + "- `remark`: additional information.", + "", + "# ", + "- O(1).", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 366, + "type": { + "path": [ + "pallet_tips", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "report_awesome", + "fields": [ + { + "name": "reason", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Report something `reason` that deserves a tip and claim any eventual the finder's fee.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "Payment: `TipReportDepositBase` will be reserved from the origin account, as well as", + "`DataDepositPerByte` for each byte in `reason`.", + "", + "- `reason`: The reason for, or the thing that deserves, the tip; generally this will be", + " a UTF-8-encoded URL.", + "- `who`: The account which should be credited for the tip.", + "", + "Emits `NewTip` if successful.", + "", + "# ", + "- Complexity: `O(R)` where `R` length of `reason`.", + " - encoding and hashing of 'reason'", + "- DbReads: `Reasons`, `Tips`", + "- DbWrites: `Reasons`, `Tips`", + "# " + ] + }, + { + "name": "retract_tip", + "fields": [ + { + "name": "hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Retract a prior tip-report from `report_awesome`, and cancel the process of tipping.", + "", + "If successful, the original deposit will be unreserved.", + "", + "The dispatch origin for this call must be _Signed_ and the tip identified by `hash`", + "must have been reported by the signing account through `report_awesome` (and not", + "through `tip_new`).", + "", + "- `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the original tip `reason` and the beneficiary account ID.", + "", + "Emits `TipRetracted` if successful.", + "", + "# ", + "- Complexity: `O(1)`", + " - Depends on the length of `T::Hash` which is fixed.", + "- DbReads: `Tips`, `origin account`", + "- DbWrites: `Reasons`, `Tips`, `origin account`", + "# " + ] + }, + { + "name": "tip_new", + "fields": [ + { + "name": "reason", + "type": 10, + "typeName": "Vec", + "docs": [] + }, + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "tip_value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Give a tip for something new; no finder's fee will be taken.", + "", + "The dispatch origin for this call must be _Signed_ and the signing account must be a", + "member of the `Tippers` set.", + "", + "- `reason`: The reason for, or the thing that deserves, the tip; generally this will be", + " a UTF-8-encoded URL.", + "- `who`: The account which should be credited for the tip.", + "- `tip_value`: The amount of tip that the sender would like to give. The median tip", + " value of active tippers will be given to the `who`.", + "", + "Emits `NewTip` if successful.", + "", + "# ", + "- Complexity: `O(R + T)` where `R` length of `reason`, `T` is the number of tippers.", + " - `O(T)`: decoding `Tipper` vec of length `T`. `T` is charged as upper bound given by", + " `ContainsLengthBound`. The actual cost depends on the implementation of", + " `T::Tippers`.", + " - `O(R)`: hashing and encoding of reason of length `R`", + "- DbReads: `Tippers`, `Reasons`", + "- DbWrites: `Reasons`, `Tips`", + "# " + ] + }, + { + "name": "tip", + "fields": [ + { + "name": "hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + }, + { + "name": "tip_value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Declare a tip value for an already-open tip.", + "", + "The dispatch origin for this call must be _Signed_ and the signing account must be a", + "member of the `Tippers` set.", + "", + "- `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the hash of the original tip `reason` and the beneficiary", + " account ID.", + "- `tip_value`: The amount of tip that the sender would like to give. The median tip", + " value of active tippers will be given to the `who`.", + "", + "Emits `TipClosing` if the threshold of tippers has been reached and the countdown period", + "has started.", + "", + "# ", + "- Complexity: `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length", + " `T`, insert tip and check closing, `T` is charged as upper bound given by", + " `ContainsLengthBound`. The actual cost depends on the implementation of `T::Tippers`.", + "", + " Actually weight could be lower as it depends on how many tips are in `OpenTip` but it", + " is weighted as if almost full i.e of length `T-1`.", + "- DbReads: `Tippers`, `Tips`", + "- DbWrites: `Tips`", + "# " + ] + }, + { + "name": "close_tip", + "fields": [ + { + "name": "hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Close and payout a tip.", + "", + "The dispatch origin for this call must be _Signed_.", + "", + "The tip identified by `hash` must have finished its countdown period.", + "", + "- `hash`: The identity of the open tip for which a tip value is declared. This is formed", + " as the hash of the tuple of the original tip `reason` and the beneficiary account ID.", + "", + "# ", + "- Complexity: `O(T)` where `T` is the number of tippers. decoding `Tipper` vec of length", + " `T`. `T` is charged as upper bound given by `ContainsLengthBound`. The actual cost", + " depends on the implementation of `T::Tippers`.", + "- DbReads: `Tips`, `Tippers`, `tip finder`", + "- DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder`", + "# " + ] + }, + { + "name": "slash_tip", + "fields": [ + { + "name": "hash", + "type": 9, + "typeName": "T::Hash", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Remove and slash an already-open tip.", + "", + "May only be called from `T::RejectOrigin`.", + "", + "As a result, the finder is slashed and the deposits are lost.", + "", + "Emits `TipSlashed` if successful.", + "", + "# ", + " `T` is charged as upper bound given by `ContainsLengthBound`.", + " The actual cost depends on the implementation of `T::Tippers`.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 367, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "submit_unsigned", + "fields": [ + { + "name": "raw_solution", + "type": 368, + "typeName": "Box>>", + "docs": [] + }, + { + "name": "witness", + "type": 445, + "typeName": "SolutionOrSnapshotSize", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Submit a solution for the unsigned phase.", + "", + "The dispatch origin fo this call must be __none__.", + "", + "This submission is checked on the fly. Moreover, this unsigned solution is only", + "validated when submitted to the pool from the **local** node. Effectively, this means", + "that only active validators can submit this transaction when authoring a block (similar", + "to an inherent).", + "", + "To prevent any incorrect solution (and thus wasted time/weight), this transaction will", + "panic if the solution submitted by the validator is invalid in any way, effectively", + "putting their authoring reward at risk.", + "", + "No deposit or reward is associated with this submission." + ] + }, + { + "name": "set_minimum_untrusted_score", + "fields": [ + { + "name": "maybe_next_score", + "type": 446, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Set a new value for `MinimumUntrustedScore`.", + "", + "Dispatch origin must be aligned with `T::ForceOrigin`.", + "", + "This check can be turned off by setting the value to `None`." + ] + }, + { + "name": "set_emergency_election_result", + "fields": [ + { + "name": "supports", + "type": 447, + "typeName": "Supports", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Set a solution in the queue, to be handed out to the client of this pallet in the next", + "call to `ElectionProvider::elect`.", + "", + "This can only be set by `T::ForceOrigin`, and only when the phase is `Emergency`.", + "", + "The solution is not checked for any feasibility and is assumed to be trustworthy, as any", + "feasibility check itself can in principle cause the election process to fail (due to", + "memory/weight constrains)." + ] + }, + { + "name": "submit", + "fields": [ + { + "name": "raw_solution", + "type": 368, + "typeName": "Box>>", + "docs": [] + }, + { + "name": "num_signed_submissions", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Submit a solution for the signed phase.", + "", + "The dispatch origin fo this call must be __signed__.", + "", + "The solution is potentially queued, based on the claimed score and processed at the end", + "of the signed phase.", + "", + "A deposit is reserved and recorded for the solution. Based on the outcome, the solution", + "might be rewarded, slashed, or get all or a part of the deposit back.", + "", + "# ", + "Queue size must be provided as witness data.", + "# " + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 368, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "RawSolution" + ], + "params": [ + { + "name": "S", + "type": 369 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "solution", + "type": 369, + "typeName": "S", + "docs": [] + }, + { + "name": "score", + "type": 444, + "typeName": "ElectionScore", + "docs": [] + }, + { + "name": "round", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 369, + "type": { + "path": [ + "kusama_runtime", + "NposCompactSolution24" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "votes1", + "type": 370, + "typeName": null, + "docs": [] + }, + { + "name": "votes2", + "type": 373, + "typeName": null, + "docs": [] + }, + { + "name": "votes3", + "type": 378, + "typeName": null, + "docs": [] + }, + { + "name": "votes4", + "type": 381, + "typeName": null, + "docs": [] + }, + { + "name": "votes5", + "type": 384, + "typeName": null, + "docs": [] + }, + { + "name": "votes6", + "type": 387, + "typeName": null, + "docs": [] + }, + { + "name": "votes7", + "type": 390, + "typeName": null, + "docs": [] + }, + { + "name": "votes8", + "type": 393, + "typeName": null, + "docs": [] + }, + { + "name": "votes9", + "type": 396, + "typeName": null, + "docs": [] + }, + { + "name": "votes10", + "type": 399, + "typeName": null, + "docs": [] + }, + { + "name": "votes11", + "type": 402, + "typeName": null, + "docs": [] + }, + { + "name": "votes12", + "type": 405, + "typeName": null, + "docs": [] + }, + { + "name": "votes13", + "type": 408, + "typeName": null, + "docs": [] + }, + { + "name": "votes14", + "type": 411, + "typeName": null, + "docs": [] + }, + { + "name": "votes15", + "type": 414, + "typeName": null, + "docs": [] + }, + { + "name": "votes16", + "type": 417, + "typeName": null, + "docs": [] + }, + { + "name": "votes17", + "type": 420, + "typeName": null, + "docs": [] + }, + { + "name": "votes18", + "type": 423, + "typeName": null, + "docs": [] + }, + { + "name": "votes19", + "type": 426, + "typeName": null, + "docs": [] + }, + { + "name": "votes20", + "type": 429, + "typeName": null, + "docs": [] + }, + { + "name": "votes21", + "type": 432, + "typeName": null, + "docs": [] + }, + { + "name": "votes22", + "type": 435, + "typeName": null, + "docs": [] + }, + { + "name": "votes23", + "type": 438, + "typeName": null, + "docs": [] + }, + { + "name": "votes24", + "type": 441, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 370, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 371 + } + }, + "docs": [] + } + }, + { + "id": 371, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 372, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 75 + } + }, + "docs": [] + } + }, + { + "id": 373, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 374 + } + }, + "docs": [] + } + }, + { + "id": 374, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 375, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 375, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 372, + 376 + ] + }, + "docs": [] + } + }, + { + "id": 376, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 377 + } + }, + "docs": [] + } + }, + { + "id": 377, + "type": { + "path": [ + "sp_arithmetic", + "per_things", + "PerU16" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 75, + "typeName": "u16", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 378, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 379 + } + }, + "docs": [] + } + }, + { + "id": 379, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 380, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 380, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 2, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 381, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 382 + } + }, + "docs": [] + } + }, + { + "id": 382, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 383, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 383, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 3, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 384, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 385 + } + }, + "docs": [] + } + }, + { + "id": 385, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 386, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 386, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 4, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 387, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 388 + } + }, + "docs": [] + } + }, + { + "id": 388, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 389, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 389, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 5, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 390, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 391 + } + }, + "docs": [] + } + }, + { + "id": 391, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 392, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 392, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 6, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 393, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 394 + } + }, + "docs": [] + } + }, + { + "id": 394, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 395, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 395, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 7, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 396, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 397 + } + }, + "docs": [] + } + }, + { + "id": 397, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 398, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 398, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 8, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 399, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 400 + } + }, + "docs": [] + } + }, + { + "id": 400, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 401, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 401, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 9, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 402, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 403 + } + }, + "docs": [] + } + }, + { + "id": 403, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 404, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 404, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 10, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 405, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 406 + } + }, + "docs": [] + } + }, + { + "id": 406, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 407, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 407, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 11, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 408, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 409 + } + }, + "docs": [] + } + }, + { + "id": 409, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 410, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 410, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 12, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 411, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 412 + } + }, + "docs": [] + } + }, + { + "id": 412, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 413, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 413, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 13, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 414, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 415 + } + }, + "docs": [] + } + }, + { + "id": 415, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 416, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 416, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 14, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 417, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 418 + } + }, + "docs": [] + } + }, + { + "id": 418, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 419, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 419, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 15, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 420, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 421 + } + }, + "docs": [] + } + }, + { + "id": 421, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 422, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 422, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 16, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 423, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 424 + } + }, + "docs": [] + } + }, + { + "id": 424, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 425, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 425, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 17, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 426, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 427 + } + }, + "docs": [] + } + }, + { + "id": 427, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 428, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 428, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 18, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 429, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 430 + } + }, + "docs": [] + } + }, + { + "id": 430, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 431, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 431, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 19, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 432, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 433 + } + }, + "docs": [] + } + }, + { + "id": 433, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 434, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 434, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 20, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 435, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 436 + } + }, + "docs": [] + } + }, + { + "id": 436, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 437, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 437, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 21, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 438, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 439 + } + }, + "docs": [] + } + }, + { + "id": 439, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 440, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 440, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 22, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 441, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 442 + } + }, + "docs": [] + } + }, + { + "id": 442, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 111, + 443, + 372 + ] + }, + "docs": [] + } + }, + { + "id": 443, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 23, + "type": 375 + } + }, + "docs": [] + } + }, + { + "id": 444, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 3, + "type": 6 + } + }, + "docs": [] + } + }, + { + "id": 445, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "SolutionOrSnapshotSize" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "voters", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "targets", + "type": 111, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 446, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 444 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 444, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 447, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 448 + } + }, + "docs": [] + } + }, + { + "id": 448, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 449 + ] + }, + "docs": [] + } + }, + { + "id": 449, + "type": { + "path": [ + "sp_npos_elections", + "Support" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "total", + "type": 6, + "typeName": "ExtendedBalance", + "docs": [] + }, + { + "name": "voters", + "type": 58, + "typeName": "Vec<(AccountId, ExtendedBalance)>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 450, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "place_bid", + "fields": [ + { + "name": "amount", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "duration", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Place a bid for a gilt to be issued.", + "", + "Origin must be Signed, and account must have at least `amount` in free balance.", + "", + "- `amount`: The amount of the bid; these funds will be reserved. If the bid is", + "successfully elevated into an issued gilt, then these funds will continue to be", + "reserved until the gilt expires. Must be at least `MinFreeze`.", + "- `duration`: The number of periods for which the funds will be locked if the gilt is", + "issued. It will expire only after this period has elapsed after the point of issuance.", + "Must be greater than 1 and no more than `QueueCount`.", + "", + "Complexities:", + "- `Queues[duration].len()` (just take max)." + ] + }, + { + "name": "retract_bid", + "fields": [ + { + "name": "amount", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "duration", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Retract a previously placed bid.", + "", + "Origin must be Signed, and the account should have previously issued a still-active bid", + "of `amount` for `duration`.", + "", + "- `amount`: The amount of the previous bid.", + "- `duration`: The duration of the previous bid." + ] + }, + { + "name": "set_target", + "fields": [ + { + "name": "target", + "type": 451, + "typeName": "Perquintill", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Set target proportion of gilt-funds.", + "", + "Origin must be `AdminOrigin`.", + "", + "- `target`: The target proportion of effective issued funds that should be under gilts", + "at any one time." + ] + }, + { + "name": "thaw", + "fields": [ + { + "name": "index", + "type": 111, + "typeName": "ActiveIndex", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Remove an active but expired gilt. Reserved funds under gilt are freed and balance is", + "adjusted to ensure that the funds grow or shrink to maintain the equivalent proportion", + "of effective total issued funds.", + "", + "Origin must be Signed and the account must be the owner of the gilt of the given index.", + "", + "- `index`: The index of the gilt to be thawed." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 451, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 452 + } + }, + "docs": [] + } + }, + { + "id": 452, + "type": { + "path": [ + "sp_arithmetic", + "per_things", + "Perquintill" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 8, + "typeName": "u64", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 453, + "type": { + "path": [ + "pallet_bags_list", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "rebag", + "fields": [ + { + "name": "dislocated", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Declare that some `dislocated` account has, through rewards or penalties, sufficiently", + "changed its weight that it should properly fall into a different bag than its current", + "one.", + "", + "Anyone can call this function about any potentially dislocated account.", + "", + "Will never return an error; if `dislocated` does not exist or doesn't need a rebag, then", + "it is a noop and fees are still collected from `origin`." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 454, + "type": { + "path": [ + "polkadot_runtime_parachains", + "configuration", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "set_validation_upgrade_frequency", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Set the validation upgrade frequency." + ] + }, + { + "name": "set_validation_upgrade_delay", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Set the validation upgrade delay." + ] + }, + { + "name": "set_code_retention_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Set the acceptance period for an included candidate." + ] + }, + { + "name": "set_max_code_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Set the max validation code size for incoming upgrades." + ] + }, + { + "name": "set_max_pov_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Set the max POV block size for incoming upgrades." + ] + }, + { + "name": "set_max_head_data_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Set the max head data size for paras." + ] + }, + { + "name": "set_parathread_cores", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Set the number of parathread execution cores." + ] + }, + { + "name": "set_parathread_retries", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Set the number of retries for a particular parathread." + ] + }, + { + "name": "set_group_rotation_frequency", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 8, + "docs": [ + "Set the parachain validator-group rotation frequency" + ] + }, + { + "name": "set_chain_availability_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 9, + "docs": [ + "Set the availability period for parachains." + ] + }, + { + "name": "set_thread_availability_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 10, + "docs": [ + "Set the availability period for parathreads." + ] + }, + { + "name": "set_scheduling_lookahead", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 11, + "docs": [ + "Set the scheduling lookahead, in expected number of blocks at peak throughput." + ] + }, + { + "name": "set_max_validators_per_core", + "fields": [ + { + "name": "new", + "type": 232, + "typeName": "Option", + "docs": [] + } + ], + "index": 12, + "docs": [ + "Set the maximum number of validators to assign to any core." + ] + }, + { + "name": "set_max_validators", + "fields": [ + { + "name": "new", + "type": 232, + "typeName": "Option", + "docs": [] + } + ], + "index": 13, + "docs": [ + "Set the maximum number of validators to use in parachain consensus." + ] + }, + { + "name": "set_dispute_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + } + ], + "index": 14, + "docs": [ + "Set the dispute period, in number of sessions to keep for disputes." + ] + }, + { + "name": "set_dispute_post_conclusion_acceptance_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 15, + "docs": [ + "Set the dispute post conclusion acceptance period." + ] + }, + { + "name": "set_dispute_max_spam_slots", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 16, + "docs": [ + "Set the maximum number of dispute spam slots." + ] + }, + { + "name": "set_dispute_conclusion_by_time_out_period", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 17, + "docs": [ + "Set the dispute conclusion by time out period." + ] + }, + { + "name": "set_no_show_slots", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 18, + "docs": [ + "Set the no show slots, in number of number of consensus slots.", + "Must be at least 1." + ] + }, + { + "name": "set_n_delay_tranches", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 19, + "docs": [ + "Set the total number of delay tranches." + ] + }, + { + "name": "set_zeroth_delay_tranche_width", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 20, + "docs": [ + "Set the zeroth delay tranche width." + ] + }, + { + "name": "set_needed_approvals", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 21, + "docs": [ + "Set the number of validators needed to approve a block." + ] + }, + { + "name": "set_relay_vrf_modulo_samples", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 22, + "docs": [ + "Set the number of samples to do of the `RelayVRFModulo` approval assignment criterion." + ] + }, + { + "name": "set_max_upward_queue_count", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 23, + "docs": [ + "Sets the maximum items that can present in a upward dispatch queue at once." + ] + }, + { + "name": "set_max_upward_queue_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 24, + "docs": [ + "Sets the maximum total size of items that can present in a upward dispatch queue at once." + ] + }, + { + "name": "set_max_downward_message_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 25, + "docs": [ + "Set the critical downward message size." + ] + }, + { + "name": "set_ump_service_total_weight", + "fields": [ + { + "name": "new", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 26, + "docs": [ + "Sets the soft limit for the phase of dispatching dispatchable upward messages." + ] + }, + { + "name": "set_max_upward_message_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 27, + "docs": [ + "Sets the maximum size of an upward message that can be sent by a candidate." + ] + }, + { + "name": "set_max_upward_message_num_per_candidate", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 28, + "docs": [ + "Sets the maximum number of messages that a candidate can contain." + ] + }, + { + "name": "set_hrmp_open_request_ttl", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 29, + "docs": [ + "Sets the number of sessions after which an HRMP open channel request expires." + ] + }, + { + "name": "set_hrmp_sender_deposit", + "fields": [ + { + "name": "new", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 30, + "docs": [ + "Sets the amount of funds that the sender should provide for opening an HRMP channel." + ] + }, + { + "name": "set_hrmp_recipient_deposit", + "fields": [ + { + "name": "new", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 31, + "docs": [ + "Sets the amount of funds that the recipient should provide for accepting opening an HRMP", + "channel." + ] + }, + { + "name": "set_hrmp_channel_max_capacity", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 32, + "docs": [ + "Sets the maximum number of messages allowed in an HRMP channel at once." + ] + }, + { + "name": "set_hrmp_channel_max_total_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 33, + "docs": [ + "Sets the maximum total size of messages in bytes allowed in an HRMP channel at once." + ] + }, + { + "name": "set_hrmp_max_parachain_inbound_channels", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 34, + "docs": [ + "Sets the maximum number of inbound HRMP channels a parachain is allowed to accept." + ] + }, + { + "name": "set_hrmp_max_parathread_inbound_channels", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 35, + "docs": [ + "Sets the maximum number of inbound HRMP channels a parathread is allowed to accept." + ] + }, + { + "name": "set_hrmp_channel_max_message_size", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 36, + "docs": [ + "Sets the maximum size of a message that could ever be put into an HRMP channel." + ] + }, + { + "name": "set_hrmp_max_parachain_outbound_channels", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 37, + "docs": [ + "Sets the maximum number of outbound HRMP channels a parachain is allowed to open." + ] + }, + { + "name": "set_hrmp_max_parathread_outbound_channels", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 38, + "docs": [ + "Sets the maximum number of outbound HRMP channels a parathread is allowed to open." + ] + }, + { + "name": "set_hrmp_max_message_num_per_candidate", + "fields": [ + { + "name": "new", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 39, + "docs": [ + "Sets the maximum number of outbound HRMP messages can be sent by a candidate." + ] + }, + { + "name": "set_ump_max_individual_weight", + "fields": [ + { + "name": "new", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 40, + "docs": [ + "Sets the maximum amount of weight any individual upward message may consume." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 455, + "type": { + "path": [ + "polkadot_runtime_parachains", + "shared", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 456, + "type": { + "path": [ + "polkadot_runtime_parachains", + "inclusion", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 457, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras_inherent", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "enter", + "fields": [ + { + "name": "data", + "type": 458, + "typeName": "ParachainsInherentData", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Enter the paras inherent. This will process bitfields and backed candidates." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 458, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "InherentData" + ], + "params": [ + { + "name": "HDR", + "type": 178 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "bitfields", + "type": 459, + "typeName": "UncheckedSignedAvailabilityBitfields", + "docs": [] + }, + { + "name": "backed_candidates", + "type": 466, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "disputes", + "type": 476, + "typeName": "MultiDisputeStatementSet", + "docs": [] + }, + { + "name": "parent_header", + "type": 178, + "typeName": "HDR", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 459, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 460 + } + }, + "docs": [] + } + }, + { + "id": 460, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "signed", + "UncheckedSigned" + ], + "params": [ + { + "name": "Payload", + "type": 461 + }, + { + "name": "RealPayload", + "type": 461 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "payload", + "type": 461, + "typeName": "Payload", + "docs": [] + }, + { + "name": "validator_index", + "type": 464, + "typeName": "ValidatorIndex", + "docs": [] + }, + { + "name": "signature", + "type": 465, + "typeName": "ValidatorSignature", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 461, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "AvailabilityBitfield" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 462, + "typeName": "BitVec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 462, + "type": { + "path": [], + "params": [], + "def": { + "bitSequence": { + "bitStoreType": 2, + "bitOrderType": 463 + } + }, + "docs": [] + } + }, + { + "id": 463, + "type": { + "path": [ + "bitvec", + "order", + "Lsb0" + ], + "params": [], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 464, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "ValidatorIndex" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 465, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "validator_app", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 91, + "typeName": "sr25519::Signature", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 466, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 467 + } + }, + "docs": [] + } + }, + { + "id": 467, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "BackedCandidate" + ], + "params": [ + { + "name": "H", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "candidate", + "type": 468, + "typeName": "CommittedCandidateReceipt", + "docs": [] + }, + { + "name": "validity_votes", + "type": 474, + "typeName": "Vec", + "docs": [] + }, + { + "name": "validator_indices", + "type": 462, + "typeName": "BitVec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 468, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CommittedCandidateReceipt" + ], + "params": [ + { + "name": "H", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "descriptor", + "type": 87, + "typeName": "CandidateDescriptor", + "docs": [] + }, + { + "name": "commitments", + "type": 469, + "typeName": "CandidateCommitments", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 469, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CandidateCommitments" + ], + "params": [ + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "upward_messages", + "type": 151, + "typeName": "Vec", + "docs": [] + }, + { + "name": "horizontal_messages", + "type": 470, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "new_validation_code", + "type": 472, + "typeName": "Option", + "docs": [] + }, + { + "name": "head_data", + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": "processed_downward_messages", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_watermark", + "type": 4, + "typeName": "N", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 470, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 471 + } + }, + "docs": [] + } + }, + { + "id": 471, + "type": { + "path": [ + "polkadot_core_primitives", + "OutboundHrmpMessage" + ], + "params": [ + { + "name": "Id", + "type": 88 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "recipient", + "type": 88, + "typeName": "Id", + "docs": [] + }, + { + "name": "data", + "type": 10, + "typeName": "sp_std::vec::Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 472, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 473 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 473, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 473, + "type": { + "path": [ + "polkadot_parachain", + "primitives", + "ValidationCode" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 474, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 475 + } + }, + "docs": [] + } + }, + { + "id": 475, + "type": { + "path": [ + "polkadot_primitives", + "v0", + "ValidityAttestation" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Implicit", + "fields": [ + { + "name": null, + "type": 465, + "typeName": "ValidatorSignature", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Explicit", + "fields": [ + { + "name": null, + "type": 465, + "typeName": "ValidatorSignature", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 476, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 477 + } + }, + "docs": [] + } + }, + { + "id": 477, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "DisputeStatementSet" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "candidate_hash", + "type": 478, + "typeName": "CandidateHash", + "docs": [] + }, + { + "name": "session", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + }, + { + "name": "statements", + "type": 479, + "typeName": "Vec<(DisputeStatement, ValidatorIndex, ValidatorSignature)>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 478, + "type": { + "path": [ + "polkadot_core_primitives", + "CandidateHash" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 479, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 480 + } + }, + "docs": [] + } + }, + { + "id": 480, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 481, + 464, + 465 + ] + }, + "docs": [] + } + }, + { + "id": 481, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "DisputeStatement" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Valid", + "fields": [ + { + "name": null, + "type": 482, + "typeName": "ValidDisputeStatementKind", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Invalid", + "fields": [ + { + "name": null, + "type": 483, + "typeName": "InvalidDisputeStatementKind", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 482, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "ValidDisputeStatementKind" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Explicit", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "BackingSeconded", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "BackingValid", + "fields": [ + { + "name": null, + "type": 9, + "typeName": "Hash", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "ApprovalChecking", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 483, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "InvalidDisputeStatementKind" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Explicit", + "fields": [], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 484, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "force_set_current_code", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "new_code", + "type": 473, + "typeName": "ValidationCode", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Set the storage for the parachain validation code immediately." + ] + }, + { + "name": "force_set_current_head", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "new_head", + "type": 94, + "typeName": "HeadData", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Set the storage for the current parachain head data immediately." + ] + }, + { + "name": "force_schedule_code_upgrade", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "new_code", + "type": 473, + "typeName": "ValidationCode", + "docs": [] + }, + { + "name": "relay_parent_number", + "type": 4, + "typeName": "T::BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Schedule an upgrade as if it was scheduled in the given relay parent block." + ] + }, + { + "name": "force_note_new_head", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "new_head", + "type": 94, + "typeName": "HeadData", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Note a new block head for para within the context of the current block." + ] + }, + { + "name": "force_queue_action", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Put a parachain directly into the next session's action queue.", + "We can't queue it any sooner than this without going into the", + "initializer..." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 485, + "type": { + "path": [ + "polkadot_runtime_parachains", + "initializer", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "force_approve", + "fields": [ + { + "name": "up_to", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Issue a signal to the consensus engine to forcibly act as though all parachain", + "blocks in all relay chain blocks up to and including the given number in the current", + "chain are valid and should be finalized." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 486, + "type": { + "path": [ + "polkadot_runtime_parachains", + "dmp", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 487, + "type": { + "path": [ + "polkadot_runtime_parachains", + "ump", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "service_overweight", + "fields": [ + { + "name": "index", + "type": 8, + "typeName": "OverweightIndex", + "docs": [] + }, + { + "name": "weight_limit", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Service a single overweight upward message.", + "", + "- `origin`: Must pass `ExecuteOverweightOrigin`.", + "- `index`: The index of the overweight message to service.", + "- `weight_limit`: The amount of weight that message execution may take.", + "", + "Errors:", + "- `UnknownMessageIndex`: Message of `index` is unknown.", + "- `WeightOverLimit`: Message execution may use greater than `weight_limit`.", + "", + "Events:", + "- `OverweightServiced`: On success." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 488, + "type": { + "path": [ + "polkadot_runtime_parachains", + "hrmp", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "hrmp_init_open_channel", + "fields": [ + { + "name": "recipient", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "proposed_max_capacity", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "proposed_max_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Initiate opening a channel from a parachain to a given recipient with given channel", + "parameters.", + "", + "- `proposed_max_capacity` - specifies how many messages can be in the channel at once.", + "- `proposed_max_message_size` - specifies the maximum size of the messages.", + "", + "These numbers are a subject to the relay-chain configuration limits.", + "", + "The channel can be opened only after the recipient confirms it and only on a session", + "change." + ] + }, + { + "name": "hrmp_accept_open_channel", + "fields": [ + { + "name": "sender", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Accept a pending open channel request from the given sender.", + "", + "The channel will be opened only on the next session boundary." + ] + }, + { + "name": "hrmp_close_channel", + "fields": [ + { + "name": "channel_id", + "type": 102, + "typeName": "HrmpChannelId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Initiate unilateral closing of a channel. The origin must be either the sender or the", + "recipient in the channel being closed.", + "", + "The closure can only happen on a session change." + ] + }, + { + "name": "force_clean_hrmp", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "This extrinsic triggers the cleanup of all the HRMP storage items that", + "a para may have. Normally this happens once per session, but this allows", + "you to trigger the cleanup immediately for a specific parachain.", + "", + "Origin must be Root." + ] + }, + { + "name": "force_process_hrmp_open", + "fields": [], + "index": 4, + "docs": [ + "Force process HRMP open channel requests.", + "", + "If there are pending HRMP open channel requests, you can use this", + "function process all of those requests immediately." + ] + }, + { + "name": "force_process_hrmp_close", + "fields": [], + "index": 5, + "docs": [ + "Force process HRMP close channel requests.", + "", + "If there are pending HRMP close channel requests, you can use this", + "function process all of those requests immediately." + ] + }, + { + "name": "hrmp_cancel_open_request", + "fields": [ + { + "name": "channel_id", + "type": 102, + "typeName": "HrmpChannelId", + "docs": [] + } + ], + "index": 6, + "docs": [ + "This cancels a pending open channel request. It can be canceled be either of the sender", + "or the recipient for that request. The origin must be either of those.", + "", + "The cancelling happens immediately. It is not possible to cancel the request if it is", + "already accepted." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 489, + "type": { + "path": [ + "polkadot_runtime_common", + "paras_registrar", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "register", + "fields": [ + { + "name": "id", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "genesis_head", + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": "validation_code", + "type": 473, + "typeName": "ValidationCode", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Register head data and validation code for a reserved Para Id.", + "", + "## Arguments", + "- `origin`: Must be called by a `Signed` origin.", + "- `id`: The para ID. Must be owned/managed by the `origin` signing account.", + "- `genesis_head`: The genesis head data of the parachain/thread.", + "- `validation_code`: The initial validation code of the parachain/thread.", + "", + "## Deposits/Fees", + "The origin signed account must reserve a corresponding deposit for the registration. Anything already", + "reserved previously for this para ID is accounted for.", + "", + "## Events", + "The `Registered` event is emitted in case of success." + ] + }, + { + "name": "force_register", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "id", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "genesis_head", + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": "validation_code", + "type": 473, + "typeName": "ValidationCode", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Force the registration of a Para Id on the relay chain.", + "", + "This function must be called by a Root origin.", + "", + "The deposit taken can be specified for this registration. Any `ParaId`", + "can be registered, including sub-1000 IDs which are System Parachains." + ] + }, + { + "name": "deregister", + "fields": [ + { + "name": "id", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Deregister a Para Id, freeing all data and returning any deposit.", + "", + "The caller must be Root, the `para` owner, or the `para` itself. The para must be a parathread." + ] + }, + { + "name": "swap", + "fields": [ + { + "name": "id", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "other", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Swap a parachain with another parachain or parathread.", + "", + "The origin must be Root, the `para` owner, or the `para` itself.", + "", + "The swap will happen only if there is already an opposite swap pending. If there is not,", + "the swap will be stored in the pending swaps map, ready for a later confirmatory swap.", + "", + "The `ParaId`s remain mapped to the same head data and code so external code can rely on", + "`ParaId` to be a long-term identifier of a notional \"parachain\". However, their", + "scheduling info (i.e. whether they're a parathread or parachain), auction information", + "and the auction deposit are switched." + ] + }, + { + "name": "force_remove_lock", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Remove a manager lock from a para. This will allow the manager of a", + "previously locked para to deregister or swap a para without using governance.", + "", + "Can only be called by the Root origin." + ] + }, + { + "name": "reserve", + "fields": [], + "index": 5, + "docs": [ + "Reserve a Para Id on the relay chain.", + "", + "This function will reserve a new Para Id to be owned/managed by the origin account.", + "The origin account is able to register head data and validation code using `register` to create", + "a parathread. Using the Slots pallet, a parathread can then be upgraded to get a parachain slot.", + "", + "## Arguments", + "- `origin`: Must be called by a `Signed` origin. Becomes the manager/owner of the new para ID.", + "", + "## Deposits/Fees", + "The origin must reserve a deposit of `ParaDeposit` for the registration.", + "", + "## Events", + "The `Reserved` event is emitted in case of success, which provides the ID reserved for use." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 490, + "type": { + "path": [ + "polkadot_runtime_common", + "slots", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "force_lease", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "leaser", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "amount", + "type": 6, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "period_begin", + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "period_count", + "type": 4, + "typeName": "LeasePeriodOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Just a connect into the `lease_out` call, in case Root wants to force some lease to happen", + "independently of any other on-chain mechanism to use it.", + "", + "Can only be called by the Root origin." + ] + }, + { + "name": "clear_all_leases", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Clear all leases for a Para Id, refunding any deposits back to the original owners.", + "", + "Can only be called by the Root origin." + ] + }, + { + "name": "trigger_onboard", + "fields": [ + { + "name": "para", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Try to onboard a parachain that has a lease for the current lease period.", + "", + "This function can be useful if there was some state issue with a para that should", + "have onboarded, but was unable to. As long as they have a lease period, we can", + "let them onboard from here.", + "", + "Origin must be signed, but can be called by anyone." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 491, + "type": { + "path": [ + "polkadot_runtime_common", + "auctions", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "new_auction", + "fields": [ + { + "name": "duration", + "type": 111, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "lease_period_index", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Create a new auction.", + "", + "This can only happen when there isn't already an auction in progress and may only be", + "called by the root origin. Accepts the `duration` of this auction and the", + "`lease_period_index` of the initial lease period of the four that are to be auctioned." + ] + }, + { + "name": "bid", + "fields": [ + { + "name": "para", + "type": 492, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "auction_index", + "type": 111, + "typeName": "AuctionIndex", + "docs": [] + }, + { + "name": "first_slot", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "last_slot", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "amount", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Make a new bid from an account (including a parachain account) for deploying a new", + "parachain.", + "", + "Multiple simultaneous bids from the same bidder are allowed only as long as all active", + "bids overlap each other (i.e. are mutually exclusive). Bids cannot be redacted.", + "", + "- `sub` is the sub-bidder ID, allowing for multiple competing bids to be made by (and", + "funded by) the same account.", + "- `auction_index` is the index of the auction to bid on. Should just be the present", + "value of `AuctionCounter`.", + "- `first_slot` is the first lease period index of the range to bid on. This is the", + "absolute lease period index value, not an auction-specific offset.", + "- `last_slot` is the last lease period index of the range to bid on. This is the", + "absolute lease period index value, not an auction-specific offset.", + "- `amount` is the amount to bid to be held as deposit for the parachain should the", + "bid win. This amount is held throughout the range." + ] + }, + { + "name": "cancel_auction", + "fields": [], + "index": 2, + "docs": [ + "Cancel an in-progress auction.", + "", + "Can only be called by Root origin." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 492, + "type": { + "path": [], + "params": [], + "def": { + "compact": { + "type": 88 + } + }, + "docs": [] + } + }, + { + "id": 493, + "type": { + "path": [ + "polkadot_runtime_common", + "crowdloan", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "create", + "fields": [ + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "cap", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "first_period", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "last_period", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "end", + "type": 111, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "verifier", + "type": 494, + "typeName": "Option", + "docs": [] + } + ], + "index": 0, + "docs": [ + "Create a new crowdloaning campaign for a parachain slot with the given lease period range.", + "", + "This applies a lock to your parachain configuration, ensuring that it cannot be changed", + "by the parachain manager." + ] + }, + { + "name": "contribute", + "fields": [ + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "value", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "signature", + "type": 498, + "typeName": "Option", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Contribute to a crowd sale. This will transfer some balance over to fund a parachain", + "slot. It will be withdrawable when the crowdloan has ended and the funds are unused." + ] + }, + { + "name": "withdraw", + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Withdraw full balance of a specific contributor.", + "", + "Origin must be signed, but can come from anyone.", + "", + "The fund must be either in, or ready for, retirement. For a fund to be *in* retirement, then the retirement", + "flag must be set. For a fund to be ready for retirement, then:", + "- it must not already be in retirement;", + "- the amount of raised funds must be bigger than the _free_ balance of the account;", + "- and either:", + " - the block number must be at least `end`; or", + " - the current lease period must be greater than the fund's `last_period`.", + "", + "In this case, the fund's retirement flag is set and its `end` is reset to the current block", + "number.", + "", + "- `who`: The account whose contribution should be withdrawn.", + "- `index`: The parachain to whose crowdloan the contribution was made." + ] + }, + { + "name": "refund", + "fields": [ + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Automatically refund contributors of an ended crowdloan.", + "Due to weight restrictions, this function may need to be called multiple", + "times to fully refund all users. We will refund `RemoveKeysLimit` users at a time.", + "", + "Origin must be signed, but can come from anyone." + ] + }, + { + "name": "dissolve", + "fields": [ + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Remove a fund after the retirement period has ended and all funds have been returned." + ] + }, + { + "name": "edit", + "fields": [ + { + "name": "index", + "type": 492, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "cap", + "type": 46, + "typeName": "BalanceOf", + "docs": [] + }, + { + "name": "first_period", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "last_period", + "type": 111, + "typeName": "LeasePeriodOf", + "docs": [] + }, + { + "name": "end", + "type": 111, + "typeName": "T::BlockNumber", + "docs": [] + }, + { + "name": "verifier", + "type": 494, + "typeName": "Option", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Edit the configuration for an in-progress crowdloan.", + "", + "Can only be called by Root origin." + ] + }, + { + "name": "add_memo", + "fields": [ + { + "name": "index", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "memo", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Add an optional memo to an existing crowdloan contribution.", + "", + "Origin must be Signed, and the user must have contributed to the crowdloan." + ] + }, + { + "name": "poke", + "fields": [ + { + "name": "index", + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Poke the fund into `NewRaise`", + "", + "Origin must be Signed, and the fund has non-zero raise." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 494, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 495 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 495, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 495, + "type": { + "path": [ + "sp_runtime", + "MultiSigner" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Ed25519", + "fields": [ + { + "name": null, + "type": 39, + "typeName": "ed25519::Public", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Sr25519", + "fields": [ + { + "name": null, + "type": 42, + "typeName": "sr25519::Public", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Ecdsa", + "fields": [ + { + "name": null, + "type": 496, + "typeName": "ecdsa::Public", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 496, + "type": { + "path": [ + "sp_core", + "ecdsa", + "Public" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 497, + "typeName": "[u8; 33]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 497, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 33, + "type": 2 + } + }, + "docs": [] + } + }, + { + "id": 498, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 499 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 499, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 499, + "type": { + "path": [ + "sp_runtime", + "MultiSignature" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Ed25519", + "fields": [ + { + "name": null, + "type": 256, + "typeName": "ed25519::Signature", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Sr25519", + "fields": [ + { + "name": null, + "type": 91, + "typeName": "sr25519::Signature", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Ecdsa", + "fields": [ + { + "name": null, + "type": 500, + "typeName": "ecdsa::Signature", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 500, + "type": { + "path": [ + "sp_core", + "ecdsa", + "Signature" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 307, + "typeName": "[u8; 65]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 501, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "Call" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "send", + "fields": [ + { + "name": "dest", + "type": 141, + "typeName": "Box", + "docs": [] + }, + { + "name": "message", + "type": 502, + "typeName": "Box>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "teleport_assets", + "fields": [ + { + "name": "dest", + "type": 141, + "typeName": "Box", + "docs": [] + }, + { + "name": "beneficiary", + "type": 141, + "typeName": "Box", + "docs": [] + }, + { + "name": "assets", + "type": 136, + "typeName": "Box", + "docs": [] + }, + { + "name": "fee_asset_item", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [ + "Teleport some assets from the local chain to some destination chain.", + "", + "Fee payment on the destination side is made from the first asset listed in the `assets` vector.", + "", + "- `origin`: Must be capable of withdrawing the `assets` and executing XCM.", + "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send", + " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain.", + "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be", + " an `AccountId32` value.", + "- `assets`: The assets to be withdrawn. The first item should be the currency used to to pay the fee on the", + " `dest` side. May not be empty.", + "- `dest_weight`: Equal to the total weight on `dest` of the XCM message", + " `Teleport { assets, effects: [ BuyExecution{..}, DepositAsset{..} ] }`." + ] + }, + { + "name": "reserve_transfer_assets", + "fields": [ + { + "name": "dest", + "type": 141, + "typeName": "Box", + "docs": [] + }, + { + "name": "beneficiary", + "type": 141, + "typeName": "Box", + "docs": [] + }, + { + "name": "assets", + "type": 136, + "typeName": "Box", + "docs": [] + }, + { + "name": "fee_asset_item", + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 2, + "docs": [ + "Transfer some assets from the local chain to the sovereign account of a destination chain and forward", + "a notification XCM.", + "", + "Fee payment on the destination side is made from the first asset listed in the `assets` vector.", + "", + "- `origin`: Must be capable of withdrawing the `assets` and executing XCM.", + "- `dest`: Destination context for the assets. Will typically be `X2(Parent, Parachain(..))` to send", + " from parachain to parachain, or `X1(Parachain(..))` to send from relay to parachain.", + "- `beneficiary`: A beneficiary location for the assets in the context of `dest`. Will generally be", + " an `AccountId32` value.", + "- `assets`: The assets to be withdrawn. This should include the assets used to pay the fee on the", + " `dest` side.", + "- `fee_asset_item`: The index into `assets` of the item which should be used to pay", + " fees." + ] + }, + { + "name": "execute", + "fields": [ + { + "name": "message", + "type": 513, + "typeName": "Box::Call>>", + "docs": [] + }, + { + "name": "max_weight", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ], + "index": 3, + "docs": [ + "Execute an XCM message from a local, signed, origin.", + "", + "An event is deposited indicating whether `msg` could be executed completely or only", + "partially.", + "", + "No more than `max_weight` will be used in its attempted execution. If this is less than the", + "maximum amount of weight that the message could take to be executed, then no execution", + "attempt will be made.", + "", + "NOTE: A successful return to this does *not* imply that the `msg` was executed successfully", + "to completion; only that *some* of it was executed." + ] + }, + { + "name": "force_xcm_version", + "fields": [ + { + "name": "location", + "type": 108, + "typeName": "Box", + "docs": [] + }, + { + "name": "xcm_version", + "type": 4, + "typeName": "XcmVersion", + "docs": [] + } + ], + "index": 4, + "docs": [ + "Extoll that a particular destination can be communicated with through a particular", + "version of XCM.", + "", + "- `origin`: Must be Root.", + "- `location`: The destination that is being described.", + "- `xcm_version`: The latest version of XCM that `location` supports." + ] + }, + { + "name": "force_default_xcm_version", + "fields": [ + { + "name": "maybe_xcm_version", + "type": 232, + "typeName": "Option", + "docs": [] + } + ], + "index": 5, + "docs": [ + "Set a safe XCM version (the version that XCM should be encoded with if the most recent", + "version a destination can accept is unknown).", + "", + "- `origin`: Must be Root.", + "- `maybe_xcm_version`: The default XCM encoding version, or `None` to disable." + ] + }, + { + "name": "force_subscribe_version_notify", + "fields": [ + { + "name": "location", + "type": 141, + "typeName": "Box", + "docs": [] + } + ], + "index": 6, + "docs": [ + "Ask a location to notify us regarding their XCM version and any changes to it.", + "", + "- `origin`: Must be Root.", + "- `location`: The location to which we should subscribe for XCM version notifications." + ] + }, + { + "name": "force_unsubscribe_version_notify", + "fields": [ + { + "name": "location", + "type": 141, + "typeName": "Box", + "docs": [] + } + ], + "index": 7, + "docs": [ + "Require that a particular destination should no longer notify us regarding any XCM", + "version changes.", + "", + "- `origin`: Must be Root.", + "- `location`: The location to which we are currently subscribed for XCM version", + " notifications which we no longer desire." + ] + } + ] + } + }, + "docs": [ + "Contains one variant per dispatchable that can be called by an extrinsic." + ] + } + }, + { + "id": 502, + "type": { + "path": [ + "xcm", + "VersionedXcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [ + { + "name": null, + "type": 503, + "typeName": "v0::Xcm", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [ + { + "name": null, + "type": 508, + "typeName": "v1::Xcm", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "V2", + "fields": [ + { + "name": null, + "type": 116, + "typeName": "v2::Xcm", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 503, + "type": { + "path": [ + "xcm", + "v0", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposit", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "TeleportAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "response", + "type": 507, + "typeName": "Response", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 130, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "RelayedFrom", + "fields": [ + { + "name": "who", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "message", + "type": 503, + "typeName": "alloc::boxed::Box>", + "docs": [] + } + ], + "index": 10, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 504, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 505 + } + }, + "docs": [] + } + }, + { + "id": 505, + "type": { + "path": [ + "xcm", + "v0", + "order", + "Order" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Null", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "receive", + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "reserve", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 138, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "debt", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "halt_on_error", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "xcm", + "type": 506, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 506, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 503 + } + }, + "docs": [] + } + }, + { + "id": 507, + "type": { + "path": [ + "xcm", + "v0", + "Response" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Assets", + "fields": [ + { + "name": null, + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 508, + "type": { + "path": [ + "xcm", + "v1", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposited", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "ReceiveTeleportedAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "response", + "type": 512, + "typeName": "Response", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 130, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "RelayedFrom", + "fields": [ + { + "name": "who", + "type": 109, + "typeName": "InteriorMultiLocation", + "docs": [] + }, + { + "name": "message", + "type": 508, + "typeName": "alloc::boxed::Box>", + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "SubscribeVersion", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "UnsubscribeVersion", + "fields": [], + "index": 12, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 509, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 510 + } + }, + "docs": [] + } + }, + { + "id": 510, + "type": { + "path": [ + "xcm", + "v1", + "order", + "Order" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Noop", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "receive", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "reserve", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 121, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "debt", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "halt_on_error", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "instructions", + "type": 511, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 511, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 508 + } + }, + "docs": [] + } + }, + { + "id": 512, + "type": { + "path": [ + "xcm", + "v1", + "Response" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Assets", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Version", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "super::Version", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 513, + "type": { + "path": [ + "xcm", + "VersionedXcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [ + { + "name": null, + "type": 514, + "typeName": "v0::Xcm", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [ + { + "name": null, + "type": 519, + "typeName": "v1::Xcm", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "V2", + "fields": [ + { + "name": null, + "type": 523, + "typeName": "v2::Xcm", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 514, + "type": { + "path": [ + "xcm", + "v0", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 515, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposit", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 515, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "TeleportAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "effects", + "type": 515, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "response", + "type": 507, + "typeName": "Response", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 518, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "RelayedFrom", + "fields": [ + { + "name": "who", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "message", + "type": 514, + "typeName": "alloc::boxed::Box>", + "docs": [] + } + ], + "index": 10, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 515, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 516 + } + }, + "docs": [] + } + }, + { + "id": 516, + "type": { + "path": [ + "xcm", + "v0", + "order", + "Order" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Null", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "receive", + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "reserve", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 504, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "dest", + "type": 139, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 137, + "typeName": "Vec", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 138, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "debt", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "halt_on_error", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "xcm", + "type": 517, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 517, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 514 + } + }, + "docs": [] + } + }, + { + "id": 518, + "type": { + "path": [ + "xcm", + "double_encoded", + "DoubleEncoded" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "encoded", + "type": 10, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 519, + "type": { + "path": [ + "xcm", + "v1", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 520, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposited", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 520, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "ReceiveTeleportedAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "effects", + "type": 520, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "response", + "type": 512, + "typeName": "Response", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 518, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "RelayedFrom", + "fields": [ + { + "name": "who", + "type": 109, + "typeName": "InteriorMultiLocation", + "docs": [] + }, + { + "name": "message", + "type": 519, + "typeName": "alloc::boxed::Box>", + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "SubscribeVersion", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "UnsubscribeVersion", + "fields": [], + "index": 12, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 520, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 521 + } + }, + "docs": [] + } + }, + { + "id": 521, + "type": { + "path": [ + "xcm", + "v1", + "order", + "Order" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Noop", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "receive", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "reserve", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "effects", + "type": 509, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 121, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "debt", + "type": 8, + "typeName": "u64", + "docs": [] + }, + { + "name": "halt_on_error", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "instructions", + "type": 522, + "typeName": "Vec>", + "docs": [] + } + ], + "index": 7, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 522, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 519 + } + }, + "docs": [] + } + }, + { + "id": 523, + "type": { + "path": [ + "xcm", + "v2", + "Xcm" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 524, + "typeName": "Vec>", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 524, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 525 + } + }, + "docs": [] + } + }, + { + "id": 525, + "type": { + "path": [ + "xcm", + "v2", + "Instruction" + ], + "params": [ + { + "name": "Call", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WithdrawAsset", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "ReserveAssetDeposited", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "ReceiveTeleportedAsset", + "fields": [ + { + "name": null, + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "QueryResponse", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "response", + "type": 126, + "typeName": "Response", + "docs": [] + }, + { + "name": "max_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "TransferAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "TransferReserveAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Transact", + "fields": [ + { + "name": "origin_type", + "type": 129, + "typeName": "OriginKind", + "docs": [] + }, + { + "name": "require_weight_at_most", + "type": 113, + "typeName": "u64", + "docs": [] + }, + { + "name": "call", + "type": 518, + "typeName": "DoubleEncoded", + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "HrmpNewChannelOpenRequest", + "fields": [ + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "HrmpChannelAccepted", + "fields": [ + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "HrmpChannelClosing", + "fields": [ + { + "name": "initiator", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "sender", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "recipient", + "type": 111, + "typeName": "u32", + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "ClearOrigin", + "fields": [], + "index": 10, + "docs": [] + }, + { + "name": "DescendOrigin", + "fields": [ + { + "name": null, + "type": 109, + "typeName": "InteriorMultiLocation", + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "ReportError", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 12, + "docs": [] + }, + { + "name": "DepositAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "beneficiary", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "DepositReserveAsset", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_assets", + "type": 111, + "typeName": "u32", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "ExchangeAsset", + "fields": [ + { + "name": "give", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "receive", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "InitiateReserveWithdraw", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "reserve", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "InitiateTeleport", + "fields": [ + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "xcm", + "type": 116, + "typeName": "Xcm<()>", + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "QueryHolding", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "dest", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + }, + { + "name": "assets", + "type": 131, + "typeName": "MultiAssetFilter", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "BuyExecution", + "fields": [ + { + "name": "fees", + "type": 121, + "typeName": "MultiAsset", + "docs": [] + }, + { + "name": "weight_limit", + "type": 134, + "typeName": "WeightLimit", + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "RefundSurplus", + "fields": [], + "index": 20, + "docs": [] + }, + { + "name": "SetErrorHandler", + "fields": [ + { + "name": null, + "type": 523, + "typeName": "Xcm", + "docs": [] + } + ], + "index": 21, + "docs": [] + }, + { + "name": "SetAppendix", + "fields": [ + { + "name": null, + "type": 523, + "typeName": "Xcm", + "docs": [] + } + ], + "index": 22, + "docs": [] + }, + { + "name": "ClearError", + "fields": [], + "index": 23, + "docs": [] + }, + { + "name": "ClaimAsset", + "fields": [ + { + "name": "assets", + "type": 119, + "typeName": "MultiAssets", + "docs": [] + }, + { + "name": "ticket", + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Trap", + "fields": [ + { + "name": null, + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "SubscribeVersion", + "fields": [ + { + "name": "query_id", + "type": 113, + "typeName": "QueryId", + "docs": [] + }, + { + "name": "max_response_weight", + "type": 113, + "typeName": "u64", + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "UnsubscribeVersion", + "fields": [], + "index": 27, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 526, + "type": { + "path": [ + "pallet_collective", + "Votes" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "index", + "type": 4, + "typeName": "ProposalIndex", + "docs": [] + }, + { + "name": "threshold", + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": "ayes", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "nays", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "end", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 527, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotMember", + "fields": [], + "index": 0, + "docs": [ + "Account is not a member" + ] + }, + { + "name": "DuplicateProposal", + "fields": [], + "index": 1, + "docs": [ + "Duplicate proposals not allowed" + ] + }, + { + "name": "ProposalMissing", + "fields": [], + "index": 2, + "docs": [ + "Proposal must exist" + ] + }, + { + "name": "WrongIndex", + "fields": [], + "index": 3, + "docs": [ + "Mismatched index" + ] + }, + { + "name": "DuplicateVote", + "fields": [], + "index": 4, + "docs": [ + "Duplicate vote ignored" + ] + }, + { + "name": "AlreadyInitialized", + "fields": [], + "index": 5, + "docs": [ + "Members are already initialized!" + ] + }, + { + "name": "TooEarly", + "fields": [], + "index": 6, + "docs": [ + "The close call was made too early, before the end of the voting." + ] + }, + { + "name": "TooManyProposals", + "fields": [], + "index": 7, + "docs": [ + "There can only be a maximum of `MaxProposals` active proposals." + ] + }, + { + "name": "WrongProposalWeight", + "fields": [], + "index": 8, + "docs": [ + "The given weight bound for the proposal was too low." + ] + }, + { + "name": "WrongProposalLength", + "fields": [], + "index": 9, + "docs": [ + "The given length bound for the proposal was too low." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 528, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 9 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 143, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 529, + "type": { + "path": [ + "pallet_collective", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotMember", + "fields": [], + "index": 0, + "docs": [ + "Account is not a member" + ] + }, + { + "name": "DuplicateProposal", + "fields": [], + "index": 1, + "docs": [ + "Duplicate proposals not allowed" + ] + }, + { + "name": "ProposalMissing", + "fields": [], + "index": 2, + "docs": [ + "Proposal must exist" + ] + }, + { + "name": "WrongIndex", + "fields": [], + "index": 3, + "docs": [ + "Mismatched index" + ] + }, + { + "name": "DuplicateVote", + "fields": [], + "index": 4, + "docs": [ + "Duplicate vote ignored" + ] + }, + { + "name": "AlreadyInitialized", + "fields": [], + "index": 5, + "docs": [ + "Members are already initialized!" + ] + }, + { + "name": "TooEarly", + "fields": [], + "index": 6, + "docs": [ + "The close call was made too early, before the end of the voting." + ] + }, + { + "name": "TooManyProposals", + "fields": [], + "index": 7, + "docs": [ + "There can only be a maximum of `MaxProposals` active proposals." + ] + }, + { + "name": "WrongProposalWeight", + "fields": [], + "index": 8, + "docs": [ + "The given weight bound for the proposal was too low." + ] + }, + { + "name": "WrongProposalLength", + "fields": [], + "index": 9, + "docs": [ + "The given length bound for the proposal was too low." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 530, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 531 + } + }, + "docs": [] + } + }, + { + "id": 531, + "type": { + "path": [ + "pallet_elections_phragmen", + "SeatHolder" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "stake", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 532, + "type": { + "path": [ + "pallet_elections_phragmen", + "Voter" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "votes", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "stake", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 533, + "type": { + "path": [ + "pallet_elections_phragmen", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "UnableToVote", + "fields": [], + "index": 0, + "docs": [ + "Cannot vote when no candidates or members exist." + ] + }, + { + "name": "NoVotes", + "fields": [], + "index": 1, + "docs": [ + "Must vote for at least one candidate." + ] + }, + { + "name": "TooManyVotes", + "fields": [], + "index": 2, + "docs": [ + "Cannot vote more than candidates." + ] + }, + { + "name": "MaximumVotesExceeded", + "fields": [], + "index": 3, + "docs": [ + "Cannot vote more than maximum allowed." + ] + }, + { + "name": "LowBalance", + "fields": [], + "index": 4, + "docs": [ + "Cannot vote with stake less than minimum balance." + ] + }, + { + "name": "UnableToPayBond", + "fields": [], + "index": 5, + "docs": [ + "Voter can not pay voting bond." + ] + }, + { + "name": "MustBeVoter", + "fields": [], + "index": 6, + "docs": [ + "Must be a voter." + ] + }, + { + "name": "ReportSelf", + "fields": [], + "index": 7, + "docs": [ + "Cannot report self." + ] + }, + { + "name": "DuplicatedCandidate", + "fields": [], + "index": 8, + "docs": [ + "Duplicated candidate submission." + ] + }, + { + "name": "MemberSubmit", + "fields": [], + "index": 9, + "docs": [ + "Member cannot re-submit candidacy." + ] + }, + { + "name": "RunnerUpSubmit", + "fields": [], + "index": 10, + "docs": [ + "Runner cannot re-submit candidacy." + ] + }, + { + "name": "InsufficientCandidateFunds", + "fields": [], + "index": 11, + "docs": [ + "Candidate does not have enough funds." + ] + }, + { + "name": "NotMember", + "fields": [], + "index": 12, + "docs": [ + "Not a member." + ] + }, + { + "name": "InvalidWitnessData", + "fields": [], + "index": 13, + "docs": [ + "The provided count of number of candidates is incorrect." + ] + }, + { + "name": "InvalidVoteCount", + "fields": [], + "index": 14, + "docs": [ + "The provided count of number of votes is incorrect." + ] + }, + { + "name": "InvalidRenouncing", + "fields": [], + "index": 15, + "docs": [ + "The renouncing origin presented a wrong `Renouncing` parameter." + ] + }, + { + "name": "InvalidReplacement", + "fields": [], + "index": 16, + "docs": [ + "Prediction regarding replacement after member removal is wrong." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 534, + "type": { + "path": [ + "pallet_membership", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "AlreadyMember", + "fields": [], + "index": 0, + "docs": [ + "Already a member." + ] + }, + { + "name": "NotMember", + "fields": [], + "index": 1, + "docs": [ + "Not a member." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 535, + "type": { + "path": [ + "pallet_treasury", + "Proposal" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "proposer", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "value", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "beneficiary", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "bond", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 536, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 4 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 211, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 537, + "type": { + "path": [ + "sp_arithmetic", + "per_things", + "Permill" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 538, + "type": { + "path": [ + "frame_support", + "PalletId" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 125, + "typeName": "[u8; 8]", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 539, + "type": { + "path": [ + "pallet_treasury", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InsufficientProposersBalance", + "fields": [], + "index": 0, + "docs": [ + "Proposer's balance is too low." + ] + }, + { + "name": "InvalidIndex", + "fields": [], + "index": 1, + "docs": [ + "No proposal or bounty at that index." + ] + }, + { + "name": "TooManyApprovals", + "fields": [], + "index": 2, + "docs": [ + "Too many approvals in the queue." + ] + } + ] + } + }, + "docs": [ + "Error for the treasury pallet." + ] + } + }, + { + "id": 540, + "type": { + "path": [ + "polkadot_runtime_common", + "claims", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidEthereumSignature", + "fields": [], + "index": 0, + "docs": [ + "Invalid Ethereum signature." + ] + }, + { + "name": "SignerHasNoClaim", + "fields": [], + "index": 1, + "docs": [ + "Ethereum address has no claim." + ] + }, + { + "name": "SenderHasNoClaim", + "fields": [], + "index": 2, + "docs": [ + "Account ID sending transaction has no claim." + ] + }, + { + "name": "PotUnderflow", + "fields": [], + "index": 3, + "docs": [ + "There's not enough in the pot to pay out some unvested amount. Generally implies a logic", + "error." + ] + }, + { + "name": "InvalidStatement", + "fields": [], + "index": 4, + "docs": [ + "A needed statement was not included." + ] + }, + { + "name": "VestedBalanceExists", + "fields": [], + "index": 5, + "docs": [ + "The account already has a vested balance." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 541, + "type": { + "path": [ + "pallet_utility", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "TooManyCalls", + "fields": [], + "index": 0, + "docs": [ + "Too many calls batched." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 542, + "type": { + "path": [ + "pallet_identity", + "types", + "Registration" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "MaxJudgements", + "type": null + }, + { + "name": "MaxAdditionalFields", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "judgements", + "type": 543, + "typeName": "BoundedVec<(RegistrarIndex, Judgement), MaxJudgements>", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "info", + "type": 315, + "typeName": "IdentityInfo", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 543, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 544 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 545, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 544, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 353 + ] + }, + "docs": [] + } + }, + { + "id": 545, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 544 + } + }, + "docs": [] + } + }, + { + "id": 546, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 6, + 547 + ] + }, + "docs": [] + } + }, + { + "id": 547, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 0 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 50, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 548, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 549 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 551, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 549, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 550 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 550, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 550, + "type": { + "path": [ + "pallet_identity", + "types", + "RegistrarInfo" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "account", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "fee", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "fields", + "type": 351, + "typeName": "IdentityFields", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 551, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 549 + } + }, + "docs": [] + } + }, + { + "id": 552, + "type": { + "path": [ + "pallet_identity", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "TooManySubAccounts", + "fields": [], + "index": 0, + "docs": [ + "Too many subs-accounts." + ] + }, + { + "name": "NotFound", + "fields": [], + "index": 1, + "docs": [ + "Account isn't found." + ] + }, + { + "name": "NotNamed", + "fields": [], + "index": 2, + "docs": [ + "Account isn't named." + ] + }, + { + "name": "EmptyIndex", + "fields": [], + "index": 3, + "docs": [ + "Empty index." + ] + }, + { + "name": "FeeChanged", + "fields": [], + "index": 4, + "docs": [ + "Fee is changed." + ] + }, + { + "name": "NoIdentity", + "fields": [], + "index": 5, + "docs": [ + "No identity found." + ] + }, + { + "name": "StickyJudgement", + "fields": [], + "index": 6, + "docs": [ + "Sticky judgement." + ] + }, + { + "name": "JudgementGiven", + "fields": [], + "index": 7, + "docs": [ + "Judgement given." + ] + }, + { + "name": "InvalidJudgement", + "fields": [], + "index": 8, + "docs": [ + "Invalid judgement." + ] + }, + { + "name": "InvalidIndex", + "fields": [], + "index": 9, + "docs": [ + "The index is invalid." + ] + }, + { + "name": "InvalidTarget", + "fields": [], + "index": 10, + "docs": [ + "The target is invalid." + ] + }, + { + "name": "TooManyFields", + "fields": [], + "index": 11, + "docs": [ + "Too many additional fields." + ] + }, + { + "name": "TooManyRegistrars", + "fields": [], + "index": 12, + "docs": [ + "Maximum amount of registrars reached. Cannot add any more." + ] + }, + { + "name": "AlreadyClaimed", + "fields": [], + "index": 13, + "docs": [ + "Account ID is already named." + ] + }, + { + "name": "NotSub", + "fields": [], + "index": 14, + "docs": [ + "Sender is not a sub-account." + ] + }, + { + "name": "NotOwned", + "fields": [], + "index": 15, + "docs": [ + "Sub-account isn't owned by sender." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 553, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 554 + } + }, + "docs": [] + } + }, + { + "id": 554, + "type": { + "path": [ + "pallet_society", + "Bid" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "kind", + "type": 555, + "typeName": "BidKind", + "docs": [] + }, + { + "name": "value", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 555, + "type": { + "path": [ + "pallet_society", + "BidKind" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Deposit", + "fields": [ + { + "name": null, + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Vouch", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": null, + "type": 6, + "typeName": "Balance", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 556, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 6, + 555 + ] + }, + "docs": [] + } + }, + { + "id": 557, + "type": { + "path": [ + "pallet_society", + "VouchingStatus" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Vouching", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Banned", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 558, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 559 + } + }, + "docs": [] + } + }, + { + "id": 559, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 560, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 0 + ] + }, + "docs": [] + } + }, + { + "id": 561, + "type": { + "path": [ + "pallet_society", + "Vote" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Skeptic", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Reject", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Approve", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 562, + "type": { + "path": [ + "pallet_society", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "BadPosition", + "fields": [], + "index": 0, + "docs": [ + "An incorrect position was provided." + ] + }, + { + "name": "NotMember", + "fields": [], + "index": 1, + "docs": [ + "User is not a member." + ] + }, + { + "name": "AlreadyMember", + "fields": [], + "index": 2, + "docs": [ + "User is already a member." + ] + }, + { + "name": "Suspended", + "fields": [], + "index": 3, + "docs": [ + "User is suspended." + ] + }, + { + "name": "NotSuspended", + "fields": [], + "index": 4, + "docs": [ + "User is not suspended." + ] + }, + { + "name": "NoPayout", + "fields": [], + "index": 5, + "docs": [ + "Nothing to payout." + ] + }, + { + "name": "AlreadyFounded", + "fields": [], + "index": 6, + "docs": [ + "Society already founded." + ] + }, + { + "name": "InsufficientPot", + "fields": [], + "index": 7, + "docs": [ + "Not enough in pot to accept candidate." + ] + }, + { + "name": "AlreadyVouching", + "fields": [], + "index": 8, + "docs": [ + "Member is already vouching or banned from vouching again." + ] + }, + { + "name": "NotVouching", + "fields": [], + "index": 9, + "docs": [ + "Member is not vouching." + ] + }, + { + "name": "Head", + "fields": [], + "index": 10, + "docs": [ + "Cannot remove the head of the chain." + ] + }, + { + "name": "Founder", + "fields": [], + "index": 11, + "docs": [ + "Cannot remove the founder." + ] + }, + { + "name": "AlreadyBid", + "fields": [], + "index": 12, + "docs": [ + "User has already made a bid." + ] + }, + { + "name": "AlreadyCandidate", + "fields": [], + "index": 13, + "docs": [ + "User is already a candidate." + ] + }, + { + "name": "NotCandidate", + "fields": [], + "index": 14, + "docs": [ + "User is not a candidate." + ] + }, + { + "name": "MaxMembers", + "fields": [], + "index": 15, + "docs": [ + "Too many members in the society." + ] + }, + { + "name": "NotFounder", + "fields": [], + "index": 16, + "docs": [ + "The caller is not the founder." + ] + }, + { + "name": "NotHead", + "fields": [], + "index": 17, + "docs": [ + "The caller is not the head." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 563, + "type": { + "path": [ + "pallet_recovery", + "RecoveryConfig" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "delay_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "friends", + "type": 50, + "typeName": "Vec", + "docs": [] + }, + { + "name": "threshold", + "type": 75, + "typeName": "u16", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 564, + "type": { + "path": [ + "pallet_recovery", + "ActiveRecovery" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "created", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "friends", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 565, + "type": { + "path": [ + "pallet_recovery", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotAllowed", + "fields": [], + "index": 0, + "docs": [ + "User is not allowed to make a call on behalf of this account" + ] + }, + { + "name": "ZeroThreshold", + "fields": [], + "index": 1, + "docs": [ + "Threshold must be greater than zero" + ] + }, + { + "name": "NotEnoughFriends", + "fields": [], + "index": 2, + "docs": [ + "Friends list must be greater than zero and threshold" + ] + }, + { + "name": "MaxFriends", + "fields": [], + "index": 3, + "docs": [ + "Friends list must be less than max friends" + ] + }, + { + "name": "NotSorted", + "fields": [], + "index": 4, + "docs": [ + "Friends list must be sorted and free of duplicates" + ] + }, + { + "name": "NotRecoverable", + "fields": [], + "index": 5, + "docs": [ + "This account is not set up for recovery" + ] + }, + { + "name": "AlreadyRecoverable", + "fields": [], + "index": 6, + "docs": [ + "This account is already set up for recovery" + ] + }, + { + "name": "AlreadyStarted", + "fields": [], + "index": 7, + "docs": [ + "A recovery process has already started for this account" + ] + }, + { + "name": "NotStarted", + "fields": [], + "index": 8, + "docs": [ + "A recovery process has not started for this rescuer" + ] + }, + { + "name": "NotFriend", + "fields": [], + "index": 9, + "docs": [ + "This account is not a friend who can vouch" + ] + }, + { + "name": "DelayPeriod", + "fields": [], + "index": 10, + "docs": [ + "The friend must wait until the delay period to vouch for this recovery" + ] + }, + { + "name": "AlreadyVouched", + "fields": [], + "index": 11, + "docs": [ + "This user has already vouched for this recovery" + ] + }, + { + "name": "Threshold", + "fields": [], + "index": 12, + "docs": [ + "The threshold for recovering this account has not been met" + ] + }, + { + "name": "StillActive", + "fields": [], + "index": 13, + "docs": [ + "There are still active recovery attempts that need to be closed" + ] + }, + { + "name": "AlreadyProxy", + "fields": [], + "index": 14, + "docs": [ + "This account is already set up for recovery" + ] + }, + { + "name": "BadState", + "fields": [], + "index": 15, + "docs": [ + "Some internal state is broken." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 566, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 358 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 567, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 567, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 358 + } + }, + "docs": [] + } + }, + { + "id": 568, + "type": { + "path": [ + "pallet_vesting", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 569, + "type": { + "path": [ + "pallet_vesting", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotVesting", + "fields": [], + "index": 0, + "docs": [ + "The account given is not vesting." + ] + }, + { + "name": "AtMaxVestingSchedules", + "fields": [], + "index": 1, + "docs": [ + "The account already has `MaxVestingSchedules` count of schedules and thus", + "cannot add another one. Consider merging existing schedules in order to add another." + ] + }, + { + "name": "AmountLow", + "fields": [], + "index": 2, + "docs": [ + "Amount being transferred is too low to create a vesting schedule." + ] + }, + { + "name": "ScheduleIndexOutOfBounds", + "fields": [], + "index": 3, + "docs": [ + "An index was out of bounds of the vesting schedules." + ] + }, + { + "name": "InvalidScheduleParams", + "fields": [], + "index": 4, + "docs": [ + "Failed to create a new schedule because some parameter was invalid." + ] + } + ] + } + }, + "docs": [ + "Error for the vesting pallet." + ] + } + }, + { + "id": 570, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 571 + } + }, + "docs": [] + } + }, + { + "id": 571, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 572 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 572, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 572, + "type": { + "path": [ + "pallet_scheduler", + "ScheduledV2" + ], + "params": [ + { + "name": "Call", + "type": 298 + }, + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "PalletsOrigin", + "type": 573 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "maybe_id", + "type": 72, + "typeName": "Option>", + "docs": [] + }, + { + "name": "priority", + "type": 2, + "typeName": "schedule::Priority", + "docs": [] + }, + { + "name": "call", + "type": 298, + "typeName": "Call", + "docs": [] + }, + { + "name": "maybe_periodic", + "type": 360, + "typeName": "Option>", + "docs": [] + }, + { + "name": "origin", + "type": 573, + "typeName": "PalletsOrigin", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 573, + "type": { + "path": [ + "kusama_runtime", + "OriginCaller" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "system", + "fields": [ + { + "name": null, + "type": 574, + "typeName": "frame_system::Origin", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Council", + "fields": [ + { + "name": null, + "type": 575, + "typeName": "pallet_collective::Origin", + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "TechnicalCommittee", + "fields": [ + { + "name": null, + "type": 576, + "typeName": "pallet_collective::Origin", + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "ParachainsOrigin", + "fields": [ + { + "name": null, + "type": 577, + "typeName": "parachains_origin::Origin", + "docs": [] + } + ], + "index": 50, + "docs": [] + }, + { + "name": "XcmPallet", + "fields": [ + { + "name": null, + "type": 578, + "typeName": "pallet_xcm::Origin", + "docs": [] + } + ], + "index": 99, + "docs": [] + }, + { + "name": "Void", + "fields": [ + { + "name": null, + "type": 579, + "typeName": "self::sp_api_hidden_includes_construct_runtime::hidden_include::Void", + "docs": [] + } + ], + "index": 5, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 574, + "type": { + "path": [ + "frame_system", + "RawOrigin" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Root", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Signed", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "None", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 575, + "type": { + "path": [ + "pallet_collective", + "RawOrigin" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Members", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Member", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "_Phantom", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 576, + "type": { + "path": [ + "pallet_collective", + "RawOrigin" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "I", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Members", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "MemberCount", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Member", + "fields": [ + { + "name": null, + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "_Phantom", + "fields": [], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 577, + "type": { + "path": [ + "polkadot_runtime_parachains", + "origin", + "pallet", + "Origin" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Parachain", + "fields": [ + { + "name": null, + "type": 88, + "typeName": "ParaId", + "docs": [] + } + ], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 578, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "Origin" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Xcm", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Response", + "fields": [ + { + "name": null, + "type": 108, + "typeName": "MultiLocation", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 579, + "type": { + "path": [ + "sp_core", + "Void" + ], + "params": [], + "def": { + "variant": { + "variants": [] + } + }, + "docs": [] + } + }, + { + "id": 580, + "type": { + "path": [ + "pallet_scheduler", + "Releases" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V1", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "V2", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 581, + "type": { + "path": [ + "pallet_scheduler", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "FailedToSchedule", + "fields": [], + "index": 0, + "docs": [ + "Failed to schedule a call" + ] + }, + { + "name": "NotFound", + "fields": [], + "index": 1, + "docs": [ + "Cannot find the scheduled call." + ] + }, + { + "name": "TargetBlockNumberInPast", + "fields": [], + "index": 2, + "docs": [ + "Given target block number is in the past." + ] + }, + { + "name": "RescheduleNoChange", + "fields": [], + "index": 3, + "docs": [ + "Reschedule failed because it does not change scheduled time." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 582, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 583, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 583, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 584 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 585, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 584, + "type": { + "path": [ + "pallet_proxy", + "ProxyDefinition" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "ProxyType", + "type": 74 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "delegate", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "proxy_type", + "type": 74, + "typeName": "ProxyType", + "docs": [] + }, + { + "name": "delay", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 585, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 584 + } + }, + "docs": [] + } + }, + { + "id": 586, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 587, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 587, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 588 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 589, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 588, + "type": { + "path": [ + "pallet_proxy", + "Announcement" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Hash", + "type": 9 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "real", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "call_hash", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "height", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 589, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 588 + } + }, + "docs": [] + } + }, + { + "id": 590, + "type": { + "path": [ + "pallet_proxy", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "TooMany", + "fields": [], + "index": 0, + "docs": [ + "There are too many proxies registered or too many announcements pending." + ] + }, + { + "name": "NotFound", + "fields": [], + "index": 1, + "docs": [ + "Proxy registration not found." + ] + }, + { + "name": "NotProxy", + "fields": [], + "index": 2, + "docs": [ + "Sender is not a proxy of the account to be proxied." + ] + }, + { + "name": "Unproxyable", + "fields": [], + "index": 3, + "docs": [ + "A call which is incompatible with the proxy type's filter was attempted." + ] + }, + { + "name": "Duplicate", + "fields": [], + "index": 4, + "docs": [ + "Account is already a proxy." + ] + }, + { + "name": "NoPermission", + "fields": [], + "index": 5, + "docs": [ + "Call may not be made by proxy because it may escalate its privileges." + ] + }, + { + "name": "Unannounced", + "fields": [], + "index": 6, + "docs": [ + "Announcement, if made at all, was made too recently." + ] + }, + { + "name": "NoSelfProxy", + "fields": [], + "index": 7, + "docs": [ + "Cannot add self as proxy." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 591, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 1 + ] + }, + "docs": [] + } + }, + { + "id": 592, + "type": { + "path": [ + "pallet_multisig", + "Multisig" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "when", + "type": 77, + "typeName": "Timepoint", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "depositor", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "approvals", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 593, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 10, + 0, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 594, + "type": { + "path": [ + "pallet_multisig", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "MinimumThreshold", + "fields": [], + "index": 0, + "docs": [ + "Threshold must be 2 or greater." + ] + }, + { + "name": "AlreadyApproved", + "fields": [], + "index": 1, + "docs": [ + "Call is already approved by this signatory." + ] + }, + { + "name": "NoApprovalsNeeded", + "fields": [], + "index": 2, + "docs": [ + "Call doesn't need any (more) approvals." + ] + }, + { + "name": "TooFewSignatories", + "fields": [], + "index": 3, + "docs": [ + "There are too few signatories in the list." + ] + }, + { + "name": "TooManySignatories", + "fields": [], + "index": 4, + "docs": [ + "There are too many signatories in the list." + ] + }, + { + "name": "SignatoriesOutOfOrder", + "fields": [], + "index": 5, + "docs": [ + "The signatories were provided out of order; they should be ordered." + ] + }, + { + "name": "SenderInSignatories", + "fields": [], + "index": 6, + "docs": [ + "The sender was contained in the other signatories; it shouldn't be." + ] + }, + { + "name": "NotFound", + "fields": [], + "index": 7, + "docs": [ + "Multisig operation not found when attempting to cancel." + ] + }, + { + "name": "NotOwner", + "fields": [], + "index": 8, + "docs": [ + "Only the account that originally created the multisig is able to cancel it." + ] + }, + { + "name": "NoTimepoint", + "fields": [], + "index": 9, + "docs": [ + "No timepoint was given, yet the multisig operation is already underway." + ] + }, + { + "name": "WrongTimepoint", + "fields": [], + "index": 10, + "docs": [ + "A different timepoint was given to the multisig operation that is underway." + ] + }, + { + "name": "UnexpectedTimepoint", + "fields": [], + "index": 11, + "docs": [ + "A timepoint was given, yet no multisig operation is underway." + ] + }, + { + "name": "MaxWeightTooLow", + "fields": [], + "index": 12, + "docs": [ + "The maximum weight information provided was too low." + ] + }, + { + "name": "AlreadyStored", + "fields": [], + "index": 13, + "docs": [ + "The data to be stored is already stored." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 595, + "type": { + "path": [ + "pallet_bounties", + "Bounty" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "proposer", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "value", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "fee", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "curator_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "bond", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "status", + "type": 596, + "typeName": "BountyStatus", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 596, + "type": { + "path": [ + "pallet_bounties", + "BountyStatus" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Proposed", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Approved", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Funded", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "CuratorProposed", + "fields": [ + { + "name": "curator", + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Active", + "fields": [ + { + "name": "curator", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "update_due", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "PendingPayout", + "fields": [ + { + "name": "curator", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "beneficiary", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "unlock_at", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 5, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 597, + "type": { + "path": [ + "pallet_bounties", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InsufficientProposersBalance", + "fields": [], + "index": 0, + "docs": [ + "Proposer's balance is too low." + ] + }, + { + "name": "InvalidIndex", + "fields": [], + "index": 1, + "docs": [ + "No proposal or bounty at that index." + ] + }, + { + "name": "ReasonTooBig", + "fields": [], + "index": 2, + "docs": [ + "The reason given is just too big." + ] + }, + { + "name": "UnexpectedStatus", + "fields": [], + "index": 3, + "docs": [ + "The bounty status is unexpected." + ] + }, + { + "name": "RequireCurator", + "fields": [], + "index": 4, + "docs": [ + "Require bounty curator." + ] + }, + { + "name": "InvalidValue", + "fields": [], + "index": 5, + "docs": [ + "Invalid bounty value." + ] + }, + { + "name": "InvalidFee", + "fields": [], + "index": 6, + "docs": [ + "Invalid bounty fee." + ] + }, + { + "name": "PendingPayout", + "fields": [], + "index": 7, + "docs": [ + "A bounty payout is pending.", + "To cancel the bounty, you must unassign and slash the curator." + ] + }, + { + "name": "Premature", + "fields": [], + "index": 8, + "docs": [ + "The bounties cannot be claimed/closed because it's still in the countdown period." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 598, + "type": { + "path": [ + "pallet_tips", + "OpenTip" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "Hash", + "type": 9 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "reason", + "type": 9, + "typeName": "Hash", + "docs": [] + }, + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "finder", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "closes", + "type": 232, + "typeName": "Option", + "docs": [] + }, + { + "name": "tips", + "type": 58, + "typeName": "Vec<(AccountId, Balance)>", + "docs": [] + }, + { + "name": "finders_fee", + "type": 55, + "typeName": "bool", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 599, + "type": { + "path": [ + "pallet_tips", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ReasonTooBig", + "fields": [], + "index": 0, + "docs": [ + "The reason given is just too big." + ] + }, + { + "name": "AlreadyKnown", + "fields": [], + "index": 1, + "docs": [ + "The tip was already found/started." + ] + }, + { + "name": "UnknownTip", + "fields": [], + "index": 2, + "docs": [ + "The tip hash is unknown." + ] + }, + { + "name": "NotFinder", + "fields": [], + "index": 3, + "docs": [ + "The account attempting to retract the tip is not the finder of the tip." + ] + }, + { + "name": "StillOpen", + "fields": [], + "index": 4, + "docs": [ + "The tip cannot be claimed/closed because there are not enough tippers yet." + ] + }, + { + "name": "Premature", + "fields": [], + "index": 5, + "docs": [ + "The tip cannot be claimed/closed because it's still in the countdown period." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 600, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "Phase" + ], + "params": [ + { + "name": "Bn", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Off", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Signed", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Unsigned", + "fields": [ + { + "name": null, + "type": 601, + "typeName": "(bool, Bn)", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Emergency", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 601, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 55, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 602, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "ReadySolution" + ], + "params": [ + { + "name": "A", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "supports", + "type": 447, + "typeName": "Supports", + "docs": [] + }, + { + "name": "score", + "type": 444, + "typeName": "ElectionScore", + "docs": [] + }, + { + "name": "compute", + "type": 81, + "typeName": "ElectionCompute", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 603, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "RoundSnapshot" + ], + "params": [ + { + "name": "A", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "voters", + "type": 604, + "typeName": "Vec<(A, VoteWeight, Vec)>", + "docs": [] + }, + { + "name": "targets", + "type": 50, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 604, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 605 + } + }, + "docs": [] + } + }, + { + "id": 605, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 8, + 50 + ] + }, + "docs": [] + } + }, + { + "id": 606, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_btree_map", + "BoundedBTreeMap" + ], + "params": [ + { + "name": "K", + "type": 444 + }, + { + "name": "V", + "type": 4 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 607, + "typeName": "BTreeMap", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 607, + "type": { + "path": [ + "BTreeMap" + ], + "params": [ + { + "name": "K", + "type": 444 + }, + { + "name": "V", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 608, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 608, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 609 + } + }, + "docs": [] + } + }, + { + "id": 609, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 444, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 610, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "signed", + "SignedSubmission" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "Solution", + "type": 369 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "raw_solution", + "type": 368, + "typeName": "RawSolution", + "docs": [] + }, + { + "name": "reward", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 611, + "type": { + "path": [ + "pallet_election_provider_multi_phase", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "PreDispatchEarlySubmission", + "fields": [], + "index": 0, + "docs": [ + "Submission was too early." + ] + }, + { + "name": "PreDispatchWrongWinnerCount", + "fields": [], + "index": 1, + "docs": [ + "Wrong number of winners presented." + ] + }, + { + "name": "PreDispatchWeakSubmission", + "fields": [], + "index": 2, + "docs": [ + "Submission was too weak, score-wise." + ] + }, + { + "name": "SignedQueueFull", + "fields": [], + "index": 3, + "docs": [ + "The queue was full, and the solution was not better than any of the existing ones." + ] + }, + { + "name": "SignedCannotPayDeposit", + "fields": [], + "index": 4, + "docs": [ + "The origin failed to pay the deposit." + ] + }, + { + "name": "SignedInvalidWitness", + "fields": [], + "index": 5, + "docs": [ + "Witness data to dispatchable is invalid." + ] + }, + { + "name": "SignedTooMuchWeight", + "fields": [], + "index": 6, + "docs": [ + "The signed submission consumes too much weight" + ] + }, + { + "name": "OcwCallWrongEra", + "fields": [], + "index": 7, + "docs": [ + "OCW submitted solution for wrong round" + ] + }, + { + "name": "MissingSnapshotMetadata", + "fields": [], + "index": 8, + "docs": [ + "Snapshot metadata should exist but didn't." + ] + }, + { + "name": "InvalidSubmissionIndex", + "fields": [], + "index": 9, + "docs": [ + "`Self::insert_submission` returned an invalid index." + ] + }, + { + "name": "CallNotAllowed", + "fields": [], + "index": 10, + "docs": [ + "The call is not allowed at this point." + ] + } + ] + } + }, + "docs": [ + "Error of the pallet that can be returned in response to dispatches." + ] + } + }, + { + "id": 612, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 613 + } + }, + "docs": [] + } + }, + { + "id": 613, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "GiltBid" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "amount", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 614, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "ActiveGiltsTotal" + ], + "params": [ + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "frozen", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "proportion", + "type": 452, + "typeName": "Perquintill", + "docs": [] + }, + { + "name": "index", + "type": 4, + "typeName": "ActiveIndex", + "docs": [] + }, + { + "name": "target", + "type": 452, + "typeName": "Perquintill", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 615, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "ActiveGilt" + ], + "params": [ + { + "name": "Balance", + "type": 6 + }, + { + "name": "AccountId", + "type": 0 + }, + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "proportion", + "type": 452, + "typeName": "Perquintill", + "docs": [] + }, + { + "name": "amount", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "who", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "expiry", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 616, + "type": { + "path": [ + "pallet_gilt", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "DurationTooSmall", + "fields": [], + "index": 0, + "docs": [ + "The duration of the bid is less than one." + ] + }, + { + "name": "DurationTooBig", + "fields": [], + "index": 1, + "docs": [ + "The duration is the bid is greater than the number of queues." + ] + }, + { + "name": "AmountTooSmall", + "fields": [], + "index": 2, + "docs": [ + "The amount of the bid is less than the minimum allowed." + ] + }, + { + "name": "BidTooLow", + "fields": [], + "index": 3, + "docs": [ + "The queue for the bid's duration is full and the amount bid is too low to get in", + "through replacing an existing bid." + ] + }, + { + "name": "Unknown", + "fields": [], + "index": 4, + "docs": [ + "Gilt index is unknown." + ] + }, + { + "name": "NotOwner", + "fields": [], + "index": 5, + "docs": [ + "Not the owner of the gilt." + ] + }, + { + "name": "NotExpired", + "fields": [], + "index": 6, + "docs": [ + "Gilt not yet at expiry date." + ] + }, + { + "name": "NotFound", + "fields": [], + "index": 7, + "docs": [ + "The given bid for retraction is not found." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 617, + "type": { + "path": [ + "pallet_bags_list", + "list", + "Node" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "id", + "type": 0, + "typeName": "T::AccountId", + "docs": [] + }, + { + "name": "prev", + "type": 204, + "typeName": "Option", + "docs": [] + }, + { + "name": "next", + "type": 204, + "typeName": "Option", + "docs": [] + }, + { + "name": "bag_upper", + "type": 8, + "typeName": "VoteWeight", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 618, + "type": { + "path": [ + "pallet_bags_list", + "list", + "Bag" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "head", + "type": 204, + "typeName": "Option", + "docs": [] + }, + { + "name": "tail", + "type": 204, + "typeName": "Option", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 619, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 8 + } + }, + "docs": [] + } + }, + { + "id": 620, + "type": { + "path": [ + "polkadot_runtime_parachains", + "configuration", + "HostConfiguration" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "max_code_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_head_data_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_upward_queue_count", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_upward_queue_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_upward_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_upward_message_num_per_candidate", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_max_message_num_per_candidate", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "validation_upgrade_frequency", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "validation_upgrade_delay", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "max_pov_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_downward_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "ump_service_total_weight", + "type": 8, + "typeName": "Weight", + "docs": [] + }, + { + "name": "hrmp_max_parachain_outbound_channels", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_max_parathread_outbound_channels", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_sender_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "hrmp_recipient_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "hrmp_channel_max_capacity", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_channel_max_total_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_max_parachain_inbound_channels", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_max_parathread_inbound_channels", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "hrmp_channel_max_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "code_retention_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "parathread_cores", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "parathread_retries", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "group_rotation_frequency", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "chain_availability_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "thread_availability_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "scheduling_lookahead", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_validators_per_core", + "type": 232, + "typeName": "Option", + "docs": [] + }, + { + "name": "max_validators", + "type": 232, + "typeName": "Option", + "docs": [] + }, + { + "name": "dispute_period", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + }, + { + "name": "dispute_post_conclusion_acceptance_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "dispute_max_spam_slots", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "dispute_conclusion_by_time_out_period", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "no_show_slots", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "n_delay_tranches", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "zeroth_delay_tranche_width", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "needed_approvals", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "relay_vrf_modulo_samples", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "ump_max_individual_weight", + "type": 8, + "typeName": "Weight", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 621, + "type": { + "path": [ + "polkadot_runtime_parachains", + "configuration", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "InvalidNewValue", + "fields": [], + "index": 0, + "docs": [ + "The new value for a configuration parameter is invalid." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 622, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 464 + } + }, + "docs": [] + } + }, + { + "id": 623, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 240 + } + }, + "docs": [] + } + }, + { + "id": 624, + "type": { + "path": [ + "polkadot_runtime_parachains", + "inclusion", + "AvailabilityBitfieldRecord" + ], + "params": [ + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "bitfield", + "type": 461, + "typeName": "AvailabilityBitfield", + "docs": [] + }, + { + "name": "submitted_at", + "type": 4, + "typeName": "N", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 625, + "type": { + "path": [ + "polkadot_runtime_parachains", + "inclusion", + "CandidatePendingAvailability" + ], + "params": [ + { + "name": "H", + "type": 9 + }, + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "core", + "type": 95, + "typeName": "CoreIndex", + "docs": [] + }, + { + "name": "hash", + "type": 478, + "typeName": "CandidateHash", + "docs": [] + }, + { + "name": "descriptor", + "type": 87, + "typeName": "CandidateDescriptor", + "docs": [] + }, + { + "name": "availability_votes", + "type": 462, + "typeName": "BitVec", + "docs": [] + }, + { + "name": "backers", + "type": 462, + "typeName": "BitVec", + "docs": [] + }, + { + "name": "relay_parent_number", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "backed_in_number", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "backing_group", + "type": 96, + "typeName": "GroupIndex", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 626, + "type": { + "path": [ + "polkadot_runtime_parachains", + "inclusion", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "WrongBitfieldSize", + "fields": [], + "index": 0, + "docs": [ + "Availability bitfield has unexpected size." + ] + }, + { + "name": "BitfieldDuplicateOrUnordered", + "fields": [], + "index": 1, + "docs": [ + "Multiple bitfields submitted by same validator or validators out of order by index." + ] + }, + { + "name": "ValidatorIndexOutOfBounds", + "fields": [], + "index": 2, + "docs": [ + "Validator index out of bounds." + ] + }, + { + "name": "InvalidBitfieldSignature", + "fields": [], + "index": 3, + "docs": [ + "Invalid signature" + ] + }, + { + "name": "UnscheduledCandidate", + "fields": [], + "index": 4, + "docs": [ + "Candidate submitted but para not scheduled." + ] + }, + { + "name": "CandidateScheduledBeforeParaFree", + "fields": [], + "index": 5, + "docs": [ + "Candidate scheduled despite pending candidate already existing for the para." + ] + }, + { + "name": "WrongCollator", + "fields": [], + "index": 6, + "docs": [ + "Candidate included with the wrong collator." + ] + }, + { + "name": "ScheduledOutOfOrder", + "fields": [], + "index": 7, + "docs": [ + "Scheduled cores out of order." + ] + }, + { + "name": "HeadDataTooLarge", + "fields": [], + "index": 8, + "docs": [ + "Head data exceeds the configured maximum." + ] + }, + { + "name": "PrematureCodeUpgrade", + "fields": [], + "index": 9, + "docs": [ + "Code upgrade prematurely." + ] + }, + { + "name": "NewCodeTooLarge", + "fields": [], + "index": 10, + "docs": [ + "Output code is too large" + ] + }, + { + "name": "CandidateNotInParentContext", + "fields": [], + "index": 11, + "docs": [ + "Candidate not in parent context." + ] + }, + { + "name": "UnoccupiedBitInBitfield", + "fields": [], + "index": 12, + "docs": [ + "The bitfield contains a bit relating to an unassigned availability core." + ] + }, + { + "name": "InvalidGroupIndex", + "fields": [], + "index": 13, + "docs": [ + "Invalid group index in core assignment." + ] + }, + { + "name": "InsufficientBacking", + "fields": [], + "index": 14, + "docs": [ + "Insufficient (non-majority) backing." + ] + }, + { + "name": "InvalidBacking", + "fields": [], + "index": 15, + "docs": [ + "Invalid (bad signature, unknown validator, etc.) backing." + ] + }, + { + "name": "NotCollatorSigned", + "fields": [], + "index": 16, + "docs": [ + "Collator did not sign PoV." + ] + }, + { + "name": "ValidationDataHashMismatch", + "fields": [], + "index": 17, + "docs": [ + "The validation data hash does not match expected." + ] + }, + { + "name": "InternalError", + "fields": [], + "index": 18, + "docs": [ + "Internal error only returned when compiled with debug assertions." + ] + }, + { + "name": "IncorrectDownwardMessageHandling", + "fields": [], + "index": 19, + "docs": [ + "The downward message queue is not processed correctly." + ] + }, + { + "name": "InvalidUpwardMessages", + "fields": [], + "index": 20, + "docs": [ + "At least one upward message sent does not pass the acceptance criteria." + ] + }, + { + "name": "HrmpWatermarkMishandling", + "fields": [], + "index": 21, + "docs": [ + "The candidate didn't follow the rules of HRMP watermark advancement." + ] + }, + { + "name": "InvalidOutboundHrmp", + "fields": [], + "index": 22, + "docs": [ + "The HRMP messages sent by the candidate is not valid." + ] + }, + { + "name": "InvalidValidationCodeHash", + "fields": [], + "index": 23, + "docs": [ + "The validation code hash of the candidate is not valid." + ] + }, + { + "name": "ParaHeadMismatch", + "fields": [], + "index": 24, + "docs": [ + "The `para_head` hash in the candidate descriptor doesn't match the hash of the actual para head in the", + "commitments." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 627, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras_inherent", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "TooManyInclusionInherents", + "fields": [], + "index": 0, + "docs": [ + "Inclusion inherent called more than once per block." + ] + }, + { + "name": "InvalidParentHeader", + "fields": [], + "index": 1, + "docs": [ + "The hash of the submitted parent header doesn't correspond to the saved block hash of", + "the parent." + ] + }, + { + "name": "CandidateConcludedInvalid", + "fields": [], + "index": 2, + "docs": [ + "Disputed candidate that was concluded invalid." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 628, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 622 + } + }, + "docs": [] + } + }, + { + "id": 629, + "type": { + "path": [ + "polkadot_runtime_parachains", + "scheduler", + "ParathreadClaimQueue" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "queue", + "type": 630, + "typeName": "Vec", + "docs": [] + }, + { + "name": "next_core_offset", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 630, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 631 + } + }, + "docs": [] + } + }, + { + "id": 631, + "type": { + "path": [ + "polkadot_runtime_parachains", + "scheduler", + "QueuedParathread" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "claim", + "type": 632, + "typeName": "ParathreadEntry", + "docs": [] + }, + { + "name": "core_offset", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 632, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "ParathreadEntry" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "claim", + "type": 633, + "typeName": "ParathreadClaim", + "docs": [] + }, + { + "name": "retries", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 633, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "ParathreadClaim" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 88, + "typeName": "Id", + "docs": [] + }, + { + "name": null, + "type": 89, + "typeName": "CollatorId", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 634, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 635 + } + }, + "docs": [] + } + }, + { + "id": 635, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 636 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 636, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 636, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "CoreOccupied" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Parathread", + "fields": [ + { + "name": null, + "type": 632, + "typeName": "ParathreadEntry", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "Parachain", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 637, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 88 + } + }, + "docs": [] + } + }, + { + "id": 638, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 639 + } + }, + "docs": [] + } + }, + { + "id": 639, + "type": { + "path": [ + "polkadot_runtime_parachains", + "scheduler", + "CoreAssignment" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "core", + "type": 95, + "typeName": "CoreIndex", + "docs": [] + }, + { + "name": "para_id", + "type": 88, + "typeName": "ParaId", + "docs": [] + }, + { + "name": "kind", + "type": 640, + "typeName": "AssignmentKind", + "docs": [] + }, + { + "name": "group_idx", + "type": 96, + "typeName": "GroupIndex", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 640, + "type": { + "path": [ + "polkadot_runtime_parachains", + "scheduler", + "AssignmentKind" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Parachain", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Parathread", + "fields": [ + { + "name": null, + "type": 89, + "typeName": "CollatorId", + "docs": [] + }, + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 641, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "ParaLifecycle" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Onboarding", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Parathread", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "Parachain", + "fields": [], + "index": 2, + "docs": [] + }, + { + "name": "UpgradingParathread", + "fields": [], + "index": 3, + "docs": [] + }, + { + "name": "DowngradingParachain", + "fields": [], + "index": 4, + "docs": [] + }, + { + "name": "OffboardingParathread", + "fields": [], + "index": 5, + "docs": [] + }, + { + "name": "OffboardingParachain", + "fields": [], + "index": 6, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 642, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 88, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 643, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "ParaPastCodeMeta" + ], + "params": [ + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "upgrade_times", + "type": 644, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "last_pruned", + "type": 232, + "typeName": "Option", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 644, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 645 + } + }, + "docs": [] + } + }, + { + "id": 645, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "ReplacementTimes" + ], + "params": [ + { + "name": "N", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "expected_at", + "type": 4, + "typeName": "N", + "docs": [] + }, + { + "name": "activated_at", + "type": 4, + "typeName": "N", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 646, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 642 + } + }, + "docs": [] + } + }, + { + "id": 647, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "UpgradeGoAhead" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Abort", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "GoAhead", + "fields": [], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 648, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "UpgradeRestriction" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Present", + "fields": [], + "index": 0, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 649, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "ParaGenesisArgs" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "genesis_head", + "type": 94, + "typeName": "HeadData", + "docs": [] + }, + { + "name": "validation_code", + "type": 473, + "typeName": "ValidationCode", + "docs": [] + }, + { + "name": "parachain", + "type": 55, + "typeName": "bool", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 650, + "type": { + "path": [ + "polkadot_runtime_parachains", + "paras", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotRegistered", + "fields": [], + "index": 0, + "docs": [ + "Para is not registered in our system." + ] + }, + { + "name": "CannotOnboard", + "fields": [], + "index": 1, + "docs": [ + "Para cannot be onboarded because it is already tracked by our system." + ] + }, + { + "name": "CannotOffboard", + "fields": [], + "index": 2, + "docs": [ + "Para cannot be offboarded at this time." + ] + }, + { + "name": "CannotUpgrade", + "fields": [], + "index": 3, + "docs": [ + "Para cannot be upgraded to a parachain." + ] + }, + { + "name": "CannotDowngrade", + "fields": [], + "index": 4, + "docs": [ + "Para cannot be downgraded to a parathread." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 651, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 652 + } + }, + "docs": [] + } + }, + { + "id": 652, + "type": { + "path": [ + "polkadot_runtime_parachains", + "initializer", + "BufferedSessionChange" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "validators", + "type": 623, + "typeName": "Vec", + "docs": [] + }, + { + "name": "queued", + "type": 623, + "typeName": "Vec", + "docs": [] + }, + { + "name": "session_index", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 653, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 654 + } + }, + "docs": [] + } + }, + { + "id": 654, + "type": { + "path": [ + "polkadot_core_primitives", + "InboundDownwardMessage" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "sent_at", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "msg", + "type": 10, + "typeName": "DownwardMessage", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 655, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 88, + 10 + ] + }, + "docs": [] + } + }, + { + "id": 656, + "type": { + "path": [ + "polkadot_runtime_parachains", + "ump", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "UnknownMessageIndex", + "fields": [], + "index": 0, + "docs": [ + "The message index given is unknown." + ] + }, + { + "name": "WeightOverLimit", + "fields": [], + "index": 1, + "docs": [ + "The amount of weight given is possibly not enough for executing the message." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 657, + "type": { + "path": [ + "polkadot_runtime_parachains", + "hrmp", + "HrmpOpenChannelRequest" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "confirmed", + "type": 55, + "typeName": "bool", + "docs": [] + }, + { + "name": "_age", + "type": 4, + "typeName": "SessionIndex", + "docs": [] + }, + { + "name": "sender_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "max_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_capacity", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_total_size", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 658, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 102 + } + }, + "docs": [] + } + }, + { + "id": 659, + "type": { + "path": [ + "polkadot_runtime_parachains", + "hrmp", + "HrmpChannel" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "max_capacity", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_total_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "max_message_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "msg_count", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "total_size", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "mqc_head", + "type": 660, + "typeName": "Option", + "docs": [] + }, + { + "name": "sender_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "recipient_deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 660, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 9 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 9, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 661, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 662 + } + }, + "docs": [] + } + }, + { + "id": 662, + "type": { + "path": [ + "polkadot_core_primitives", + "InboundHrmpMessage" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "sent_at", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "data", + "type": 10, + "typeName": "sp_std::vec::Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 663, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 664 + } + }, + "docs": [] + } + }, + { + "id": 664, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 637 + ] + }, + "docs": [] + } + }, + { + "id": 665, + "type": { + "path": [ + "polkadot_runtime_parachains", + "hrmp", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "OpenHrmpChannelToSelf", + "fields": [], + "index": 0, + "docs": [ + "The sender tried to open a channel to themselves." + ] + }, + { + "name": "OpenHrmpChannelInvalidRecipient", + "fields": [], + "index": 1, + "docs": [ + "The recipient is not a valid para." + ] + }, + { + "name": "OpenHrmpChannelZeroCapacity", + "fields": [], + "index": 2, + "docs": [ + "The requested capacity is zero." + ] + }, + { + "name": "OpenHrmpChannelCapacityExceedsLimit", + "fields": [], + "index": 3, + "docs": [ + "The requested capacity exceeds the global limit." + ] + }, + { + "name": "OpenHrmpChannelZeroMessageSize", + "fields": [], + "index": 4, + "docs": [ + "The requested maximum message size is 0." + ] + }, + { + "name": "OpenHrmpChannelMessageSizeExceedsLimit", + "fields": [], + "index": 5, + "docs": [ + "The open request requested the message size that exceeds the global limit." + ] + }, + { + "name": "OpenHrmpChannelAlreadyExists", + "fields": [], + "index": 6, + "docs": [ + "The channel already exists" + ] + }, + { + "name": "OpenHrmpChannelAlreadyRequested", + "fields": [], + "index": 7, + "docs": [ + "There is already a request to open the same channel." + ] + }, + { + "name": "OpenHrmpChannelLimitExceeded", + "fields": [], + "index": 8, + "docs": [ + "The sender already has the maximum number of allowed outbound channels." + ] + }, + { + "name": "AcceptHrmpChannelDoesntExist", + "fields": [], + "index": 9, + "docs": [ + "The channel from the sender to the origin doesn't exist." + ] + }, + { + "name": "AcceptHrmpChannelAlreadyConfirmed", + "fields": [], + "index": 10, + "docs": [ + "The channel is already confirmed." + ] + }, + { + "name": "AcceptHrmpChannelLimitExceeded", + "fields": [], + "index": 11, + "docs": [ + "The recipient already has the maximum number of allowed inbound channels." + ] + }, + { + "name": "CloseHrmpChannelUnauthorized", + "fields": [], + "index": 12, + "docs": [ + "The origin tries to close a channel where it is neither the sender nor the recipient." + ] + }, + { + "name": "CloseHrmpChannelDoesntExist", + "fields": [], + "index": 13, + "docs": [ + "The channel to be closed doesn't exist." + ] + }, + { + "name": "CloseHrmpChannelAlreadyUnderway", + "fields": [], + "index": 14, + "docs": [ + "The channel close request is already requested." + ] + }, + { + "name": "CancelHrmpOpenChannelUnauthorized", + "fields": [], + "index": 15, + "docs": [ + "Canceling is requested by neither the sender nor recipient of the open channel request." + ] + }, + { + "name": "OpenHrmpChannelDoesntExist", + "fields": [], + "index": 16, + "docs": [ + "The open request doesn't exist." + ] + }, + { + "name": "OpenHrmpChannelAlreadyConfirmed", + "fields": [], + "index": 17, + "docs": [ + "Cannot cancel an HRMP open channel request because it is already confirmed." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 666, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 241 + } + }, + "docs": [] + } + }, + { + "id": 667, + "type": { + "path": [ + "polkadot_primitives", + "v1", + "SessionInfo" + ], + "params": [], + "def": { + "composite": { + "fields": [ + { + "name": "validators", + "type": 623, + "typeName": "Vec", + "docs": [] + }, + { + "name": "discovery_keys", + "type": 668, + "typeName": "Vec", + "docs": [] + }, + { + "name": "assignment_keys", + "type": 666, + "typeName": "Vec", + "docs": [] + }, + { + "name": "validator_groups", + "type": 628, + "typeName": "Vec>", + "docs": [] + }, + { + "name": "n_cores", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "zeroth_delay_tranche_width", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "relay_vrf_modulo_samples", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "n_delay_tranches", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "no_show_slots", + "type": 4, + "typeName": "u32", + "docs": [] + }, + { + "name": "needed_approvals", + "type": 4, + "typeName": "u32", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 668, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 242 + } + }, + "docs": [] + } + }, + { + "id": 669, + "type": { + "path": [ + "polkadot_runtime_common", + "paras_registrar", + "ParaInfo" + ], + "params": [ + { + "name": "Account", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "manager", + "type": 0, + "typeName": "Account", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "locked", + "type": 55, + "typeName": "bool", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 670, + "type": { + "path": [ + "polkadot_runtime_common", + "paras_registrar", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "NotRegistered", + "fields": [], + "index": 0, + "docs": [ + "The ID is not registered." + ] + }, + { + "name": "AlreadyRegistered", + "fields": [], + "index": 1, + "docs": [ + "The ID is already registered." + ] + }, + { + "name": "NotOwner", + "fields": [], + "index": 2, + "docs": [ + "The caller is not the owner of this Id." + ] + }, + { + "name": "CodeTooLarge", + "fields": [], + "index": 3, + "docs": [ + "Invalid para code size." + ] + }, + { + "name": "HeadDataTooLarge", + "fields": [], + "index": 4, + "docs": [ + "Invalid para head data size." + ] + }, + { + "name": "NotParachain", + "fields": [], + "index": 5, + "docs": [ + "Para is not a Parachain." + ] + }, + { + "name": "NotParathread", + "fields": [], + "index": 6, + "docs": [ + "Para is not a Parathread." + ] + }, + { + "name": "CannotDeregister", + "fields": [], + "index": 7, + "docs": [ + "Cannot deregister para" + ] + }, + { + "name": "CannotDowngrade", + "fields": [], + "index": 8, + "docs": [ + "Cannot schedule downgrade of parachain to parathread" + ] + }, + { + "name": "CannotUpgrade", + "fields": [], + "index": 9, + "docs": [ + "Cannot schedule upgrade of parathread to parachain" + ] + }, + { + "name": "ParaLocked", + "fields": [], + "index": 10, + "docs": [ + "Para is locked from manipulation by the manager. Must use parachain or relay chain governance." + ] + }, + { + "name": "NotReserved", + "fields": [], + "index": 11, + "docs": [ + "The ID given for registration has not been reserved." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 671, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 672 + } + }, + "docs": [] + } + }, + { + "id": 672, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 59 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 59, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 673, + "type": { + "path": [ + "polkadot_runtime_common", + "slots", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "ParaNotOnboarding", + "fields": [], + "index": 0, + "docs": [ + "The parachain ID is not onboarding." + ] + }, + { + "name": "LeaseError", + "fields": [], + "index": 1, + "docs": [ + "There was an error with the lease." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 674, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 88 + ] + }, + "docs": [] + } + }, + { + "id": 675, + "type": { + "path": [], + "params": [], + "def": { + "array": { + "len": 36, + "type": 676 + } + }, + "docs": [] + } + }, + { + "id": 676, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 677 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 677, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 677, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 0, + 88, + 6 + ] + }, + "docs": [] + } + }, + { + "id": 678, + "type": { + "path": [ + "polkadot_runtime_common", + "auctions", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "AuctionInProgress", + "fields": [], + "index": 0, + "docs": [ + "This auction is already in progress." + ] + }, + { + "name": "LeasePeriodInPast", + "fields": [], + "index": 1, + "docs": [ + "The lease period is in the past." + ] + }, + { + "name": "ParaNotRegistered", + "fields": [], + "index": 2, + "docs": [ + "Para is not registered" + ] + }, + { + "name": "NotCurrentAuction", + "fields": [], + "index": 3, + "docs": [ + "Not a current auction." + ] + }, + { + "name": "NotAuction", + "fields": [], + "index": 4, + "docs": [ + "Not an auction." + ] + }, + { + "name": "AuctionEnded", + "fields": [], + "index": 5, + "docs": [ + "Auction has already ended." + ] + }, + { + "name": "AlreadyLeasedOut", + "fields": [], + "index": 6, + "docs": [ + "The para is already leased out for part of this range." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 679, + "type": { + "path": [ + "polkadot_runtime_common", + "crowdloan", + "FundInfo" + ], + "params": [ + { + "name": "AccountId", + "type": 0 + }, + { + "name": "Balance", + "type": 6 + }, + { + "name": "BlockNumber", + "type": 4 + }, + { + "name": "LeasePeriod", + "type": 4 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": "depositor", + "type": 0, + "typeName": "AccountId", + "docs": [] + }, + { + "name": "verifier", + "type": 494, + "typeName": "Option", + "docs": [] + }, + { + "name": "deposit", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "raised", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "end", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + }, + { + "name": "cap", + "type": 6, + "typeName": "Balance", + "docs": [] + }, + { + "name": "last_contribution", + "type": 680, + "typeName": "LastContribution", + "docs": [] + }, + { + "name": "first_period", + "type": 4, + "typeName": "LeasePeriod", + "docs": [] + }, + { + "name": "last_period", + "type": 4, + "typeName": "LeasePeriod", + "docs": [] + }, + { + "name": "trie_index", + "type": 4, + "typeName": "TrieIndex", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 680, + "type": { + "path": [ + "polkadot_runtime_common", + "crowdloan", + "LastContribution" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Never", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "PreEnding", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "u32", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Ending", + "fields": [ + { + "name": null, + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 681, + "type": { + "path": [ + "polkadot_runtime_common", + "crowdloan", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "FirstPeriodInPast", + "fields": [], + "index": 0, + "docs": [ + "The current lease period is more than the first lease period." + ] + }, + { + "name": "FirstPeriodTooFarInFuture", + "fields": [], + "index": 1, + "docs": [ + "The first lease period needs to at least be less than 3 `max_value`." + ] + }, + { + "name": "LastPeriodBeforeFirstPeriod", + "fields": [], + "index": 2, + "docs": [ + "Last lease period must be greater than first lease period." + ] + }, + { + "name": "LastPeriodTooFarInFuture", + "fields": [], + "index": 3, + "docs": [ + "The last lease period cannot be more than 3 periods after the first period." + ] + }, + { + "name": "CannotEndInPast", + "fields": [], + "index": 4, + "docs": [ + "The campaign ends before the current block number. The end must be in the future." + ] + }, + { + "name": "EndTooFarInFuture", + "fields": [], + "index": 5, + "docs": [ + "The end date for this crowdloan is not sensible." + ] + }, + { + "name": "Overflow", + "fields": [], + "index": 6, + "docs": [ + "There was an overflow." + ] + }, + { + "name": "ContributionTooSmall", + "fields": [], + "index": 7, + "docs": [ + "The contribution was below the minimum, `MinContribution`." + ] + }, + { + "name": "InvalidParaId", + "fields": [], + "index": 8, + "docs": [ + "Invalid fund index." + ] + }, + { + "name": "CapExceeded", + "fields": [], + "index": 9, + "docs": [ + "Contributions exceed maximum amount." + ] + }, + { + "name": "ContributionPeriodOver", + "fields": [], + "index": 10, + "docs": [ + "The contribution period has already ended." + ] + }, + { + "name": "InvalidOrigin", + "fields": [], + "index": 11, + "docs": [ + "The origin of this call is invalid." + ] + }, + { + "name": "NotParachain", + "fields": [], + "index": 12, + "docs": [ + "This crowdloan does not correspond to a parachain." + ] + }, + { + "name": "LeaseActive", + "fields": [], + "index": 13, + "docs": [ + "This parachain lease is still active and retirement cannot yet begin." + ] + }, + { + "name": "BidOrLeaseActive", + "fields": [], + "index": 14, + "docs": [ + "This parachain's bid or lease is still active and withdraw cannot yet begin." + ] + }, + { + "name": "FundNotEnded", + "fields": [], + "index": 15, + "docs": [ + "The crowdloan has not yet ended." + ] + }, + { + "name": "NoContributions", + "fields": [], + "index": 16, + "docs": [ + "There are no contributions stored in this crowdloan." + ] + }, + { + "name": "NotReadyToDissolve", + "fields": [], + "index": 17, + "docs": [ + "The crowdloan is not ready to dissolve. Potentially still has a slot or in retirement period." + ] + }, + { + "name": "InvalidSignature", + "fields": [], + "index": 18, + "docs": [ + "Invalid signature." + ] + }, + { + "name": "MemoTooLarge", + "fields": [], + "index": 19, + "docs": [ + "The provided memo is too large." + ] + }, + { + "name": "AlreadyInNewRaise", + "fields": [], + "index": 20, + "docs": [ + "The fund is already in `NewRaise`" + ] + }, + { + "name": "VrfDelayInProgress", + "fields": [], + "index": 21, + "docs": [ + "No contributions allowed during the VRF delay" + ] + }, + { + "name": "NoLeasePeriod", + "fields": [], + "index": 22, + "docs": [ + "A lease period has not started yet, due to an offset in the starting block." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 682, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "QueryStatus" + ], + "params": [ + { + "name": "BlockNumber", + "type": 4 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Pending", + "fields": [ + { + "name": "responder", + "type": 141, + "typeName": "VersionedMultiLocation", + "docs": [] + }, + { + "name": "maybe_notify", + "type": 683, + "typeName": "Option<(u8, u8)>", + "docs": [] + }, + { + "name": "timeout", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "VersionNotifier", + "fields": [ + { + "name": "origin", + "type": 141, + "typeName": "VersionedMultiLocation", + "docs": [] + }, + { + "name": "is_active", + "type": 55, + "typeName": "bool", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Ready", + "fields": [ + { + "name": "response", + "type": 685, + "typeName": "VersionedResponse", + "docs": [] + }, + { + "name": "at", + "type": 4, + "typeName": "BlockNumber", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 683, + "type": { + "path": [ + "Option" + ], + "params": [ + { + "name": "T", + "type": 684 + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "None", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Some", + "fields": [ + { + "name": null, + "type": 684, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 684, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 2, + 2 + ] + }, + "docs": [] + } + }, + { + "id": 685, + "type": { + "path": [ + "xcm", + "VersionedResponse" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "V0", + "fields": [ + { + "name": null, + "type": 507, + "typeName": "v0::Response", + "docs": [] + } + ], + "index": 0, + "docs": [] + }, + { + "name": "V1", + "fields": [ + { + "name": null, + "type": 512, + "typeName": "v1::Response", + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "V2", + "fields": [ + { + "name": null, + "type": 126, + "typeName": "v2::Response", + "docs": [] + } + ], + "index": 2, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 686, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 4, + 141 + ] + }, + "docs": [] + } + }, + { + "id": 687, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 8, + 8, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 688, + "type": { + "path": [ + "frame_support", + "storage", + "bounded_vec", + "BoundedVec" + ], + "params": [ + { + "name": "T", + "type": 689 + }, + { + "name": "S", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 690, + "typeName": "Vec", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 689, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 141, + 4 + ] + }, + "docs": [] + } + }, + { + "id": 690, + "type": { + "path": [], + "params": [], + "def": { + "sequence": { + "type": 689 + } + }, + "docs": [] + } + }, + { + "id": 691, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "VersionMigrationStage" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "MigrateSupportedVersion", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "MigrateVersionNotifiers", + "fields": [], + "index": 1, + "docs": [] + }, + { + "name": "NotifyCurrentTargets", + "fields": [ + { + "name": null, + "type": 72, + "typeName": "Option>", + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "MigrateAndNotifyOldTargets", + "fields": [], + "index": 3, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 692, + "type": { + "path": [ + "pallet_xcm", + "pallet", + "Error" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "variant": { + "variants": [ + { + "name": "Unreachable", + "fields": [], + "index": 0, + "docs": [ + "The desired destination was unreachable, generally because there is a no way of routing", + "to it." + ] + }, + { + "name": "SendFailure", + "fields": [], + "index": 1, + "docs": [ + "There was some other issue (i.e. not to do with routing) in sending the message. Perhaps", + "a lack of space for buffering the message." + ] + }, + { + "name": "Filtered", + "fields": [], + "index": 2, + "docs": [ + "The message execution fails the filter." + ] + }, + { + "name": "UnweighableMessage", + "fields": [], + "index": 3, + "docs": [ + "The message's weight could not be determined." + ] + }, + { + "name": "DestinationNotInvertible", + "fields": [], + "index": 4, + "docs": [ + "The destination `MultiLocation` provided cannot be inverted." + ] + }, + { + "name": "Empty", + "fields": [], + "index": 5, + "docs": [ + "The assets to be sent are empty." + ] + }, + { + "name": "CannotReanchor", + "fields": [], + "index": 6, + "docs": [ + "Could not re-anchor the assets to declare the fees for the destination chain." + ] + }, + { + "name": "TooManyAssets", + "fields": [], + "index": 7, + "docs": [ + "Too many assets have been attempted for transfer." + ] + }, + { + "name": "InvalidOrigin", + "fields": [], + "index": 8, + "docs": [ + "Origin is invalid for sending." + ] + }, + { + "name": "BadVersion", + "fields": [], + "index": 9, + "docs": [ + "The version of the `Versioned` value used is not able to be interpreted." + ] + }, + { + "name": "BadLocation", + "fields": [], + "index": 10, + "docs": [ + "The given location could not be used (e.g. because it cannot be expressed in the", + "desired version of XCM)." + ] + }, + { + "name": "NoSubscription", + "fields": [], + "index": 11, + "docs": [ + "The referenced subscription could not be found." + ] + }, + { + "name": "AlreadySubscribed", + "fields": [], + "index": 12, + "docs": [ + "The location is invalid since it already has a subscription from us." + ] + } + ] + } + }, + "docs": [ + "\n\t\t\tCustom [dispatch errors](https://substrate.dev/docs/en/knowledgebase/runtime/errors)\n\t\t\tof this pallet.\n\t\t\t" + ] + } + }, + { + "id": 693, + "type": { + "path": [ + "sp_runtime", + "generic", + "unchecked_extrinsic", + "UncheckedExtrinsic" + ], + "params": [ + { + "name": "Address", + "type": 195 + }, + { + "name": "Call", + "type": 298 + }, + { + "name": "Signature", + "type": 499 + }, + { + "name": "Extra", + "type": 694 + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 10, + "typeName": null, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 694, + "type": { + "path": [], + "params": [], + "def": { + "tuple": [ + 695, + 696, + 697, + 698, + 700, + 701, + 702 + ] + }, + "docs": [] + } + }, + { + "id": 695, + "type": { + "path": [ + "frame_system", + "extensions", + "check_spec_version", + "CheckSpecVersion" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 696, + "type": { + "path": [ + "frame_system", + "extensions", + "check_tx_version", + "CheckTxVersion" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 697, + "type": { + "path": [ + "frame_system", + "extensions", + "check_genesis", + "CheckGenesis" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 698, + "type": { + "path": [ + "frame_system", + "extensions", + "check_mortality", + "CheckMortality" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 699, + "typeName": "Era", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 699, + "type": { + "path": [ + "sp_runtime", + "generic", + "era", + "Era" + ], + "params": [], + "def": { + "variant": { + "variants": [ + { + "name": "Immortal", + "fields": [], + "index": 0, + "docs": [] + }, + { + "name": "Mortal1", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 1, + "docs": [] + }, + { + "name": "Mortal2", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 2, + "docs": [] + }, + { + "name": "Mortal3", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 3, + "docs": [] + }, + { + "name": "Mortal4", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 4, + "docs": [] + }, + { + "name": "Mortal5", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 5, + "docs": [] + }, + { + "name": "Mortal6", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 6, + "docs": [] + }, + { + "name": "Mortal7", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 7, + "docs": [] + }, + { + "name": "Mortal8", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 8, + "docs": [] + }, + { + "name": "Mortal9", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 9, + "docs": [] + }, + { + "name": "Mortal10", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 10, + "docs": [] + }, + { + "name": "Mortal11", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 11, + "docs": [] + }, + { + "name": "Mortal12", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 12, + "docs": [] + }, + { + "name": "Mortal13", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 13, + "docs": [] + }, + { + "name": "Mortal14", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 14, + "docs": [] + }, + { + "name": "Mortal15", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 15, + "docs": [] + }, + { + "name": "Mortal16", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 16, + "docs": [] + }, + { + "name": "Mortal17", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 17, + "docs": [] + }, + { + "name": "Mortal18", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 18, + "docs": [] + }, + { + "name": "Mortal19", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 19, + "docs": [] + }, + { + "name": "Mortal20", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 20, + "docs": [] + }, + { + "name": "Mortal21", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 21, + "docs": [] + }, + { + "name": "Mortal22", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 22, + "docs": [] + }, + { + "name": "Mortal23", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 23, + "docs": [] + }, + { + "name": "Mortal24", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 24, + "docs": [] + }, + { + "name": "Mortal25", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 25, + "docs": [] + }, + { + "name": "Mortal26", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 26, + "docs": [] + }, + { + "name": "Mortal27", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 27, + "docs": [] + }, + { + "name": "Mortal28", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 28, + "docs": [] + }, + { + "name": "Mortal29", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 29, + "docs": [] + }, + { + "name": "Mortal30", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 30, + "docs": [] + }, + { + "name": "Mortal31", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 31, + "docs": [] + }, + { + "name": "Mortal32", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 32, + "docs": [] + }, + { + "name": "Mortal33", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 33, + "docs": [] + }, + { + "name": "Mortal34", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 34, + "docs": [] + }, + { + "name": "Mortal35", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 35, + "docs": [] + }, + { + "name": "Mortal36", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 36, + "docs": [] + }, + { + "name": "Mortal37", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 37, + "docs": [] + }, + { + "name": "Mortal38", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 38, + "docs": [] + }, + { + "name": "Mortal39", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 39, + "docs": [] + }, + { + "name": "Mortal40", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 40, + "docs": [] + }, + { + "name": "Mortal41", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 41, + "docs": [] + }, + { + "name": "Mortal42", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 42, + "docs": [] + }, + { + "name": "Mortal43", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 43, + "docs": [] + }, + { + "name": "Mortal44", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 44, + "docs": [] + }, + { + "name": "Mortal45", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 45, + "docs": [] + }, + { + "name": "Mortal46", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 46, + "docs": [] + }, + { + "name": "Mortal47", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 47, + "docs": [] + }, + { + "name": "Mortal48", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 48, + "docs": [] + }, + { + "name": "Mortal49", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 49, + "docs": [] + }, + { + "name": "Mortal50", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 50, + "docs": [] + }, + { + "name": "Mortal51", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 51, + "docs": [] + }, + { + "name": "Mortal52", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 52, + "docs": [] + }, + { + "name": "Mortal53", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 53, + "docs": [] + }, + { + "name": "Mortal54", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 54, + "docs": [] + }, + { + "name": "Mortal55", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 55, + "docs": [] + }, + { + "name": "Mortal56", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 56, + "docs": [] + }, + { + "name": "Mortal57", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 57, + "docs": [] + }, + { + "name": "Mortal58", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 58, + "docs": [] + }, + { + "name": "Mortal59", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 59, + "docs": [] + }, + { + "name": "Mortal60", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 60, + "docs": [] + }, + { + "name": "Mortal61", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 61, + "docs": [] + }, + { + "name": "Mortal62", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 62, + "docs": [] + }, + { + "name": "Mortal63", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 63, + "docs": [] + }, + { + "name": "Mortal64", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 64, + "docs": [] + }, + { + "name": "Mortal65", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 65, + "docs": [] + }, + { + "name": "Mortal66", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 66, + "docs": [] + }, + { + "name": "Mortal67", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 67, + "docs": [] + }, + { + "name": "Mortal68", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 68, + "docs": [] + }, + { + "name": "Mortal69", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 69, + "docs": [] + }, + { + "name": "Mortal70", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 70, + "docs": [] + }, + { + "name": "Mortal71", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 71, + "docs": [] + }, + { + "name": "Mortal72", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 72, + "docs": [] + }, + { + "name": "Mortal73", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 73, + "docs": [] + }, + { + "name": "Mortal74", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 74, + "docs": [] + }, + { + "name": "Mortal75", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 75, + "docs": [] + }, + { + "name": "Mortal76", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 76, + "docs": [] + }, + { + "name": "Mortal77", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 77, + "docs": [] + }, + { + "name": "Mortal78", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 78, + "docs": [] + }, + { + "name": "Mortal79", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 79, + "docs": [] + }, + { + "name": "Mortal80", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 80, + "docs": [] + }, + { + "name": "Mortal81", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 81, + "docs": [] + }, + { + "name": "Mortal82", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 82, + "docs": [] + }, + { + "name": "Mortal83", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 83, + "docs": [] + }, + { + "name": "Mortal84", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 84, + "docs": [] + }, + { + "name": "Mortal85", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 85, + "docs": [] + }, + { + "name": "Mortal86", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 86, + "docs": [] + }, + { + "name": "Mortal87", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 87, + "docs": [] + }, + { + "name": "Mortal88", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 88, + "docs": [] + }, + { + "name": "Mortal89", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 89, + "docs": [] + }, + { + "name": "Mortal90", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 90, + "docs": [] + }, + { + "name": "Mortal91", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 91, + "docs": [] + }, + { + "name": "Mortal92", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 92, + "docs": [] + }, + { + "name": "Mortal93", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 93, + "docs": [] + }, + { + "name": "Mortal94", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 94, + "docs": [] + }, + { + "name": "Mortal95", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 95, + "docs": [] + }, + { + "name": "Mortal96", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 96, + "docs": [] + }, + { + "name": "Mortal97", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 97, + "docs": [] + }, + { + "name": "Mortal98", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 98, + "docs": [] + }, + { + "name": "Mortal99", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 99, + "docs": [] + }, + { + "name": "Mortal100", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 100, + "docs": [] + }, + { + "name": "Mortal101", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 101, + "docs": [] + }, + { + "name": "Mortal102", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 102, + "docs": [] + }, + { + "name": "Mortal103", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 103, + "docs": [] + }, + { + "name": "Mortal104", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 104, + "docs": [] + }, + { + "name": "Mortal105", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 105, + "docs": [] + }, + { + "name": "Mortal106", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 106, + "docs": [] + }, + { + "name": "Mortal107", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 107, + "docs": [] + }, + { + "name": "Mortal108", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 108, + "docs": [] + }, + { + "name": "Mortal109", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 109, + "docs": [] + }, + { + "name": "Mortal110", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 110, + "docs": [] + }, + { + "name": "Mortal111", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 111, + "docs": [] + }, + { + "name": "Mortal112", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 112, + "docs": [] + }, + { + "name": "Mortal113", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 113, + "docs": [] + }, + { + "name": "Mortal114", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 114, + "docs": [] + }, + { + "name": "Mortal115", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 115, + "docs": [] + }, + { + "name": "Mortal116", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 116, + "docs": [] + }, + { + "name": "Mortal117", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 117, + "docs": [] + }, + { + "name": "Mortal118", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 118, + "docs": [] + }, + { + "name": "Mortal119", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 119, + "docs": [] + }, + { + "name": "Mortal120", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 120, + "docs": [] + }, + { + "name": "Mortal121", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 121, + "docs": [] + }, + { + "name": "Mortal122", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 122, + "docs": [] + }, + { + "name": "Mortal123", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 123, + "docs": [] + }, + { + "name": "Mortal124", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 124, + "docs": [] + }, + { + "name": "Mortal125", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 125, + "docs": [] + }, + { + "name": "Mortal126", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 126, + "docs": [] + }, + { + "name": "Mortal127", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 127, + "docs": [] + }, + { + "name": "Mortal128", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 128, + "docs": [] + }, + { + "name": "Mortal129", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 129, + "docs": [] + }, + { + "name": "Mortal130", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 130, + "docs": [] + }, + { + "name": "Mortal131", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 131, + "docs": [] + }, + { + "name": "Mortal132", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 132, + "docs": [] + }, + { + "name": "Mortal133", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 133, + "docs": [] + }, + { + "name": "Mortal134", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 134, + "docs": [] + }, + { + "name": "Mortal135", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 135, + "docs": [] + }, + { + "name": "Mortal136", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 136, + "docs": [] + }, + { + "name": "Mortal137", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 137, + "docs": [] + }, + { + "name": "Mortal138", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 138, + "docs": [] + }, + { + "name": "Mortal139", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 139, + "docs": [] + }, + { + "name": "Mortal140", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 140, + "docs": [] + }, + { + "name": "Mortal141", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 141, + "docs": [] + }, + { + "name": "Mortal142", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 142, + "docs": [] + }, + { + "name": "Mortal143", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 143, + "docs": [] + }, + { + "name": "Mortal144", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 144, + "docs": [] + }, + { + "name": "Mortal145", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 145, + "docs": [] + }, + { + "name": "Mortal146", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 146, + "docs": [] + }, + { + "name": "Mortal147", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 147, + "docs": [] + }, + { + "name": "Mortal148", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 148, + "docs": [] + }, + { + "name": "Mortal149", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 149, + "docs": [] + }, + { + "name": "Mortal150", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 150, + "docs": [] + }, + { + "name": "Mortal151", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 151, + "docs": [] + }, + { + "name": "Mortal152", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 152, + "docs": [] + }, + { + "name": "Mortal153", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 153, + "docs": [] + }, + { + "name": "Mortal154", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 154, + "docs": [] + }, + { + "name": "Mortal155", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 155, + "docs": [] + }, + { + "name": "Mortal156", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 156, + "docs": [] + }, + { + "name": "Mortal157", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 157, + "docs": [] + }, + { + "name": "Mortal158", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 158, + "docs": [] + }, + { + "name": "Mortal159", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 159, + "docs": [] + }, + { + "name": "Mortal160", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 160, + "docs": [] + }, + { + "name": "Mortal161", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 161, + "docs": [] + }, + { + "name": "Mortal162", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 162, + "docs": [] + }, + { + "name": "Mortal163", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 163, + "docs": [] + }, + { + "name": "Mortal164", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 164, + "docs": [] + }, + { + "name": "Mortal165", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 165, + "docs": [] + }, + { + "name": "Mortal166", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 166, + "docs": [] + }, + { + "name": "Mortal167", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 167, + "docs": [] + }, + { + "name": "Mortal168", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 168, + "docs": [] + }, + { + "name": "Mortal169", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 169, + "docs": [] + }, + { + "name": "Mortal170", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 170, + "docs": [] + }, + { + "name": "Mortal171", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 171, + "docs": [] + }, + { + "name": "Mortal172", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 172, + "docs": [] + }, + { + "name": "Mortal173", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 173, + "docs": [] + }, + { + "name": "Mortal174", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 174, + "docs": [] + }, + { + "name": "Mortal175", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 175, + "docs": [] + }, + { + "name": "Mortal176", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 176, + "docs": [] + }, + { + "name": "Mortal177", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 177, + "docs": [] + }, + { + "name": "Mortal178", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 178, + "docs": [] + }, + { + "name": "Mortal179", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 179, + "docs": [] + }, + { + "name": "Mortal180", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 180, + "docs": [] + }, + { + "name": "Mortal181", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 181, + "docs": [] + }, + { + "name": "Mortal182", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 182, + "docs": [] + }, + { + "name": "Mortal183", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 183, + "docs": [] + }, + { + "name": "Mortal184", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 184, + "docs": [] + }, + { + "name": "Mortal185", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 185, + "docs": [] + }, + { + "name": "Mortal186", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 186, + "docs": [] + }, + { + "name": "Mortal187", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 187, + "docs": [] + }, + { + "name": "Mortal188", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 188, + "docs": [] + }, + { + "name": "Mortal189", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 189, + "docs": [] + }, + { + "name": "Mortal190", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 190, + "docs": [] + }, + { + "name": "Mortal191", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 191, + "docs": [] + }, + { + "name": "Mortal192", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 192, + "docs": [] + }, + { + "name": "Mortal193", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 193, + "docs": [] + }, + { + "name": "Mortal194", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 194, + "docs": [] + }, + { + "name": "Mortal195", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 195, + "docs": [] + }, + { + "name": "Mortal196", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 196, + "docs": [] + }, + { + "name": "Mortal197", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 197, + "docs": [] + }, + { + "name": "Mortal198", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 198, + "docs": [] + }, + { + "name": "Mortal199", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 199, + "docs": [] + }, + { + "name": "Mortal200", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 200, + "docs": [] + }, + { + "name": "Mortal201", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 201, + "docs": [] + }, + { + "name": "Mortal202", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 202, + "docs": [] + }, + { + "name": "Mortal203", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 203, + "docs": [] + }, + { + "name": "Mortal204", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 204, + "docs": [] + }, + { + "name": "Mortal205", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 205, + "docs": [] + }, + { + "name": "Mortal206", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 206, + "docs": [] + }, + { + "name": "Mortal207", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 207, + "docs": [] + }, + { + "name": "Mortal208", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 208, + "docs": [] + }, + { + "name": "Mortal209", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 209, + "docs": [] + }, + { + "name": "Mortal210", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 210, + "docs": [] + }, + { + "name": "Mortal211", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 211, + "docs": [] + }, + { + "name": "Mortal212", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 212, + "docs": [] + }, + { + "name": "Mortal213", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 213, + "docs": [] + }, + { + "name": "Mortal214", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 214, + "docs": [] + }, + { + "name": "Mortal215", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 215, + "docs": [] + }, + { + "name": "Mortal216", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 216, + "docs": [] + }, + { + "name": "Mortal217", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 217, + "docs": [] + }, + { + "name": "Mortal218", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 218, + "docs": [] + }, + { + "name": "Mortal219", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 219, + "docs": [] + }, + { + "name": "Mortal220", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 220, + "docs": [] + }, + { + "name": "Mortal221", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 221, + "docs": [] + }, + { + "name": "Mortal222", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 222, + "docs": [] + }, + { + "name": "Mortal223", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 223, + "docs": [] + }, + { + "name": "Mortal224", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 224, + "docs": [] + }, + { + "name": "Mortal225", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 225, + "docs": [] + }, + { + "name": "Mortal226", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 226, + "docs": [] + }, + { + "name": "Mortal227", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 227, + "docs": [] + }, + { + "name": "Mortal228", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 228, + "docs": [] + }, + { + "name": "Mortal229", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 229, + "docs": [] + }, + { + "name": "Mortal230", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 230, + "docs": [] + }, + { + "name": "Mortal231", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 231, + "docs": [] + }, + { + "name": "Mortal232", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 232, + "docs": [] + }, + { + "name": "Mortal233", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 233, + "docs": [] + }, + { + "name": "Mortal234", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 234, + "docs": [] + }, + { + "name": "Mortal235", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 235, + "docs": [] + }, + { + "name": "Mortal236", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 236, + "docs": [] + }, + { + "name": "Mortal237", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 237, + "docs": [] + }, + { + "name": "Mortal238", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 238, + "docs": [] + }, + { + "name": "Mortal239", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 239, + "docs": [] + }, + { + "name": "Mortal240", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 240, + "docs": [] + }, + { + "name": "Mortal241", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 241, + "docs": [] + }, + { + "name": "Mortal242", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 242, + "docs": [] + }, + { + "name": "Mortal243", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 243, + "docs": [] + }, + { + "name": "Mortal244", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 244, + "docs": [] + }, + { + "name": "Mortal245", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 245, + "docs": [] + }, + { + "name": "Mortal246", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 246, + "docs": [] + }, + { + "name": "Mortal247", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 247, + "docs": [] + }, + { + "name": "Mortal248", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 248, + "docs": [] + }, + { + "name": "Mortal249", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 249, + "docs": [] + }, + { + "name": "Mortal250", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 250, + "docs": [] + }, + { + "name": "Mortal251", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 251, + "docs": [] + }, + { + "name": "Mortal252", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 252, + "docs": [] + }, + { + "name": "Mortal253", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 253, + "docs": [] + }, + { + "name": "Mortal254", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 254, + "docs": [] + }, + { + "name": "Mortal255", + "fields": [ + { + "name": null, + "type": 2, + "typeName": null, + "docs": [] + } + ], + "index": 255, + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 700, + "type": { + "path": [ + "frame_system", + "extensions", + "check_nonce", + "CheckNonce" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 111, + "typeName": "T::Index", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 701, + "type": { + "path": [ + "frame_system", + "extensions", + "check_weight", + "CheckWeight" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + }, + { + "id": 702, + "type": { + "path": [ + "pallet_transaction_payment", + "ChargeTransactionPayment" + ], + "params": [ + { + "name": "T", + "type": null + } + ], + "def": { + "composite": { + "fields": [ + { + "name": null, + "type": 46, + "typeName": "BalanceOf", + "docs": [] + } + ] + } + }, + "docs": [] + } + }, + { + "id": 703, + "type": { + "path": [ + "kusama_runtime", + "Runtime" + ], + "params": [], + "def": { + "composite": { + "fields": [] + } + }, + "docs": [] + } + } + ] + }, + "pallets": [ + { + "name": "System", + "storage": { + "prefix": "System", + "items": [ + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 3 + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The full account information for a particular account ID." + ] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " Total extrinsics count for the current block." + ] + }, + { + "name": "BlockWeight", + "modifier": "Default", + "type": { + "plain": 7 + }, + "fallback": "0x000000000000000000000000000000000000000000000000", + "docs": [ + " The current weight for the block." + ] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " Total length (in bytes) for all extrinsics put together, for the current block." + ] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 9 + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Map of block numbers to block hashes." + ] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 10 + } + }, + "fallback": "0x00", + "docs": [ + " Extrinsics data for the current block (maps an extrinsic's index to its data)." + ] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "plain": 9 + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Hash of the previous block." + ] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "plain": 11 + }, + "fallback": "0x00", + "docs": [ + " Digest of the current block, also part of the block header." + ] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "plain": 18 + }, + "fallback": "0x00", + "docs": [ + " Events deposited for the current block.", + "", + " NOTE: This storage item is explicitly unbounded since it is never intended to be read", + " from within the runtime." + ] + }, + { + "name": "EventCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The number of events in the `Events` list." + ] + }, + { + "name": "EventTopics", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 9, + "value": 144 + } + }, + "fallback": "0x00", + "docs": [ + " Mapping between a topic (represented by T::Hash) and a vector of indexes", + " of events in the `>` list.", + "", + " All topic vectors have deterministic storage locations depending on the topic. This", + " allows light-clients to leverage the changes trie storage tracking mechanism and", + " in case of changes fetch the list of events of interest.", + "", + " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just", + " the `EventIndex` then in case if the topic has the same contents on the next block", + " no notification will be triggered thus the event might be lost." + ] + }, + { + "name": "LastRuntimeUpgrade", + "modifier": "Optional", + "type": { + "plain": 145 + }, + "fallback": "0x00", + "docs": [ + " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened." + ] + }, + { + "name": "UpgradedToU32RefCount", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not." + ] + }, + { + "name": "UpgradedToTripleRefCount", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " True if we have upgraded so that AccountInfo contains three types of `RefCount`. False", + " (default) if not." + ] + }, + { + "name": "ExecutionPhase", + "modifier": "Optional", + "type": { + "plain": 142 + }, + "fallback": "0x00", + "docs": [ + " The execution phase of the block." + ] + } + ] + }, + "calls": { + "type": 147 + }, + "events": { + "type": 21 + }, + "constants": [ + { + "name": "BlockWeights", + "type": 152, + "value": "0x00f2052a0100000000204aa9d1010000405973070000000001c0766c8f58010000010098f73e5d010000010000000000000000405973070000000001c0febef9cc0100000100204aa9d1010000010088526a740000004059730700000000000000", + "docs": [ + " Block & extrinsics weights: base values and limits." + ] + }, + { + "name": "BlockLength", + "type": 156, + "value": "0x00003c000000500000005000", + "docs": [ + " The maximum length of a block (in bytes)." + ] + }, + { + "name": "BlockHashCount", + "type": 4, + "value": "0x60090000", + "docs": [ + " Maximum number of block number to block hash mappings to keep (oldest pruned first)." + ] + }, + { + "name": "DbWeight", + "type": 158, + "value": "0x40787d010000000000e1f50500000000", + "docs": [ + " The weight of runtime database operations the runtime can invoke." + ] + }, + { + "name": "Version", + "type": 159, + "value": "0x186b7573616d61347061726974792d6b7573616d6102000000972300000000000038df6acb689907609b0300000037e397fc7c91f5e40100000040fe3ad401f8959a05000000d2bc9897eed08f1503000000f78b278be53f454c02000000af2c0297a23e6d3d0100000049eaaf1b548a0cb00100000091d5df18b0d2cf5801000000ed99c5acb25eedf503000000cbca25e39f14238702000000687ad44ad37f03c201000000ab3c0572291feb8b01000000bc9d89904f5b923f0100000037c8bb1350a9a2a80100000007000000", + "docs": [ + " Get the chain's current version." + ] + }, + { + "name": "SS58Prefix", + "type": 75, + "value": "0x0200", + "docs": [ + " The designated SS85 prefix of this chain.", + "", + " This replaces the \"ss58Format\" property declared in the chain spec. Reason is", + " that the runtime should know about the prefix in order to make use of it as", + " an identifier of the chain." + ] + } + ], + "errors": { + "type": 163 + }, + "index": 0 + }, + { + "name": "Babe", + "storage": { + "prefix": "Babe", + "items": [ + { + "name": "EpochIndex", + "modifier": "Default", + "type": { + "plain": 8 + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current epoch index." + ] + }, + { + "name": "Authorities", + "modifier": "Default", + "type": { + "plain": 164 + }, + "fallback": "0x00", + "docs": [ + " Current epoch authorities." + ] + }, + { + "name": "GenesisSlot", + "modifier": "Default", + "type": { + "plain": 168 + }, + "fallback": "0x0000000000000000", + "docs": [ + " The slot at which the first epoch actually started. This is 0", + " until the first block of the chain." + ] + }, + { + "name": "CurrentSlot", + "modifier": "Default", + "type": { + "plain": 168 + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current slot number." + ] + }, + { + "name": "Randomness", + "modifier": "Default", + "type": { + "plain": 1 + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The epoch randomness for the *current* epoch.", + "", + " # Security", + "", + " This MUST NOT be used for gambling, as it can be influenced by a", + " malicious validator in the short term. It MAY be used in many", + " cryptographic protocols, however, so long as one remembers that this", + " (like everything else on-chain) it is public. For example, it can be", + " used where a number is needed that cannot have been chosen by an", + " adversary, for purposes such as public-coin zero-knowledge proofs." + ] + }, + { + "name": "PendingEpochConfigChange", + "modifier": "Optional", + "type": { + "plain": 169 + }, + "fallback": "0x00", + "docs": [ + " Pending epoch configuration change that will be applied when the next epoch is enacted." + ] + }, + { + "name": "NextRandomness", + "modifier": "Default", + "type": { + "plain": 1 + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Next epoch randomness." + ] + }, + { + "name": "NextAuthorities", + "modifier": "Default", + "type": { + "plain": 164 + }, + "fallback": "0x00", + "docs": [ + " Next epoch authorities." + ] + }, + { + "name": "SegmentIndex", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Randomness under construction.", + "", + " We make a tradeoff between storage accesses and list length.", + " We store the under-construction randomness in segments of up to", + " `UNDER_CONSTRUCTION_SEGMENT_LENGTH`.", + "", + " Once a segment reaches this length, we begin the next one.", + " We reset all segments and return to `0` at the beginning of every", + " epoch." + ] + }, + { + "name": "UnderConstruction", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 172 + } + }, + "fallback": "0x00", + "docs": [ + " TWOX-NOTE: `SegmentIndex` is an increasing integer, so this is okay." + ] + }, + { + "name": "Initialized", + "modifier": "Optional", + "type": { + "plain": 174 + }, + "fallback": "0x00", + "docs": [ + " Temporary value (cleared at block finalization) which is `Some`", + " if per-block initialization has already been called for current block." + ] + }, + { + "name": "AuthorVrfRandomness", + "modifier": "Default", + "type": { + "plain": 174 + }, + "fallback": "0x00", + "docs": [ + " This field should always be populated during block processing unless", + " secondary plain slots are enabled (which don't contain a VRF output).", + "", + " It is set in `on_initialize`, before it will contain the value from the last block." + ] + }, + { + "name": "EpochStart", + "modifier": "Default", + "type": { + "plain": 71 + }, + "fallback": "0x0000000000000000", + "docs": [ + " The block numbers when the last and current epoch have started, respectively `N-1` and", + " `N`.", + " NOTE: We track this is in order to annotate the block number when a given pool of", + " entropy was fixed (i.e. it was known to chain observers). Since epochs are defined in", + " slots, which may be skipped, the block numbers may not line up with the slot numbers." + ] + }, + { + "name": "Lateness", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " How late the current block is compared to its parent.", + "", + " This entry is populated as part of block execution and is cleaned up", + " on block finalization. Querying this storage entry outside of block", + " execution context should always yield zero." + ] + }, + { + "name": "EpochConfig", + "modifier": "Optional", + "type": { + "plain": 175 + }, + "fallback": "0x00", + "docs": [ + " The configuration for the current epoch. Should never be `None` as it is initialized in", + " genesis." + ] + }, + { + "name": "NextEpochConfig", + "modifier": "Optional", + "type": { + "plain": 175 + }, + "fallback": "0x00", + "docs": [ + " The configuration for the next epoch, `None` if the config will not change", + " (you can fallback to `EpochConfig` instead in that case)." + ] + } + ] + }, + "calls": { + "type": 176 + }, + "events": null, + "constants": [ + { + "name": "EpochDuration", + "type": 8, + "value": "0x5802000000000000", + "docs": [ + " The amount of time, in slots, that each epoch should last.", + " NOTE: Currently it is not possible to change the epoch duration after", + " the chain has started. Attempting to do so will brick block production." + ] + }, + { + "name": "ExpectedBlockTime", + "type": 8, + "value": "0x7017000000000000", + "docs": [ + " The expected average block time at which BABE should be creating", + " blocks. Since BABE is probabilistic it is not trivial to figure out", + " what the expected average block time should be based on the slot", + " duration and the security parameter `c` (where `1 - c` represents", + " the probability of a slot being empty)." + ] + }, + { + "name": "MaxAuthorities", + "type": 4, + "value": "0xa0860100", + "docs": [ + " Max number of authorities allowed" + ] + } + ], + "errors": { + "type": 181 + }, + "index": 1 + }, + { + "name": "Timestamp", + "storage": { + "prefix": "Timestamp", + "items": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "plain": 8 + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current time for the current block." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " Did the timestamp get updated in this block?" + ] + } + ] + }, + "calls": { + "type": 182 + }, + "events": null, + "constants": [ + { + "name": "MinimumPeriod", + "type": 8, + "value": "0xb80b000000000000", + "docs": [ + " The minimum period between blocks. Beware that this is different to the *expected*", + " period that the block production apparatus provides. Your chosen consensus system will", + " generally work with this to determine a sensible block time. e.g. For Aura, it will be", + " double this period on default settings." + ] + } + ], + "errors": null, + "index": 2 + }, + { + "name": "Indices", + "storage": { + "prefix": "Indices", + "items": [ + { + "name": "Accounts", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 4, + "value": 183 + } + }, + "fallback": "0x00", + "docs": [ + " The lookup from index to account." + ] + } + ] + }, + "calls": { + "type": 184 + }, + "events": { + "type": 28 + }, + "constants": [ + { + "name": "Deposit", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " The deposit needed for reserving an index." + ] + } + ], + "errors": { + "type": 185 + }, + "index": 3 + }, + { + "name": "Balances", + "storage": { + "prefix": "Balances", + "items": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total units issued in the system." + ] + }, + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 5 + } + }, + "fallback": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The balance of an account.", + "", + " NOTE: This is only used in the case that this pallet is used to store balances." + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 186 + } + }, + "fallback": "0x00", + "docs": [ + " Any liquidity locks on some account balances.", + " NOTE: Should only be accessed when setting, changing and freeing a lock." + ] + }, + { + "name": "Reserves", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 190 + } + }, + "fallback": "0x00", + "docs": [ + " Named reserves on some account balances." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": 193 + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " This is set to v2.0.0 for new networks." + ] + } + ] + }, + "calls": { + "type": 194 + }, + "events": { + "type": 29 + }, + "constants": [ + { + "name": "ExistentialDeposit", + "type": 6, + "value": "0x55a0fc01000000000000000000000000", + "docs": [ + " The minimum amount required to keep an account open." + ] + }, + { + "name": "MaxLocks", + "type": 4, + "value": "0x32000000", + "docs": [ + " The maximum number of locks that should exist on an account.", + " Not strictly enforced, but used for weight estimation." + ] + }, + { + "name": "MaxReserves", + "type": 4, + "value": "0x32000000", + "docs": [ + " The maximum number of named reserves that can exist on an account." + ] + } + ], + "errors": { + "type": 197 + }, + "index": 4 + }, + { + "name": "TransactionPayment", + "storage": { + "prefix": "TransactionPayment", + "items": [ + { + "name": "NextFeeMultiplier", + "modifier": "Default", + "type": { + "plain": 198 + }, + "fallback": "0x000064a7b3b6e00d0000000000000000", + "docs": [] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": 199 + }, + "fallback": "0x00", + "docs": [] + } + ] + }, + "calls": null, + "events": null, + "constants": [ + { + "name": "TransactionByteFee", + "type": 6, + "value": "0x12160500000000000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "OperationalFeeMultiplier", + "type": 2, + "value": "0x05", + "docs": [ + " A fee mulitplier for `Operational` extrinsics to compute \"virtual tip\" to boost their", + " `priority`", + "", + " This value is multipled by the `final_fee` to obtain a \"virtual tip\" that is later", + " added to a tip component in regular `priority` calculations.", + " It means that a `Normal` transaction can front-run a similarly-sized `Operational`", + " extrinsic (with no tip), by including a tip value greater than the virtual tip.", + "", + " ```rust,ignore", + " // For `Normal`", + " let priority = priority_calc(tip);", + "", + " // For `Operational`", + " let virtual_tip = (inclusion_fee + tip) * OperationalFeeMultiplier;", + " let priority = priority_calc(tip + virtual_tip);", + " ```", + "", + " Note that since we use `final_fee` the multiplier applies also to the regular `tip`", + " sent with the transaction. So, not only does the transaction get a priority bump based", + " on the `inclusion_fee`, but we also amplify the impact of tips applied to `Operational`", + " transactions." + ] + }, + { + "name": "WeightToFee", + "type": 200, + "value": "0x0400000000000000000000000000000000a9e696010001", + "docs": [ + " The polynomial that is applied in order to derive fee from weight." + ] + } + ], + "errors": null, + "index": 33 + }, + { + "name": "Authorship", + "storage": { + "prefix": "Authorship", + "items": [ + { + "name": "Uncles", + "modifier": "Default", + "type": { + "plain": 202 + }, + "fallback": "0x00", + "docs": [ + " Uncles" + ] + }, + { + "name": "Author", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " Author of current block." + ] + }, + { + "name": "DidSetUncles", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " Whether uncles were already set in this block." + ] + } + ] + }, + "calls": { + "type": 205 + }, + "events": null, + "constants": [ + { + "name": "UncleGenerations", + "type": 4, + "value": "0x00000000", + "docs": [ + " The number of blocks back we should accept uncles.", + " This means that we will deal with uncle-parents that are", + " `UncleGenerations + 1` before `now`." + ] + } + ], + "errors": { + "type": 207 + }, + "index": 5 + }, + { + "name": "Staking", + "storage": { + "prefix": "Staking", + "items": [ + { + "name": "HistoryDepth", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x54000000", + "docs": [ + " Number of eras to keep in history.", + "", + " Information is kept for eras in `[current_era - history_depth; current_era]`.", + "", + " Must be more than the number of eras delayed by session otherwise. I.e. active era must", + " always be in history. I.e. `active_era > current_era - history_depth` must be", + " guaranteed." + ] + }, + { + "name": "ValidatorCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The ideal number of staking participants." + ] + }, + { + "name": "MinimumValidatorCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Minimum number of staking participants before emergency conditions are imposed." + ] + }, + { + "name": "Invulnerables", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " Any validators that may never be slashed or forcibly kicked. It's a Vec since they're", + " easy to initialize and the performance hit is minimal (we expect no more than four", + " invulnerables) and restricted to testnets." + ] + }, + { + "name": "Bonded", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 0 + } + }, + "fallback": "0x00", + "docs": [ + " Map from all locked \"stash\" accounts to the controller account." + ] + }, + { + "name": "MinNominatorBond", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum active bond to become and maintain the role of a nominator." + ] + }, + { + "name": "MinValidatorBond", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The minimum active bond to become and maintain the role of a validator." + ] + }, + { + "name": "Ledger", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 208 + } + }, + "fallback": "0x00", + "docs": [ + " Map from all (unlocked) \"controller\" accounts to the info regarding the staking." + ] + }, + { + "name": "Payee", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 212 + } + }, + "fallback": "0x00", + "docs": [ + " Where the reward payment should be made. Keyed by stash." + ] + }, + { + "name": "Validators", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 213 + } + }, + "fallback": "0x0000", + "docs": [ + " The map from (wannabe) validator stash key to the preferences of that validator.", + "", + " When updating this storage item, you must also update the `CounterForValidators`." + ] + }, + { + "name": "CounterForValidators", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " A tracker to keep count of the number of items in the `Validators` map." + ] + }, + { + "name": "MaxValidatorsCount", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " The maximum validator count before we stop allowing new validators to join.", + "", + " When this value is not set, no limits are enforced." + ] + }, + { + "name": "Nominators", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 215 + } + }, + "fallback": "0x00", + "docs": [ + " The map from nominator stash key to the set of stash keys of all validators to nominate.", + "", + " When updating this storage item, you must also update the `CounterForNominators`." + ] + }, + { + "name": "CounterForNominators", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " A tracker to keep count of the number of items in the `Nominators` map." + ] + }, + { + "name": "MaxNominatorsCount", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " The maximum nominator count before we stop allowing new validators to join.", + "", + " When this value is not set, no limits are enforced." + ] + }, + { + "name": "CurrentEra", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " The current era index.", + "", + " This is the latest planned era, depending on how the Session pallet queues the validator", + " set, it might be active or not." + ] + }, + { + "name": "ActiveEra", + "modifier": "Optional", + "type": { + "plain": 216 + }, + "fallback": "0x00", + "docs": [ + " The active era information, it holds index and start.", + "", + " The active era is the era being currently rewarded. Validator set of this era must be", + " equal to [`SessionInterface::validators`]." + ] + }, + { + "name": "ErasStartSessionIndex", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " The session index at which the era start for the last `HISTORY_DEPTH` eras.", + "", + " Note: This tracks the starting session (i.e. session index when era start being active)", + " for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`." + ] + }, + { + "name": "ErasStakers", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 45 + } + }, + "fallback": "0x000000", + "docs": [ + " Exposure of validator at era.", + "", + " This is keyed first by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras.", + " If stakers hasn't been set or has been removed then empty exposure is returned." + ] + }, + { + "name": "ErasStakersClipped", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 45 + } + }, + "fallback": "0x000000", + "docs": [ + " Clipped Exposure of validator at era.", + "", + " This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the", + " `T::MaxNominatorRewardedPerValidator` biggest stakers.", + " (Note: the field `total` and `own` of the exposure remains unchanged).", + " This is used to limit the i/o cost for the nominator payout.", + "", + " This is keyed fist by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras.", + " If stakers hasn't been set or has been removed then empty exposure is returned." + ] + }, + { + "name": "ErasValidatorPrefs", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 213 + } + }, + "fallback": "0x0000", + "docs": [ + " Similar to `ErasStakers`, this holds the preferences of validators.", + "", + " This is keyed first by the era index to allow bulk deletion and then the stash account.", + "", + " Is it removed after `HISTORY_DEPTH` eras." + ] + }, + { + "name": "ErasValidatorReward", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 6 + } + }, + "fallback": "0x00", + "docs": [ + " The total validator era payout for the last `HISTORY_DEPTH` eras.", + "", + " Eras that haven't finished yet or has been removed doesn't have reward." + ] + }, + { + "name": "ErasRewardPoints", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 218 + } + }, + "fallback": "0x0000000000", + "docs": [ + " Rewards for the last `HISTORY_DEPTH` eras.", + " If reward hasn't been set or has been removed then 0 reward is returned." + ] + }, + { + "name": "ErasTotalStake", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 6 + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total amount staked for the last `HISTORY_DEPTH` eras.", + " If total hasn't been set or has been removed then 0 stake is returned." + ] + }, + { + "name": "ForceEra", + "modifier": "Default", + "type": { + "plain": 222 + }, + "fallback": "0x00", + "docs": [ + " Mode of era forcing." + ] + }, + { + "name": "SlashRewardFraction", + "modifier": "Default", + "type": { + "plain": 148 + }, + "fallback": "0x00000000", + "docs": [ + " The percentage of the slash that is distributed to reporters.", + "", + " The rest of the slashed value is handled by the `Slash`." + ] + }, + { + "name": "CanceledSlashPayout", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The amount of currency given to reporters of a slash event which was", + " canceled by extraordinary circumstances (e.g. governance)." + ] + }, + { + "name": "UnappliedSlashes", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 223 + } + }, + "fallback": "0x00", + "docs": [ + " All unapplied slashes that are queued for later." + ] + }, + { + "name": "BondedEras", + "modifier": "Default", + "type": { + "plain": 144 + }, + "fallback": "0x00", + "docs": [ + " A mapping from still-bonded eras to the first session index of that era.", + "", + " Must contains information for eras for the range:", + " `[active_era - bounding_duration; active_era]`" + ] + }, + { + "name": "ValidatorSlashInEra", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 225 + } + }, + "fallback": "0x00", + "docs": [ + " All slashing events on validators, mapped by era to the highest slash proportion", + " and slash value of the era." + ] + }, + { + "name": "NominatorSlashInEra", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 6 + } + }, + "fallback": "0x00", + "docs": [ + " All slashing events on nominators, mapped by era to the highest slash value of the era." + ] + }, + { + "name": "SlashingSpans", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 226 + } + }, + "fallback": "0x00", + "docs": [ + " Slashing spans for stash accounts." + ] + }, + { + "name": "SpanSlash", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 221, + "value": 227 + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Records information about the maximum slash of a stash within a slashing span,", + " as well as how much reward has been paid out." + ] + }, + { + "name": "EarliestUnappliedSlash", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " The earliest era for which we have a pending, unapplied slash." + ] + }, + { + "name": "CurrentPlannedSession", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The last planned session scheduled by the session pallet.", + "", + " This is basically in sync with the call to [`pallet_session::SessionManager::new_session`]." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": 228 + }, + "fallback": "0x07", + "docs": [ + " True if network has been upgraded to this version.", + " Storage version of the pallet.", + "", + " This is set to v7.0.0 for new networks." + ] + }, + { + "name": "ChillThreshold", + "modifier": "Optional", + "type": { + "plain": 229 + }, + "fallback": "0x00", + "docs": [ + " The threshold for when users can start calling `chill_other` for other validators /", + " nominators. The threshold is compared to the actual number of validators / nominators", + " (`CountFor*`) in the system compared to the configured max (`Max*Count`)." + ] + } + ] + }, + "calls": { + "type": 230 + }, + "events": { + "type": 31 + }, + "constants": [ + { + "name": "SessionsPerEra", + "type": 4, + "value": "0x06000000", + "docs": [ + " Number of sessions per era." + ] + }, + { + "name": "BondingDuration", + "type": 4, + "value": "0x1c000000", + "docs": [ + " Number of eras that staked funds must remain bonded for." + ] + }, + { + "name": "SlashDeferDuration", + "type": 4, + "value": "0x1b000000", + "docs": [ + " Number of eras that slashes are deferred by, after computation.", + "", + " This should be less than the bonding duration. Set to 0 if slashes", + " should be applied immediately, without opportunity for intervention." + ] + }, + { + "name": "MaxNominatorRewardedPerValidator", + "type": 4, + "value": "0x00010000", + "docs": [ + " The maximum number of nominators rewarded for each validator.", + "", + " For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can", + " claim their reward. This used to limit the i/o cost for the nominator payout." + ] + }, + { + "name": "MaxNominations", + "type": 4, + "value": "0x18000000", + "docs": [] + } + ], + "errors": { + "type": 234 + }, + "index": 6 + }, + { + "name": "Offences", + "storage": { + "prefix": "Offences", + "items": [ + { + "name": "Reports", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 9, + "value": 235 + } + }, + "fallback": "0x00", + "docs": [ + " The primary structure that holds all offence records keyed by report identifiers." + ] + }, + { + "name": "ConcurrentReportsIndex", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 236, + "value": 143 + } + }, + "fallback": "0x00", + "docs": [ + " A vector of reports of the same kind that happened at the same time slot." + ] + }, + { + "name": "ReportsByKindIndex", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 33, + "value": 10 + } + }, + "fallback": "0x00", + "docs": [ + " Enumerates all reports of a kind along with the time they happened.", + "", + " All reports are sorted by the time of offence.", + "", + " Note that the actual type of this mapping is `Vec`, this is because values of", + " different types are not supported at the moment so we are doing the manual serialization." + ] + } + ] + }, + "calls": null, + "events": { + "type": 32 + }, + "constants": [], + "errors": null, + "index": 7 + }, + { + "name": "Historical", + "storage": null, + "calls": null, + "events": null, + "constants": [], + "errors": null, + "index": 34 + }, + { + "name": "Session", + "storage": { + "prefix": "Session", + "items": [ + { + "name": "Validators", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " The current set of validators." + ] + }, + { + "name": "CurrentIndex", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Current index of the session." + ] + }, + { + "name": "QueuedChanged", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " True if the underlying economic identities or weighting behind the validators", + " has changed in the queued validator set." + ] + }, + { + "name": "QueuedKeys", + "modifier": "Default", + "type": { + "plain": 237 + }, + "fallback": "0x00", + "docs": [ + " The queued keys for the next session. When the next session begins, these keys", + " will be used to determine the validator's session keys." + ] + }, + { + "name": "DisabledValidators", + "modifier": "Default", + "type": { + "plain": 211 + }, + "fallback": "0x00", + "docs": [ + " Indices of disabled validators.", + "", + " The set is cleared when `on_session_ending` returns a new set of identities." + ] + }, + { + "name": "NextKeys", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 239 + } + }, + "fallback": "0x00", + "docs": [ + " The next session keys for a validator." + ] + }, + { + "name": "KeyOwner", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 243, + "value": 0 + } + }, + "fallback": "0x00", + "docs": [ + " The owner of a key. The key is the `KeyTypeId` + the encoded key." + ] + } + ] + }, + "calls": { + "type": 245 + }, + "events": { + "type": 34 + }, + "constants": [], + "errors": { + "type": 246 + }, + "index": 8 + }, + { + "name": "Grandpa", + "storage": { + "prefix": "Grandpa", + "items": [ + { + "name": "State", + "modifier": "Default", + "type": { + "plain": 247 + }, + "fallback": "0x00", + "docs": [ + " State of the current authority set." + ] + }, + { + "name": "PendingChange", + "modifier": "Optional", + "type": { + "plain": 248 + }, + "fallback": "0x00", + "docs": [ + " Pending change: (signaled at, scheduled change)." + ] + }, + { + "name": "NextForced", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " next block number where we can force a change." + ] + }, + { + "name": "Stalled", + "modifier": "Optional", + "type": { + "plain": 71 + }, + "fallback": "0x00", + "docs": [ + " `true` if we are currently stalled." + ] + }, + { + "name": "CurrentSetId", + "modifier": "Default", + "type": { + "plain": 8 + }, + "fallback": "0x0000000000000000", + "docs": [ + " The number of changes (both in terms of keys and underlying economic responsibilities)", + " in the \"set\" of Grandpa validators from genesis." + ] + }, + { + "name": "SetIdSession", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 8, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " A mapping from grandpa set ID to the index of the *most recent* session for which its", + " members were responsible.", + "", + " TWOX-NOTE: `SetId` is not under user control." + ] + } + ] + }, + "calls": { + "type": 250 + }, + "events": { + "type": 35 + }, + "constants": [ + { + "name": "MaxAuthorities", + "type": 4, + "value": "0xa0860100", + "docs": [ + " Max Authorities in use" + ] + } + ], + "errors": { + "type": 261 + }, + "index": 10 + }, + { + "name": "ImOnline", + "storage": { + "prefix": "ImOnline", + "items": [ + { + "name": "HeartbeatAfter", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The block number after which it's ok to send heartbeats in the current", + " session.", + "", + " At the beginning of each session we set this to a value that should fall", + " roughly in the middle of the session duration. The idea is to first wait for", + " the validators to produce a block in the current session, so that the", + " heartbeat later on will not be necessary.", + "", + " This value will only be used as a fallback if we fail to get a proper session", + " progress estimate from `NextSessionRotation`, as those estimates should be", + " more accurate then the value we calculate for `HeartbeatAfter`." + ] + }, + { + "name": "Keys", + "modifier": "Default", + "type": { + "plain": 262 + }, + "fallback": "0x00", + "docs": [ + " The current set of keys that may issue a heartbeat." + ] + }, + { + "name": "ReceivedHeartbeats", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 71, + "value": 264 + } + }, + "fallback": "0x00", + "docs": [ + " For each session index, we keep a mapping of 'SessionIndex` and `AuthIndex` to", + " `WrapperOpaque`." + ] + }, + { + "name": "AuthoredBlocks", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 217, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " For each session index, we keep a mapping of `ValidatorId` to the", + " number of blocks authored by the given authority." + ] + } + ] + }, + "calls": { + "type": 269 + }, + "events": { + "type": 40 + }, + "constants": [ + { + "name": "UnsignedPriority", + "type": 8, + "value": "0xffffffffffffffff", + "docs": [ + " A configuration for base priority of unsigned transactions.", + "", + " This is exposed so that it can be tuned for particular runtime, when", + " multiple pallets send unsigned transactions." + ] + } + ], + "errors": { + "type": 276 + }, + "index": 11 + }, + { + "name": "AuthorityDiscovery", + "storage": null, + "calls": null, + "events": null, + "constants": [], + "errors": null, + "index": 12 + }, + { + "name": "Democracy", + "storage": { + "prefix": "Democracy", + "items": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "plain": 277 + }, + "fallback": "0x00", + "docs": [ + " The public proposals. Unsorted. The second item is the proposal's hash." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 279 + } + }, + "fallback": "0x00", + "docs": [ + " Those who have locked a deposit.", + "", + " TWOX-NOTE: Safe, as increasing integer keys are safe." + ] + }, + { + "name": "Preimages", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 280 + } + }, + "fallback": "0x00", + "docs": [ + " Map of hashes to the proposal preimage, along with who registered it and their deposit.", + " The block number is the block at which it was deposited." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The next free referendum index, aka the number of referenda started so far." + ] + }, + { + "name": "LowestUnbaked", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The lowest referendum index representing an unbaked referendum. Equal to", + " `ReferendumCount` if there isn't a unbaked referendum." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 281 + } + }, + "fallback": "0x00", + "docs": [ + " Information concerning any given referendum.", + "", + " TWOX-NOTE: SAFE as indexes are not under an attacker’s control." + ] + }, + { + "name": "VotingOf", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 284 + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " All votes for a particular voter. We store the balance for the number of votes that we", + " have recorded. The second item is the total amount of delegations, that will be added.", + "", + " TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway." + ] + }, + { + "name": "Locks", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " Accounts for which there are locks in action which may be removed at some point in the", + " future. The value is the block number at which the lock expires and may be removed.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "LastTabledWasExternal", + "modifier": "Default", + "type": { + "plain": 55 + }, + "fallback": "0x00", + "docs": [ + " True if the last referendum tabled was submitted externally. False if it was a public", + " proposal." + ] + }, + { + "name": "NextExternal", + "modifier": "Optional", + "type": { + "plain": 292 + }, + "fallback": "0x00", + "docs": [ + " The referendum to be tabled whenever it would be valid to table an external proposal.", + " This happens when a referendum needs to be tabled and one of two conditions are met:", + " - `LastTabledWasExternal` is `false`; or", + " - `PublicProps` is empty." + ] + }, + { + "name": "Blacklist", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 293 + } + }, + "fallback": "0x00", + "docs": [ + " A record of who vetoed what. Maps proposal hash to a possible existent block number", + " (until when it may not be resubmitted) and who vetoed it." + ] + }, + { + "name": "Cancellations", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 55 + } + }, + "fallback": "0x00", + "docs": [ + " Record of all proposals that have been subject to emergency cancellation." + ] + }, + { + "name": "StorageVersion", + "modifier": "Optional", + "type": { + "plain": 294 + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": { + "type": 295 + }, + "events": { + "type": 49 + }, + "constants": [ + { + "name": "EnactmentPeriod", + "type": 4, + "value": "0x00c20100", + "docs": [ + " The period between a proposal being approved and enacted.", + "", + " It should generally be a little more than the unstake period to ensure that", + " voting stakers have an opportunity to remove themselves from the system in the case", + " where they are on the losing side of a vote." + ] + }, + { + "name": "LaunchPeriod", + "type": 4, + "value": "0xc0890100", + "docs": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "VotingPeriod", + "type": 4, + "value": "0xc0890100", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "VoteLockingPeriod", + "type": 4, + "value": "0x00c20100", + "docs": [ + " The minimum period of vote locking.", + "", + " It should be no shorter than enactment period to ensure that in the case of an approval,", + " those successful voters are locked into the consequences that their votes entail." + ] + }, + { + "name": "MinimumDeposit", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "InstantAllowed", + "type": 55, + "value": "0x01", + "docs": [ + " Indicator for whether an emergency origin is even allowed to happen. Some chains may", + " want to set this permanently to `false`, others may want to condition it on things such", + " as an upgrade having happened recently." + ] + }, + { + "name": "FastTrackVotingPeriod", + "type": 4, + "value": "0x08070000", + "docs": [ + " Minimum voting period allowed for a fast-track referendum." + ] + }, + { + "name": "CooloffPeriod", + "type": 4, + "value": "0xc0890100", + "docs": [ + " Period in blocks where an external proposal may not be re-submitted after being vetoed." + ] + }, + { + "name": "PreimageByteDeposit", + "type": 6, + "value": "0x12160500000000000000000000000000", + "docs": [ + " The amount of balance that must be deposited per byte of preimage stored." + ] + }, + { + "name": "MaxVotes", + "type": 4, + "value": "0x64000000", + "docs": [ + " The maximum number of votes for an account.", + "", + " Also used to compute weight, an overly big value can", + " lead to extrinsic with very big weight: see `delegate` for instance." + ] + }, + { + "name": "MaxProposals", + "type": 4, + "value": "0x64000000", + "docs": [ + " The maximum number of public proposals that can exist at any time." + ] + } + ], + "errors": { + "type": 296 + }, + "index": 13 + }, + { + "name": "Council", + "storage": { + "prefix": "Council", + "items": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "plain": 297 + }, + "fallback": "0x00", + "docs": [ + " The hashes of the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 298 + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 526 + } + }, + "fallback": "0x00", + "docs": [ + " Votes on a given proposal, if it is ongoing." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " The current members of the collective. This is stored sorted (just by value)." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The prime member that helps determine the default vote behavior in case of absentations." + ] + } + ] + }, + "calls": { + "type": 299 + }, + "events": { + "type": 54 + }, + "constants": [], + "errors": { + "type": 527 + }, + "index": 14 + }, + { + "name": "TechnicalCommittee", + "storage": { + "prefix": "TechnicalCommittee", + "items": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "plain": 528 + }, + "fallback": "0x00", + "docs": [ + " The hashes of the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 298 + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 526 + } + }, + "fallback": "0x00", + "docs": [ + " Votes on a given proposal, if it is ongoing." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " The current members of the collective. This is stored sorted (just by value)." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The prime member that helps determine the default vote behavior in case of absentations." + ] + } + ] + }, + "calls": { + "type": 300 + }, + "events": { + "type": 56 + }, + "constants": [], + "errors": { + "type": 529 + }, + "index": 15 + }, + { + "name": "PhragmenElection", + "storage": { + "prefix": "PhragmenElection", + "items": [ + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": 530 + }, + "fallback": "0x00", + "docs": [ + " The current elected members.", + "", + " Invariant: Always sorted based on account id." + ] + }, + { + "name": "RunnersUp", + "modifier": "Default", + "type": { + "plain": 530 + }, + "fallback": "0x00", + "docs": [ + " The current reserved runners-up.", + "", + " Invariant: Always sorted based on rank (worse to best). Upon removal of a member, the", + " last (i.e. _best_) runner-up will be replaced." + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "plain": 58 + }, + "fallback": "0x00", + "docs": [ + " The present candidate list. A current member or runner-up can never enter this vector", + " and is always implicitly assumed to be a candidate.", + "", + " Second element is the deposit.", + "", + " Invariant: Always sorted based on account id." + ] + }, + { + "name": "ElectionRounds", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The total number of vote rounds that have happened, excluding the upcoming one." + ] + }, + { + "name": "Voting", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 532 + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Votes and locked stake of a particular voter.", + "", + " TWOX-NOTE: SAFE as `AccountId` is a crypto hash." + ] + } + ] + }, + "calls": { + "type": 301 + }, + "events": { + "type": 57 + }, + "constants": [ + { + "name": "PalletId", + "type": 125, + "value": "0x706872656c656374", + "docs": [ + " Identifier for the elections-phragmen pallet's lock" + ] + }, + { + "name": "CandidacyBond", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " How much should be locked up in order to submit one's candidacy." + ] + }, + { + "name": "VotingBondBase", + "type": 6, + "value": "0x10c55b920f0000000000000000000000", + "docs": [ + " Base deposit associated with voting.", + "", + " This should be sensibly high to economically ensure the pallet cannot be attacked by", + " creating a gigantic number of votes." + ] + }, + { + "name": "VotingBondFactor", + "type": 6, + "value": "0x80965b06000000000000000000000000", + "docs": [ + " The amount of bond that need to be locked for each vote (32 bytes)." + ] + }, + { + "name": "DesiredMembers", + "type": 4, + "value": "0x13000000", + "docs": [ + " Number of members to elect." + ] + }, + { + "name": "DesiredRunnersUp", + "type": 4, + "value": "0x13000000", + "docs": [ + " Number of runners_up to keep." + ] + }, + { + "name": "TermDuration", + "type": 4, + "value": "0x40380000", + "docs": [ + " How long each seat is kept. This defines the next block number at which an election", + " round will happen. If set to zero, no elections are ever triggered and the module will", + " be in passive mode." + ] + } + ], + "errors": { + "type": 533 + }, + "index": 16 + }, + { + "name": "TechnicalMembership", + "storage": { + "prefix": "TechnicalMembership", + "items": [ + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " The current membership, stored as an ordered Vec." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The current prime member, if one exists." + ] + } + ] + }, + "calls": { + "type": 303 + }, + "events": { + "type": 60 + }, + "constants": [], + "errors": { + "type": 534 + }, + "index": 17 + }, + { + "name": "Treasury", + "storage": { + "prefix": "Treasury", + "items": [ + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 535 + } + }, + "fallback": "0x00", + "docs": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "plain": 536 + }, + "fallback": "0x00", + "docs": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + }, + "calls": { + "type": 304 + }, + "events": { + "type": 61 + }, + "constants": [ + { + "name": "ProposalBond", + "type": 537, + "value": "0x50c30000", + "docs": [ + " Fraction of a proposal's value that should be bonded in order to place the proposal.", + " An accepted proposal gets these back. A rejected proposal does not." + ] + }, + { + "name": "ProposalBondMinimum", + "type": 6, + "value": "0x1098a4850f0000000000000000000000", + "docs": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "type": 4, + "value": "0x80510100", + "docs": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "type": 537, + "value": "0xd0070000", + "docs": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "PalletId", + "type": 538, + "value": "0x70792f7472737279", + "docs": [ + " The treasury's pallet id, used for deriving its sovereign account ID." + ] + }, + { + "name": "MaxApprovals", + "type": 4, + "value": "0x64000000", + "docs": [ + " The maximum number of approvals that can wait in the spending queue." + ] + } + ], + "errors": { + "type": 539 + }, + "index": 18 + }, + { + "name": "Claims", + "storage": { + "prefix": "Claims", + "items": [ + { + "name": "Claims", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 63, + "value": 6 + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "Total", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [] + }, + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 63, + "value": 309 + } + }, + "fallback": "0x00", + "docs": [ + " Vesting schedule for a claim.", + " First balance is the total amount that should be held for vesting.", + " Second balance is how much should be unlocked per block.", + " The block number is when the vesting should start." + ] + }, + { + "name": "Signing", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 63, + "value": 311 + } + }, + "fallback": "0x00", + "docs": [ + " The statement kind that must be signed, if any." + ] + }, + { + "name": "Preclaims", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 0, + "value": 63 + } + }, + "fallback": "0x00", + "docs": [ + " Pre-claimed Ethereum accounts, by the Account ID that they are claimed to." + ] + } + ] + }, + "calls": { + "type": 305 + }, + "events": { + "type": 62 + }, + "constants": [ + { + "name": "Prefix", + "type": 10, + "value": "0x7c506179204b534d7320746f20746865204b7573616d61206163636f756e743a", + "docs": [] + } + ], + "errors": { + "type": 540 + }, + "index": 19 + }, + { + "name": "Utility", + "storage": null, + "calls": { + "type": 312 + }, + "events": { + "type": 65 + }, + "constants": [ + { + "name": "batched_calls_limit", + "type": 4, + "value": "0x0dd20000", + "docs": [ + " The limit on the number of batched calls." + ] + } + ], + "errors": { + "type": 541 + }, + "index": 24 + }, + { + "name": "Identity", + "storage": { + "prefix": "Identity", + "items": [ + { + "name": "IdentityOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 542 + } + }, + "fallback": "0x00", + "docs": [ + " Information that is pertinent to identify the entity behind an account.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "SuperOf", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 350 + } + }, + "fallback": "0x00", + "docs": [ + " The super-identity of an alternative \"sub\" identity together with its name, within that", + " context. If the account is not some other account's sub-identity, then just `None`." + ] + }, + { + "name": "SubsOf", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 546 + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " Alternative \"sub\" identities of this account.", + "", + " The first item is the deposit, the second is a vector of the accounts.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "Registrars", + "modifier": "Default", + "type": { + "plain": 548 + }, + "fallback": "0x00", + "docs": [ + " The set of registrars. Not expected to get very big as can only be added through a", + " special origin (likely a council motion).", + "", + " The index into this can be cast to `RegistrarIndex` to get a valid value." + ] + } + ] + }, + "calls": { + "type": 314 + }, + "events": { + "type": 66 + }, + "constants": [ + { + "name": "BasicDeposit", + "type": 6, + "value": "0x084cd2c2070000000000000000000000", + "docs": [ + " The amount held on deposit for a registered identity" + ] + }, + { + "name": "FieldDeposit", + "type": 6, + "value": "0x0293b4f0010000000000000000000000", + "docs": [ + " The amount held on deposit per additional field for a registered identity." + ] + }, + { + "name": "SubAccountDeposit", + "type": 6, + "value": "0x68425d8d010000000000000000000000", + "docs": [ + " The amount held on deposit for a registered subaccount. This should account for the fact", + " that one storage item's value will increase by the size of an account ID, and there will", + " be another trie item whose value is the size of an account ID plus 32 bytes." + ] + }, + { + "name": "MaxSubAccounts", + "type": 4, + "value": "0x64000000", + "docs": [ + " The maximum number of sub-accounts allowed per identified account." + ] + }, + { + "name": "MaxAdditionalFields", + "type": 4, + "value": "0x64000000", + "docs": [ + " Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O", + " required to access an identity, but can be pretty high." + ] + }, + { + "name": "MaxRegistrars", + "type": 4, + "value": "0x14000000", + "docs": [ + " Maxmimum number of registrars allowed in the system. Needed to bound the complexity", + " of, e.g., updating judgements." + ] + } + ], + "errors": { + "type": 552 + }, + "index": 25 + }, + { + "name": "Society", + "storage": { + "prefix": "Society", + "items": [ + { + "name": "Founder", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The first member." + ] + }, + { + "name": "Rules", + "modifier": "Optional", + "type": { + "plain": 9 + }, + "fallback": "0x00", + "docs": [ + " A hash of the rules of this society concerning membership. Can only be set once and", + " only by the founder." + ] + }, + { + "name": "Candidates", + "modifier": "Default", + "type": { + "plain": 553 + }, + "fallback": "0x00", + "docs": [ + " The current set of candidates; bidders that are attempting to become members." + ] + }, + { + "name": "SuspendedCandidates", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 556 + } + }, + "fallback": "0x00", + "docs": [ + " The set of suspended candidates." + ] + }, + { + "name": "Pot", + "modifier": "Default", + "type": { + "plain": 6 + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Amount of our account balance that is specifically for the next round's bid(s)." + ] + }, + { + "name": "Head", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The most primary from the most recently approved members." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": 50 + }, + "fallback": "0x00", + "docs": [ + " The current set of members, ordered." + ] + }, + { + "name": "SuspendedMembers", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 55 + } + }, + "fallback": "0x00", + "docs": [ + " The set of suspended members." + ] + }, + { + "name": "Bids", + "modifier": "Default", + "type": { + "plain": 553 + }, + "fallback": "0x00", + "docs": [ + " The current bids, stored ordered by the value of the bid." + ] + }, + { + "name": "Vouching", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 557 + } + }, + "fallback": "0x00", + "docs": [ + " Members currently vouching or banned from vouching again" + ] + }, + { + "name": "Payouts", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 558 + } + }, + "fallback": "0x00", + "docs": [ + " Pending payouts; ordered by block number, with the amount that should be paid out." + ] + }, + { + "name": "Strikes", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " The ongoing number of losing votes cast by the member." + ] + }, + { + "name": "Votes", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 560, + "value": 561 + } + }, + "fallback": "0x00", + "docs": [ + " Double map from Candidate -> Voter -> (Maybe) Vote." + ] + }, + { + "name": "Defender", + "modifier": "Optional", + "type": { + "plain": 0 + }, + "fallback": "0x00", + "docs": [ + " The defending member currently being challenged." + ] + }, + { + "name": "DefenderVotes", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 561 + } + }, + "fallback": "0x00", + "docs": [ + " Votes for the defender." + ] + }, + { + "name": "MaxMembers", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The max number of members for the society at one time." + ] + } + ] + }, + "calls": { + "type": 354 + }, + "events": { + "type": 67 + }, + "constants": [ + { + "name": "PalletId", + "type": 538, + "value": "0x70792f736f636965", + "docs": [ + " The societies's pallet id" + ] + }, + { + "name": "CandidateDeposit", + "type": 6, + "value": "0x084cd2c2070000000000000000000000", + "docs": [ + " The minimum amount of a deposit required for a bid to be made." + ] + }, + { + "name": "WrongSideDeduction", + "type": 6, + "value": "0x68425d8d010000000000000000000000", + "docs": [ + " The amount of the unpaid reward that gets deducted in the case that either a skeptic", + " doesn't vote or someone votes in the wrong way." + ] + }, + { + "name": "MaxStrikes", + "type": 4, + "value": "0x0a000000", + "docs": [ + " The number of times a member may vote the wrong way (or not at all, when they are a", + " skeptic) before they become suspended." + ] + }, + { + "name": "PeriodSpend", + "type": 6, + "value": "0x90d9120d840100000000000000000000", + "docs": [ + " The amount of incentive paid within each period. Doesn't include VoterTip." + ] + }, + { + "name": "RotationPeriod", + "type": 4, + "value": "0xc0890100", + "docs": [ + " The number of blocks between candidate/membership rotation periods." + ] + }, + { + "name": "MaxLockDuration", + "type": 4, + "value": "0x004eed00", + "docs": [ + " The maximum duration of the payout lock." + ] + }, + { + "name": "ChallengePeriod", + "type": 4, + "value": "0xc0890100", + "docs": [ + " The number of blocks between membership challenges." + ] + }, + { + "name": "MaxCandidateIntake", + "type": 4, + "value": "0x01000000", + "docs": [ + " The maximum number of candidates that we accept per round." + ] + } + ], + "errors": { + "type": 562 + }, + "index": 26 + }, + { + "name": "Recovery", + "storage": { + "prefix": "Recovery", + "items": [ + { + "name": "Recoverable", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 563 + } + }, + "fallback": "0x00", + "docs": [ + " The set of recoverable accounts and their recovery configuration." + ] + }, + { + "name": "ActiveRecoveries", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Twox64Concat" + ], + "key": 560, + "value": 564 + } + }, + "fallback": "0x00", + "docs": [ + " Active recovery attempts.", + "", + " First account is the account to be recovered, and the second account", + " is the user trying to recover the account." + ] + }, + { + "name": "Proxy", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 0 + } + }, + "fallback": "0x00", + "docs": [ + " The list of allowed proxy accounts.", + "", + " Map from the user who can access it to the recovered account." + ] + } + ] + }, + "calls": { + "type": 356 + }, + "events": { + "type": 68 + }, + "constants": [ + { + "name": "ConfigDepositBase", + "type": 6, + "value": "0x042669e1030000000000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating a recovery configuration.", + "", + " This is held for an additional storage item whose value size is", + " `2 + sizeof(BlockNumber, Balance)` bytes." + ] + }, + { + "name": "FriendDepositFactor", + "type": 6, + "value": "0x9a505763000000000000000000000000", + "docs": [ + " The amount of currency needed per additional user when creating a recovery", + " configuration.", + "", + " This is held for adding `sizeof(AccountId)` bytes more into a pre-existing storage", + " value." + ] + }, + { + "name": "MaxFriends", + "type": 75, + "value": "0x0900", + "docs": [ + " The maximum amount of friends allowed in a recovery configuration." + ] + }, + { + "name": "RecoveryDeposit", + "type": 6, + "value": "0x042669e1030000000000000000000000", + "docs": [ + " The base amount of currency needed to reserve for starting a recovery.", + "", + " This is primarily held for deterring malicious recovery attempts, and should", + " have a value large enough that a bad actor would choose not to place this", + " deposit. It also acts to fund additional storage item whose value size is", + " `sizeof(BlockNumber, Balance + T * AccountId)` bytes. Where T is a configurable", + " threshold." + ] + } + ], + "errors": { + "type": 565 + }, + "index": 27 + }, + { + "name": "Vesting", + "storage": { + "prefix": "Vesting", + "items": [ + { + "name": "Vesting", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 0, + "value": 566 + } + }, + "fallback": "0x00", + "docs": [ + " Information regarding the vesting of a given account." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": 568 + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " New networks start with latest version, as determined by the genesis build." + ] + } + ] + }, + "calls": { + "type": 357 + }, + "events": { + "type": 69 + }, + "constants": [ + { + "name": "MinVestedTransfer", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " The minimum amount transferred to call `vested_transfer`." + ] + }, + { + "name": "MaxVestingSchedules", + "type": 4, + "value": "0x1c000000", + "docs": [] + } + ], + "errors": { + "type": 569 + }, + "index": 28 + }, + { + "name": "Scheduler", + "storage": { + "prefix": "Scheduler", + "items": [ + { + "name": "Agenda", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 570 + } + }, + "fallback": "0x00", + "docs": [ + " Items to be executed, indexed by the block number that they should be executed on." + ] + }, + { + "name": "Lookup", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 10, + "value": 71 + } + }, + "fallback": "0x00", + "docs": [ + " Lookup from identity to the block number and index of the task." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": 580 + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": { + "type": 359 + }, + "events": { + "type": 70 + }, + "constants": [ + { + "name": "MaximumWeight", + "type": 8, + "value": "0x00806e8774010000", + "docs": [ + " The maximum weight that may be scheduled per block for any dispatchables of less", + " priority than `schedule::HARD_DEADLINE`." + ] + }, + { + "name": "MaxScheduledPerBlock", + "type": 4, + "value": "0x32000000", + "docs": [ + " The maximum number of scheduled calls in the queue for a single block.", + " Not strictly enforced, but used for weight estimation." + ] + } + ], + "errors": { + "type": 581 + }, + "index": 29 + }, + { + "name": "Proxy", + "storage": { + "prefix": "Proxy", + "items": [ + { + "name": "Proxies", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 582 + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " The set of account proxies. Maps the account which has delegated to the accounts", + " which are being delegated to, together with the amount held on deposit." + ] + }, + { + "name": "Announcements", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 586 + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " The announcements made by the proxy (key)." + ] + } + ] + }, + "calls": { + "type": 361 + }, + "events": { + "type": 73 + }, + "constants": [ + { + "name": "ProxyDepositBase", + "type": 6, + "value": "0xb07d3b870f0000000000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating a proxy.", + "", + " This is held for an additional storage item whose value size is", + " `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes." + ] + }, + { + "name": "ProxyDepositFactor", + "type": 6, + "value": "0x34738e06000000000000000000000000", + "docs": [ + " The amount of currency needed per proxy added.", + "", + " This is held for adding 32 bytes plus an instance of `ProxyType` more into a", + " pre-existing storage value. Thus, when configuring `ProxyDepositFactor` one should take", + " into account `32 + proxy_type.encode().len()` bytes of data." + ] + }, + { + "name": "MaxProxies", + "type": 4, + "value": "0x20000000", + "docs": [ + " The maximum amount of proxies allowed for a single account." + ] + }, + { + "name": "MaxPending", + "type": 4, + "value": "0x20000000", + "docs": [ + " The maximum amount of time-delayed announcements that are allowed to be pending." + ] + }, + { + "name": "AnnouncementDepositBase", + "type": 6, + "value": "0xb07d3b870f0000000000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating an announcement.", + "", + " This is held when a new storage item holding a `Balance` is created (typically 16", + " bytes)." + ] + }, + { + "name": "AnnouncementDepositFactor", + "type": 6, + "value": "0x68e61c0d000000000000000000000000", + "docs": [ + " The amount of currency needed per announcement made.", + "", + " This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)", + " into a pre-existing storage value." + ] + } + ], + "errors": { + "type": 590 + }, + "index": 30 + }, + { + "name": "Multisig", + "storage": { + "prefix": "Multisig", + "items": [ + { + "name": "Multisigs", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Blake2_128Concat" + ], + "key": 591, + "value": 592 + } + }, + "fallback": "0x00", + "docs": [ + " The set of open multisig operations." + ] + }, + { + "name": "Calls", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 1, + "value": 593 + } + }, + "fallback": "0x00", + "docs": [] + } + ] + }, + "calls": { + "type": 363 + }, + "events": { + "type": 76 + }, + "constants": [ + { + "name": "DepositBase", + "type": 6, + "value": "0xf07520970f0000000000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating a multisig execution or to", + " store a dispatch call for later.", + "", + " This is held for an additional storage item whose value size is", + " `4 + sizeof((BlockNumber, Balance, AccountId))` bytes and whose key size is", + " `32 + sizeof(AccountId)` bytes." + ] + }, + { + "name": "DepositFactor", + "type": 6, + "value": "0x80965b06000000000000000000000000", + "docs": [ + " The amount of currency needed per unit threshold when creating a multisig execution.", + "", + " This is held for adding 32 bytes more into a pre-existing storage value." + ] + }, + { + "name": "MaxSignatories", + "type": 75, + "value": "0x6400", + "docs": [ + " The maximum amount of signatories allowed in the multisig." + ] + } + ], + "errors": { + "type": 594 + }, + "index": 31 + }, + { + "name": "Bounties", + "storage": { + "prefix": "Bounties", + "items": [ + { + "name": "BountyCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Number of bounty proposals that have been made." + ] + }, + { + "name": "Bounties", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 595 + } + }, + "fallback": "0x00", + "docs": [ + " Bounties that have been made." + ] + }, + { + "name": "BountyDescriptions", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 10 + } + }, + "fallback": "0x00", + "docs": [ + " The description of each bounty." + ] + }, + { + "name": "BountyApprovals", + "modifier": "Default", + "type": { + "plain": 211 + }, + "fallback": "0x00", + "docs": [ + " Bounty indices that have been approved but not yet funded." + ] + } + ] + }, + "calls": { + "type": 365 + }, + "events": { + "type": 78 + }, + "constants": [ + { + "name": "BountyDepositBase", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " The amount held on deposit for placing a bounty proposal." + ] + }, + { + "name": "BountyDepositPayoutDelay", + "type": 4, + "value": "0x00e10000", + "docs": [ + " The delay period for which a bounty beneficiary need to wait before claim the payout." + ] + }, + { + "name": "BountyUpdatePeriod", + "type": 4, + "value": "0x80c61300", + "docs": [ + " Bounty duration in blocks." + ] + }, + { + "name": "BountyCuratorDeposit", + "type": 537, + "value": "0x20a10700", + "docs": [ + " Percentage of the curator fee that will be reserved upfront as deposit for bounty", + " curator." + ] + }, + { + "name": "BountyValueMinimum", + "type": 6, + "value": "0x68425d8d010000000000000000000000", + "docs": [ + " Minimum value for a bounty." + ] + }, + { + "name": "DataDepositPerByte", + "type": 6, + "value": "0x55a0fc01000000000000000000000000", + "docs": [ + " The amount held on deposit per byte within the tip report reason or bounty description." + ] + }, + { + "name": "MaximumReasonLength", + "type": 4, + "value": "0x00400000", + "docs": [ + " Maximum acceptable reason length." + ] + } + ], + "errors": { + "type": 597 + }, + "index": 35 + }, + { + "name": "Tips", + "storage": { + "prefix": "Tips", + "items": [ + { + "name": "Tips", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 9, + "value": 598 + } + }, + "fallback": "0x00", + "docs": [ + " TipsMap that are not yet completed. Keyed by the hash of `(reason, who)` from the value.", + " This has the insecure enumerable hash function since the key itself is already", + " guaranteed to be a secure hash." + ] + }, + { + "name": "Reasons", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 10 + } + }, + "fallback": "0x00", + "docs": [ + " Simple preimage lookup from the reason's hash to the original data. Again, has an", + " insecure enumerable hash since the key is guaranteed to be the result of a secure hash." + ] + } + ] + }, + "calls": { + "type": 366 + }, + "events": { + "type": 79 + }, + "constants": [ + { + "name": "MaximumReasonLength", + "type": 4, + "value": "0x00400000", + "docs": [ + " Maximum acceptable reason length." + ] + }, + { + "name": "DataDepositPerByte", + "type": 6, + "value": "0x55a0fc01000000000000000000000000", + "docs": [ + " The amount held on deposit per byte within the tip report reason or bounty description." + ] + }, + { + "name": "TipCountdown", + "type": 4, + "value": "0x40380000", + "docs": [ + " The period for which a tip remains open after is has achieved threshold tippers." + ] + }, + { + "name": "TipFindersFee", + "type": 229, + "value": "0x14", + "docs": [ + " The percent of the final tip which goes to the original reporter of the tip." + ] + }, + { + "name": "TipReportDepositBase", + "type": 6, + "value": "0x34a1aec6000000000000000000000000", + "docs": [ + " The amount held on deposit for placing a tip report." + ] + } + ], + "errors": { + "type": 599 + }, + "index": 36 + }, + { + "name": "ElectionProviderMultiPhase", + "storage": { + "prefix": "ElectionProviderMultiPhase", + "items": [ + { + "name": "Round", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x01000000", + "docs": [ + " Internal counter for the number of rounds.", + "", + " This is useful for de-duplication of transactions submitted to the pool, and general", + " diagnostics of the pallet.", + "", + " This is merely incremented once per every time that an upstream `elect` is called." + ] + }, + { + "name": "CurrentPhase", + "modifier": "Default", + "type": { + "plain": 600 + }, + "fallback": "0x00", + "docs": [ + " Current phase." + ] + }, + { + "name": "QueuedSolution", + "modifier": "Optional", + "type": { + "plain": 602 + }, + "fallback": "0x00", + "docs": [ + " Current best solution, signed or unsigned, queued to be returned upon `elect`." + ] + }, + { + "name": "Snapshot", + "modifier": "Optional", + "type": { + "plain": 603 + }, + "fallback": "0x00", + "docs": [ + " Snapshot data of the round.", + "", + " This is created at the beginning of the signed phase and cleared upon calling `elect`." + ] + }, + { + "name": "DesiredTargets", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " Desired number of targets to elect for this round.", + "", + " Only exists when [`Snapshot`] is present." + ] + }, + { + "name": "SnapshotMetadata", + "modifier": "Optional", + "type": { + "plain": 445 + }, + "fallback": "0x00", + "docs": [ + " The metadata of the [`RoundSnapshot`]", + "", + " Only exists when [`Snapshot`] is present." + ] + }, + { + "name": "SignedSubmissionNextIndex", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The next index to be assigned to an incoming signed submission.", + "", + " Every accepted submission is assigned a unique index; that index is bound to that particular", + " submission for the duration of the election. On election finalization, the next index is", + " reset to 0.", + "", + " We can't just use `SignedSubmissionIndices.len()`, because that's a bounded set; past its", + " capacity, it will simply saturate. We can't just iterate over `SignedSubmissionsMap`,", + " because iteration is slow. Instead, we store the value here." + ] + }, + { + "name": "SignedSubmissionIndices", + "modifier": "Default", + "type": { + "plain": 606 + }, + "fallback": "0x00", + "docs": [ + " A sorted, bounded set of `(score, index)`, where each `index` points to a value in", + " `SignedSubmissions`.", + "", + " We never need to process more than a single signed submission at a time. Signed submissions", + " can be quite large, so we're willing to pay the cost of multiple database accesses to access", + " them one at a time instead of reading and decoding all of them at once." + ] + }, + { + "name": "SignedSubmissionsMap", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 610 + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000", + "docs": [ + " Unchecked, signed solutions.", + "", + " Together with `SubmissionIndices`, this stores a bounded set of `SignedSubmissions` while", + " allowing us to keep only a single one in memory at a time.", + "", + " Twox note: the key of the map is an auto-incrementing index which users cannot inspect or", + " affect; we shouldn't need a cryptographically secure hasher." + ] + }, + { + "name": "MinimumUntrustedScore", + "modifier": "Optional", + "type": { + "plain": 444 + }, + "fallback": "0x00", + "docs": [ + " The minimum score that each 'untrusted' solution must attain in order to be considered", + " feasible.", + "", + " Can be set via `set_minimum_untrusted_score`." + ] + } + ] + }, + "calls": { + "type": 367 + }, + "events": { + "type": 80 + }, + "constants": [ + { + "name": "UnsignedPhase", + "type": 4, + "value": "0x96000000", + "docs": [ + " Duration of the unsigned phase." + ] + }, + { + "name": "SignedPhase", + "type": 4, + "value": "0x96000000", + "docs": [ + " Duration of the signed phase." + ] + }, + { + "name": "SolutionImprovementThreshold", + "type": 148, + "value": "0x20a10700", + "docs": [ + " The minimum amount of improvement to the solution score that defines a solution as", + " \"better\" (in any phase)." + ] + }, + { + "name": "OffchainRepeat", + "type": 4, + "value": "0x05000000", + "docs": [ + " The repeat threshold of the offchain worker.", + "", + " For example, if it is 5, that means that at least 5 blocks will elapse between attempts", + " to submit the worker's solution." + ] + }, + { + "name": "MinerTxPriority", + "type": 8, + "value": "0x65666666666666e6", + "docs": [ + " The priority of the unsigned transaction submitted in the unsigned-phase" + ] + }, + { + "name": "MinerMaxWeight", + "type": 8, + "value": "0xc084666557010000", + "docs": [ + " Maximum weight that the miner should consume.", + "", + " The miner will ensure that the total weight of the unsigned solution will not exceed", + " this value, based on [`WeightInfo::submit_unsigned`]." + ] + }, + { + "name": "SignedMaxSubmissions", + "type": 4, + "value": "0x10000000", + "docs": [ + " Maximum number of signed submissions that can be queued.", + "", + " It is best to avoid adjusting this during an election, as it impacts downstream data", + " structures. In particular, `SignedSubmissionIndices` is bounded on this value. If you", + " update this value during an election, you _must_ ensure that", + " `SignedSubmissionIndices.len()` is less than or equal to the new value. Otherwise,", + " attempts to submit new solutions may cause a runtime panic." + ] + }, + { + "name": "SignedMaxWeight", + "type": 8, + "value": "0xc084666557010000", + "docs": [ + " Maximum weight of a signed solution.", + "", + " This should probably be similar to [`Config::MinerMaxWeight`]." + ] + }, + { + "name": "SignedRewardBase", + "type": 6, + "value": "0x00e87648170000000000000000000000", + "docs": [ + " Base reward for a signed solution" + ] + }, + { + "name": "SignedDepositBase", + "type": 6, + "value": "0x2030490b1f0000000000000000000000", + "docs": [ + " Base deposit for a signed solution." + ] + }, + { + "name": "SignedDepositByte", + "type": 6, + "value": "0x277f0000000000000000000000000000", + "docs": [ + " Per-byte deposit for a signed solution." + ] + }, + { + "name": "SignedDepositWeight", + "type": 6, + "value": "0x00000000000000000000000000000000", + "docs": [ + " Per-weight deposit for a signed solution." + ] + }, + { + "name": "VoterSnapshotPerBlock", + "type": 4, + "value": "0xe4570000", + "docs": [ + " The maximum number of voters to put in the snapshot. At the moment, snapshots are only", + " over a single block, but once multi-block elections are introduced they will take place", + " over multiple blocks.", + "", + " Also, note the data type: If the voters are represented by a `u32` in `type", + " CompactSolution`, the same `u32` is used here to ensure bounds are respected." + ] + }, + { + "name": "MinerMaxLength", + "type": 4, + "value": "0x00003600", + "docs": [ + " Maximum length (bytes) that the mined solution should consume.", + "", + " The miner will ensure that the total length of the unsigned solution will not exceed", + " this value." + ] + } + ], + "errors": { + "type": 611 + }, + "index": 37 + }, + { + "name": "Gilt", + "storage": { + "prefix": "Gilt", + "items": [ + { + "name": "QueueTotals", + "modifier": "Default", + "type": { + "plain": 558 + }, + "fallback": "0x00", + "docs": [ + " The totals of items and balances within each queue. Saves a lot of storage reads in the", + " case of sparsely packed queues.", + "", + " The vector is indexed by duration in `Period`s, offset by one, so information on the queue", + " whose duration is one `Period` would be storage `0`." + ] + }, + { + "name": "Queues", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 4, + "value": 612 + } + }, + "fallback": "0x00", + "docs": [ + " The queues of bids ready to become gilts. Indexed by duration (in `Period`s)." + ] + }, + { + "name": "ActiveTotal", + "modifier": "Default", + "type": { + "plain": 614 + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Information relating to the gilts currently active." + ] + }, + { + "name": "Active", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 4, + "value": 615 + } + }, + "fallback": "0x00", + "docs": [ + " The currently active gilts, indexed according to the order of creation." + ] + } + ] + }, + "calls": { + "type": 450 + }, + "events": { + "type": 83 + }, + "constants": [ + { + "name": "IgnoredIssuance", + "type": 6, + "value": "0x9dd3fbb6510f3f060000000000000000", + "docs": [ + " The issuance to ignore. This is subtracted from the `Currency`'s `total_issuance` to get", + " the issuance by which we inflate or deflate the gilt." + ] + }, + { + "name": "QueueCount", + "type": 4, + "value": "0x2c010000", + "docs": [ + " Number of duration queues in total. This sets the maximum duration supported, which is", + " this value multiplied by `Period`." + ] + }, + { + "name": "MaxQueueLen", + "type": 4, + "value": "0xe8030000", + "docs": [ + " Maximum number of items that may be in each duration queue." + ] + }, + { + "name": "FifoQueueLen", + "type": 4, + "value": "0xfa000000", + "docs": [ + " Portion of the queue which is free from ordering and just a FIFO.", + "", + " Must be no greater than `MaxQueueLen`." + ] + }, + { + "name": "Period", + "type": 4, + "value": "0x80970600", + "docs": [ + " The base period for the duration queues. This is the common multiple across all", + " supported freezing durations that can be bid upon." + ] + }, + { + "name": "MinFreeze", + "type": 6, + "value": "0x50f8369c4d0000000000000000000000", + "docs": [ + " The minimum amount of funds that may be offered to freeze for a gilt. Note that this", + " does not actually limit the amount which may be frozen in a gilt since gilts may be", + " split up in order to satisfy the desired amount of funds under gilts.", + "", + " It should be at least big enough to ensure that there is no possible storage spam attack", + " or queue-filling attack." + ] + }, + { + "name": "IntakePeriod", + "type": 4, + "value": "0x32000000", + "docs": [ + " The number of blocks between consecutive attempts to issue more gilts in an effort to", + " get to the target amount to be frozen.", + "", + " A larger value results in fewer storage hits each block, but a slower period to get to", + " the target." + ] + }, + { + "name": "MaxIntakeBids", + "type": 4, + "value": "0x64000000", + "docs": [ + " The maximum amount of bids that can be turned into issued gilts each block. A larger", + " value here means less of the block available for transactions should there be a glut of", + " bids to make into gilts to reach the target." + ] + } + ], + "errors": { + "type": 616 + }, + "index": 38 + }, + { + "name": "BagsList", + "storage": { + "prefix": "BagsList", + "items": [ + { + "name": "CounterForListNodes", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " How many ids are registered." + ] + }, + { + "name": "ListNodes", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 0, + "value": 617 + } + }, + "fallback": "0x00", + "docs": [ + " A single node, within some bag.", + "", + " Nodes store links forward and back within their respective bags." + ] + }, + { + "name": "ListBags", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 8, + "value": 618 + } + }, + "fallback": "0x00", + "docs": [ + " A bag stored in storage.", + "", + " Stores a `Bag` struct, which stores head and tail pointers to itself." + ] + } + ] + }, + "calls": { + "type": 453 + }, + "events": { + "type": 84 + }, + "constants": [ + { + "name": "BagThresholds", + "type": 619, + "value": "0x210355a0fc0100000000daa64602000000006e739b02000000007997fc0200000000d0de6b03000000003358eb03000000000d5f7d04000000009aa6240500000000b146e4050000000067cabf0600000000d640bb07000000005350db0800000000714c250a00000000364f9f0b000000000056500d000000009862400f000000001ba17811000000006593031400000000cd42ed16000000002079431a00000000e401161e000000001ef5762200000000f90c7b2700000000e0073a2d00000000e818cf33000000008c68593b000000002ea8fc43000000000abbe14d00000000c3773759000000001986336600000000e85c13750000000018651d8600000000e846a29900000000be67feaf00000000849f9bc900000000ad2df3e60000000028f78f0801000000d817112f01000000bed32c5b01000000c2f5b38d010000000aac95c7010000002bf4e3090200000022acd855020000001060dbac020000002ef08710030000007c2eb682030000002b988205040000001754589b040000009da5fc4605000000ff099c0b060000006c3ed9ec06000000c475deee07000000960f711609000000aa2d08690a000000f892e6ec0b0000008c4638a90d000000978634a60f0000006dac44ed1100000078b93089140000001660528617000000e479cff21a0000004000ddde1e000000ffc30b5d23000000824fa082280000002793f7672e000000a638fa283500000048bfa0e53c00000047d28ac245000000c5a5ace94f000000f68e158b5b0000009083d3dd6800000066b5f72078000000cf1bc19c89000000fc6ff2a39d0000001eef5995b4000000c02092ddce000000b2ed03f9ec000000078933760f010000d30e63f8360100001252973a64010000e1230d1398010000a0722f77d301000078012180170200006533ef6f65020000428586b7be02000028e784fd24030000b13f0a269a030000d016ac5b2004000022c8b619ba04000079c7ec376a050000e092fbf7330600003d05e6141b070000f701add423080000d8108a1c53090000c8ab1b88ae0a0000b2eff0833c0c0000e858f26b040e00000f7d37ae0e100000d5a7eef264120000583f134a121500001753cb5f231800005c3664b8a61b0000a61a0af5ac1f000033f27f22492400004b3a4c1391290000288805c79d2f000037d3a7e08b360000ffa1222e7c3e0000f0c4a14394470000e5ad6f2dff510000076ebb3bee5d0000abf006ec996b00008c6c8ef4427b00003ad69a76338d0000ba57695dc0a100005dda24f04ab90000b66f609e42d400007655960f27f30000258d6c7f8a1601005169eb71143f0100b9be72cc846d01003c4b1762b7a20100cc2f3404a8df0100f7276e2a77250200480b33486f7502001d5cf5e80ad102000f6410b0fb390300a904775d32b203002de121fde73b040030afb76ca8d90400fb753e695e8e05003c44e45d615d06002cb93b35854a0700a8f8cb772c5a08007a48b90d5d9109003d3dc705d8f50a000d1e42d2348e0c001cb0be7c00620e0024796364e17910001b8ded2fc0df1200d3e942b5f69e1500e8ca99b485c41800d0c88c65525f1c00c2f577f96c8020000abce260613b250074bd4dd293a62a00ec4b61c8aadb300048b0376d08f83700c01384b1551d4000dc2bfda12172490070b645ed972254006cfc51fa516160006c93086d46686e009caae886db797e00c036837621e29000a0649b653af8a50028a34ceef61fbe00385aa297aecbd900483335165d7ef900d0cae4520ece1d010090a7aea4664701e09d92a5060d770130778edcc2a2ad01d00bb8d53b2aec0140b18c096fcb3302805193026ed98502a0f6d663a3d8e30260bbcb8701864f03a045f8b63cdfca0340816de8372c5804405e20a9d009fa04808d72453d76b30580f35bc037df8706804eeca838327b0700b198a10eef9108800b2f9b2a3dd10980a2489405043f0b00724c5a1307e20c00d8f897c605c20e009890be3de0e71000434f6546c15d1300d61cff7d4e2f16009b32b873df691900008775d0bc1c1d00da56ebaf68592100dacb4281f13326003c889ef750c32b000ab7e6cbd8213200346dad52af6d39005047e9335ec9410024ee18e8755c4b0038d4b40049545600087d76b2c2e46200981c03995c497100881e553f38c68100b0cb90a161a99400284fe59e404caa00c0e54a304015c30060cd7437b379dfffffffffffffffff", + "docs": [ + " The list of thresholds separating the various bags.", + "", + " Ids are separated into unsorted bags according to their vote weight. This specifies the", + " thresholds separating the bags. An id's bag is the largest bag for which the id's weight", + " is less than or equal to its upper threshold.", + "", + " When ids are iterated, higher bags are iterated completely before lower bags. This means", + " that iteration is _semi-sorted_: ids of higher weight tend to come before ids of lower", + " weight, but peer ids within a particular bag are sorted in insertion order.", + "", + " # Expressing the constant", + "", + " This constant must be sorted in strictly increasing order. Duplicate items are not", + " permitted.", + "", + " There is an implied upper limit of `VoteWeight::MAX`; that value does not need to be", + " specified within the bag. For any two threshold lists, if one ends with", + " `VoteWeight::MAX`, the other one does not, and they are otherwise equal, the two lists", + " will behave identically.", + "", + " # Calculation", + "", + " It is recommended to generate the set of thresholds in a geometric series, such that", + " there exists some constant ratio such that `threshold[k + 1] == (threshold[k] *", + " constant_ratio).max(threshold[k] + 1)` for all `k`.", + "", + " The helpers in the `/utils/frame/generate-bags` module can simplify this calculation.", + "", + " # Examples", + "", + " - If `BagThresholds::get().is_empty()`, then all ids are put into the same bag, and", + " iteration is strictly in insertion order.", + " - If `BagThresholds::get().len() == 64`, and the thresholds are determined according to", + " the procedure given above, then the constant ratio is equal to 2.", + " - If `BagThresholds::get().len() == 200`, and the thresholds are determined according to", + " the procedure given above, then the constant ratio is approximately equal to 1.248.", + " - If the threshold list begins `[1, 2, 3, ...]`, then an id with weight 0 or 1 will fall", + " into bag 0, an id with weight 2 will fall into bag 1, etc.", + "", + " # Migration", + "", + " In the event that this list ever changes, a copy of the old bags list must be retained.", + " With that `List::migrate` can be called, which will perform the appropriate migration." + ] + } + ], + "errors": null, + "index": 39 + }, + { + "name": "ParachainsOrigin", + "storage": null, + "calls": null, + "events": null, + "constants": [], + "errors": null, + "index": 50 + }, + { + "name": "Configuration", + "storage": { + "prefix": "Configuration", + "items": [ + { + "name": "ActiveConfig", + "modifier": "Default", + "type": { + "plain": 620 + }, + "fallback": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000100000001000000000000000000060000006400000002000000c8000000010000000000000000000000000000000000000000c817a804000000", + "docs": [ + " The active configuration for the current session." + ] + }, + { + "name": "PendingConfig", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 620 + } + }, + "fallback": "0x00", + "docs": [ + " Pending configuration (if any) for the next session." + ] + } + ] + }, + "calls": { + "type": 454 + }, + "events": null, + "constants": [], + "errors": { + "type": 621 + }, + "index": 51 + }, + { + "name": "ParasShared", + "storage": { + "prefix": "ParasShared", + "items": [ + { + "name": "CurrentSessionIndex", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The current session index." + ] + }, + { + "name": "ActiveValidatorIndices", + "modifier": "Default", + "type": { + "plain": 622 + }, + "fallback": "0x00", + "docs": [ + " All the validators actively participating in parachain consensus.", + " Indices are into the broader validator set." + ] + }, + { + "name": "ActiveValidatorKeys", + "modifier": "Default", + "type": { + "plain": 623 + }, + "fallback": "0x00", + "docs": [ + " The parachain attestation keys of the validators actively participating in parachain consensus.", + " This should be the same length as `ActiveValidatorIndices`." + ] + } + ] + }, + "calls": { + "type": 455 + }, + "events": null, + "constants": [], + "errors": null, + "index": 52 + }, + { + "name": "ParaInclusion", + "storage": { + "prefix": "ParaInclusion", + "items": [ + { + "name": "AvailabilityBitfields", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 464, + "value": 624 + } + }, + "fallback": "0x00", + "docs": [ + " The latest bitfield for each validator, referred to by their index in the validator set." + ] + }, + { + "name": "PendingAvailability", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 625 + } + }, + "fallback": "0x00", + "docs": [ + " Candidates pending availability by `ParaId`." + ] + }, + { + "name": "PendingAvailabilityCommitments", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 469 + } + }, + "fallback": "0x00", + "docs": [ + " The commitments of candidates pending availability, by `ParaId`." + ] + } + ] + }, + "calls": { + "type": 456 + }, + "events": { + "type": 85 + }, + "constants": [], + "errors": { + "type": 626 + }, + "index": 53 + }, + { + "name": "ParaInherent", + "storage": { + "prefix": "ParaInherent", + "items": [ + { + "name": "Included", + "modifier": "Optional", + "type": { + "plain": 53 + }, + "fallback": "0x00", + "docs": [ + " Whether the paras inherent was included within this block.", + "", + " The `Option<()>` is effectively a `bool`, but it never hits storage in the `None` variant", + " due to the guarantees of FRAME's storage APIs.", + "", + " If this is `None` at the end of the block, we panic and render the block invalid." + ] + } + ] + }, + "calls": { + "type": 457 + }, + "events": null, + "constants": [], + "errors": { + "type": 627 + }, + "index": 54 + }, + { + "name": "ParaScheduler", + "storage": { + "prefix": "ParaScheduler", + "items": [ + { + "name": "ValidatorGroups", + "modifier": "Default", + "type": { + "plain": 628 + }, + "fallback": "0x00", + "docs": [ + " All the validator groups. One for each core. Indices are into `ActiveValidators` - not the", + " broader set of Polkadot validators, but instead just the subset used for parachains during", + " this session.", + "", + " Bound: The number of cores is the sum of the numbers of parachains and parathread multiplexers.", + " Reasonably, 100-1000. The dominant factor is the number of validators: safe upper bound at 10k." + ] + }, + { + "name": "ParathreadQueue", + "modifier": "Default", + "type": { + "plain": 629 + }, + "fallback": "0x0000000000", + "docs": [ + " A queue of upcoming claims and which core they should be mapped onto.", + "", + " The number of queued claims is bounded at the `scheduling_lookahead`", + " multiplied by the number of parathread multiplexer cores. Reasonably, 10 * 50 = 500." + ] + }, + { + "name": "AvailabilityCores", + "modifier": "Default", + "type": { + "plain": 634 + }, + "fallback": "0x00", + "docs": [ + " One entry for each availability core. Entries are `None` if the core is not currently occupied. Can be", + " temporarily `Some` if scheduled but not occupied.", + " The i'th parachain belongs to the i'th core, with the remaining cores all being", + " parathread-multiplexers.", + "", + " Bounded by the maximum of either of these two values:", + " * The number of parachains and parathread multiplexers", + " * The number of validators divided by `configuration.max_validators_per_core`." + ] + }, + { + "name": "ParathreadClaimIndex", + "modifier": "Default", + "type": { + "plain": 637 + }, + "fallback": "0x00", + "docs": [ + " An index used to ensure that only one claim on a parathread exists in the queue or is", + " currently being handled by an occupied core.", + "", + " Bounded by the number of parathread cores and scheduling lookahead. Reasonably, 10 * 50 = 500." + ] + }, + { + "name": "SessionStartBlock", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The block number where the session start occurred. Used to track how many group rotations have occurred.", + "", + " Note that in the context of parachains modules the session change is signaled during", + " the block and enacted at the end of the block (at the finalization stage, to be exact).", + " Thus for all intents and purposes the effect of the session change is observed at the", + " block following the session change, block number of which we save in this storage value." + ] + }, + { + "name": "Scheduled", + "modifier": "Default", + "type": { + "plain": 638 + }, + "fallback": "0x00", + "docs": [ + " Currently scheduled cores - free but up to be occupied.", + "", + " Bounded by the number of cores: one for each parachain and parathread multiplexer.", + "", + " The value contained here will not be valid after the end of a block. Runtime APIs should be used to determine scheduled cores/", + " for the upcoming block." + ] + } + ] + }, + "calls": null, + "events": null, + "constants": [], + "errors": null, + "index": 55 + }, + { + "name": "Paras", + "storage": { + "prefix": "Paras", + "items": [ + { + "name": "Parachains", + "modifier": "Default", + "type": { + "plain": 637 + }, + "fallback": "0x00", + "docs": [ + " All parachains. Ordered ascending by `ParaId`. Parathreads are not included." + ] + }, + { + "name": "ParaLifecycles", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 641 + } + }, + "fallback": "0x00", + "docs": [ + " The current lifecycle of a all known Para IDs." + ] + }, + { + "name": "Heads", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 94 + } + }, + "fallback": "0x00", + "docs": [ + " The head-data of every registered para." + ] + }, + { + "name": "CurrentCodeHash", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 93 + } + }, + "fallback": "0x00", + "docs": [ + " The validation code hash of every live para.", + "", + " Corresponding code can be retrieved with [`CodeByHash`]." + ] + }, + { + "name": "PastCodeHash", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 642, + "value": 93 + } + }, + "fallback": "0x00", + "docs": [ + " Actual past code hash, indicated by the para id as well as the block number at which it", + " became outdated.", + "", + " Corresponding code can be retrieved with [`CodeByHash`]." + ] + }, + { + "name": "PastCodeMeta", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 643 + } + }, + "fallback": "0x0000", + "docs": [ + " Past code of parachains. The parachains themselves may not be registered anymore,", + " but we also keep their code on-chain for the same amount of time as outdated code", + " to keep it available for secondary checkers." + ] + }, + { + "name": "PastCodePruning", + "modifier": "Default", + "type": { + "plain": 646 + }, + "fallback": "0x00", + "docs": [ + " Which paras have past code that needs pruning and the relay-chain block at which the code was replaced.", + " Note that this is the actual height of the included block, not the expected height at which the", + " code upgrade would be applied, although they may be equal.", + " This is to ensure the entire acceptance period is covered, not an offset acceptance period starting", + " from the time at which the parachain perceives a code upgrade as having occurred.", + " Multiple entries for a single para are permitted. Ordered ascending by block number." + ] + }, + { + "name": "FutureCodeUpgrades", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " The block number at which the planned code change is expected for a para.", + " The change will be applied after the first parablock for this ID included which executes", + " in the context of a relay chain block with a number >= `expected_at`." + ] + }, + { + "name": "FutureCodeHash", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 93 + } + }, + "fallback": "0x00", + "docs": [ + " The actual future code hash of a para.", + "", + " Corresponding code can be retrieved with [`CodeByHash`]." + ] + }, + { + "name": "UpgradeGoAheadSignal", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 647 + } + }, + "fallback": "0x00", + "docs": [ + " This is used by the relay-chain to communicate to a parachain a go-ahead with in the upgrade procedure.", + "", + " This value is absent when there are no upgrades scheduled or during the time the relay chain", + " performs the checks. It is set at the first relay-chain block when the corresponding parachain", + " can switch its upgrade function. As soon as the parachain's block is included, the value", + " gets reset to `None`.", + "", + " NOTE that this field is used by parachains via merkle storage proofs, therefore changing", + " the format will require migration of parachains." + ] + }, + { + "name": "UpgradeRestrictionSignal", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 648 + } + }, + "fallback": "0x00", + "docs": [ + " This is used by the relay-chain to communicate that there are restrictions for performing", + " an upgrade for this parachain.", + "", + " This may be a because the parachain waits for the upgrade cooldown to expire. Another", + " potential use case is when we want to perform some maintenance (such as storage migration)", + " we could restrict upgrades to make the process simpler.", + "", + " NOTE that this field is used by parachains via merkle storage proofs, therefore changing", + " the format will require migration of parachains." + ] + }, + { + "name": "UpgradeCooldowns", + "modifier": "Default", + "type": { + "plain": 646 + }, + "fallback": "0x00", + "docs": [ + " The list of parachains that are awaiting for their upgrade restriction to cooldown.", + "", + " Ordered ascending by block number." + ] + }, + { + "name": "UpcomingUpgrades", + "modifier": "Default", + "type": { + "plain": 646 + }, + "fallback": "0x00", + "docs": [ + " The list of upcoming code upgrades. Each item is a pair of which para performs a code", + " upgrade and at which relay-chain block it is expected at.", + "", + " Ordered ascending by block number." + ] + }, + { + "name": "ActionsQueue", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 637 + } + }, + "fallback": "0x00", + "docs": [ + " The actions to perform during the start of a specific session index." + ] + }, + { + "name": "UpcomingParasGenesis", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 649 + } + }, + "fallback": "0x00", + "docs": [ + " Upcoming paras instantiation arguments." + ] + }, + { + "name": "CodeByHashRefs", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 93, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " The number of reference on the validation code in [`CodeByHash`] storage." + ] + }, + { + "name": "CodeByHash", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 93, + "value": 473 + } + }, + "fallback": "0x00", + "docs": [ + " Validation code stored by its hash.", + "", + " This storage is consistent with [`FutureCodeHash`], [`CurrentCodeHash`] and", + " [`PastCodeHash`]." + ] + } + ] + }, + "calls": { + "type": 484 + }, + "events": { + "type": 97 + }, + "constants": [], + "errors": { + "type": 650 + }, + "index": 56 + }, + { + "name": "Initializer", + "storage": { + "prefix": "Initializer", + "items": [ + { + "name": "HasInitialized", + "modifier": "Optional", + "type": { + "plain": 53 + }, + "fallback": "0x00", + "docs": [ + " Whether the parachains modules have been initialized within this block.", + "", + " Semantically a `bool`, but this guarantees it should never hit the trie,", + " as this is cleared in `on_finalize` and Frame optimizes `None` values to be empty values.", + "", + " As a `bool`, `set(false)` and `remove()` both lead to the next `get()` being false, but one of", + " them writes to the trie and one does not. This confusion makes `Option<()>` more suitable for", + " the semantics of this variable." + ] + }, + { + "name": "BufferedSessionChanges", + "modifier": "Default", + "type": { + "plain": 651 + }, + "fallback": "0x00", + "docs": [ + " Buffered session changes along with the block number at which they should be applied.", + "", + " Typically this will be empty or one element long. Apart from that this item never hits", + " the storage.", + "", + " However this is a `Vec` regardless to handle various edge cases that may occur at runtime", + " upgrade boundaries or if governance intervenes." + ] + } + ] + }, + "calls": { + "type": 485 + }, + "events": null, + "constants": [], + "errors": null, + "index": 57 + }, + { + "name": "Dmp", + "storage": { + "prefix": "Dmp", + "items": [ + { + "name": "DownwardMessageQueues", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 653 + } + }, + "fallback": "0x00", + "docs": [ + " The downward messages addressed for a certain para." + ] + }, + { + "name": "DownwardMessageQueueHeads", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 9 + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " A mapping that stores the downward message queue MQC head for each para.", + "", + " Each link in this chain has a form:", + " `(prev_head, B, H(M))`, where", + " - `prev_head`: is the previous head hash or zero if none.", + " - `B`: is the relay-chain block number in which a message was appended.", + " - `H(M)`: is the hash of the message being appended." + ] + } + ] + }, + "calls": { + "type": 486 + }, + "events": null, + "constants": [], + "errors": null, + "index": 58 + }, + { + "name": "Ump", + "storage": { + "prefix": "Ump", + "items": [ + { + "name": "RelayDispatchQueues", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 151 + } + }, + "fallback": "0x00", + "docs": [ + " The messages waiting to be handled by the relay-chain originating from a certain parachain.", + "", + " Note that some upward messages might have been already processed by the inclusion logic. E.g.", + " channel management messages.", + "", + " The messages are processed in FIFO order." + ] + }, + { + "name": "RelayDispatchQueueSize", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 71 + } + }, + "fallback": "0x0000000000000000", + "docs": [ + " Size of the dispatch queues. Caches sizes of the queues in `RelayDispatchQueue`.", + "", + " First item in the tuple is the count of messages and second", + " is the total length (in bytes) of the message payloads.", + "", + " Note that this is an auxiliary mapping: it's possible to tell the byte size and the number of", + " messages only looking at `RelayDispatchQueues`. This mapping is separate to avoid the cost of", + " loading the whole message queue if only the total size and count are required.", + "", + " Invariant:", + " - The set of keys should exactly match the set of keys of `RelayDispatchQueues`." + ] + }, + { + "name": "NeedsDispatch", + "modifier": "Default", + "type": { + "plain": 637 + }, + "fallback": "0x00", + "docs": [ + " The ordered list of `ParaId`s that have a `RelayDispatchQueue` entry.", + "", + " Invariant:", + " - The set of items from this vector should be exactly the set of the keys in", + " `RelayDispatchQueues` and `RelayDispatchQueueSize`." + ] + }, + { + "name": "NextDispatchRoundStartWith", + "modifier": "Optional", + "type": { + "plain": 88 + }, + "fallback": "0x00", + "docs": [ + " This is the para that gets will get dispatched first during the next upward dispatchable queue", + " execution round.", + "", + " Invariant:", + " - If `Some(para)`, then `para` must be present in `NeedsDispatch`." + ] + }, + { + "name": "Overweight", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 8, + "value": 655 + } + }, + "fallback": "0x00", + "docs": [ + " The messages that exceeded max individual message weight budget.", + "", + " These messages stay there until manually dispatched." + ] + }, + { + "name": "OverweightCount", + "modifier": "Default", + "type": { + "plain": 8 + }, + "fallback": "0x0000000000000000", + "docs": [ + " The number of overweight messages ever recorded in `Overweight` (and thus the lowest free", + " index)." + ] + } + ] + }, + "calls": { + "type": 487 + }, + "events": { + "type": 98 + }, + "constants": [], + "errors": { + "type": 656 + }, + "index": 59 + }, + { + "name": "Hrmp", + "storage": { + "prefix": "Hrmp", + "items": [ + { + "name": "HrmpOpenChannelRequests", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 102, + "value": 657 + } + }, + "fallback": "0x00", + "docs": [ + " The set of pending HRMP open channel requests.", + "", + " The set is accompanied by a list for iteration.", + "", + " Invariant:", + " - There are no channels that exists in list but not in the set and vice versa." + ] + }, + { + "name": "HrmpOpenChannelRequestsList", + "modifier": "Default", + "type": { + "plain": 658 + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "HrmpOpenChannelRequestCount", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " This mapping tracks how many open channel requests are initiated by a given sender para.", + " Invariant: `HrmpOpenChannelRequests` should contain the same number of items that has `(X, _)`", + " as the number of `HrmpOpenChannelRequestCount` for `X`." + ] + }, + { + "name": "HrmpAcceptedChannelRequestCount", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " This mapping tracks how many open channel requests were accepted by a given recipient para.", + " Invariant: `HrmpOpenChannelRequests` should contain the same number of items `(_, X)` with", + " `confirmed` set to true, as the number of `HrmpAcceptedChannelRequestCount` for `X`." + ] + }, + { + "name": "HrmpCloseChannelRequests", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 102, + "value": 53 + } + }, + "fallback": "0x00", + "docs": [ + " A set of pending HRMP close channel requests that are going to be closed during the session change.", + " Used for checking if a given channel is registered for closure.", + "", + " The set is accompanied by a list for iteration.", + "", + " Invariant:", + " - There are no channels that exists in list but not in the set and vice versa." + ] + }, + { + "name": "HrmpCloseChannelRequestsList", + "modifier": "Default", + "type": { + "plain": 658 + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "HrmpWatermarks", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " The HRMP watermark associated with each para.", + " Invariant:", + " - each para `P` used here as a key should satisfy `Paras::is_valid_para(P)` within a session." + ] + }, + { + "name": "HrmpChannels", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 102, + "value": 659 + } + }, + "fallback": "0x00", + "docs": [ + " HRMP channel data associated with each para.", + " Invariant:", + " - each participant in the channel should satisfy `Paras::is_valid_para(P)` within a session." + ] + }, + { + "name": "HrmpIngressChannelsIndex", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 637 + } + }, + "fallback": "0x00", + "docs": [ + " Ingress/egress indexes allow to find all the senders and receivers given the opposite", + " side. I.e.", + "", + " (a) ingress index allows to find all the senders for a given recipient.", + " (b) egress index allows to find all the recipients for a given sender.", + "", + " Invariants:", + " - for each ingress index entry for `P` each item `I` in the index should present in `HrmpChannels`", + " as `(I, P)`.", + " - for each egress index entry for `P` each item `E` in the index should present in `HrmpChannels`", + " as `(P, E)`.", + " - there should be no other dangling channels in `HrmpChannels`.", + " - the vectors are sorted." + ] + }, + { + "name": "HrmpEgressChannelsIndex", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 637 + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "HrmpChannelContents", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 102, + "value": 661 + } + }, + "fallback": "0x00", + "docs": [ + " Storage for the messages for each channel.", + " Invariant: cannot be non-empty if the corresponding channel in `HrmpChannels` is `None`." + ] + }, + { + "name": "HrmpChannelDigests", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 663 + } + }, + "fallback": "0x00", + "docs": [ + " Maintains a mapping that can be used to answer the question:", + " What paras sent a message at the given block number for a given receiver.", + " Invariants:", + " - The inner `Vec` is never empty.", + " - The inner `Vec` cannot store two same `ParaId`.", + " - The outer vector is sorted ascending by block number and cannot store two items with the same", + " block number." + ] + } + ] + }, + "calls": { + "type": 488 + }, + "events": { + "type": 101 + }, + "constants": [], + "errors": { + "type": 665 + }, + "index": 60 + }, + { + "name": "ParaSessionInfo", + "storage": { + "prefix": "ParaSessionInfo", + "items": [ + { + "name": "AssignmentKeysUnsafe", + "modifier": "Default", + "type": { + "plain": 666 + }, + "fallback": "0x00", + "docs": [ + " Assignment keys for the current session.", + " Note that this API is private due to it being prone to 'off-by-one' at session boundaries.", + " When in doubt, use `Sessions` API instead." + ] + }, + { + "name": "EarliestStoredSession", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The earliest session for which previous session info is stored." + ] + }, + { + "name": "Sessions", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 4, + "value": 667 + } + }, + "fallback": "0x00", + "docs": [ + " Session information in a rolling window.", + " Should have an entry in range `EarliestStoredSession..=CurrentSessionIndex`.", + " Does not have any entries before the session index in the first session change notification." + ] + } + ] + }, + "calls": null, + "events": null, + "constants": [], + "errors": null, + "index": 61 + }, + { + "name": "Registrar", + "storage": { + "prefix": "Registrar", + "items": [ + { + "name": "PendingSwap", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 88 + } + }, + "fallback": "0x00", + "docs": [ + " Pending swap operations." + ] + }, + { + "name": "Paras", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 669 + } + }, + "fallback": "0x00", + "docs": [ + " Amount held on deposit for each para and the original depositor.", + "", + " The given account ID is responsible for registering the code and initial head data, but may only do", + " so if it isn't yet registered. (After that, it's up to governance to do so.)" + ] + }, + { + "name": "NextFreeParaId", + "modifier": "Default", + "type": { + "plain": 88 + }, + "fallback": "0x00000000", + "docs": [ + " The next free `ParaId`." + ] + } + ] + }, + "calls": { + "type": 489 + }, + "events": { + "type": 103 + }, + "constants": [ + { + "name": "ParaDeposit", + "type": 6, + "value": "0x0080ca39612400000000000000000000", + "docs": [ + " The deposit to be paid to run a parathread.", + " This should include the cost for storing the genesis head and validation code." + ] + }, + { + "name": "DataDepositPerByte", + "type": 6, + "value": "0x55a0fc01000000000000000000000000", + "docs": [ + " The deposit to be paid per byte stored on chain." + ] + } + ], + "errors": { + "type": 670 + }, + "index": 70 + }, + { + "name": "Slots", + "storage": { + "prefix": "Slots", + "items": [ + { + "name": "Leases", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 671 + } + }, + "fallback": "0x00", + "docs": [ + " Amounts held on deposit for each (possibly future) leased parachain.", + "", + " The actual amount locked on its behalf by any account at any time is the maximum of the second values", + " of the items in this list whose first value is the account.", + "", + " The first item in the list is the amount locked for the current Lease Period. Following", + " items are for the subsequent lease periods.", + "", + " The default value (an empty list) implies that the parachain no longer exists (or never", + " existed) as far as this pallet is concerned.", + "", + " If a parachain doesn't exist *yet* but is scheduled to exist in the future, then it", + " will be left-padded with one or more `None`s to denote the fact that nothing is held on", + " deposit for the non-existent chain currently, but is held at some point in the future.", + "", + " It is illegal for a `None` value to trail in the list." + ] + } + ] + }, + "calls": { + "type": 490 + }, + "events": { + "type": 104 + }, + "constants": [ + { + "name": "LeasePeriod", + "type": 4, + "value": "0x803a0900", + "docs": [ + " The number of blocks over which a single period lasts." + ] + }, + { + "name": "LeaseOffset", + "type": 4, + "value": "0x00000000", + "docs": [ + " The number of blocks to offset each lease period by." + ] + } + ], + "errors": { + "type": 673 + }, + "index": 71 + }, + { + "name": "Auctions", + "storage": { + "prefix": "Auctions", + "items": [ + { + "name": "AuctionCounter", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Number of auctions started so far." + ] + }, + { + "name": "AuctionInfo", + "modifier": "Optional", + "type": { + "plain": 71 + }, + "fallback": "0x00", + "docs": [ + " Information relating to the current auction, if there is one.", + "", + " The first item in the tuple is the lease period index that the first of the four", + " contiguous lease periods on auction is for. The second is the block number when the", + " auction will \"begin to end\", i.e. the first block of the Ending Period of the auction." + ] + }, + { + "name": "ReservedAmounts", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 674, + "value": 6 + } + }, + "fallback": "0x00", + "docs": [ + " Amounts currently reserved in the accounts of the bidders currently winning", + " (sub-)ranges." + ] + }, + { + "name": "Winning", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 4, + "value": 675 + } + }, + "fallback": "0x00", + "docs": [ + " The winning bids for each of the 10 ranges at each sample in the final Ending Period of", + " the current auction. The map's key is the 0-based index into the Sample Size. The", + " first sample of the ending period is 0; the last is `Sample Size - 1`." + ] + } + ] + }, + "calls": { + "type": 491 + }, + "events": { + "type": 105 + }, + "constants": [ + { + "name": "EndingPeriod", + "type": 4, + "value": "0x40190100", + "docs": [ + " The number of blocks over which an auction may be retroactively ended." + ] + }, + { + "name": "SampleLength", + "type": 4, + "value": "0x14000000", + "docs": [ + " The length of each sample to take during the ending period.", + "", + " `EndingPeriod` / `SampleLength` = Total # of Samples" + ] + }, + { + "name": "SlotRangeCount", + "type": 4, + "value": "0x24000000", + "docs": [] + }, + { + "name": "LeasePeriodsPerSlot", + "type": 4, + "value": "0x08000000", + "docs": [] + } + ], + "errors": { + "type": 678 + }, + "index": 72 + }, + { + "name": "Crowdloan", + "storage": { + "prefix": "Crowdloan", + "items": [ + { + "name": "Funds", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat" + ], + "key": 88, + "value": 679 + } + }, + "fallback": "0x00", + "docs": [ + " Info on all of the funds." + ] + }, + { + "name": "NewRaise", + "modifier": "Default", + "type": { + "plain": 637 + }, + "fallback": "0x00", + "docs": [ + " The funds that have had additional contributions during the last block. This is used", + " in order to determine which funds should submit new or updated bids." + ] + }, + { + "name": "EndingsCount", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " The number of auctions that have entered into their ending period so far." + ] + }, + { + "name": "NextTrieIndex", + "modifier": "Default", + "type": { + "plain": 4 + }, + "fallback": "0x00000000", + "docs": [ + " Tracker for the next available trie index" + ] + } + ] + }, + "calls": { + "type": 493 + }, + "events": { + "type": 106 + }, + "constants": [ + { + "name": "PalletId", + "type": 538, + "value": "0x70792f6366756e64", + "docs": [ + " `PalletId` for the crowdloan pallet. An appropriate value could be `PalletId(*b\"py/cfund\")`" + ] + }, + { + "name": "MinContribution", + "type": 6, + "value": "0x18e47648170000000000000000000000", + "docs": [ + " The minimum amount that may be contributed into a crowdloan. Should almost certainly be at", + " least `ExistentialDeposit`." + ] + }, + { + "name": "RemoveKeysLimit", + "type": 4, + "value": "0xe8030000", + "docs": [ + " Max number of storage keys to remove per extrinsic call." + ] + } + ], + "errors": { + "type": 681 + }, + "index": 73 + }, + { + "name": "XcmPallet", + "storage": { + "prefix": "XcmPallet", + "items": [ + { + "name": "QueryCounter", + "modifier": "Default", + "type": { + "plain": 8 + }, + "fallback": "0x0000000000000000", + "docs": [ + " The latest available query index." + ] + }, + { + "name": "Queries", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Blake2_128Concat" + ], + "key": 8, + "value": 682 + } + }, + "fallback": "0x00", + "docs": [ + " The ongoing queries." + ] + }, + { + "name": "AssetTraps", + "modifier": "Default", + "type": { + "map": { + "hashers": [ + "Identity" + ], + "key": 9, + "value": 4 + } + }, + "fallback": "0x00000000", + "docs": [ + " The existing asset traps.", + "", + " Key is the blake2 256 hash of (origin, versioned `MultiAssets`) pair. Value is the number of", + " times this pair has been trapped (usually just 1 if it exists at all)." + ] + }, + { + "name": "SafeXcmVersion", + "modifier": "Optional", + "type": { + "plain": 4 + }, + "fallback": "0x00", + "docs": [ + " Default version to encode XCM when latest version of destination is unknown. If `None`,", + " then the destinations whose XCM version is unknown are considered unreachable." + ] + }, + { + "name": "SupportedVersion", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Blake2_128Concat" + ], + "key": 686, + "value": 4 + } + }, + "fallback": "0x00", + "docs": [ + " The Latest versions that we know various locations support." + ] + }, + { + "name": "VersionNotifiers", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Blake2_128Concat" + ], + "key": 686, + "value": 8 + } + }, + "fallback": "0x00", + "docs": [ + " All locations that we have requested version notifications from." + ] + }, + { + "name": "VersionNotifyTargets", + "modifier": "Optional", + "type": { + "map": { + "hashers": [ + "Twox64Concat", + "Blake2_128Concat" + ], + "key": 686, + "value": 687 + } + }, + "fallback": "0x00", + "docs": [ + " The target locations that are subscribed to our version changes, as well as the most recent", + " of our versions we informed them of." + ] + }, + { + "name": "VersionDiscoveryQueue", + "modifier": "Default", + "type": { + "plain": 688 + }, + "fallback": "0x00", + "docs": [ + " Destinations whose latest XCM version we would like to know. Duplicates not allowed, and", + " the `u32` counter is the number of times that a send to the destination has been attempted,", + " which is used as a prioritization." + ] + }, + { + "name": "CurrentMigration", + "modifier": "Optional", + "type": { + "plain": 691 + }, + "fallback": "0x00", + "docs": [ + " The current migration's stage, if any." + ] + } + ] + }, + "calls": { + "type": 501 + }, + "events": { + "type": 107 + }, + "constants": [], + "errors": { + "type": 692 + }, + "index": 99 + } + ], + "extrinsic": { + "type": 693, + "version": 4, + "signedExtensions": [ + { + "identifier": "CheckSpecVersion", + "type": 695, + "additionalSigned": 4 + }, + { + "identifier": "CheckTxVersion", + "type": 696, + "additionalSigned": 4 + }, + { + "identifier": "CheckGenesis", + "type": 697, + "additionalSigned": 9 + }, + { + "identifier": "CheckMortality", + "type": 698, + "additionalSigned": 9 + }, + { + "identifier": "CheckNonce", + "type": 700, + "additionalSigned": 53 + }, + { + "identifier": "CheckWeight", + "type": 701, + "additionalSigned": 53 + }, + { + "identifier": "ChargeTransactionPayment", + "type": 702, + "additionalSigned": 53 + } + ] + }, + "type": 703 + } + } +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/MoonriverMetaData.json b/javascore/bmv/eventDecoder/MoonriverMetaData.json new file mode 100644 index 00000000..7a104687 --- /dev/null +++ b/javascore/bmv/eventDecoder/MoonriverMetaData.json @@ -0,0 +1,7830 @@ +{ + "magicNumber": 1635018093, + "metadata": { + "v13": { + "modules": [ + { + "name": "System", + "storage": { + "prefix": "System", + "items": [ + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "AccountInfo", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The full account information for a particular account ID." + ] + }, + { + "name": "ExtrinsicCount", + "modifier": "Optional", + "type": { + "plain": "u32" + }, + "fallback": "0x00", + "docs": [ + " Total extrinsics count for the current block." + ] + }, + { + "name": "BlockWeight", + "modifier": "Default", + "type": { + "plain": "ConsumedWeight" + }, + "fallback": "0x000000000000000000000000000000000000000000000000", + "docs": [ + " The current weight for the block." + ] + }, + { + "name": "AllExtrinsicsLen", + "modifier": "Optional", + "type": { + "plain": "u32" + }, + "fallback": "0x00", + "docs": [ + " Total length (in bytes) for all extrinsics put together, for the current block." + ] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BlockNumber", + "value": "Hash", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Map of block numbers to block hashes." + ] + }, + { + "name": "ExtrinsicData", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "u32", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Extrinsics data for the current block (maps an extrinsic's index to its data)." + ] + }, + { + "name": "Number", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "docs": [ + " The current block number being processed. Set by `execute_block`." + ] + }, + { + "name": "ParentHash", + "modifier": "Default", + "type": { + "plain": "Hash" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Hash of the previous block." + ] + }, + { + "name": "Digest", + "modifier": "Default", + "type": { + "plain": "DigestOf" + }, + "fallback": "0x00", + "docs": [ + " Digest of the current block, also part of the block header." + ] + }, + { + "name": "Events", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Events deposited for the current block." + ] + }, + { + "name": "EventCount", + "modifier": "Default", + "type": { + "plain": "EventIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The number of events in the `Events` list." + ] + }, + { + "name": "EventTopics", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "Hash", + "value": "Vec<(BlockNumber,EventIndex)>", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Mapping between a topic (represented by T::Hash) and a vector of indexes", + " of events in the `>` list.", + "", + " All topic vectors have deterministic storage locations depending on the topic. This", + " allows light-clients to leverage the changes trie storage tracking mechanism and", + " in case of changes fetch the list of events of interest.", + "", + " The value has the type `(T::BlockNumber, EventIndex)` because if we used only just", + " the `EventIndex` then in case if the topic has the same contents on the next block", + " no notification will be triggered thus the event might be lost." + ] + }, + { + "name": "LastRuntimeUpgrade", + "modifier": "Optional", + "type": { + "plain": "LastRuntimeUpgradeInfo" + }, + "fallback": "0x00", + "docs": [ + " Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened." + ] + }, + { + "name": "UpgradedToU32RefCount", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " True if we have upgraded so that `type RefCount` is `u32`. False (default) if not." + ] + }, + { + "name": "UpgradedToTripleRefCount", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " True if we have upgraded so that AccountInfo contains three types of `RefCount`. False", + " (default) if not." + ] + }, + { + "name": "ExecutionPhase", + "modifier": "Optional", + "type": { + "plain": "Phase" + }, + "fallback": "0x00", + "docs": [ + " The execution phase of the block." + ] + } + ] + }, + "calls": [ + { + "name": "fill_block", + "args": [ + { + "name": "_ratio", + "type": "Perbill" + } + ], + "docs": [ + " A dispatch that will fill the block weight up to the given ratio." + ] + }, + { + "name": "remark", + "args": [ + { + "name": "_remark", + "type": "Bytes" + } + ], + "docs": [ + " Make some on-chain remark.", + "", + " # ", + " - `O(1)`", + " # " + ] + }, + { + "name": "set_heap_pages", + "args": [ + { + "name": "pages", + "type": "u64" + } + ], + "docs": [ + " Set the number of pages in the WebAssembly environment's heap.", + "", + " # ", + " - `O(1)`", + " - 1 storage write.", + " - Base Weight: 1.405 µs", + " - 1 write to HEAP_PAGES", + " # " + ] + }, + { + "name": "set_code", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [ + " Set the new runtime code.", + "", + " # ", + " - `O(C + S)` where `C` length of `code` and `S` complexity of `can_set_code`", + " - 1 storage write (codec `O(C)`).", + " - 1 call to `can_set_code`: `O(S)` (calls `sp_io::misc::runtime_version` which is expensive).", + " - 1 event.", + " The weight of this function is dependent on the runtime, but generally this is very expensive.", + " We will treat this as a full block.", + " # " + ] + }, + { + "name": "set_code_without_checks", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [ + " Set the new runtime code without doing any checks of the given `code`.", + "", + " # ", + " - `O(C)` where `C` length of `code`", + " - 1 storage write (codec `O(C)`).", + " - 1 event.", + " The weight of this function is dependent on the runtime. We will treat this as a full block.", + " # " + ] + }, + { + "name": "set_changes_trie_config", + "args": [ + { + "name": "changes_trie_config", + "type": "Option" + } + ], + "docs": [ + " Set the new changes trie configuration.", + "", + " # ", + " - `O(1)`", + " - 1 storage write or delete (codec `O(1)`).", + " - 1 call to `deposit_log`: Uses `append` API, so O(1)", + " - Base Weight: 7.218 µs", + " - DB Weight:", + " - Writes: Changes Trie, System Digest", + " # " + ] + }, + { + "name": "set_storage", + "args": [ + { + "name": "items", + "type": "Vec" + } + ], + "docs": [ + " Set some items of storage.", + "", + " # ", + " - `O(I)` where `I` length of `items`", + " - `I` storage writes (`O(1)`).", + " - Base Weight: 0.568 * i µs", + " - Writes: Number of items", + " # " + ] + }, + { + "name": "kill_storage", + "args": [ + { + "name": "keys", + "type": "Vec" + } + ], + "docs": [ + " Kill some items from storage.", + "", + " # ", + " - `O(IK)` where `I` length of `keys` and `K` length of one key", + " - `I` storage deletions.", + " - Base Weight: .378 * i µs", + " - Writes: Number of items", + " # " + ] + }, + { + "name": "kill_prefix", + "args": [ + { + "name": "prefix", + "type": "Key" + }, + { + "name": "_subkeys", + "type": "u32" + } + ], + "docs": [ + " Kill all storage items with a key that starts with the given prefix.", + "", + " **NOTE:** We rely on the Root origin to provide us the number of subkeys under", + " the prefix we are removing to accurately calculate the weight of this function.", + "", + " # ", + " - `O(P)` where `P` amount of keys with prefix `prefix`", + " - `P` storage deletions.", + " - Base Weight: 0.834 * P µs", + " - Writes: Number of subkeys + 1", + " # " + ] + }, + { + "name": "remark_with_event", + "args": [ + { + "name": "remark", + "type": "Bytes" + } + ], + "docs": [ + " Make some on-chain remark and emit event.", + "", + " # ", + " - `O(b)` where b is the length of the remark.", + " - 1 event.", + " # " + ] + } + ], + "events": [ + { + "name": "ExtrinsicSuccess", + "args": [ + "DispatchInfo" + ], + "docs": [ + " An extrinsic completed successfully. \\[info\\]" + ] + }, + { + "name": "ExtrinsicFailed", + "args": [ + "DispatchError", + "DispatchInfo" + ], + "docs": [ + " An extrinsic failed. \\[error, info\\]" + ] + }, + { + "name": "CodeUpdated", + "args": [], + "docs": [ + " `:code` was updated." + ] + }, + { + "name": "NewAccount", + "args": [ + "AccountId" + ], + "docs": [ + " A new \\[account\\] was created." + ] + }, + { + "name": "KilledAccount", + "args": [ + "AccountId" + ], + "docs": [ + " An \\[account\\] was reaped." + ] + }, + { + "name": "Remarked", + "args": [ + "AccountId", + "Hash" + ], + "docs": [ + " On on-chain remark happened. \\[origin, remark_hash\\]" + ] + } + ], + "constants": [ + { + "name": "BlockWeights", + "type": "BlockWeights", + "value": "0x00f2052a010000000088526a74000000405973070000000001c0180fa44b0000000100e6bd4f57000000010000000000000000405973070000000001c0baa3be68000000010088526a740000000100a2941a1d0000004059730700000000000000", + "docs": [ + " Block & extrinsics weights: base values and limits." + ] + }, + { + "name": "BlockLength", + "type": "BlockLength", + "value": "0x00003c000000500000005000", + "docs": [ + " The maximum length of a block (in bytes)." + ] + }, + { + "name": "BlockHashCount", + "type": "BlockNumber", + "value": "0x00010000", + "docs": [ + " Maximum number of block number to block hash mappings to keep (oldest pruned first)." + ] + }, + { + "name": "DbWeight", + "type": "RuntimeDbWeight", + "value": "0x40787d010000000000e1f50500000000", + "docs": [ + " The weight of runtime database operations the runtime can invoke." + ] + }, + { + "name": "Version", + "type": "RuntimeVersion", + "value": "0x246d6f6f6e7269766572246d6f6f6e726976657203000000200300000000000034d2bc9897eed08f1503000000df6acb689907609b0300000037e397fc7c91f5e40100000040fe3ad401f8959a05000000f78b278be53f454c02000000ab3c0572291feb8b01000000bc9d89904f5b923f01000000bd78255d4feeea1f01000000a33d43f58731ad8401000000582211f65bb14b890100000037c8bb1350a9a2a8010000001fba3ffbb7e07e8d02000000ea93e3f16f3d69620100000002000000", + "docs": [ + " Get the chain's current version." + ] + }, + { + "name": "SS58Prefix", + "type": "u16", + "value": "0x0505", + "docs": [ + " The designated SS85 prefix of this chain.", + "", + " This replaces the \"ss58Format\" property declared in the chain spec. Reason is", + " that the runtime should know about the prefix in order to make use of it as", + " an identifier of the chain." + ] + } + ], + "errors": [ + { + "name": "InvalidSpecName", + "docs": [ + " The name of specification does not match between the current runtime", + " and the new runtime." + ] + }, + { + "name": "SpecVersionNeedsToIncrease", + "docs": [ + " The specification version is not allowed to decrease between the current runtime", + " and the new runtime." + ] + }, + { + "name": "FailedToExtractRuntimeVersion", + "docs": [ + " Failed to extract the runtime version from the new runtime.", + "", + " Either calling `Core_version` or decoding `RuntimeVersion` failed." + ] + }, + { + "name": "NonDefaultComposite", + "docs": [ + " Suicide called when the account has non-default composite data." + ] + }, + { + "name": "NonZeroRefCount", + "docs": [ + " There is a non-zero reference count preventing the account from being purged." + ] + } + ], + "index": 0 + }, + { + "name": "ParachainSystem", + "storage": { + "prefix": "ParachainSystem", + "items": [ + { + "name": "PendingRelayChainBlockNumber", + "modifier": "Optional", + "type": { + "plain": "RelayChainBlockNumber" + }, + "fallback": "0x00", + "docs": [ + " We need to store the new validation function for the span between", + " setting it and applying it. If it has a", + " value, then [`PendingValidationCode`] must have a real value, and", + " together will coordinate the block number where the upgrade will happen." + ] + }, + { + "name": "PendingValidationCode", + "modifier": "Default", + "type": { + "plain": "Bytes" + }, + "fallback": "0x00", + "docs": [ + " The new validation function we will upgrade to when the relay chain", + " reaches [`PendingRelayChainBlockNumber`]. A real validation function must", + " exist here as long as [`PendingRelayChainBlockNumber`] is set." + ] + }, + { + "name": "ValidationData", + "modifier": "Optional", + "type": { + "plain": "PersistedValidationData" + }, + "fallback": "0x00", + "docs": [ + " The [`PersistedValidationData`] set for this block." + ] + }, + { + "name": "DidSetValidationCode", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " Were the validation data set to notify the relay chain?" + ] + }, + { + "name": "LastUpgrade", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "docs": [ + " The last relay parent block number at which we signalled the code upgrade." + ] + }, + { + "name": "RelevantMessagingState", + "modifier": "Optional", + "type": { + "plain": "MessagingStateSnapshot" + }, + "fallback": "0x00", + "docs": [ + " The snapshot of some state related to messaging relevant to the current parachain as per", + " the relay parent.", + "", + " This field is meant to be updated each block with the validation data inherent. Therefore,", + " before processing of the inherent, e.g. in `on_initialize` this data may be stale.", + "", + " This data is also absent from the genesis." + ] + }, + { + "name": "HostConfiguration", + "modifier": "Optional", + "type": { + "plain": "AbridgedHostConfiguration" + }, + "fallback": "0x00", + "docs": [ + " The parachain host configuration that was obtained from the relay parent.", + "", + " This field is meant to be updated each block with the validation data inherent. Therefore,", + " before processing of the inherent, e.g. in `on_initialize` this data may be stale.", + "", + " This data is also absent from the genesis." + ] + }, + { + "name": "LastDmqMqcHead", + "modifier": "Default", + "type": { + "plain": "MessageQueueChain" + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The last downward message queue chain head we have observed.", + "", + " This value is loaded before and saved after processing inbound downward messages carried", + " by the system inherent." + ] + }, + { + "name": "LastHrmpMqcHeads", + "modifier": "Default", + "type": { + "plain": "BTreeMap" + }, + "fallback": "0x00", + "docs": [ + " The message queue chain heads we have observed per each channel incoming channel.", + "", + " This value is loaded before and saved after processing inbound downward messages carried", + " by the system inherent." + ] + }, + { + "name": "ProcessedDownwardMessages", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Number of downward messages processed in a block.", + "", + " This will be cleared in `on_initialize` of each new block." + ] + }, + { + "name": "NewValidationCode", + "modifier": "Optional", + "type": { + "plain": "Bytes" + }, + "fallback": "0x00", + "docs": [ + " New validation code that was set in a block.", + "", + " This will be cleared in `on_initialize` of each new block if no other pallet already set", + " the value." + ] + }, + { + "name": "HrmpWatermark", + "modifier": "Default", + "type": { + "plain": "BlockNumber" + }, + "fallback": "0x00000000", + "docs": [ + " HRMP watermark that was set in a block.", + "", + " This will be cleared in `on_initialize` of each new block." + ] + }, + { + "name": "HrmpOutboundMessages", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " HRMP messages that were sent in a block.", + "", + " This will be cleared in `on_initialize` of each new block." + ] + }, + { + "name": "UpwardMessages", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Upward messages that were sent in a block.", + "", + " This will be cleared in `on_initialize` of each new block." + ] + }, + { + "name": "PendingUpwardMessages", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Upward messages that are still pending and not yet send to the relay chain." + ] + }, + { + "name": "AnnouncedHrmpMessagesPerCandidate", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " The number of HRMP messages we observed in `on_initialize` and thus used that number for", + " announcing the weight of `on_initialize` and `on_finalize`." + ] + }, + { + "name": "ReservedXcmpWeightOverride", + "modifier": "Optional", + "type": { + "plain": "Weight" + }, + "fallback": "0x00", + "docs": [ + " The weight we reserve at the beginning of the block for processing XCMP messages. This", + " overrides the amount set in the Config trait." + ] + }, + { + "name": "ReservedDmpWeightOverride", + "modifier": "Optional", + "type": { + "plain": "Weight" + }, + "fallback": "0x00", + "docs": [ + " The weight we reserve at the beginning of the block for processing DMP messages. This", + " overrides the amount set in the Config trait." + ] + }, + { + "name": "AuthorizedUpgrade", + "modifier": "Optional", + "type": { + "plain": "Hash" + }, + "fallback": "0x00", + "docs": [ + " The next authorized upgrade, if there is one." + ] + } + ] + }, + "calls": [ + { + "name": "set_upgrade_block", + "args": [ + { + "name": "relay_chain_block", + "type": "RelayChainBlockNumber" + } + ], + "docs": [ + " Force an already scheduled validation function upgrade to happen on a particular block.", + "", + " Note that coordinating this block for the upgrade has to happen independently on the", + " relay chain and this parachain. Synchronizing the block for the upgrade is sensitive,", + " and this bypasses all checks and and normal protocols. Very easy to brick your chain", + " if done wrong." + ] + }, + { + "name": "set_validation_data", + "args": [ + { + "name": "data", + "type": "ParachainInherentData" + } + ], + "docs": [ + " Set the current validation data.", + "", + " This should be invoked exactly once per block. It will panic at the finalization", + " phase if the call was not invoked.", + "", + " The dispatch origin for this call must be `Inherent`", + "", + " As a side effect, this function upgrades the current validation function", + " if the appropriate time has come." + ] + }, + { + "name": "sudo_send_upward_message", + "args": [ + { + "name": "message", + "type": "UpwardMessage" + } + ], + "docs": [] + }, + { + "name": "authorize_upgrade", + "args": [ + { + "name": "code_hash", + "type": "Hash" + } + ], + "docs": [] + }, + { + "name": "enact_authorized_upgrade", + "args": [ + { + "name": "code", + "type": "Bytes" + } + ], + "docs": [] + } + ], + "events": [ + { + "name": "ValidationFunctionStored", + "args": [ + "RelayChainBlockNumber" + ], + "docs": [ + " The validation function has been scheduled to apply as of the contained relay chain", + " block number." + ] + }, + { + "name": "ValidationFunctionApplied", + "args": [ + "RelayChainBlockNumber" + ], + "docs": [ + " The validation function was applied as of the contained relay chain block number." + ] + }, + { + "name": "UpgradeAuthorized", + "args": [ + "Hash" + ], + "docs": [ + " An upgrade has been authorized." + ] + }, + { + "name": "DownwardMessagesReceived", + "args": [ + "u32" + ], + "docs": [ + " Some downward messages have been received and will be processed.", + " \\[ count \\]" + ] + }, + { + "name": "DownwardMessagesProcessed", + "args": [ + "Weight", + "Hash" + ], + "docs": [ + " Downward messages were processed using the given weight.", + " \\[ weight_used, result_mqc_head \\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "OverlappingUpgrades", + "docs": [ + " Attempt to upgrade validation function while existing upgrade pending" + ] + }, + { + "name": "ProhibitedByPolkadot", + "docs": [ + " Polkadot currently prohibits this parachain from upgrading its validation function" + ] + }, + { + "name": "TooBig", + "docs": [ + " The supplied validation function has compiled into a blob larger than Polkadot is", + " willing to run" + ] + }, + { + "name": "ValidationDataNotAvailable", + "docs": [ + " The inherent which supplies the validation data did not run this block" + ] + }, + { + "name": "HostConfigurationNotAvailable", + "docs": [ + " The inherent which supplies the host configuration did not run this block" + ] + }, + { + "name": "NotScheduled", + "docs": [ + " No validation function upgrade is currently scheduled." + ] + }, + { + "name": "NothingAuthorized", + "docs": [ + " No code upgrade has been authorized." + ] + }, + { + "name": "Unauthorized", + "docs": [ + " The given code upgrade has not been authorized." + ] + } + ], + "index": 1 + }, + { + "name": "RandomnessCollectiveFlip", + "storage": { + "prefix": "RandomnessCollectiveFlip", + "items": [ + { + "name": "RandomMaterial", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Series of block headers from the last 81 blocks that acts as random seed material. This", + " is arranged as a ring buffer with `block_number % 81` being the index into the `Vec` of", + " the oldest hash." + ] + } + ] + }, + "calls": null, + "events": null, + "constants": [], + "errors": [], + "index": 2 + }, + { + "name": "Timestamp", + "storage": { + "prefix": "Timestamp", + "items": [ + { + "name": "Now", + "modifier": "Default", + "type": { + "plain": "Moment" + }, + "fallback": "0x0000000000000000", + "docs": [ + " Current time for the current block." + ] + }, + { + "name": "DidUpdate", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " Did the timestamp get updated in this block?" + ] + } + ] + }, + "calls": [ + { + "name": "set", + "args": [ + { + "name": "now", + "type": "Compact" + } + ], + "docs": [ + " Set the current time.", + "", + " This call should be invoked exactly once per block. It will panic at the finalization", + " phase, if this call hasn't been invoked by that time.", + "", + " The timestamp should be greater than the previous one by the amount specified by", + " `MinimumPeriod`.", + "", + " The dispatch origin for this call must be `Inherent`.", + "", + " # ", + " - `O(1)` (Note that implementations of `OnTimestampSet` must also be `O(1)`)", + " - 1 storage read and 1 storage mutation (codec `O(1)`). (because of `DidUpdate::take` in `on_finalize`)", + " - 1 event handler `on_timestamp_set`. Must be `O(1)`.", + " # " + ] + } + ], + "events": null, + "constants": [ + { + "name": "MinimumPeriod", + "type": "Moment", + "value": "0x0100000000000000", + "docs": [ + " The minimum period between blocks. Beware that this is different to the *expected* period", + " that the block production apparatus provides. Your chosen consensus system will generally", + " work with this to determine a sensible block time. e.g. For Aura, it will be double this", + " period on default settings." + ] + } + ], + "errors": [], + "index": 3 + }, + { + "name": "ParachainInfo", + "storage": { + "prefix": "ParachainInfo", + "items": [ + { + "name": "ParachainId", + "modifier": "Default", + "type": { + "plain": "ParaId" + }, + "fallback": "0x64000000", + "docs": [] + } + ] + }, + "calls": null, + "events": null, + "constants": [], + "errors": [], + "index": 4 + }, + { + "name": "Balances", + "storage": { + "prefix": "Balances", + "items": [ + { + "name": "TotalIssuance", + "modifier": "Default", + "type": { + "plain": "Balance" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " The total units issued in the system." + ] + }, + { + "name": "Account", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "AccountData", + "linked": false + } + }, + "fallback": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " The balance of an account.", + "", + " NOTE: This is only used in the case that this pallet is used to store balances." + ] + }, + { + "name": "Locks", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "Vec", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Any liquidity locks on some account balances.", + " NOTE: Should only be accessed when setting, changing and freeing a lock." + ] + }, + { + "name": "Reserves", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "Vec", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Named reserves on some account balances." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " This is set to v2.0.0 for new networks." + ] + } + ] + }, + "calls": [ + { + "name": "transfer", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Transfer some liquid free balance to another account.", + "", + " `transfer` will set the `FreeBalance` of the sender and receiver.", + " It will decrease the total issuance of the system by the `TransferFee`.", + " If the sender's account is below the existential deposit as a result", + " of the transfer, the account will be reaped.", + "", + " The dispatch origin for this call must be `Signed` by the transactor.", + "", + " # ", + " - Dependent on arguments but not critical, given proper implementations for", + " input config types. See related functions below.", + " - It contains a limited number of reads and writes internally and no complex computation.", + "", + " Related functions:", + "", + " - `ensure_can_withdraw` is always called internally but has a bounded complexity.", + " - Transferring balances to accounts that did not exist before will cause", + " `T::OnNewAccount::on_new_account` to be called.", + " - Removing enough funds from an account will trigger `T::DustRemoval::on_unbalanced`.", + " - `transfer_keep_alive` works the same way as `transfer`, but has an additional", + " check that the transfer will not kill the origin account.", + " ---------------------------------", + " - Base Weight: 73.64 µs, worst case scenario (account created, account removed)", + " - DB Weight: 1 Read and 1 Write to destination account", + " - Origin account is already in memory, so no DB operations for them.", + " # " + ] + }, + { + "name": "set_balance", + "args": [ + { + "name": "who", + "type": "LookupSource" + }, + { + "name": "new_free", + "type": "Compact" + }, + { + "name": "new_reserved", + "type": "Compact" + } + ], + "docs": [ + " Set the balances of a given account.", + "", + " This will alter `FreeBalance` and `ReservedBalance` in storage. it will", + " also decrease the total issuance of the system (`TotalIssuance`).", + " If the new free or reserved balance is below the existential deposit,", + " it will reset the account nonce (`frame_system::AccountNonce`).", + "", + " The dispatch origin for this call is `root`.", + "", + " # ", + " - Independent of the arguments.", + " - Contains a limited number of reads and writes.", + " ---------------------", + " - Base Weight:", + " - Creating: 27.56 µs", + " - Killing: 35.11 µs", + " - DB Weight: 1 Read, 1 Write to `who`", + " # " + ] + }, + { + "name": "force_transfer", + "args": [ + { + "name": "source", + "type": "LookupSource" + }, + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Exactly as `transfer`, except the origin must be root and the source account may be", + " specified.", + " # ", + " - Same as transfer, but additional read and write because the source account is", + " not assumed to be in the overlay.", + " # " + ] + }, + { + "name": "transfer_keep_alive", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Same as the [`transfer`] call, but with a check that the transfer will not kill the", + " origin account.", + "", + " 99% of the time you want [`transfer`] instead.", + "", + " [`transfer`]: struct.Pallet.html#method.transfer", + " # ", + " - Cheaper than transfer because account cannot be killed.", + " - Base Weight: 51.4 µs", + " - DB Weight: 1 Read and 1 Write to dest (sender is in overlay already)", + " #" + ] + }, + { + "name": "transfer_all", + "args": [ + { + "name": "dest", + "type": "LookupSource" + }, + { + "name": "keep_alive", + "type": "bool" + } + ], + "docs": [ + " Transfer the entire transferable balance from the caller account.", + "", + " NOTE: This function only attempts to transfer _transferable_ balances. This means that", + " any locked, reserved, or existential deposits (when `keep_alive` is `true`), will not be", + " transferred by this function. To ensure that this function results in a killed account,", + " you might need to prepare the account by removing any reference counters, storage", + " deposits, etc...", + "", + " The dispatch origin of this call must be Signed.", + "", + " - `dest`: The recipient of the transfer.", + " - `keep_alive`: A boolean to determine if the `transfer_all` operation should send all", + " of the funds the account has, causing the sender account to be killed (false), or", + " transfer everything except at least the existential deposit, which will guarantee to", + " keep the sender account alive (true).", + " # ", + " - O(1). Just like transfer, but reading the user's transferable balance first.", + " #" + ] + } + ], + "events": [ + { + "name": "Endowed", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " An account was created with some free balance. \\[account, free_balance\\]" + ] + }, + { + "name": "DustLost", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " An account was removed whose balance was non-zero but below ExistentialDeposit,", + " resulting in an outright loss. \\[account, balance\\]" + ] + }, + { + "name": "Transfer", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " Transfer succeeded. \\[from, to, value\\]" + ] + }, + { + "name": "BalanceSet", + "args": [ + "AccountId", + "Balance", + "Balance" + ], + "docs": [ + " A balance was set by root. \\[who, free, reserved\\]" + ] + }, + { + "name": "Deposit", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " Some amount was deposited (e.g. for transaction fees). \\[who, deposit\\]" + ] + }, + { + "name": "Reserved", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " Some balance was reserved (moved from free to reserved). \\[who, value\\]" + ] + }, + { + "name": "Unreserved", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " Some balance was unreserved (moved from reserved to free). \\[who, value\\]" + ] + }, + { + "name": "ReserveRepatriated", + "args": [ + "AccountId", + "AccountId", + "Balance", + "BalanceStatus" + ], + "docs": [ + " Some balance was moved from the reserve of the first account to the second account.", + " Final argument indicates the destination balance type.", + " \\[from, to, balance, destination_status\\]" + ] + } + ], + "constants": [ + { + "name": "ExistentialDeposit", + "type": "Balance", + "value": "0x00000000000000000000000000000000", + "docs": [ + " The minimum amount required to keep an account open." + ] + }, + { + "name": "MaxLocks", + "type": "u32", + "value": "0x32000000", + "docs": [ + " The maximum number of locks that should exist on an account.", + " Not strictly enforced, but used for weight estimation." + ] + }, + { + "name": "MaxReserves", + "type": "u32", + "value": "0x32000000", + "docs": [ + " The maximum number of named reserves that can exist on an account." + ] + } + ], + "errors": [ + { + "name": "VestingBalance", + "docs": [ + " Vesting balance too high to send value" + ] + }, + { + "name": "LiquidityRestrictions", + "docs": [ + " Account liquidity restrictions prevent withdrawal" + ] + }, + { + "name": "InsufficientBalance", + "docs": [ + " Balance too low to send value" + ] + }, + { + "name": "ExistentialDeposit", + "docs": [ + " Value too low to create account due to existential deposit" + ] + }, + { + "name": "KeepAlive", + "docs": [ + " Transfer/payment would kill account" + ] + }, + { + "name": "ExistingVestingSchedule", + "docs": [ + " A vesting schedule already exists for this account" + ] + }, + { + "name": "DeadAccount", + "docs": [ + " Beneficiary account must pre-exist" + ] + }, + { + "name": "TooManyReserves", + "docs": [ + " Number of named reserves exceed MaxReserves" + ] + } + ], + "index": 10 + }, + { + "name": "TransactionPayment", + "storage": { + "prefix": "TransactionPayment", + "items": [ + { + "name": "NextFeeMultiplier", + "modifier": "Default", + "type": { + "plain": "Multiplier" + }, + "fallback": "0x000064a7b3b6e00d0000000000000000", + "docs": [] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "docs": [] + } + ] + }, + "calls": null, + "events": null, + "constants": [ + { + "name": "TransactionByteFee", + "type": "BalanceOf", + "value": "0x00a0724e180900000000000000000000", + "docs": [ + " The fee to be paid for making a transaction; the per-byte portion." + ] + }, + { + "name": "WeightToFee", + "type": "Vec", + "value": "0x0401000000000000000000000000000000000000000001", + "docs": [ + " The polynomial that is applied in order to derive fee from weight." + ] + } + ], + "errors": [], + "index": 11 + }, + { + "name": "ParachainStaking", + "storage": { + "prefix": "ParachainStaking", + "items": [ + { + "name": "CollatorCommission", + "modifier": "Default", + "type": { + "plain": "Perbill" + }, + "fallback": "0x00000000", + "docs": [ + " Commission percent taken off of rewards for all collators" + ] + }, + { + "name": "TotalSelected", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " The total candidates selected every round" + ] + }, + { + "name": "ParachainBondInfo", + "modifier": "Default", + "type": { + "plain": "ParachainBondConfig" + }, + "fallback": "0x000000000000000000000000000000000000000000", + "docs": [ + " Parachain bond config info { account, percent_of_inflation }" + ] + }, + { + "name": "Round", + "modifier": "Default", + "type": { + "plain": "RoundInfo" + }, + "fallback": "0x010000000100000014000000", + "docs": [ + " Current round index and next round scheduled transition" + ] + }, + { + "name": "NominatorState2", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Nominator2", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get nominator state associated with an account if account is nominating else None" + ] + }, + { + "name": "CollatorState2", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Collator2", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Get collator state associated with an account if account is collating else None" + ] + }, + { + "name": "SelectedCandidates", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The collator candidates selected for the current round" + ] + }, + { + "name": "Total", + "modifier": "Default", + "type": { + "plain": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total capital locked by this staking pallet" + ] + }, + { + "name": "CandidatePool", + "modifier": "Default", + "type": { + "plain": "OrderedSet" + }, + "fallback": "0x00", + "docs": [ + " The pool of collator candidates, each with their total backing stake" + ] + }, + { + "name": "ExitQueue2", + "modifier": "Default", + "type": { + "plain": "ExitQ" + }, + "fallback": "0x00000000", + "docs": [ + " A queue of collators and nominators awaiting exit" + ] + }, + { + "name": "AtStake", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "RoundIndex", + "key2": "AccountId", + "value": "CollatorSnapshot", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Snapshot of collator nomination stake at the start of the round" + ] + }, + { + "name": "Staked", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "RoundIndex", + "value": "BalanceOf", + "linked": false + } + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total backing stake for selected candidates in the round" + ] + }, + { + "name": "InflationConfig", + "modifier": "Default", + "type": { + "plain": "InflationInfo" + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " Inflation configuration" + ] + }, + { + "name": "Points", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "RoundIndex", + "value": "RewardPoint", + "linked": false + } + }, + "fallback": "0x00000000", + "docs": [ + " Total points awarded to collators for block production in the round" + ] + }, + { + "name": "AwardedPts", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Twox64Concat", + "key1": "RoundIndex", + "key2": "AccountId", + "value": "RewardPoint", + "key2Hasher": "Twox64Concat" + } + }, + "fallback": "0x00000000", + "docs": [ + " Points for each collator per round" + ] + } + ] + }, + "calls": [ + { + "name": "set_staking_expectations", + "args": [ + { + "name": "expectations", + "type": "Range" + } + ], + "docs": [ + " Set the expectations for total staked. These expectations determine the issuance for", + " the round according to logic in `fn compute_issuance`" + ] + }, + { + "name": "set_inflation", + "args": [ + { + "name": "schedule", + "type": "Range" + } + ], + "docs": [ + " Set the annual inflation rate to derive per-round inflation" + ] + }, + { + "name": "set_parachain_bond_account", + "args": [ + { + "name": "new", + "type": "AccountId" + } + ], + "docs": [ + " Set the account that will hold funds set aside for parachain bond" + ] + }, + { + "name": "set_parachain_bond_reserve_percent", + "args": [ + { + "name": "new", + "type": "Percent" + } + ], + "docs": [ + " Set the percent of inflation set aside for parachain bond" + ] + }, + { + "name": "set_total_selected", + "args": [ + { + "name": "new", + "type": "u32" + } + ], + "docs": [ + " Set the total number of collator candidates selected per round", + " - changes are not applied until the start of the next round" + ] + }, + { + "name": "set_collator_commission", + "args": [ + { + "name": "new", + "type": "Perbill" + } + ], + "docs": [ + " Set the commission for all collators" + ] + }, + { + "name": "set_blocks_per_round", + "args": [ + { + "name": "new", + "type": "u32" + } + ], + "docs": [ + " Set blocks per round", + " - if called with `new` less than length of current round, will transition immediately", + " in the next block", + " - also updates per-round inflation config" + ] + }, + { + "name": "join_candidates", + "args": [ + { + "name": "bond", + "type": "BalanceOf" + }, + { + "name": "candidate_count", + "type": "u32" + } + ], + "docs": [ + " Join the set of collator candidates" + ] + }, + { + "name": "leave_candidates", + "args": [ + { + "name": "candidate_count", + "type": "u32" + } + ], + "docs": [ + " Request to leave the set of candidates. If successful, the account is immediately", + " removed from the candidate pool to prevent selection as a collator, but unbonding is", + " executed with a delay of `T::LeaveCandidates` rounds." + ] + }, + { + "name": "go_offline", + "args": [], + "docs": [ + " Temporarily leave the set of collator candidates without unbonding" + ] + }, + { + "name": "go_online", + "args": [], + "docs": [ + " Rejoin the set of collator candidates if previously had called `go_offline`" + ] + }, + { + "name": "candidate_bond_more", + "args": [ + { + "name": "more", + "type": "BalanceOf" + } + ], + "docs": [ + " Bond more for collator candidates" + ] + }, + { + "name": "candidate_bond_less", + "args": [ + { + "name": "less", + "type": "BalanceOf" + } + ], + "docs": [ + " Bond less for collator candidates" + ] + }, + { + "name": "nominate", + "args": [ + { + "name": "collator", + "type": "AccountId" + }, + { + "name": "amount", + "type": "BalanceOf" + }, + { + "name": "collator_nominator_count", + "type": "u32" + }, + { + "name": "nomination_count", + "type": "u32" + } + ], + "docs": [ + " If caller is not a nominator, then join the set of nominators", + " If caller is a nominator, then makes nomination to change their nomination state" + ] + }, + { + "name": "leave_nominators", + "args": [ + { + "name": "nomination_count", + "type": "u32" + } + ], + "docs": [ + " Request to leave the set of nominators. If successful, the nominator is scheduled", + " to exit" + ] + }, + { + "name": "revoke_nomination", + "args": [ + { + "name": "collator", + "type": "AccountId" + } + ], + "docs": [ + " Request to revoke an existing nomination. If successful, the nomination is scheduled", + " to exit" + ] + }, + { + "name": "nominator_bond_more", + "args": [ + { + "name": "candidate", + "type": "AccountId" + }, + { + "name": "more", + "type": "BalanceOf" + } + ], + "docs": [ + " Bond more for nominators with respect to a specific collator candidate" + ] + }, + { + "name": "nominator_bond_less", + "args": [ + { + "name": "candidate", + "type": "AccountId" + }, + { + "name": "less", + "type": "BalanceOf" + } + ], + "docs": [ + " Bond less for nominators with respect to a specific nominator candidate" + ] + } + ], + "events": [ + { + "name": "NewRound", + "args": [ + "BlockNumber", + "RoundIndex", + "u32", + "BalanceOf" + ], + "docs": [ + " Starting Block, Round, Number of Collators Selected, Total Balance" + ] + }, + { + "name": "JoinedCollatorCandidates", + "args": [ + "AccountId", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Account, Amount Locked, New Total Amt Locked" + ] + }, + { + "name": "CollatorChosen", + "args": [ + "RoundIndex", + "AccountId", + "BalanceOf" + ], + "docs": [ + " Round, Collator Account, Total Exposed Amount (includes all nominations)" + ] + }, + { + "name": "CollatorBondedMore", + "args": [ + "AccountId", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Collator Account, Old Bond, New Bond" + ] + }, + { + "name": "CollatorBondedLess", + "args": [ + "AccountId", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Collator Account, Old Bond, New Bond" + ] + }, + { + "name": "CollatorWentOffline", + "args": [ + "RoundIndex", + "AccountId" + ], + "docs": [] + }, + { + "name": "CollatorBackOnline", + "args": [ + "RoundIndex", + "AccountId" + ], + "docs": [] + }, + { + "name": "CollatorScheduledExit", + "args": [ + "RoundIndex", + "AccountId", + "RoundIndex" + ], + "docs": [ + " Round, Collator Account, Scheduled Exit" + ] + }, + { + "name": "CollatorLeft", + "args": [ + "AccountId", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Account, Amount Unlocked, New Total Amt Locked" + ] + }, + { + "name": "NominationIncreased", + "args": [ + "AccountId", + "AccountId", + "BalanceOf", + "bool" + ], + "docs": [] + }, + { + "name": "NominationDecreased", + "args": [ + "AccountId", + "AccountId", + "BalanceOf", + "bool" + ], + "docs": [] + }, + { + "name": "NominatorExitScheduled", + "args": [ + "RoundIndex", + "AccountId", + "RoundIndex" + ], + "docs": [ + " Round, Nominator, Scheduled Exit" + ] + }, + { + "name": "NominationRevocationScheduled", + "args": [ + "RoundIndex", + "AccountId", + "AccountId", + "RoundIndex" + ], + "docs": [ + " Round, Nominator, Collator, Scheduled Exit" + ] + }, + { + "name": "NominatorLeft", + "args": [ + "AccountId", + "BalanceOf" + ], + "docs": [ + " Nominator, Amount Unstaked" + ] + }, + { + "name": "Nomination", + "args": [ + "AccountId", + "BalanceOf", + "AccountId", + "NominatorAdded" + ], + "docs": [ + " Nominator, Amount Locked, Collator, Nominator Position with New Total Counted if in Top" + ] + }, + { + "name": "NominatorLeftCollator", + "args": [ + "AccountId", + "AccountId", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Nominator, Collator, Amount Unstaked, New Total Amt Staked for Collator" + ] + }, + { + "name": "Rewarded", + "args": [ + "AccountId", + "BalanceOf" + ], + "docs": [ + " Paid the account (nominator or collator) the balance as liquid rewards" + ] + }, + { + "name": "ReservedForParachainBond", + "args": [ + "AccountId", + "BalanceOf" + ], + "docs": [ + " Transferred to account which holds funds reserved for parachain bond" + ] + }, + { + "name": "ParachainBondAccountSet", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " Account (re)set for parachain bond treasury [old, new]" + ] + }, + { + "name": "ParachainBondReservePercentSet", + "args": [ + "Percent", + "Percent" + ], + "docs": [ + " Percent of inflation reserved for parachain bond (re)set [old, new]" + ] + }, + { + "name": "InflationSet", + "args": [ + "Perbill", + "Perbill", + "Perbill", + "Perbill", + "Perbill", + "Perbill" + ], + "docs": [ + " Annual inflation input (first 3) was used to derive new per-round inflation (last 3)" + ] + }, + { + "name": "StakeExpectationsSet", + "args": [ + "BalanceOf", + "BalanceOf", + "BalanceOf" + ], + "docs": [ + " Staking expectations set" + ] + }, + { + "name": "TotalSelectedSet", + "args": [ + "u32", + "u32" + ], + "docs": [ + " Set total selected candidates to this value [old, new]" + ] + }, + { + "name": "CollatorCommissionSet", + "args": [ + "Perbill", + "Perbill" + ], + "docs": [ + " Set collator commission to this value [old, new]" + ] + }, + { + "name": "BlocksPerRoundSet", + "args": [ + "RoundIndex", + "BlockNumber", + "u32", + "u32", + "Perbill", + "Perbill", + "Perbill" + ], + "docs": [ + " Set blocks per round [current_round, first_block, old, new, new_per_round_inflation]" + ] + } + ], + "constants": [ + { + "name": "MinBlocksPerRound", + "type": "u32", + "value": "0x0a000000", + "docs": [ + " Minimum number of blocks per round" + ] + }, + { + "name": "DefaultBlocksPerRound", + "type": "u32", + "value": "0x2c010000", + "docs": [ + " Default number of blocks per round at genesis" + ] + }, + { + "name": "LeaveCandidatesDelay", + "type": "RoundIndex", + "value": "0x02000000", + "docs": [ + " Number of rounds that collators remain bonded before exit request is executed" + ] + }, + { + "name": "LeaveNominatorsDelay", + "type": "RoundIndex", + "value": "0x02000000", + "docs": [ + " Number of rounds that nominators remain bonded before exit request is executed" + ] + }, + { + "name": "RevokeNominationDelay", + "type": "RoundIndex", + "value": "0x02000000", + "docs": [ + " Number of rounds that nominations remain bonded before revocation request is executed" + ] + }, + { + "name": "RewardPaymentDelay", + "type": "RoundIndex", + "value": "0x02000000", + "docs": [ + " Number of rounds after which block authors are rewarded" + ] + }, + { + "name": "MinSelectedCandidates", + "type": "u32", + "value": "0x08000000", + "docs": [ + " Minimum number of selected candidates every round" + ] + }, + { + "name": "MaxNominatorsPerCollator", + "type": "u32", + "value": "0x64000000", + "docs": [ + " Maximum nominators counted per collator" + ] + }, + { + "name": "MaxCollatorsPerNominator", + "type": "u32", + "value": "0x64000000", + "docs": [ + " Maximum collators per nominator" + ] + }, + { + "name": "DefaultCollatorCommission", + "type": "Perbill", + "value": "0x00c2eb0b", + "docs": [ + " Default commission due to collators, set at genesis" + ] + }, + { + "name": "DefaultParachainBondReservePercent", + "type": "Percent", + "value": "0x1e", + "docs": [ + " Default percent of inflation set aside for parachain bond account" + ] + }, + { + "name": "MinCollatorStk", + "type": "BalanceOf", + "value": "0x0000a0dec5adc9353600000000000000", + "docs": [ + " Minimum stake required for any account to be in `SelectedCandidates` for the round" + ] + }, + { + "name": "MinCollatorCandidateStk", + "type": "BalanceOf", + "value": "0x0000a0dec5adc9353600000000000000", + "docs": [ + " Minimum stake required for any account to be a collator candidate" + ] + }, + { + "name": "MinNomination", + "type": "BalanceOf", + "value": "0x0000f444829163450000000000000000", + "docs": [ + " Minimum stake for any registered on-chain account to nominate" + ] + }, + { + "name": "MinNominatorStk", + "type": "BalanceOf", + "value": "0x0000f444829163450000000000000000", + "docs": [ + " Minimum stake for any registered on-chain account to become a nominator" + ] + } + ], + "errors": [ + { + "name": "NominatorDNE", + "docs": [] + }, + { + "name": "NominatorDNEinTopNorBottom", + "docs": [] + }, + { + "name": "NominatorDNEInNominatorSet", + "docs": [] + }, + { + "name": "CandidateDNE", + "docs": [] + }, + { + "name": "NominationDNE", + "docs": [] + }, + { + "name": "NominatorExists", + "docs": [] + }, + { + "name": "CandidateExists", + "docs": [] + }, + { + "name": "ValBondBelowMin", + "docs": [] + }, + { + "name": "NomBondBelowMin", + "docs": [] + }, + { + "name": "NominationBelowMin", + "docs": [] + }, + { + "name": "AlreadyOffline", + "docs": [] + }, + { + "name": "AlreadyActive", + "docs": [] + }, + { + "name": "NominatorAlreadyLeaving", + "docs": [] + }, + { + "name": "NominationAlreadyRevoked", + "docs": [] + }, + { + "name": "CandidateAlreadyLeaving", + "docs": [] + }, + { + "name": "CannotActBecauseLeaving", + "docs": [] + }, + { + "name": "CannotActBecauseRevoking", + "docs": [] + }, + { + "name": "ExceedMaxCollatorsPerNom", + "docs": [] + }, + { + "name": "AlreadyNominatedCollator", + "docs": [] + }, + { + "name": "InvalidSchedule", + "docs": [] + }, + { + "name": "CannotSetBelowMin", + "docs": [] + }, + { + "name": "NoWritingSameValue", + "docs": [] + }, + { + "name": "TooLowCandidateCountWeightHintJoinCandidates", + "docs": [] + }, + { + "name": "TooLowCollatorCandidateCountToLeaveCandidates", + "docs": [] + }, + { + "name": "TooLowNominationCountToNominate", + "docs": [] + }, + { + "name": "TooLowCollatorNominationCountToNominate", + "docs": [] + }, + { + "name": "TooLowNominationCountToLeaveNominators", + "docs": [] + } + ], + "index": 20 + }, + { + "name": "AuthorInherent", + "storage": { + "prefix": "AuthorInherent", + "items": [ + { + "name": "Author", + "modifier": "Optional", + "type": { + "plain": "AccountId" + }, + "fallback": "0x00", + "docs": [ + " Author of current block." + ] + } + ] + }, + "calls": [ + { + "name": "set_author", + "args": [ + { + "name": "author", + "type": "AuthorId" + } + ], + "docs": [ + " Inherent to set the author of a block" + ] + } + ], + "events": null, + "constants": [], + "errors": [ + { + "name": "AuthorAlreadySet", + "docs": [ + " Author already set in block." + ] + }, + { + "name": "NoAccountId", + "docs": [ + " No AccountId was found to be associated with this author" + ] + }, + { + "name": "CannotBeAuthor", + "docs": [ + " The author in the inherent is not an eligible author." + ] + } + ], + "index": 21 + }, + { + "name": "AuthorFilter", + "storage": { + "prefix": "AuthorFilter", + "items": [ + { + "name": "EligibleRatio", + "modifier": "Default", + "type": { + "plain": "Percent" + }, + "fallback": "0x32", + "docs": [ + " The percentage of active authors that will be eligible at each height." + ] + } + ] + }, + "calls": [ + { + "name": "set_eligible", + "args": [ + { + "name": "new", + "type": "Percent" + } + ], + "docs": [ + " Update the eligible ratio. Intended to be called by governance." + ] + } + ], + "events": [ + { + "name": "EligibleUpdated", + "args": [ + "Percent" + ], + "docs": [ + " The amount of eligible authors for the filter to select has been changed." + ] + } + ], + "constants": [], + "errors": [], + "index": 22 + }, + { + "name": "AuthorMapping", + "storage": { + "prefix": "AuthorMapping", + "items": [ + { + "name": "MappingWithDeposit", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AuthorId", + "value": "RegistrationInfo", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " We maintain a mapping from the AuthorIds used in the consensus layer", + " to the AccountIds runtime (including this staking pallet)." + ] + } + ] + }, + "calls": [ + { + "name": "add_association", + "args": [ + { + "name": "author_id", + "type": "AuthorId" + } + ], + "docs": [ + " Register your AuthorId onchain so blocks you author are associated with your account.", + "", + " Users who have been (or will soon be) elected active collators in staking,", + " should submit this extrinsic to have their blocks accepted and earn rewards." + ] + }, + { + "name": "update_association", + "args": [ + { + "name": "old_author_id", + "type": "AuthorId" + }, + { + "name": "new_author_id", + "type": "AuthorId" + } + ], + "docs": [ + " Change your AuthorId.", + "", + " This is useful for normal key rotation or for when switching from one physical collator", + " machine to another. No new security deposit is required." + ] + }, + { + "name": "clear_association", + "args": [ + { + "name": "author_id", + "type": "AuthorId" + } + ], + "docs": [ + " Clear your AuthorId.", + "", + " This is useful when you are no longer an author and would like to re-claim your security", + " deposit." + ] + } + ], + "events": [ + { + "name": "AuthorRegistered", + "args": [ + "AuthorId", + "AccountId" + ], + "docs": [ + " An AuthorId has been registered and mapped to an AccountId." + ] + }, + { + "name": "AuthorDeRegistered", + "args": [ + "AuthorId" + ], + "docs": [ + " An AuthorId has been de-registered, and its AccountId mapping removed." + ] + }, + { + "name": "AuthorRotated", + "args": [ + "AuthorId", + "AccountId" + ], + "docs": [ + " An AuthorId has been registered, replacing a previous registration and its mapping." + ] + }, + { + "name": "DefunctAuthorBusted", + "args": [ + "AuthorId", + "AccountId" + ], + "docs": [ + " An AuthorId has been forcibly deregistered after not being rotated or cleaned up.", + " The reporteing account has been rewarded accordingly." + ] + } + ], + "constants": [], + "errors": [ + { + "name": "AssociationNotFound", + "docs": [ + " The association can't be cleared because it is not found." + ] + }, + { + "name": "NotYourAssociation", + "docs": [ + " The association can't be cleared because it belongs to another account." + ] + }, + { + "name": "CannotAffordSecurityDeposit", + "docs": [ + " This account cannot set an author because it cannon afford the security deposit" + ] + }, + { + "name": "AlreadyAssociated", + "docs": [ + " The AuthorId in question is already associated and cannot be overwritten" + ] + } + ], + "index": 23 + }, + { + "name": "Utility", + "storage": null, + "calls": [ + { + "name": "batch", + "args": [ + { + "name": "calls", + "type": "Vec" + } + ], + "docs": [ + " Send a batch of dispatch calls.", + "", + " May be called from any origin.", + "", + " - `calls`: The calls to be dispatched from the same origin. The number of call must not", + " exceed the constant: `batched_calls_limit` (available in constant metadata).", + "", + " If origin is root then call are dispatch without checking origin filter. (This includes", + " bypassing `frame_system::Config::BaseCallFilter`).", + "", + " # ", + " - Complexity: O(C) where C is the number of calls to be batched.", + " # ", + "", + " This will return `Ok` in all circumstances. To determine the success of the batch, an", + " event is deposited. If a call failed and the batch was interrupted, then the", + " `BatchInterrupted` event is deposited, along with the number of successful calls made", + " and the error of the failed call. If all were successful, then the `BatchCompleted`", + " event is deposited." + ] + }, + { + "name": "as_derivative", + "args": [ + { + "name": "index", + "type": "u16" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Send a call through an indexed pseudonym of the sender.", + "", + " Filter from origin are passed along. The call will be dispatched with an origin which", + " use the same filter as the origin of this call.", + "", + " NOTE: If you need to ensure that any account-based filtering is not honored (i.e.", + " because you expect `proxy` to have been used prior in the call stack and you do not want", + " the call restrictions to apply to any sub-accounts), then use `as_multi_threshold_1`", + " in the Multisig pallet instead.", + "", + " NOTE: Prior to version *12, this was called `as_limited_sub`.", + "", + " The dispatch origin for this call must be _Signed_." + ] + }, + { + "name": "batch_all", + "args": [ + { + "name": "calls", + "type": "Vec" + } + ], + "docs": [ + " Send a batch of dispatch calls and atomically execute them.", + " The whole transaction will rollback and fail if any of the calls failed.", + "", + " May be called from any origin.", + "", + " - `calls`: The calls to be dispatched from the same origin. The number of call must not", + " exceed the constant: `batched_calls_limit` (available in constant metadata).", + "", + " If origin is root then call are dispatch without checking origin filter. (This includes", + " bypassing `frame_system::Config::BaseCallFilter`).", + "", + " # ", + " - Complexity: O(C) where C is the number of calls to be batched.", + " # " + ] + } + ], + "events": [ + { + "name": "BatchInterrupted", + "args": [ + "u32", + "DispatchError" + ], + "docs": [ + " Batch of dispatches did not complete fully. Index of first failing dispatch given, as", + " well as the error. \\[index, error\\]" + ] + }, + { + "name": "BatchCompleted", + "args": [], + "docs": [ + " Batch of dispatches completed fully with no error." + ] + }, + { + "name": "ItemCompleted", + "args": [], + "docs": [ + " A single item within a Batch of dispatches has completed with no error." + ] + } + ], + "constants": [ + { + "name": "batched_calls_limit", + "type": "u32", + "value": "0x2cb00000", + "docs": [ + " The limit on the number of batched calls." + ] + } + ], + "errors": [ + { + "name": "TooManyCalls", + "docs": [ + " Too many calls batched." + ] + } + ], + "index": 30 + }, + { + "name": "Proxy", + "storage": { + "prefix": "Proxy", + "items": [ + { + "name": "Proxies", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " The set of account proxies. Maps the account which has delegated to the accounts", + " which are being delegated to, together with the amount held on deposit." + ] + }, + { + "name": "Announcements", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " The announcements made by the proxy (key)." + ] + } + ] + }, + "calls": [ + { + "name": "proxy", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "force_proxy_type", + "type": "Option" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Dispatch the given `call` from an account that the sender is authorised for through", + " `add_proxy`.", + "", + " Removes any corresponding announcement(s).", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + " - `call`: The call to be made by the `real` account.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "add_proxy", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Register a proxy account for the sender that is able to make calls on its behalf.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `proxy`: The account that the `caller` would like to make a proxy.", + " - `proxy_type`: The permissions allowed for this proxy account.", + " - `delay`: The announcement period required of the initial proxy. Will generally be", + " zero.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "remove_proxy", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Unregister a proxy account for the sender.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `proxy`: The account that the `caller` would like to remove as a proxy.", + " - `proxy_type`: The permissions currently enabled for the removed proxy account.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "remove_proxies", + "args": [], + "docs": [ + " Unregister all proxy accounts for the sender.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " WARNING: This may be called on accounts created by `anonymous`, however if done, then", + " the unreserved fees will be inaccessible. **All access to this account will be lost.**", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "anonymous", + "args": [ + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "delay", + "type": "BlockNumber" + }, + { + "name": "index", + "type": "u16" + } + ], + "docs": [ + " Spawn a fresh new account that is guaranteed to be otherwise inaccessible, and", + " initialize it with a proxy of `proxy_type` for `origin` sender.", + "", + " Requires a `Signed` origin.", + "", + " - `proxy_type`: The type of the proxy that the sender will be registered as over the", + " new account. This will almost always be the most permissive `ProxyType` possible to", + " allow for maximum flexibility.", + " - `index`: A disambiguation index, in case this is called multiple times in the same", + " transaction (e.g. with `utility::batch`). Unless you're using `batch` you probably just", + " want to use `0`.", + " - `delay`: The announcement period required of the initial proxy. Will generally be", + " zero.", + "", + " Fails with `Duplicate` if this has already been called in this transaction, from the", + " same sender, with the same parameters.", + "", + " Fails if there are insufficient funds to pay for deposit.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # ", + " TODO: Might be over counting 1 read" + ] + }, + { + "name": "kill_anonymous", + "args": [ + { + "name": "spawner", + "type": "AccountId" + }, + { + "name": "proxy_type", + "type": "ProxyType" + }, + { + "name": "index", + "type": "u16" + }, + { + "name": "height", + "type": "Compact" + }, + { + "name": "ext_index", + "type": "Compact" + } + ], + "docs": [ + " Removes a previously spawned anonymous proxy.", + "", + " WARNING: **All access to this account will be lost.** Any funds held in it will be", + " inaccessible.", + "", + " Requires a `Signed` origin, and the sender account must have been created by a call to", + " `anonymous` with corresponding parameters.", + "", + " - `spawner`: The account that originally called `anonymous` to create this account.", + " - `index`: The disambiguation index originally passed to `anonymous`. Probably `0`.", + " - `proxy_type`: The proxy type originally passed to `anonymous`.", + " - `height`: The height of the chain when the call to `anonymous` was processed.", + " - `ext_index`: The extrinsic index in which the call to `anonymous` was processed.", + "", + " Fails with `NoPermission` in case the caller is not a previously created anonymous", + " account whose `anonymous` call has corresponding parameters.", + "", + " # ", + " Weight is a function of the number of proxies the user has (P).", + " # " + ] + }, + { + "name": "announce", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "docs": [ + " Publish the hash of a proxy-call that will be made in the future.", + "", + " This must be called some number of blocks before the corresponding `proxy` is attempted", + " if the delay associated with the proxy relationship is greater than zero.", + "", + " No more than `MaxPending` announcements may be made at any one time.", + "", + " This will take a deposit of `AnnouncementDepositFactor` as well as", + " `AnnouncementDepositBase` if there are no other pending announcements.", + "", + " The dispatch origin for this call must be _Signed_ and a proxy of `real`.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `call_hash`: The hash of the call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "remove_announcement", + "args": [ + { + "name": "real", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "docs": [ + " Remove a given announcement.", + "", + " May be called by a proxy account to remove a call they previously announced and return", + " the deposit.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `call_hash`: The hash of the call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "reject_announcement", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "call_hash", + "type": "CallHashOf" + } + ], + "docs": [ + " Remove the given announcement of a delegate.", + "", + " May be called by a target (proxied) account to remove a call that one of their delegates", + " (`delegate`) has announced they want to execute. The deposit is returned.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `delegate`: The account that previously announced the call.", + " - `call_hash`: The hash of the call to be made.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + }, + { + "name": "proxy_announced", + "args": [ + { + "name": "delegate", + "type": "AccountId" + }, + { + "name": "real", + "type": "AccountId" + }, + { + "name": "force_proxy_type", + "type": "Option" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Dispatch the given `call` from an account that the sender is authorized for through", + " `add_proxy`.", + "", + " Removes any corresponding announcement(s).", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " Parameters:", + " - `real`: The account that the proxy will make a call on behalf of.", + " - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call.", + " - `call`: The call to be made by the `real` account.", + "", + " # ", + " Weight is a function of:", + " - A: the number of announcements made.", + " - P: the number of proxies the user has.", + " # " + ] + } + ], + "events": [ + { + "name": "ProxyExecuted", + "args": [ + "DispatchResult" + ], + "docs": [ + " A proxy was executed correctly, with the given \\[result\\]." + ] + }, + { + "name": "AnonymousCreated", + "args": [ + "AccountId", + "AccountId", + "ProxyType", + "u16" + ], + "docs": [ + " Anonymous account has been created by new proxy with given", + " disambiguation index and proxy type. \\[anonymous, who, proxy_type, disambiguation_index\\]" + ] + }, + { + "name": "Announced", + "args": [ + "AccountId", + "AccountId", + "Hash" + ], + "docs": [ + " An announcement was placed to make a call in the future. \\[real, proxy, call_hash\\]" + ] + } + ], + "constants": [ + { + "name": "ProxyDepositBase", + "type": "BalanceOf", + "value": "0x0000362b4c8ee30d0000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating a proxy.", + "", + " This is held for an additional storage item whose value size is", + " `sizeof(Balance)` bytes and whose key size is `sizeof(AccountId)` bytes." + ] + }, + { + "name": "ProxyDepositFactor", + "type": "BalanceOf", + "value": "0x0040075af07507000000000000000000", + "docs": [ + " The amount of currency needed per proxy added.", + "", + " This is held for adding 32 bytes plus an instance of `ProxyType` more into a pre-existing", + " storage value. Thus, when configuring `ProxyDepositFactor` one should take into account", + " `32 + proxy_type.encode().len()` bytes of data." + ] + }, + { + "name": "MaxProxies", + "type": "u32", + "value": "0x20000000", + "docs": [ + " The maximum amount of proxies allowed for a single account." + ] + }, + { + "name": "MaxPending", + "type": "u32", + "value": "0x20000000", + "docs": [ + " The maximum amount of time-delayed announcements that are allowed to be pending." + ] + }, + { + "name": "AnnouncementDepositBase", + "type": "BalanceOf", + "value": "0x0000362b4c8ee30d0000000000000000", + "docs": [ + " The base amount of currency needed to reserve for creating an announcement.", + "", + " This is held when a new storage item holding a `Balance` is created (typically 16 bytes)." + ] + }, + { + "name": "AnnouncementDepositFactor", + "type": "BalanceOf", + "value": "0x0000be9a2be513000000000000000000", + "docs": [ + " The amount of currency needed per announcement made.", + "", + " This is held for adding an `AccountId`, `Hash` and `BlockNumber` (typically 68 bytes)", + " into a pre-existing storage value." + ] + } + ], + "errors": [ + { + "name": "TooMany", + "docs": [ + " There are too many proxies registered or too many announcements pending." + ] + }, + { + "name": "NotFound", + "docs": [ + " Proxy registration not found." + ] + }, + { + "name": "NotProxy", + "docs": [ + " Sender is not a proxy of the account to be proxied." + ] + }, + { + "name": "Unproxyable", + "docs": [ + " A call which is incompatible with the proxy type's filter was attempted." + ] + }, + { + "name": "Duplicate", + "docs": [ + " Account is already a proxy." + ] + }, + { + "name": "NoPermission", + "docs": [ + " Call may not be made by proxy because it may escalate its privileges." + ] + }, + { + "name": "Unannounced", + "docs": [ + " Announcement, if made at all, was made too recently." + ] + }, + { + "name": "NoSelfProxy", + "docs": [ + " Cannot add self as proxy." + ] + } + ], + "index": 31 + }, + { + "name": "MaintenanceMode", + "storage": { + "prefix": "MaintenanceMode", + "items": [ + { + "name": "MaintenanceMode", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " Whether the site is in maintenance mode" + ] + } + ] + }, + "calls": [ + { + "name": "enter_maintenance_mode", + "args": [], + "docs": [ + " Place the chain in maintenance mode", + "", + " Weight cost is:", + " * One DB read to ensure we're not already in maintenance mode", + " * Two DB writes - 1 for the mode and 1 for the event" + ] + }, + { + "name": "resume_normal_operation", + "args": [], + "docs": [ + " Return the chain to normal operating mode", + "", + " Weight cost is:", + " * One DB read to ensure we're in maintenance mode", + " * Two DB writes - 1 for the mode and 1 for the event" + ] + } + ], + "events": [ + { + "name": "EnteredMaintenanceMode", + "args": [], + "docs": [ + " The chain was put into Maintenance Mode" + ] + }, + { + "name": "NormalOperationResumed", + "args": [], + "docs": [ + " The chain returned to its normal operating state" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "AlreadyInMaintenanceMode", + "docs": [ + " The chain cannot enter maintenance mode because it is already in maintenance mode" + ] + }, + { + "name": "NotInMaintenanceMode", + "docs": [ + " The chain cannot resume normal operation because it is not in maintenance mode" + ] + } + ], + "index": 32 + }, + { + "name": "Identity", + "storage": { + "prefix": "Identity", + "items": [ + { + "name": "IdentityOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Registration", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information that is pertinent to identify the entity behind an account.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "SuperOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "(AccountId,Data)", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " The super-identity of an alternative \"sub\" identity together with its name, within that", + " context. If the account is not some other account's sub-identity, then just `None`." + ] + }, + { + "name": "SubsOf", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "(BalanceOf,Vec)", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000", + "docs": [ + " Alternative \"sub\" identities of this account.", + "", + " The first item is the deposit, the second is a vector of the accounts.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "Registrars", + "modifier": "Default", + "type": { + "plain": "Vec>" + }, + "fallback": "0x00", + "docs": [ + " The set of registrars. Not expected to get very big as can only be added through a", + " special origin (likely a council motion).", + "", + " The index into this can be cast to `RegistrarIndex` to get a valid value." + ] + } + ] + }, + "calls": [ + { + "name": "add_registrar", + "args": [ + { + "name": "account", + "type": "AccountId" + } + ], + "docs": [ + " Add a registrar to the system.", + "", + " The dispatch origin for this call must be `T::RegistrarOrigin`.", + "", + " - `account`: the account of the registrar.", + "", + " Emits `RegistrarAdded` if successful.", + "", + " # ", + " - `O(R)` where `R` registrar-count (governance-bounded and code-bounded).", + " - One storage mutation (codec `O(R)`).", + " - One event.", + " # " + ] + }, + { + "name": "set_identity", + "args": [ + { + "name": "info", + "type": "IdentityInfo" + } + ], + "docs": [ + " Set an account's identity information and reserve the appropriate deposit.", + "", + " If the account already has identity information, the deposit is taken as part payment", + " for the new deposit.", + "", + " The dispatch origin for this call must be _Signed_.", + "", + " - `info`: The identity information.", + "", + " Emits `IdentitySet` if successful.", + "", + " # ", + " - `O(X + X' + R)`", + " - where `X` additional-field-count (deposit-bounded and code-bounded)", + " - where `R` judgements-count (registrar-count-bounded)", + " - One balance reserve operation.", + " - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`).", + " - One event.", + " # " + ] + }, + { + "name": "set_subs", + "args": [ + { + "name": "subs", + "type": "Vec<(AccountId,Data)>" + } + ], + "docs": [ + " Set the sub-accounts of the sender.", + "", + " Payment: Any aggregate balance reserved by previous `set_subs` calls will be returned", + " and an amount `SubAccountDeposit` will be reserved for each item in `subs`.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " identity.", + "", + " - `subs`: The identity's (new) sub-accounts.", + "", + " # ", + " - `O(P + S)`", + " - where `P` old-subs-count (hard- and deposit-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + " - At most one balance operations.", + " - DB:", + " - `P + S` storage mutations (codec complexity `O(1)`)", + " - One storage read (codec complexity `O(P)`).", + " - One storage write (codec complexity `O(S)`).", + " - One storage-exists (`IdentityOf::contains_key`).", + " # " + ] + }, + { + "name": "clear_identity", + "args": [], + "docs": [ + " Clear an account's identity info and all sub-accounts and return all deposits.", + "", + " Payment: All reserved balances on the account are returned.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " identity.", + "", + " Emits `IdentityCleared` if successful.", + "", + " # ", + " - `O(R + S + X)`", + " - where `R` registrar-count (governance-bounded).", + " - where `S` subs-count (hard- and deposit-bounded).", + " - where `X` additional-field-count (deposit-bounded and code-bounded).", + " - One balance-unreserve operation.", + " - `2` storage reads and `S + 2` storage deletions.", + " - One event.", + " # " + ] + }, + { + "name": "request_judgement", + "args": [ + { + "name": "reg_index", + "type": "Compact" + }, + { + "name": "max_fee", + "type": "Compact" + } + ], + "docs": [ + " Request a judgement from a registrar.", + "", + " Payment: At most `max_fee` will be reserved for payment to the registrar if judgement", + " given.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a", + " registered identity.", + "", + " - `reg_index`: The index of the registrar whose judgement is requested.", + " - `max_fee`: The maximum fee that may be paid. This should just be auto-populated as:", + "", + " ```nocompile", + " Self::registrars().get(reg_index).unwrap().fee", + " ```", + "", + " Emits `JudgementRequested` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-reserve operation.", + " - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`.", + " - One event.", + " # " + ] + }, + { + "name": "cancel_request", + "args": [ + { + "name": "reg_index", + "type": "RegistrarIndex" + } + ], + "docs": [ + " Cancel a previous request.", + "", + " Payment: A previously reserved deposit is returned on success.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a", + " registered identity.", + "", + " - `reg_index`: The index of the registrar whose judgement is no longer requested.", + "", + " Emits `JudgementUnrequested` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-reserve operation.", + " - One storage mutation `O(R + X)`.", + " - One event", + " # " + ] + }, + { + "name": "set_fee", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "fee", + "type": "Compact" + } + ], + "docs": [ + " Set the fee required for a judgement to be requested from a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `fee`: the new fee.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 7.315 + R * 0.329 µs (min squares analysis)", + " # " + ] + }, + { + "name": "set_account_id", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "new", + "type": "AccountId" + } + ], + "docs": [ + " Change the account associated with a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `new`: the new account ID.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 8.823 + R * 0.32 µs (min squares analysis)", + " # " + ] + }, + { + "name": "set_fields", + "args": [ + { + "name": "index", + "type": "Compact" + }, + { + "name": "fields", + "type": "IdentityFields" + } + ], + "docs": [ + " Set the field information for a registrar.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `index`.", + "", + " - `index`: the index of the registrar whose fee is to be set.", + " - `fields`: the fields that the registrar concerns themselves with.", + "", + " # ", + " - `O(R)`.", + " - One storage mutation `O(R)`.", + " - Benchmark: 7.464 + R * 0.325 µs (min squares analysis)", + " # " + ] + }, + { + "name": "provide_judgement", + "args": [ + { + "name": "reg_index", + "type": "Compact" + }, + { + "name": "target", + "type": "LookupSource" + }, + { + "name": "judgement", + "type": "IdentityJudgement" + } + ], + "docs": [ + " Provide a judgement for an account's identity.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must be the account", + " of the registrar whose index is `reg_index`.", + "", + " - `reg_index`: the index of the registrar whose judgement is being made.", + " - `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + " - `judgement`: the judgement of the registrar of index `reg_index` about `target`.", + "", + " Emits `JudgementGiven` if successful.", + "", + " # ", + " - `O(R + X)`.", + " - One balance-transfer operation.", + " - Up to one account-lookup operation.", + " - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`.", + " - One event.", + " # " + ] + }, + { + "name": "kill_identity", + "args": [ + { + "name": "target", + "type": "LookupSource" + } + ], + "docs": [ + " Remove an account's identity and sub-account information and slash the deposits.", + "", + " Payment: Reserved balances from `set_subs` and `set_identity` are slashed and handled by", + " `Slash`. Verification request deposits are not returned; they should be cancelled", + " manually using `cancel_request`.", + "", + " The dispatch origin for this call must match `T::ForceOrigin`.", + "", + " - `target`: the account whose identity the judgement is upon. This must be an account", + " with a registered identity.", + "", + " Emits `IdentityKilled` if successful.", + "", + " # ", + " - `O(R + S + X)`.", + " - One balance-reserve operation.", + " - `S + 2` storage mutations.", + " - One event.", + " # " + ] + }, + { + "name": "add_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + }, + { + "name": "data", + "type": "Data" + } + ], + "docs": [ + " Add the given account to the sender's subs.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "rename_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + }, + { + "name": "data", + "type": "Data" + } + ], + "docs": [ + " Alter the associated name of the given sub-account.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "remove_sub", + "args": [ + { + "name": "sub", + "type": "LookupSource" + } + ], + "docs": [ + " Remove the given account from the sender's subs.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender.", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " sub identity of `sub`." + ] + }, + { + "name": "quit_sub", + "args": [], + "docs": [ + " Remove the sender as a sub-account.", + "", + " Payment: Balance reserved by a previous `set_subs` call for one sub will be repatriated", + " to the sender (*not* the original depositor).", + "", + " The dispatch origin for this call must be _Signed_ and the sender must have a registered", + " super-identity.", + "", + " NOTE: This should not normally be used, but is provided in the case that the non-", + " controller of an account is maliciously registered as a sub-account." + ] + } + ], + "events": [ + { + "name": "IdentitySet", + "args": [ + "AccountId" + ], + "docs": [ + " A name was set or reset (which will remove all judgements). \\[who\\]" + ] + }, + { + "name": "IdentityCleared", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " A name was cleared, and the given balance returned. \\[who, deposit\\]" + ] + }, + { + "name": "IdentityKilled", + "args": [ + "AccountId", + "Balance" + ], + "docs": [ + " A name was removed and the given balance slashed. \\[who, deposit\\]" + ] + }, + { + "name": "JudgementRequested", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "docs": [ + " A judgement was asked from a registrar. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementUnrequested", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "docs": [ + " A judgement request was retracted. \\[who, registrar_index\\]" + ] + }, + { + "name": "JudgementGiven", + "args": [ + "AccountId", + "RegistrarIndex" + ], + "docs": [ + " A judgement was given by a registrar. \\[target, registrar_index\\]" + ] + }, + { + "name": "RegistrarAdded", + "args": [ + "RegistrarIndex" + ], + "docs": [ + " A registrar was added. \\[registrar_index\\]" + ] + }, + { + "name": "SubIdentityAdded", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " A sub-identity was added to an identity and the deposit paid. \\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRemoved", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " A sub-identity was removed from an identity and the deposit freed.", + " \\[sub, main, deposit\\]" + ] + }, + { + "name": "SubIdentityRevoked", + "args": [ + "AccountId", + "AccountId", + "Balance" + ], + "docs": [ + " A sub-identity was cleared, and the given deposit repatriated from the", + " main identity account to the sub-identity account. \\[sub, main, deposit\\]" + ] + } + ], + "constants": [ + { + "name": "BasicDeposit", + "type": "BalanceOf", + "value": "0x00809842aa5f3c0e0000000000000000", + "docs": [ + " The amount held on deposit for a registered identity" + ] + }, + { + "name": "FieldDeposit", + "type": "BalanceOf", + "value": "0x0080843faa7217000000000000000000", + "docs": [ + " The amount held on deposit per additional field for a registered identity." + ] + }, + { + "name": "SubAccountDeposit", + "type": "BalanceOf", + "value": "0x0040b310068bf30d0000000000000000", + "docs": [ + " The amount held on deposit for a registered subaccount. This should account for the fact", + " that one storage item's value will increase by the size of an account ID, and there will be", + " another trie item whose value is the size of an account ID plus 32 bytes." + ] + }, + { + "name": "MaxSubAccounts", + "type": "u32", + "value": "0x64000000", + "docs": [ + " The maximum number of sub-accounts allowed per identified account." + ] + }, + { + "name": "MaxAdditionalFields", + "type": "u32", + "value": "0x64000000", + "docs": [ + " Maximum number of additional fields that may be stored in an ID. Needed to bound the I/O", + " required to access an identity, but can be pretty high." + ] + }, + { + "name": "MaxRegistrars", + "type": "u32", + "value": "0x14000000", + "docs": [ + " Maxmimum number of registrars allowed in the system. Needed to bound the complexity", + " of, e.g., updating judgements." + ] + } + ], + "errors": [ + { + "name": "TooManySubAccounts", + "docs": [ + " Too many subs-accounts." + ] + }, + { + "name": "NotFound", + "docs": [ + " Account isn't found." + ] + }, + { + "name": "NotNamed", + "docs": [ + " Account isn't named." + ] + }, + { + "name": "EmptyIndex", + "docs": [ + " Empty index." + ] + }, + { + "name": "FeeChanged", + "docs": [ + " Fee is changed." + ] + }, + { + "name": "NoIdentity", + "docs": [ + " No identity found." + ] + }, + { + "name": "StickyJudgement", + "docs": [ + " Sticky judgement." + ] + }, + { + "name": "JudgementGiven", + "docs": [ + " Judgement given." + ] + }, + { + "name": "InvalidJudgement", + "docs": [ + " Invalid judgement." + ] + }, + { + "name": "InvalidIndex", + "docs": [ + " The index is invalid." + ] + }, + { + "name": "InvalidTarget", + "docs": [ + " The target is invalid." + ] + }, + { + "name": "TooManyFields", + "docs": [ + " Too many additional fields." + ] + }, + { + "name": "TooManyRegistrars", + "docs": [ + " Maximum amount of registrars reached. Cannot add any more." + ] + }, + { + "name": "AlreadyClaimed", + "docs": [ + " Account ID is already named." + ] + }, + { + "name": "NotSub", + "docs": [ + " Sender is not a sub-account." + ] + }, + { + "name": "NotOwned", + "docs": [ + " Sub-account isn't owned by sender." + ] + } + ], + "index": 33 + }, + { + "name": "Migrations", + "storage": { + "prefix": "Migrations", + "items": [ + { + "name": "FullyUpgraded", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " True if all required migrations have completed" + ] + }, + { + "name": "MigrationState", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "Bytes", + "value": "bool", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " MigrationState tracks the progress of a migration.", + " Maps name (Vec) -> whether or not migration has been completed (bool)" + ] + } + ] + }, + "calls": null, + "events": [ + { + "name": "RuntimeUpgradeStarted", + "args": [], + "docs": [] + }, + { + "name": "RuntimeUpgradeCompleted", + "args": [ + "Weight" + ], + "docs": [] + }, + { + "name": "MigrationStarted", + "args": [ + "Bytes" + ], + "docs": [] + }, + { + "name": "MigrationCompleted", + "args": [ + "Bytes", + "Weight" + ], + "docs": [] + } + ], + "constants": [], + "errors": [], + "index": 34 + }, + { + "name": "EthereumChainId", + "storage": { + "prefix": "EthereumChainId", + "items": [ + { + "name": "ChainId", + "modifier": "Default", + "type": { + "plain": "u64" + }, + "fallback": "0x0000000000000000", + "docs": [] + } + ] + }, + "calls": null, + "events": null, + "constants": [], + "errors": [], + "index": 50 + }, + { + "name": "EVM", + "storage": { + "prefix": "EVM", + "items": [ + { + "name": "AccountCodes", + "modifier": "Default", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "H160", + "value": "Bytes", + "linked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "AccountStorages", + "modifier": "Default", + "type": { + "doubleMap": { + "hasher": "Blake2_128Concat", + "key1": "H160", + "key2": "H256", + "value": "H256", + "key2Hasher": "Blake2_128Concat" + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + } + ] + }, + "calls": [ + { + "name": "withdraw", + "args": [ + { + "name": "address", + "type": "H160" + }, + { + "name": "value", + "type": "BalanceOf" + } + ], + "docs": [ + " Withdraw balance from EVM into currency/balances pallet." + ] + }, + { + "name": "call", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "target", + "type": "H160" + }, + { + "name": "input", + "type": "Bytes" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u64" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "docs": [ + " Issue an EVM call operation. This is similar to a message call transaction in Ethereum." + ] + }, + { + "name": "create", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "init", + "type": "Bytes" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u64" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "docs": [ + " Issue an EVM create operation. This is similar to a contract creation transaction in", + " Ethereum." + ] + }, + { + "name": "create2", + "args": [ + { + "name": "source", + "type": "H160" + }, + { + "name": "init", + "type": "Bytes" + }, + { + "name": "salt", + "type": "H256" + }, + { + "name": "value", + "type": "U256" + }, + { + "name": "gas_limit", + "type": "u64" + }, + { + "name": "gas_price", + "type": "U256" + }, + { + "name": "nonce", + "type": "Option" + } + ], + "docs": [ + " Issue an EVM create2 operation." + ] + } + ], + "events": [ + { + "name": "Log", + "args": [ + "EvmLog" + ], + "docs": [ + " Ethereum events from contracts." + ] + }, + { + "name": "Created", + "args": [ + "H160" + ], + "docs": [ + " A contract has been created at given \\[address\\]." + ] + }, + { + "name": "CreatedFailed", + "args": [ + "H160" + ], + "docs": [ + " A \\[contract\\] was attempted to be created, but the execution failed." + ] + }, + { + "name": "Executed", + "args": [ + "H160" + ], + "docs": [ + " A \\[contract\\] has been executed successfully with states applied." + ] + }, + { + "name": "ExecutedFailed", + "args": [ + "H160" + ], + "docs": [ + " A \\[contract\\] has been executed with errors. States are reverted with only gas fees applied." + ] + }, + { + "name": "BalanceDeposit", + "args": [ + "AccountId", + "H160", + "U256" + ], + "docs": [ + " A deposit has been made at a given address. \\[sender, address, value\\]" + ] + }, + { + "name": "BalanceWithdraw", + "args": [ + "AccountId", + "H160", + "U256" + ], + "docs": [ + " A withdrawal has been made from a given address. \\[sender, address, value\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "BalanceLow", + "docs": [ + " Not enough balance to perform action" + ] + }, + { + "name": "FeeOverflow", + "docs": [ + " Calculating total fee overflowed" + ] + }, + { + "name": "PaymentOverflow", + "docs": [ + " Calculating total payment overflowed" + ] + }, + { + "name": "WithdrawFailed", + "docs": [ + " Withdraw fee failed" + ] + }, + { + "name": "GasPriceTooLow", + "docs": [ + " Gas price is too low." + ] + }, + { + "name": "InvalidNonce", + "docs": [ + " Nonce is invalid" + ] + } + ], + "index": 51 + }, + { + "name": "Ethereum", + "storage": { + "prefix": "Ethereum", + "items": [ + { + "name": "Pending", + "modifier": "Default", + "type": { + "plain": "Vec<(EthTransaction,EthTransactionStatus,EthReceipt)>" + }, + "fallback": "0x00", + "docs": [ + " Current building block's transactions and receipts." + ] + }, + { + "name": "CurrentBlock", + "modifier": "Optional", + "type": { + "plain": "BlockV0" + }, + "fallback": "0x00", + "docs": [ + " The current Ethereum block." + ] + }, + { + "name": "CurrentReceipts", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current Ethereum receipts." + ] + }, + { + "name": "CurrentTransactionStatuses", + "modifier": "Optional", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current transaction statuses." + ] + }, + { + "name": "BlockHash", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "U256", + "value": "H256", + "linked": false + } + }, + "fallback": "0x0000000000000000000000000000000000000000000000000000000000000000", + "docs": [] + } + ] + }, + "calls": [ + { + "name": "transact", + "args": [ + { + "name": "transaction", + "type": "EthTransaction" + } + ], + "docs": [ + " Transact an Ethereum transaction." + ] + } + ], + "events": [ + { + "name": "Executed", + "args": [ + "H160", + "H160", + "H256", + "ExitReason" + ], + "docs": [ + " An ethereum transaction was successfully executed. [from, to/contract_address, transaction_hash, exit_reason]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "InvalidSignature", + "docs": [ + " Signature is invalid." + ] + }, + { + "name": "PreLogExists", + "docs": [ + " Pre-log is present, therefore transact is not allowed." + ] + } + ], + "index": 52 + }, + { + "name": "Scheduler", + "storage": { + "prefix": "Scheduler", + "items": [ + { + "name": "Agenda", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "BlockNumber", + "value": "Vec>", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Items to be executed, indexed by the block number that they should be executed on." + ] + }, + { + "name": "Lookup", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "Bytes", + "value": "TaskAddress", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Lookup from identity to the block number and index of the task." + ] + }, + { + "name": "StorageVersion", + "modifier": "Default", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": [ + { + "name": "schedule", + "args": [ + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Anonymously schedule a task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 22.29 + .126 * S µs", + " - DB Weight:", + " - Read: Agenda", + " - Write: Agenda", + " - Will use base weight of 25 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "cancel", + "args": [ + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "index", + "type": "u32" + } + ], + "docs": [ + " Cancel an anonymously scheduled task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 22.15 + 2.869 * S µs", + " - DB Weight:", + " - Read: Agenda", + " - Write: Agenda, Lookup", + " - Will use base weight of 100 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "schedule_named", + "args": [ + { + "name": "id", + "type": "Bytes" + }, + { + "name": "when", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Schedule a named task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 29.6 + .159 * S µs", + " - DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + " - Will use base weight of 35 which should be good for more than 30 scheduled calls", + " # " + ] + }, + { + "name": "cancel_named", + "args": [ + { + "name": "id", + "type": "Bytes" + } + ], + "docs": [ + " Cancel a named scheduled task.", + "", + " # ", + " - S = Number of already scheduled calls", + " - Base Weight: 24.91 + 2.907 * S µs", + " - DB Weight:", + " - Read: Agenda, Lookup", + " - Write: Agenda, Lookup", + " - Will use base weight of 100 which should be good for up to 30 scheduled calls", + " # " + ] + }, + { + "name": "schedule_after", + "args": [ + { + "name": "after", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Anonymously schedule a task after a delay.", + "", + " # ", + " Same as [`schedule`].", + " # " + ] + }, + { + "name": "schedule_named_after", + "args": [ + { + "name": "id", + "type": "Bytes" + }, + { + "name": "after", + "type": "BlockNumber" + }, + { + "name": "maybe_periodic", + "type": "Option" + }, + { + "name": "priority", + "type": "Priority" + }, + { + "name": "call", + "type": "Call" + } + ], + "docs": [ + " Schedule a named task after a delay.", + "", + " # ", + " Same as [`schedule_named`](Self::schedule_named).", + " # " + ] + } + ], + "events": [ + { + "name": "Scheduled", + "args": [ + "BlockNumber", + "u32" + ], + "docs": [ + " Scheduled some task. \\[when, index\\]" + ] + }, + { + "name": "Canceled", + "args": [ + "BlockNumber", + "u32" + ], + "docs": [ + " Canceled some task. \\[when, index\\]" + ] + }, + { + "name": "Dispatched", + "args": [ + "TaskAddress", + "Option", + "DispatchResult" + ], + "docs": [ + " Dispatched some task. \\[task, id, result\\]" + ] + } + ], + "constants": [ + { + "name": "MaximumWeight", + "type": "Weight", + "value": "0x00e6bd4f57000000", + "docs": [ + " The maximum weight that may be scheduled per block for any dispatchables of less priority", + " than `schedule::HARD_DEADLINE`." + ] + }, + { + "name": "MaxScheduledPerBlock", + "type": "u32", + "value": "0x32000000", + "docs": [ + " The maximum number of scheduled calls in the queue for a single block.", + " Not strictly enforced, but used for weight estimation." + ] + } + ], + "errors": [ + { + "name": "FailedToSchedule", + "docs": [ + " Failed to schedule a call" + ] + }, + { + "name": "NotFound", + "docs": [ + " Cannot find the scheduled call." + ] + }, + { + "name": "TargetBlockNumberInPast", + "docs": [ + " Given target block number is in the past." + ] + }, + { + "name": "RescheduleNoChange", + "docs": [ + " Reschedule failed because it does not change scheduled time." + ] + } + ], + "index": 60 + }, + { + "name": "Democracy", + "storage": { + "prefix": "Democracy", + "items": [ + { + "name": "PublicPropCount", + "modifier": "Default", + "type": { + "plain": "PropIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The number of (public) proposals that have been made so far." + ] + }, + { + "name": "PublicProps", + "modifier": "Default", + "type": { + "plain": "Vec<(PropIndex,Hash,AccountId)>" + }, + "fallback": "0x00", + "docs": [ + " The public proposals. Unsorted. The second item is the proposal's hash." + ] + }, + { + "name": "DepositOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "PropIndex", + "value": "(Vec,BalanceOf)", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Those who have locked a deposit.", + "", + " TWOX-NOTE: Safe, as increasing integer keys are safe." + ] + }, + { + "name": "Preimages", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "PreimageStatus", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Map of hashes to the proposal preimage, along with who registered it and their deposit.", + " The block number is the block at which it was deposited." + ] + }, + { + "name": "ReferendumCount", + "modifier": "Default", + "type": { + "plain": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The next free referendum index, aka the number of referenda started so far." + ] + }, + { + "name": "LowestUnbaked", + "modifier": "Default", + "type": { + "plain": "ReferendumIndex" + }, + "fallback": "0x00000000", + "docs": [ + " The lowest referendum index representing an unbaked referendum. Equal to", + " `ReferendumCount` if there isn't a unbaked referendum." + ] + }, + { + "name": "ReferendumInfoOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ReferendumIndex", + "value": "ReferendumInfo", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Information concerning any given referendum.", + "", + " TWOX-NOTE: SAFE as indexes are not under an attacker’s control." + ] + }, + { + "name": "VotingOf", + "modifier": "Default", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "Voting", + "linked": false + } + }, + "fallback": "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "docs": [ + " All votes for a particular voter. We store the balance for the number of votes that we", + " have recorded. The second item is the total amount of delegations, that will be added.", + "", + " TWOX-NOTE: SAFE as `AccountId`s are crypto hashes anyway." + ] + }, + { + "name": "Locks", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "AccountId", + "value": "BlockNumber", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Accounts for which there are locks in action which may be removed at some point in the", + " future. The value is the block number at which the lock expires and may be removed.", + "", + " TWOX-NOTE: OK ― `AccountId` is a secure hash." + ] + }, + { + "name": "LastTabledWasExternal", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [ + " True if the last referendum tabled was submitted externally. False if it was a public", + " proposal." + ] + }, + { + "name": "NextExternal", + "modifier": "Optional", + "type": { + "plain": "(Hash,VoteThreshold)" + }, + "fallback": "0x00", + "docs": [ + " The referendum to be tabled whenever it would be valid to table an external proposal.", + " This happens when a referendum needs to be tabled and one of two conditions are met:", + " - `LastTabledWasExternal` is `false`; or", + " - `PublicProps` is empty." + ] + }, + { + "name": "Blacklist", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "(BlockNumber,Vec)", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " A record of who vetoed what. Maps proposal hash to a possible existent block number", + " (until when it may not be resubmitted) and who vetoed it." + ] + }, + { + "name": "Cancellations", + "modifier": "Default", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "bool", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Record of all proposals that have been subject to emergency cancellation." + ] + }, + { + "name": "StorageVersion", + "modifier": "Optional", + "type": { + "plain": "Releases" + }, + "fallback": "0x00", + "docs": [ + " Storage version of the pallet.", + "", + " New networks start with last version." + ] + } + ] + }, + "calls": [ + { + "name": "propose", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "value", + "type": "Compact" + } + ], + "docs": [ + " Propose a sensitive action to be taken.", + "", + " The dispatch origin of this call must be _Signed_ and the sender must", + " have funds to cover the deposit.", + "", + " - `proposal_hash`: The hash of the proposal preimage.", + " - `value`: The amount of deposit (must be at least `MinimumDeposit`).", + "", + " Emits `Proposed`.", + "", + " Weight: `O(p)`" + ] + }, + { + "name": "second", + "args": [ + { + "name": "proposal", + "type": "Compact" + }, + { + "name": "seconds_upper_bound", + "type": "Compact" + } + ], + "docs": [ + " Signals agreement with a particular proposal.", + "", + " The dispatch origin of this call must be _Signed_ and the sender", + " must have funds to cover the deposit, equal to the original deposit.", + "", + " - `proposal`: The index of the proposal to second.", + " - `seconds_upper_bound`: an upper bound on the current number of seconds on this", + " proposal. Extrinsic is weighted according to this value with no refund.", + "", + " Weight: `O(S)` where S is the number of seconds a proposal already has." + ] + }, + { + "name": "vote", + "args": [ + { + "name": "ref_index", + "type": "Compact" + }, + { + "name": "vote", + "type": "AccountVote" + } + ], + "docs": [ + " Vote in a referendum. If `vote.is_aye()`, the vote is to enact the proposal;", + " otherwise it is a vote to keep the status quo.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `ref_index`: The index of the referendum to vote for.", + " - `vote`: The vote configuration.", + "", + " Weight: `O(R)` where R is the number of referendums the voter has voted on." + ] + }, + { + "name": "emergency_cancel", + "args": [ + { + "name": "ref_index", + "type": "ReferendumIndex" + } + ], + "docs": [ + " Schedule an emergency cancellation of a referendum. Cannot happen twice to the same", + " referendum.", + "", + " The dispatch origin of this call must be `CancellationOrigin`.", + "", + " -`ref_index`: The index of the referendum to cancel.", + "", + " Weight: `O(1)`." + ] + }, + { + "name": "external_propose", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Schedule a referendum to be tabled once it is legal to schedule an external", + " referendum.", + "", + " The dispatch origin of this call must be `ExternalOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Weight: `O(V)` with V number of vetoers in the blacklist of proposal.", + " Decoding vec of length V. Charged as maximum" + ] + }, + { + "name": "external_propose_majority", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Schedule a majority-carries referendum to be tabled next once it is legal to schedule", + " an external referendum.", + "", + " The dispatch of this call must be `ExternalMajorityOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + " pre-scheduled `external_propose` call.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "external_propose_default", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Schedule a negative-turnout-bias referendum to be tabled next once it is legal to", + " schedule an external referendum.", + "", + " The dispatch of this call must be `ExternalDefaultOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal.", + "", + " Unlike `external_propose`, blacklisting has no effect on this and it may replace a", + " pre-scheduled `external_propose` call.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "fast_track", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "voting_period", + "type": "BlockNumber" + }, + { + "name": "delay", + "type": "BlockNumber" + } + ], + "docs": [ + " Schedule the currently externally-proposed majority-carries referendum to be tabled", + " immediately. If there is no externally-proposed referendum currently, or if there is one", + " but it is not a majority-carries referendum then it fails.", + "", + " The dispatch of this call must be `FastTrackOrigin`.", + "", + " - `proposal_hash`: The hash of the current external proposal.", + " - `voting_period`: The period that is allowed for voting on this proposal. Increased to", + " `FastTrackVotingPeriod` if too low.", + " - `delay`: The number of block after voting has ended in approval and this should be", + " enacted. This doesn't have a minimum amount.", + "", + " Emits `Started`.", + "", + " Weight: `O(1)`" + ] + }, + { + "name": "veto_external", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Veto and blacklist the external proposal hash.", + "", + " The dispatch origin of this call must be `VetoOrigin`.", + "", + " - `proposal_hash`: The preimage hash of the proposal to veto and blacklist.", + "", + " Emits `Vetoed`.", + "", + " Weight: `O(V + log(V))` where V is number of `existing vetoers`" + ] + }, + { + "name": "cancel_referendum", + "args": [ + { + "name": "ref_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a referendum.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " - `ref_index`: The index of the referendum to cancel.", + "", + " # Weight: `O(1)`." + ] + }, + { + "name": "cancel_queued", + "args": [ + { + "name": "which", + "type": "ReferendumIndex" + } + ], + "docs": [ + " Cancel a proposal queued for enactment.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " - `which`: The index of the referendum to cancel.", + "", + " Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`." + ] + }, + { + "name": "delegate", + "args": [ + { + "name": "to", + "type": "AccountId" + }, + { + "name": "conviction", + "type": "Conviction" + }, + { + "name": "balance", + "type": "BalanceOf" + } + ], + "docs": [ + " Delegate the voting power (with some given conviction) of the sending account.", + "", + " The balance delegated is locked for as long as it's delegated, and thereafter for the", + " time appropriate for the conviction's lock period.", + "", + " The dispatch origin of this call must be _Signed_, and the signing account must either:", + " - be delegating already; or", + " - have no voting activity (if there is, then it will need to be removed/consolidated", + " through `reap_vote` or `unvote`).", + "", + " - `to`: The account whose voting the `target` account's voting power will follow.", + " - `conviction`: The conviction that will be attached to the delegated votes. When the", + " account is undelegated, the funds will be locked for the corresponding period.", + " - `balance`: The amount of the account's balance to be used in delegating. This must", + " not be more than the account's current balance.", + "", + " Emits `Delegated`.", + "", + " Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "undelegate", + "args": [], + "docs": [ + " Undelegate the voting power of the sending account.", + "", + " Tokens may be unlocked following once an amount of time consistent with the lock period", + " of the conviction with which the delegation was issued.", + "", + " The dispatch origin of this call must be _Signed_ and the signing account must be", + " currently delegating.", + "", + " Emits `Undelegated`.", + "", + " Weight: `O(R)` where R is the number of referendums the voter delegating to has", + " voted on. Weight is charged as if maximum votes." + ] + }, + { + "name": "clear_public_proposals", + "args": [], + "docs": [ + " Clears all public proposals.", + "", + " The dispatch origin of this call must be _Root_.", + "", + " Weight: `O(1)`." + ] + }, + { + "name": "note_preimage", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "docs": [ + " Register the preimage for an upcoming proposal. This doesn't require the proposal to be", + " in the dispatch queue but does require a deposit, returned once enacted.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `encoded_proposal`: The preimage of a proposal.", + "", + " Emits `PreimageNoted`.", + "", + " Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_preimage_operational", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "docs": [ + " Same as `note_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "note_imminent_preimage", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "docs": [ + " Register the preimage for an upcoming proposal. This requires the proposal to be", + " in the dispatch queue. No deposit is needed. When this call is successful, i.e.", + " the preimage has not been uploaded before and matches some imminent proposal,", + " no fee is paid.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `encoded_proposal`: The preimage of a proposal.", + "", + " Emits `PreimageNoted`.", + "", + " Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit)." + ] + }, + { + "name": "note_imminent_preimage_operational", + "args": [ + { + "name": "encoded_proposal", + "type": "Bytes" + } + ], + "docs": [ + " Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`." + ] + }, + { + "name": "reap_preimage", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "proposal_len_upper_bound", + "type": "Compact" + } + ], + "docs": [ + " Remove an expired proposal preimage and collect the deposit.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `proposal_hash`: The preimage hash of a proposal.", + " - `proposal_length_upper_bound`: an upper bound on length of the proposal.", + " Extrinsic is weighted according to this value with no refund.", + "", + " This will only work after `VotingPeriod` blocks from the time that the preimage was", + " noted, if it's the same account doing it. If it's a different account, then it'll only", + " work an additional `EnactmentPeriod` later.", + "", + " Emits `PreimageReaped`.", + "", + " Weight: `O(D)` where D is length of proposal." + ] + }, + { + "name": "unlock", + "args": [ + { + "name": "target", + "type": "AccountId" + } + ], + "docs": [ + " Unlock tokens that have an expired lock.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `target`: The account to remove the lock on.", + "", + " Weight: `O(R)` with R number of vote of target." + ] + }, + { + "name": "remove_vote", + "args": [ + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "docs": [ + " Remove a vote for a referendum.", + "", + " If:", + " - the referendum was cancelled, or", + " - the referendum is ongoing, or", + " - the referendum has ended such that", + " - the vote of the account was in opposition to the result; or", + " - there was no conviction to the account's vote; or", + " - the account made a split vote", + " ...then the vote is removed cleanly and a following call to `unlock` may result in more", + " funds being available.", + "", + " If, however, the referendum has ended and:", + " - it finished corresponding to the vote of the account, and", + " - the account made a standard vote with conviction, and", + " - the lock period of the conviction is not over", + " ...then the lock will be aggregated into the overall account's lock, which may involve", + " *overlocking* (where the two locks are combined into a single lock that is the maximum", + " of both the amount locked and the time is it locked for).", + "", + " The dispatch origin of this call must be _Signed_, and the signer must have a vote", + " registered for referendum `index`.", + "", + " - `index`: The index of referendum of the vote to be removed.", + "", + " Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "remove_other_vote", + "args": [ + { + "name": "target", + "type": "AccountId" + }, + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "docs": [ + " Remove a vote for a referendum.", + "", + " If the `target` is equal to the signer, then this function is exactly equivalent to", + " `remove_vote`. If not equal to the signer, then the vote must have expired,", + " either because the referendum was cancelled, because the voter lost the referendum or", + " because the conviction period is over.", + "", + " The dispatch origin of this call must be _Signed_.", + "", + " - `target`: The account of the vote to be removed; this account must have voted for", + " referendum `index`.", + " - `index`: The index of referendum of the vote to be removed.", + "", + " Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on.", + " Weight is calculated for the maximum number of vote." + ] + }, + { + "name": "enact_proposal", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "index", + "type": "ReferendumIndex" + } + ], + "docs": [ + " Enact a proposal from a referendum. For now we just make the weight be the maximum." + ] + }, + { + "name": "blacklist", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "maybe_ref_index", + "type": "Option" + } + ], + "docs": [ + " Permanently place a proposal into the blacklist. This prevents it from ever being", + " proposed again.", + "", + " If called on a queued public or external proposal, then this will result in it being", + " removed. If the `ref_index` supplied is an active referendum with the proposal hash,", + " then it will be cancelled.", + "", + " The dispatch origin of this call must be `BlacklistOrigin`.", + "", + " - `proposal_hash`: The proposal hash to blacklist permanently.", + " - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be", + " cancelled.", + "", + " Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a", + " reasonable value)." + ] + }, + { + "name": "cancel_proposal", + "args": [ + { + "name": "prop_index", + "type": "Compact" + } + ], + "docs": [ + " Remove a proposal.", + "", + " The dispatch origin of this call must be `CancelProposalOrigin`.", + "", + " - `prop_index`: The index of the proposal to cancel.", + "", + " Weight: `O(p)` where `p = PublicProps::::decode_len()`" + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "PropIndex", + "Balance" + ], + "docs": [ + " A motion has been proposed by a public account. \\[proposal_index, deposit\\]" + ] + }, + { + "name": "Tabled", + "args": [ + "PropIndex", + "Balance", + "Vec" + ], + "docs": [ + " A public proposal has been tabled for referendum vote. \\[proposal_index, deposit, depositors\\]" + ] + }, + { + "name": "ExternalTabled", + "args": [], + "docs": [ + " An external proposal has been tabled." + ] + }, + { + "name": "Started", + "args": [ + "ReferendumIndex", + "VoteThreshold" + ], + "docs": [ + " A referendum has begun. \\[ref_index, threshold\\]" + ] + }, + { + "name": "Passed", + "args": [ + "ReferendumIndex" + ], + "docs": [ + " A proposal has been approved by referendum. \\[ref_index\\]" + ] + }, + { + "name": "NotPassed", + "args": [ + "ReferendumIndex" + ], + "docs": [ + " A proposal has been rejected by referendum. \\[ref_index\\]" + ] + }, + { + "name": "Cancelled", + "args": [ + "ReferendumIndex" + ], + "docs": [ + " A referendum has been cancelled. \\[ref_index\\]" + ] + }, + { + "name": "Executed", + "args": [ + "ReferendumIndex", + "DispatchResult" + ], + "docs": [ + " A proposal has been enacted. \\[ref_index, result\\]" + ] + }, + { + "name": "Delegated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " An account has delegated their vote to another account. \\[who, target\\]" + ] + }, + { + "name": "Undelegated", + "args": [ + "AccountId" + ], + "docs": [ + " An \\[account\\] has cancelled a previous delegation operation." + ] + }, + { + "name": "Vetoed", + "args": [ + "AccountId", + "Hash", + "BlockNumber" + ], + "docs": [ + " An external proposal has been vetoed. \\[who, proposal_hash, until\\]" + ] + }, + { + "name": "PreimageNoted", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "docs": [ + " A proposal's preimage was noted, and the deposit taken. \\[proposal_hash, who, deposit\\]" + ] + }, + { + "name": "PreimageUsed", + "args": [ + "Hash", + "AccountId", + "Balance" + ], + "docs": [ + " A proposal preimage was removed and used (the deposit was returned).", + " \\[proposal_hash, provider, deposit\\]" + ] + }, + { + "name": "PreimageInvalid", + "args": [ + "Hash", + "ReferendumIndex" + ], + "docs": [ + " A proposal could not be executed because its preimage was invalid.", + " \\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageMissing", + "args": [ + "Hash", + "ReferendumIndex" + ], + "docs": [ + " A proposal could not be executed because its preimage was missing.", + " \\[proposal_hash, ref_index\\]" + ] + }, + { + "name": "PreimageReaped", + "args": [ + "Hash", + "AccountId", + "Balance", + "AccountId" + ], + "docs": [ + " A registered preimage was removed and the deposit collected by the reaper.", + " \\[proposal_hash, provider, deposit, reaper\\]" + ] + }, + { + "name": "Blacklisted", + "args": [ + "Hash" + ], + "docs": [ + " A proposal \\[hash\\] has been blacklisted permanently." + ] + } + ], + "constants": [ + { + "name": "EnactmentPeriod", + "type": "BlockNumber", + "value": "0x201c0000", + "docs": [ + " The minimum period of locking and the period between a proposal being approved and enacted.", + "", + " It should generally be a little more than the unstake period to ensure that", + " voting stakers have an opportunity to remove themselves from the system in the case where", + " they are on the losing side of a vote." + ] + }, + { + "name": "LaunchPeriod", + "type": "BlockNumber", + "value": "0x201c0000", + "docs": [ + " How often (in blocks) new public referenda are launched." + ] + }, + { + "name": "VotingPeriod", + "type": "BlockNumber", + "value": "0xa08c0000", + "docs": [ + " How often (in blocks) to check for new votes." + ] + }, + { + "name": "MinimumDeposit", + "type": "BalanceOf", + "value": "0x0000909dceda82370000000000000000", + "docs": [ + " The minimum amount to be used as a deposit for a public referendum proposal." + ] + }, + { + "name": "InstantAllowed", + "type": "bool", + "value": "0x01", + "docs": [ + " Indicator for whether an emergency origin is even allowed to happen. Some chains may want", + " to set this permanently to `false`, others may want to condition it on things such as", + " an upgrade having happened recently." + ] + }, + { + "name": "FastTrackVotingPeriod", + "type": "BlockNumber", + "value": "0x84030000", + "docs": [ + " Minimum voting period allowed for a fast-track referendum." + ] + }, + { + "name": "CooloffPeriod", + "type": "BlockNumber", + "value": "0xe0c40000", + "docs": [ + " Period in blocks where an external proposal may not be re-submitted after being vetoed." + ] + }, + { + "name": "PreimageByteDeposit", + "type": "BalanceOf", + "value": "0x00407a10f35a00000000000000000000", + "docs": [ + " The amount of balance that must be deposited per byte of preimage stored." + ] + }, + { + "name": "MaxVotes", + "type": "u32", + "value": "0x64000000", + "docs": [ + " The maximum number of votes for an account.", + "", + " Also used to compute weight, an overly big value can", + " lead to extrinsic with very big weight: see `delegate` for instance." + ] + }, + { + "name": "MaxProposals", + "type": "u32", + "value": "0x64000000", + "docs": [ + " The maximum number of public proposals that can exist at any time." + ] + } + ], + "errors": [ + { + "name": "ValueLow", + "docs": [ + " Value too low" + ] + }, + { + "name": "ProposalMissing", + "docs": [ + " Proposal does not exist" + ] + }, + { + "name": "AlreadyCanceled", + "docs": [ + " Cannot cancel the same proposal twice" + ] + }, + { + "name": "DuplicateProposal", + "docs": [ + " Proposal already made" + ] + }, + { + "name": "ProposalBlacklisted", + "docs": [ + " Proposal still blacklisted" + ] + }, + { + "name": "NotSimpleMajority", + "docs": [ + " Next external proposal not simple majority" + ] + }, + { + "name": "InvalidHash", + "docs": [ + " Invalid hash" + ] + }, + { + "name": "NoProposal", + "docs": [ + " No external proposal" + ] + }, + { + "name": "AlreadyVetoed", + "docs": [ + " Identity may not veto a proposal twice" + ] + }, + { + "name": "DuplicatePreimage", + "docs": [ + " Preimage already noted" + ] + }, + { + "name": "NotImminent", + "docs": [ + " Not imminent" + ] + }, + { + "name": "TooEarly", + "docs": [ + " Too early" + ] + }, + { + "name": "Imminent", + "docs": [ + " Imminent" + ] + }, + { + "name": "PreimageMissing", + "docs": [ + " Preimage not found" + ] + }, + { + "name": "ReferendumInvalid", + "docs": [ + " Vote given for invalid referendum" + ] + }, + { + "name": "PreimageInvalid", + "docs": [ + " Invalid preimage" + ] + }, + { + "name": "NoneWaiting", + "docs": [ + " No proposals waiting" + ] + }, + { + "name": "NotVoter", + "docs": [ + " The given account did not vote on the referendum." + ] + }, + { + "name": "NoPermission", + "docs": [ + " The actor has no permission to conduct the action." + ] + }, + { + "name": "AlreadyDelegating", + "docs": [ + " The account is already delegating." + ] + }, + { + "name": "InsufficientFunds", + "docs": [ + " Too high a balance was provided that the account cannot afford." + ] + }, + { + "name": "NotDelegating", + "docs": [ + " The account is not currently delegating." + ] + }, + { + "name": "VotesExist", + "docs": [ + " The account currently has votes attached to it and the operation cannot succeed until", + " these are removed, either through `unvote` or `reap_vote`." + ] + }, + { + "name": "InstantNotAllowed", + "docs": [ + " The instant referendum origin is currently disallowed." + ] + }, + { + "name": "Nonsense", + "docs": [ + " Delegation to oneself makes no sense." + ] + }, + { + "name": "WrongUpperBound", + "docs": [ + " Invalid upper bound." + ] + }, + { + "name": "MaxVotesReached", + "docs": [ + " Maximum number of votes reached." + ] + }, + { + "name": "TooManyProposals", + "docs": [ + " Maximum number of proposals reached." + ] + } + ], + "index": 61 + }, + { + "name": "CouncilCollective", + "storage": { + "prefix": "Instance1Collective", + "items": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The hashes of the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Proposal", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Votes", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Votes on a given proposal, if it is ongoing." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current members of the collective. This is stored sorted (just by value)." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": "AccountId" + }, + "fallback": "0x00", + "docs": [ + " The prime member that helps determine the default vote behavior in case of absentations." + ] + } + ] + }, + "calls": [ + { + "name": "set_members", + "args": [ + { + "name": "new_members", + "type": "Vec" + }, + { + "name": "prime", + "type": "Option" + }, + { + "name": "old_count", + "type": "MemberCount" + } + ], + "docs": [ + " Set the collective's membership.", + "", + " - `new_members`: The new member list. Be nice to the chain and provide it sorted.", + " - `prime`: The prime member whose vote sets the default.", + " - `old_count`: The upper bound for the previous number of members in storage.", + " Used for weight estimation.", + "", + " Requires root origin.", + "", + " NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but", + " the weight estimations rely on it to estimate dispatchable weight.", + "", + " # ", + " ## Weight", + " - `O(MP + N)` where:", + " - `M` old-members-count (code- and governance-bounded)", + " - `N` new-members-count (code- and governance-bounded)", + " - `P` proposals-count (code-bounded)", + " - DB:", + " - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members", + " - 1 storage read (codec `O(P)`) for reading the proposals", + " - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal", + " - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one", + " # " + ] + }, + { + "name": "execute", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Dispatch a proposal from a member using the `Member` origin.", + "", + " Origin must be a member of the collective.", + "", + " # ", + " ## Weight", + " - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`", + " - DB: 1 read (codec `O(M)`) + DB access of `proposal`", + " - 1 event", + " # " + ] + }, + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Add a new proposal to either be voted on or executed directly.", + "", + " Requires the sender to be member.", + "", + " `threshold` determines whether `proposal` is executed directly (`threshold < 2`)", + " or put up for voting.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1)` or `O(B + M + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - branching is influenced by `threshold` where:", + " - `P1` is proposal execution complexity (`threshold < 2`)", + " - `P2` is proposals-count (code-bounded) (`threshold >= 2`)", + " - DB:", + " - 1 storage read `is_member` (codec `O(M)`)", + " - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)", + " - DB accesses influenced by `threshold`:", + " - EITHER storage accesses done by `proposal` (`threshold < 2`)", + " - OR proposal insertion (`threshold <= 2`)", + " - 1 storage mutation `Proposals` (codec `O(P2)`)", + " - 1 storage mutation `ProposalCount` (codec `O(1)`)", + " - 1 storage write `ProposalOf` (codec `O(B)`)", + " - 1 storage write `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [ + " Add an aye or nay vote for the sender to the given proposal.", + "", + " Requires the sender to be a member.", + "", + " Transaction fees will be waived if the member is voting on any particular proposal", + " for the first time and the call is successful. Subsequent vote changes will charge a fee.", + " # ", + " ## Weight", + " - `O(M)` where `M` is members-count (code- and governance-bounded)", + " - DB:", + " - 1 storage read `Members` (codec `O(M)`)", + " - 1 storage mutation `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "close", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "proposal_weight_bound", + "type": "Compact" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Close a vote that is either approved, disapproved or whose voting period has ended.", + "", + " May be called by any signed account in order to finish voting and close the proposal.", + "", + " If called before the end of the voting period it will only close the vote if it is", + " has enough votes to be approved or disapproved.", + "", + " If called after the end of the voting period abstentions are counted as rejections", + " unless there is a prime member set and the prime member cast an approval.", + "", + " If the close operation completes successfully with disapproval, the transaction fee will", + " be waived. Otherwise execution of the approved operation will be charged to the caller.", + "", + " + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal.", + " + `length_bound`: The upper bound for the length of the proposal in storage. Checked via", + " `storage::read` so it is `size_of::() == 4` larger than the pure length.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1 + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - `P1` is the complexity of `proposal` preimage.", + " - `P2` is proposal-count (code-bounded)", + " - DB:", + " - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)", + " - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)", + " - any mutations done while executing `proposal` (`P1`)", + " - up to 3 events", + " # " + ] + }, + { + "name": "disapprove_proposal", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Disapprove a proposal, close, and remove it from the system, regardless of its current state.", + "", + " Must be called by the Root origin.", + "", + " Parameters:", + " * `proposal_hash`: The hash of the proposal that should be disapproved.", + "", + " # ", + " Complexity: O(P) where P is the number of max proposals", + " DB Weight:", + " * Reads: Proposals", + " * Writes: Voting, Proposals, ProposalOf", + " # " + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "MemberCount" + ], + "docs": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given", + " `MemberCount`).", + " \\[account, proposal_index, proposal_hash, threshold\\]" + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "MemberCount", + "MemberCount" + ], + "docs": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given respectively as `MemberCount`).", + " \\[account, proposal_hash, voted, yes, no\\]" + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was not approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "DispatchResult" + ], + "docs": [ + " A motion was executed; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "MemberExecuted", + "args": [ + "Hash", + "DispatchResult" + ], + "docs": [ + " A single member did some action; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "Closed", + "args": [ + "Hash", + "MemberCount", + "MemberCount" + ], + "docs": [ + " A proposal was closed because its threshold was reached or after its duration was up.", + " \\[proposal_hash, yes, no\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "NotMember", + "docs": [ + " Account is not a member" + ] + }, + { + "name": "DuplicateProposal", + "docs": [ + " Duplicate proposals not allowed" + ] + }, + { + "name": "ProposalMissing", + "docs": [ + " Proposal must exist" + ] + }, + { + "name": "WrongIndex", + "docs": [ + " Mismatched index" + ] + }, + { + "name": "DuplicateVote", + "docs": [ + " Duplicate vote ignored" + ] + }, + { + "name": "AlreadyInitialized", + "docs": [ + " Members are already initialized!" + ] + }, + { + "name": "TooEarly", + "docs": [ + " The close call was made too early, before the end of the voting." + ] + }, + { + "name": "TooManyProposals", + "docs": [ + " There can only be a maximum of `MaxProposals` active proposals." + ] + }, + { + "name": "WrongProposalWeight", + "docs": [ + " The given weight bound for the proposal was too low." + ] + }, + { + "name": "WrongProposalLength", + "docs": [ + " The given length bound for the proposal was too low." + ] + } + ], + "index": 70 + }, + { + "name": "TechComitteeCollective", + "storage": { + "prefix": "Instance2Collective", + "items": [ + { + "name": "Proposals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The hashes of the active proposals." + ] + }, + { + "name": "ProposalOf", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Proposal", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Actual proposal for a given hash, if it's current." + ] + }, + { + "name": "Voting", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Identity", + "key": "Hash", + "value": "Votes", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Votes on a given proposal, if it is ongoing." + ] + }, + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Proposals so far." + ] + }, + { + "name": "Members", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " The current members of the collective. This is stored sorted (just by value)." + ] + }, + { + "name": "Prime", + "modifier": "Optional", + "type": { + "plain": "AccountId" + }, + "fallback": "0x00", + "docs": [ + " The prime member that helps determine the default vote behavior in case of absentations." + ] + } + ] + }, + "calls": [ + { + "name": "set_members", + "args": [ + { + "name": "new_members", + "type": "Vec" + }, + { + "name": "prime", + "type": "Option" + }, + { + "name": "old_count", + "type": "MemberCount" + } + ], + "docs": [ + " Set the collective's membership.", + "", + " - `new_members`: The new member list. Be nice to the chain and provide it sorted.", + " - `prime`: The prime member whose vote sets the default.", + " - `old_count`: The upper bound for the previous number of members in storage.", + " Used for weight estimation.", + "", + " Requires root origin.", + "", + " NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but", + " the weight estimations rely on it to estimate dispatchable weight.", + "", + " # ", + " ## Weight", + " - `O(MP + N)` where:", + " - `M` old-members-count (code- and governance-bounded)", + " - `N` new-members-count (code- and governance-bounded)", + " - `P` proposals-count (code-bounded)", + " - DB:", + " - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members", + " - 1 storage read (codec `O(P)`) for reading the proposals", + " - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal", + " - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one", + " # " + ] + }, + { + "name": "execute", + "args": [ + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Dispatch a proposal from a member using the `Member` origin.", + "", + " Origin must be a member of the collective.", + "", + " # ", + " ## Weight", + " - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`", + " - DB: 1 read (codec `O(M)`) + DB access of `proposal`", + " - 1 event", + " # " + ] + }, + { + "name": "propose", + "args": [ + { + "name": "threshold", + "type": "Compact" + }, + { + "name": "proposal", + "type": "Proposal" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Add a new proposal to either be voted on or executed directly.", + "", + " Requires the sender to be member.", + "", + " `threshold` determines whether `proposal` is executed directly (`threshold < 2`)", + " or put up for voting.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1)` or `O(B + M + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - branching is influenced by `threshold` where:", + " - `P1` is proposal execution complexity (`threshold < 2`)", + " - `P2` is proposals-count (code-bounded) (`threshold >= 2`)", + " - DB:", + " - 1 storage read `is_member` (codec `O(M)`)", + " - 1 storage read `ProposalOf::contains_key` (codec `O(1)`)", + " - DB accesses influenced by `threshold`:", + " - EITHER storage accesses done by `proposal` (`threshold < 2`)", + " - OR proposal insertion (`threshold <= 2`)", + " - 1 storage mutation `Proposals` (codec `O(P2)`)", + " - 1 storage mutation `ProposalCount` (codec `O(1)`)", + " - 1 storage write `ProposalOf` (codec `O(B)`)", + " - 1 storage write `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "vote", + "args": [ + { + "name": "proposal", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "approve", + "type": "bool" + } + ], + "docs": [ + " Add an aye or nay vote for the sender to the given proposal.", + "", + " Requires the sender to be a member.", + "", + " Transaction fees will be waived if the member is voting on any particular proposal", + " for the first time and the call is successful. Subsequent vote changes will charge a fee.", + " # ", + " ## Weight", + " - `O(M)` where `M` is members-count (code- and governance-bounded)", + " - DB:", + " - 1 storage read `Members` (codec `O(M)`)", + " - 1 storage mutation `Voting` (codec `O(M)`)", + " - 1 event", + " # " + ] + }, + { + "name": "close", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + }, + { + "name": "index", + "type": "Compact" + }, + { + "name": "proposal_weight_bound", + "type": "Compact" + }, + { + "name": "length_bound", + "type": "Compact" + } + ], + "docs": [ + " Close a vote that is either approved, disapproved or whose voting period has ended.", + "", + " May be called by any signed account in order to finish voting and close the proposal.", + "", + " If called before the end of the voting period it will only close the vote if it is", + " has enough votes to be approved or disapproved.", + "", + " If called after the end of the voting period abstentions are counted as rejections", + " unless there is a prime member set and the prime member cast an approval.", + "", + " If the close operation completes successfully with disapproval, the transaction fee will", + " be waived. Otherwise execution of the approved operation will be charged to the caller.", + "", + " + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal.", + " + `length_bound`: The upper bound for the length of the proposal in storage. Checked via", + " `storage::read` so it is `size_of::() == 4` larger than the pure length.", + "", + " # ", + " ## Weight", + " - `O(B + M + P1 + P2)` where:", + " - `B` is `proposal` size in bytes (length-fee-bounded)", + " - `M` is members-count (code- and governance-bounded)", + " - `P1` is the complexity of `proposal` preimage.", + " - `P2` is proposal-count (code-bounded)", + " - DB:", + " - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)", + " - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)", + " - any mutations done while executing `proposal` (`P1`)", + " - up to 3 events", + " # " + ] + }, + { + "name": "disapprove_proposal", + "args": [ + { + "name": "proposal_hash", + "type": "Hash" + } + ], + "docs": [ + " Disapprove a proposal, close, and remove it from the system, regardless of its current state.", + "", + " Must be called by the Root origin.", + "", + " Parameters:", + " * `proposal_hash`: The hash of the proposal that should be disapproved.", + "", + " # ", + " Complexity: O(P) where P is the number of max proposals", + " DB Weight:", + " * Reads: Proposals", + " * Writes: Voting, Proposals, ProposalOf", + " # " + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "AccountId", + "ProposalIndex", + "Hash", + "MemberCount" + ], + "docs": [ + " A motion (given hash) has been proposed (by given account) with a threshold (given", + " `MemberCount`).", + " \\[account, proposal_index, proposal_hash, threshold\\]" + ] + }, + { + "name": "Voted", + "args": [ + "AccountId", + "Hash", + "bool", + "MemberCount", + "MemberCount" + ], + "docs": [ + " A motion (given hash) has been voted on by given account, leaving", + " a tally (yes votes and no votes given respectively as `MemberCount`).", + " \\[account, proposal_hash, voted, yes, no\\]" + ] + }, + { + "name": "Approved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Disapproved", + "args": [ + "Hash" + ], + "docs": [ + " A motion was not approved by the required threshold.", + " \\[proposal_hash\\]" + ] + }, + { + "name": "Executed", + "args": [ + "Hash", + "DispatchResult" + ], + "docs": [ + " A motion was executed; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "MemberExecuted", + "args": [ + "Hash", + "DispatchResult" + ], + "docs": [ + " A single member did some action; result will be `Ok` if it returned without error.", + " \\[proposal_hash, result\\]" + ] + }, + { + "name": "Closed", + "args": [ + "Hash", + "MemberCount", + "MemberCount" + ], + "docs": [ + " A proposal was closed because its threshold was reached or after its duration was up.", + " \\[proposal_hash, yes, no\\]" + ] + } + ], + "constants": [], + "errors": [ + { + "name": "NotMember", + "docs": [ + " Account is not a member" + ] + }, + { + "name": "DuplicateProposal", + "docs": [ + " Duplicate proposals not allowed" + ] + }, + { + "name": "ProposalMissing", + "docs": [ + " Proposal must exist" + ] + }, + { + "name": "WrongIndex", + "docs": [ + " Mismatched index" + ] + }, + { + "name": "DuplicateVote", + "docs": [ + " Duplicate vote ignored" + ] + }, + { + "name": "AlreadyInitialized", + "docs": [ + " Members are already initialized!" + ] + }, + { + "name": "TooEarly", + "docs": [ + " The close call was made too early, before the end of the voting." + ] + }, + { + "name": "TooManyProposals", + "docs": [ + " There can only be a maximum of `MaxProposals` active proposals." + ] + }, + { + "name": "WrongProposalWeight", + "docs": [ + " The given weight bound for the proposal was too low." + ] + }, + { + "name": "WrongProposalLength", + "docs": [ + " The given length bound for the proposal was too low." + ] + } + ], + "index": 71 + }, + { + "name": "Treasury", + "storage": { + "prefix": "Treasury", + "items": [ + { + "name": "ProposalCount", + "modifier": "Default", + "type": { + "plain": "ProposalIndex" + }, + "fallback": "0x00000000", + "docs": [ + " Number of proposals that have been made." + ] + }, + { + "name": "Proposals", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Twox64Concat", + "key": "ProposalIndex", + "value": "TreasuryProposal", + "linked": false + } + }, + "fallback": "0x00", + "docs": [ + " Proposals that have been made." + ] + }, + { + "name": "Approvals", + "modifier": "Default", + "type": { + "plain": "Vec" + }, + "fallback": "0x00", + "docs": [ + " Proposal indices that have been approved but not yet awarded." + ] + } + ] + }, + "calls": [ + { + "name": "propose_spend", + "args": [ + { + "name": "value", + "type": "Compact" + }, + { + "name": "beneficiary", + "type": "LookupSource" + } + ], + "docs": [ + " Put forward a suggestion for spending. A deposit proportional to the value", + " is reserved and slashed if the proposal is rejected. It is returned once the", + " proposal is awarded.", + "", + " # ", + " - Complexity: O(1)", + " - DbReads: `ProposalCount`, `origin account`", + " - DbWrites: `ProposalCount`, `Proposals`, `origin account`", + " # " + ] + }, + { + "name": "reject_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Reject a proposed spend. The original deposit will be slashed.", + "", + " May only be called from `T::RejectOrigin`.", + "", + " # ", + " - Complexity: O(1)", + " - DbReads: `Proposals`, `rejected proposer account`", + " - DbWrites: `Proposals`, `rejected proposer account`", + " # " + ] + }, + { + "name": "approve_proposal", + "args": [ + { + "name": "proposal_id", + "type": "Compact" + } + ], + "docs": [ + " Approve a proposal. At a later time, the proposal will be allocated to the beneficiary", + " and the original deposit will be returned.", + "", + " May only be called from `T::ApproveOrigin`.", + "", + " # ", + " - Complexity: O(1).", + " - DbReads: `Proposals`, `Approvals`", + " - DbWrite: `Approvals`", + " # " + ] + } + ], + "events": [ + { + "name": "Proposed", + "args": [ + "ProposalIndex" + ], + "docs": [ + " New proposal. \\[proposal_index\\]" + ] + }, + { + "name": "Spending", + "args": [ + "Balance" + ], + "docs": [ + " We have ended a spend period and will now allocate funds. \\[budget_remaining\\]" + ] + }, + { + "name": "Awarded", + "args": [ + "ProposalIndex", + "Balance", + "AccountId" + ], + "docs": [ + " Some funds have been allocated. \\[proposal_index, award, beneficiary\\]" + ] + }, + { + "name": "Rejected", + "args": [ + "ProposalIndex", + "Balance" + ], + "docs": [ + " A proposal was rejected; funds were slashed. \\[proposal_index, slashed\\]" + ] + }, + { + "name": "Burnt", + "args": [ + "Balance" + ], + "docs": [ + " Some of our funds have been burnt. \\[burn\\]" + ] + }, + { + "name": "Rollover", + "args": [ + "Balance" + ], + "docs": [ + " Spending has finished; this is the amount that rolls over until next spend.", + " \\[budget_remaining\\]" + ] + }, + { + "name": "Deposit", + "args": [ + "Balance" + ], + "docs": [ + " Some funds have been deposited. \\[deposit\\]" + ] + } + ], + "constants": [ + { + "name": "ProposalBond", + "type": "Permill", + "value": "0x50c30000", + "docs": [ + " Fraction of a proposal's value that should be bonded in order to place the proposal.", + " An accepted proposal gets these back. A rejected proposal does not." + ] + }, + { + "name": "ProposalBondMinimum", + "type": "BalanceOf", + "value": "0x000064a7b3b6e00d0000000000000000", + "docs": [ + " Minimum amount of funds that should be placed in a deposit for making a proposal." + ] + }, + { + "name": "SpendPeriod", + "type": "BlockNumber", + "value": "0xc0a80000", + "docs": [ + " Period between successive spends." + ] + }, + { + "name": "Burn", + "type": "Permill", + "value": "0x00000000", + "docs": [ + " Percentage of spare funds (if any) that are burnt per spend period." + ] + }, + { + "name": "PalletId", + "type": "PalletId", + "value": "0x70792f7472737279", + "docs": [ + " The treasury's pallet id, used for deriving its sovereign account ID." + ] + }, + { + "name": "MaxApprovals", + "type": "u32", + "value": "0x64000000", + "docs": [ + " The maximum number of approvals that can wait in the spending queue." + ] + } + ], + "errors": [ + { + "name": "InsufficientProposersBalance", + "docs": [ + " Proposer's balance is too low." + ] + }, + { + "name": "InvalidIndex", + "docs": [ + " No proposal or bounty at that index." + ] + }, + { + "name": "TooManyApprovals", + "docs": [ + " Too many approvals in the queue." + ] + } + ], + "index": 80 + }, + { + "name": "CrowdloanRewards", + "storage": { + "prefix": "CrowdloanRewards", + "items": [ + { + "name": "AccountsPayable", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "AccountId", + "value": "RewardInfo", + "linked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "ClaimedRelayChainIds", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "RelayChainAccountId", + "value": "()", + "linked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "UnassociatedContributions", + "modifier": "Optional", + "type": { + "map": { + "hasher": "Blake2_128Concat", + "key": "RelayChainAccountId", + "value": "RewardInfo", + "linked": false + } + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "Initialized", + "modifier": "Default", + "type": { + "plain": "bool" + }, + "fallback": "0x00", + "docs": [] + }, + { + "name": "InitRelayBlock", + "modifier": "Default", + "type": { + "plain": "VestingBlockNumber" + }, + "fallback": "0x00000000", + "docs": [ + " Vesting block height at the initialization of the pallet" + ] + }, + { + "name": "EndRelayBlock", + "modifier": "Default", + "type": { + "plain": "VestingBlockNumber" + }, + "fallback": "0x00000000", + "docs": [ + " Vesting block height at the initialization of the pallet" + ] + }, + { + "name": "InitializedRewardAmount", + "modifier": "Default", + "type": { + "plain": "BalanceOf" + }, + "fallback": "0x00000000000000000000000000000000", + "docs": [ + " Total initialized amount so far. We store this to make pallet funds == contributors reward", + " check easier and more efficient" + ] + }, + { + "name": "TotalContributors", + "modifier": "Default", + "type": { + "plain": "u32" + }, + "fallback": "0x00000000", + "docs": [ + " Total number of contributors to aid hinting benchmarking" + ] + } + ] + }, + "calls": [ + { + "name": "associate_native_identity", + "args": [ + { + "name": "reward_account", + "type": "AccountId" + }, + { + "name": "relay_account", + "type": "RelayChainAccountId" + }, + { + "name": "proof", + "type": "MultiSignature" + } + ], + "docs": [ + " Associate a native rewards_destination identity with a crowdloan contribution.", + "", + " The caller needs to provide the unassociated relay account and a proof to succeed", + " with the association", + " The proof is nothing but a signature over the reward_address using the relay keys" + ] + }, + { + "name": "change_association_with_relay_keys", + "args": [ + { + "name": "reward_account", + "type": "AccountId" + }, + { + "name": "previous_account", + "type": "AccountId" + }, + { + "name": "proofs", + "type": "Vec<(RelayChainAccountId,MultiSignature)>" + } + ], + "docs": [ + " Change reward account by submitting proofs from relay accounts", + "", + " The number of valid proofs needs to be bigger than 'RewardAddressRelayVoteThreshold'", + " The account to be changed needs to be submitted as 'previous_account'", + " Origin must be RewardAddressChangeOrigin" + ] + }, + { + "name": "claim", + "args": [], + "docs": [ + " Collect whatever portion of your reward are currently vested." + ] + }, + { + "name": "update_reward_address", + "args": [ + { + "name": "new_reward_account", + "type": "AccountId" + } + ], + "docs": [ + " Update reward address, proving that the caller owns the current native key" + ] + }, + { + "name": "complete_initialization", + "args": [ + { + "name": "lease_ending_block", + "type": "VestingBlockNumber" + } + ], + "docs": [ + " This extrinsic completes the initialization if some checks are fullfiled. These checks are:", + " -The reward contribution money matches the crowdloan pot", + " -The end vesting block is higher than the init vesting block", + " -The initialization has not complete yet" + ] + }, + { + "name": "initialize_reward_vec", + "args": [ + { + "name": "rewards", + "type": "Vec<(RelayChainAccountId,Option,BalanceOf)>" + } + ], + "docs": [ + " Initialize the reward distribution storage. It shortcuts whenever an error is found", + " This does not enforce any checks other than making sure we dont go over funds", + " complete_initialization should perform any additional" + ] + } + ], + "events": [ + { + "name": "InitialPaymentMade", + "args": [ + "AccountId", + "BalanceOf" + ], + "docs": [ + " The initial payment of InitializationPayment % was paid" + ] + }, + { + "name": "NativeIdentityAssociated", + "args": [ + "RelayChainAccountId", + "AccountId", + "BalanceOf" + ], + "docs": [ + " Someone has proven they made a contribution and associated a native identity with it.", + " Data is the relay account, native account and the total amount of _rewards_ that will be paid" + ] + }, + { + "name": "RewardsPaid", + "args": [ + "AccountId", + "BalanceOf" + ], + "docs": [ + " A contributor has claimed some rewards.", + " Data is the account getting paid and the amount of rewards paid." + ] + }, + { + "name": "RewardAddressUpdated", + "args": [ + "AccountId", + "AccountId" + ], + "docs": [ + " A contributor has updated the reward address." + ] + }, + { + "name": "InitializedAlreadyInitializedAccount", + "args": [ + "RelayChainAccountId", + "Option", + "BalanceOf" + ], + "docs": [ + " When initializing the reward vec an already initialized account was found" + ] + }, + { + "name": "InitializedAccountWithNotEnoughContribution", + "args": [ + "RelayChainAccountId", + "Option", + "BalanceOf" + ], + "docs": [ + " When initializing the reward vec an already initialized account was found" + ] + } + ], + "constants": [ + { + "name": "InitializationPayment", + "type": "Perbill", + "value": "0x00a3e111", + "docs": [ + " Percentage to be payed at initialization" + ] + }, + { + "name": "MaxInitContributors", + "type": "u32", + "value": "0xf4010000", + "docs": [] + }, + { + "name": "RewardAddressRelayVoteThreshold", + "type": "Perbill", + "value": "0x00ca9a3b", + "docs": [ + " A fraction representing the percentage of proofs", + " that need to be presented to change a reward address through the relay keys" + ] + } + ], + "errors": [ + { + "name": "AlreadyAssociated", + "docs": [ + " User trying to associate a native identity with a relay chain identity for posterior", + " reward claiming provided an already associated relay chain identity" + ] + }, + { + "name": "BatchBeyondFundPot", + "docs": [ + " Trying to introduce a batch that goes beyond the limits of the funds" + ] + }, + { + "name": "FirstClaimAlreadyDone", + "docs": [ + " First claim already done" + ] + }, + { + "name": "RewardNotHighEnough", + "docs": [ + " The contribution is not high enough to be eligible for rewards" + ] + }, + { + "name": "InvalidClaimSignature", + "docs": [ + " User trying to associate a native identity with a relay chain identity for posterior", + " reward claiming provided a wrong signature" + ] + }, + { + "name": "InvalidFreeClaimSignature", + "docs": [ + " User trying to claim the first free reward provided the wrong signature" + ] + }, + { + "name": "NoAssociatedClaim", + "docs": [ + " User trying to claim an award did not have an claim associated with it. This may mean", + " they did not contribute to the crowdloan, or they have not yet associated a native id", + " with their contribution" + ] + }, + { + "name": "RewardsAlreadyClaimed", + "docs": [ + " User trying to claim rewards has already claimed all rewards associated with its", + " identity and contribution" + ] + }, + { + "name": "RewardVecAlreadyInitialized", + "docs": [ + " Reward vec has already been initialized" + ] + }, + { + "name": "RewardVecNotFullyInitializedYet", + "docs": [ + " Reward vec has not yet been fully initialized" + ] + }, + { + "name": "RewardsDoNotMatchFund", + "docs": [ + " Rewards should match funds of the pallet" + ] + }, + { + "name": "TooManyContributors", + "docs": [ + " Initialize_reward_vec received too many contributors" + ] + }, + { + "name": "VestingPeriodNonValid", + "docs": [ + " Provided vesting period is not valid" + ] + }, + { + "name": "NonContributedAddressProvided", + "docs": [ + " User provided a signature from a non-contributor relay account" + ] + }, + { + "name": "InsufficientNumberOfValidProofs", + "docs": [ + " User submitted an unsifficient number of proofs to change the reward address" + ] + } + ], + "index": 90 + } + ], + "extrinsic": { + "version": 4, + "signedExtensions": [ + "CheckSpecVersion", + "CheckTxVersion", + "CheckGenesis", + "CheckMortality", + "CheckNonce", + "CheckWeight", + "ChargeTransactionPayment" + ] + } + } + } +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/build.gradle b/javascore/bmv/eventDecoder/build.gradle new file mode 100644 index 00000000..354f424a --- /dev/null +++ b/javascore/bmv/eventDecoder/build.gradle @@ -0,0 +1,126 @@ +plugins { + id 'java' + id 'maven-publish' + id 'signing' +} + +sourceSets { + intTest {} +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +configurations { + intTestImplementation.extendsFrom testImplementation + intTestRuntimeOnly.extendsFrom testRuntimeOnly +} + +apply from: 'generateEventDecoderCode.gradle' + +dependencies { + implementation 'com.github.sink772:javaee-scorex:0.5.2' + implementation project(':lib') + compileOnly files('../api-0.8.8-SNAPSHOT.jar') + + testImplementation "com.squareup.okhttp3:okhttp:3.11.0" + intTestImplementation project(':testinteg') + intTestImplementation 'foundation.icon:icon-sdk:2.0.0' + intTestImplementation 'org.web3j:core:4.8.4' + testImplementation project(':testsvc') + testImplementation 'org.mockito:mockito-core:3.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.eventDecoder.EventDecoderScore' + // archivesBaseName = 'EventDecoderScore' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = 'http://localhost:9082/api/v3' + nid = 3 + } + } + keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : '' + password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : '' + parameters {} +} + +test { + useJUnitPlatform() + testLogging.showStandardStreams = true + testLogging.exceptionFormat = "full" +} + +tasks.register('buildKusamaDecoder') { + archivesBaseName = 'KusamaEventDecoder' + project.ext.metaDataFilePath = """${projectDir}/KusamaMetaData.json""" + dependsOn loadMetaData + buildKusamaDecoder.finalizedBy optimizedJar +} + +tasks.register('buildMoonriverDecoder') { + archivesBaseName = 'MoonriverEventDecoder' + project.ext.metaDataFilePath = """${projectDir}/MoonriverMetaData.json""" + project.ext.accountIdSize = 20 + dependsOn loadMetaData + buildMoonriverDecoder.finalizedBy optimizedJar +} + +tasks.register('buildEdgewareDecoder') { + archivesBaseName = 'EdgewareEventDecoder' + project.ext.metaDataFilePath = """${projectDir}/EdgewareMetaData.json""" + dependsOn loadMetaData + buildEdgewareDecoder.finalizedBy optimizedJar +} + +task deleteGeneratedEventDecoderFiles(type: Delete) { + delete fileTree("""${projectDir}/src/main/java/foundation/icon/btp/lib/EventDecoder/Module/""") { + include '**/*Event.java' + } + delete fileTree("""${projectDir}/src/main/java/foundation/icon/btp/lib/EventDecoder/Module/Base""") { + include '**/*EventBase.java' + } + delete fileTree("""${projectDir}/src/main/java/foundation/icon/btp/lib/EventDecoder/""") { + include '**/EventDecoder.java' + } + delete fileTree("""${projectDir}/src/main/java/foundation/icon/btp/lib/EventDecoder/""") { + include '**/TypeDecoder.java' + } +} + +tasks.register('loadMetaData') { + dependsOn deleteGeneratedEventDecoderFiles + doLast { + handleGenerateEventDecoderCode() + } +} + +task integrationTest(type: Test, dependsOn: optimizedJar) { + useJUnitPlatform() + description = 'Runs integration tests.' + group = 'verification' + + testClassesDirs = sourceSets.intTest.output.classesDirs + classpath = sourceSets.intTest.runtimeClasspath + testLogging.showStandardStreams = true + + // use the common config files + systemProperty('env.props', new File(project(':testinteg').projectDir, 'conf/env.props')) + + def prefix = 'score.path.' + systemProperty(prefix + project.name, optimizedJar.outputJarName) +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/generateEventDecoderCode.gradle b/javascore/bmv/eventDecoder/generateEventDecoderCode.gradle new file mode 100644 index 00000000..257ece4c --- /dev/null +++ b/javascore/bmv/eventDecoder/generateEventDecoderCode.gradle @@ -0,0 +1,364 @@ +project.ext.handleGenerateEventDecoderCode = { -> + def accountIdSize = 32; + if (project.hasProperty('accountIdSize')) { + accountIdSize = project.ext.accountIdSize + } + if (!project.hasProperty('metaDataFilePath')) { + throw new Exception("missing metadata file path") + } + + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/Module/Base").mkdirs() + def inputFile = new File(project.ext.metaDataFilePath) + def json = new groovy.json.JsonSlurper().parseText(inputFile.text) + if (json.metadata.v12 || json.metadata.v13) { + generateV12V13(json, accountIdSize) + } else if (json.metadata.v14) { + generateV14(json) + } else { + throw new Exception("unsupport metadata version"); + } +} + +project.ext.generateV12V13 = { json, accountIdSize -> + def modules = json.metadata.v12 != null ? json.metadata.v12.modules : json.metadata.v13.modules + def geratingEventDecoderCode = +"""package foundation.icon.btp.lib.EventDecoder; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class EventDecoder extends EventDecoderBase { + public static byte[] decodeEvent(byte mainIndex, byte subIndex, ByteSliceInput input) { + SizeDecoder.accountIdSize = ${accountIdSize}; + switch (mainIndex) { +""" + modules.each { item -> + if (item.events != null && item.events.size() != 0) { + def geratingModuleEventDecoderCode = +"""package foundation.icon.btp.lib.EventDecoder; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class ${item.name}Event extends ${item.name}EventBase { + public static byte[] decodeEvent(byte subIndex, ByteSliceInput input) { + switch (subIndex) { +""" + def geratingModuleEventDecoderBaseCode = +"""package foundation.icon.btp.lib.EventDecoder; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class ${item.name}EventBase { +""" + def subIndex = 0; + item.events.each { event -> + geratingModuleEventDecoderCode += +""" case (byte)(${subIndex}): + return ${event.name[0].toLowerCase() + event.name.substring(1)}(input); +""" + geratingModuleEventDecoderBaseCode += +""" public static byte[] ${event.name[0].toLowerCase() + event.name.substring(1)}(ByteSliceInput input) { +""" + if (event.args != null && event.args.size() > 0) { + geratingModuleEventDecoderBaseCode += +""" int size = 0; +""" + event.args.each { arg -> + def functionName = arg + if (functionName.startsWith("Option<") || functionName.startsWith("Vec<")) { + functionName = functionName.replace('<', '_') + functionName = functionName.replace('(', '_') + functionName = functionName.replace(')', '') + functionName = functionName.replace('>', '') + functionName = functionName.replace(',', '_') + functionName = functionName.replace('__', '_') + } + + if (functionName.startsWith("[")) { + functionName = functionName.replace('[', '_') + functionName = functionName.replace(';', '_') + functionName = functionName.replace(']', '_') + } + geratingModuleEventDecoderBaseCode += +""" size += SizeDecoder.${functionName}(input, size); +""" + } + + geratingModuleEventDecoderBaseCode += +""" return input.take(size); +""" + } else { + geratingModuleEventDecoderBaseCode += +""" return null; +""" + } + subIndex++ + geratingModuleEventDecoderBaseCode += +""" } + +""" + } + geratingModuleEventDecoderBaseCode += +"""} +""" + geratingModuleEventDecoderCode += +""" } + return null; + } +} +""" + geratingEventDecoderCode += +""" case (byte)(${item.index}): + return ${item.name}Event.decodeEvent(subIndex, input); +""" + + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/Module/${item.name}Event.java").text = geratingModuleEventDecoderCode + + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/Module/Base/${item.name}EventBase.java").text = geratingModuleEventDecoderBaseCode + } + } + + geratingEventDecoderCode += +""" } + return null; + } +} +""" + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/EventDecoder.java").text = geratingEventDecoderCode +} + +project.ext.generateV14 = { json -> + generateTypeDecoderV14(json) + generateV14EventDecoder(json) +} + +project.ext.generateV14EventDecoder = { json -> + def modules = json.metadata.v14.pallets + def types = json.metadata.v14.lookup.types + def geratingEventDecoderCode = +"""package foundation.icon.btp.lib.EventDecoder; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class EventDecoder extends EventDecoderBase { + public static byte[] decodeEvent(byte mainIndex, byte subIndex, ByteSliceInput input) { + int eventSize = 0; + switch (mainIndex) { +""" + modules.each { item -> + if (item.events != null && item.events.type != null) { + geratingEventDecoderCode += +""" case (byte)(${item.index}): + eventSize = TypeDecoder.decodeType${item.events.type}(input, -1); + break; +""" + } + } + + geratingEventDecoderCode += +""" } + if (eventSize > 0) { + return input.take(eventSize - 1); + } else { + return input.take(eventSize); + } + } +} +""" + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/EventDecoder.java").text = geratingEventDecoderCode +} + +project.ext.generateTypeDecoderV14 = { json -> + def types = json.metadata.v14.lookup.types + def geratingTypeDecoderCode = +"""package foundation.icon.btp.lib.EventDecoder; + +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.scale.ScaleReader; +import java.math.BigInteger; + +public class TypeDecoder { +""" + types.each { typeObject -> + def type = typeObject.type + geratingTypeDecoderCode += +""" + public static int decodeType${typeObject.id}(ByteSliceInput input, int offset) { +""" + if (type.def.primitive) { + switch (type.def.primitive) { + case "U8": + case "Bool": + case "Char": + geratingTypeDecoderCode += +""" return 1; + } +""" + break; + case "U16": + geratingTypeDecoderCode += +""" return 2; + } +""" + break; + case "U32": + geratingTypeDecoderCode += +""" return 4; + } +""" + break; + case "U64": + geratingTypeDecoderCode += +""" return 8; + } +""" + break; + case "U128": + geratingTypeDecoderCode += +""" return 16; + } +""" + break; + case "Str": + geratingTypeDecoderCode += +""" int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int itemSize = ScaleReader.readUintCompactSize(input); + int size = input.getOffset() - startPoint - offset; + + size += itemSize; + input.seek(startPoint); + return size; + } +""" + } + } + + if (type.def.tuple != null) { + geratingTypeDecoderCode += +""" int size = 0; +""" + type.def.tuple.each { typeIndex -> + geratingTypeDecoderCode += +""" size += TypeDecoder.decodeType${typeIndex}(input, offset + size); +""" + } + geratingTypeDecoderCode += +""" return size; + } +""" + } + + if (type.def.array) { + geratingTypeDecoderCode += +""" int size = 0; + + for(int i=0; i < ${type.def.array.len}; i++) { + size += TypeDecoder.decodeType${type.def.array.type}(input, offset + size); + } + return size; + } +""" + } + + if (type.def.sequence) { + geratingTypeDecoderCode += +""" int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int itemSize = ScaleReader.readUintCompactSize(input); + int size = input.getOffset() - startPoint - offset; + input.seek(startPoint); + + for(int i=0; i < itemSize; i++) { + size += TypeDecoder.decodeType${type.def.sequence.type}(input, offset + size); + } + return size; + } +""" + } + + if (type.def.bitSequence) { + geratingTypeDecoderCode += +""" int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int itemSize = ScaleReader.readUintCompactSize(input); + int size = input.getOffset() - startPoint - offset; + input.seek(startPoint); + + for(int i=0; i < itemSize; i++) { + size += TypeDecoder.decodeType${type.def.bitSequence.bitStoreType}(input, offset + size); + } + return size; + } +""" + } + + if (type.def.variant) { + geratingTypeDecoderCode += +""" int startPoint = input.getOffset(); + input.seek(startPoint + offset); + int size = 1; + int enumIndex = input.takeUByte(); + input.seek(startPoint); + switch (enumIndex) { +""" + type.def.variant.variants.each { variant -> + geratingTypeDecoderCode += +""" case ${variant.index}: +""" + variant.fields.each { field -> + geratingTypeDecoderCode += +""" size += TypeDecoder.decodeType${field.type}(input, offset + size); +""" + } + geratingTypeDecoderCode += +""" break; +""" + } + geratingTypeDecoderCode += +""" } + return size; + } +""" + } + + if (type.def.composite) { + geratingTypeDecoderCode += +""" int size = 0; +""" + type.def.composite.fields.each { field -> + geratingTypeDecoderCode += +""" + size += TypeDecoder.decodeType${field.type}(input, offset + size); +""" + } + + geratingTypeDecoderCode += +""" + return size; + } +""" + } + + if (type.def.compact) { + geratingTypeDecoderCode += +""" int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + BigInteger itemSize = ScaleReader.readCompacBigInteger(input); + int size = input.getOffset() - startPoint - offset; + input.seek(startPoint); + return size; + } +""" + } + } + + geratingTypeDecoderCode += +""" +} +""" + new File(projectDir, "/src/main/java/foundation/icon/btp/lib/EventDecoder/TypeDecoder.java").text = geratingTypeDecoderCode +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/eventDecoder/EventDecoderScore.java b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/eventDecoder/EventDecoderScore.java new file mode 100644 index 00000000..ca7630bf --- /dev/null +++ b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/eventDecoder/EventDecoderScore.java @@ -0,0 +1,29 @@ +package foundation.icon.btp.eventDecoder; + +import scorex.util.ArrayList; + +import score.annotation.External; +import foundation.icon.btp.lib.EventDecoder.EventDecoder; +import foundation.icon.btp.lib.scale.ScaleReader; +import foundation.icon.btp.lib.utils.ByteSliceInput; + +import java.util.List; +import java.util.Map; + +public class EventDecoderScore { + public EventDecoderScore() {} + + /** + * decode event + */ + @External + public List> decodeEvent(byte[] encodedEventList) { + ByteSliceInput input = new ByteSliceInput(encodedEventList); + int eventSize = ScaleReader.readUintCompactSize(input); + List> result = new ArrayList>(eventSize); + for (int i = 0; i((int)topicsLength); + for (int i = 0; i < topicsLength; i++) { + eventRecord.topics.add(input.take(32)); + } + return eventRecord; + } +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/EventRecord.java b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/EventRecord.java new file mode 100644 index 00000000..ca0f7b6a --- /dev/null +++ b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/EventRecord.java @@ -0,0 +1,23 @@ +package foundation.icon.btp.lib.EventDecoder; +import java.util.List; +import java.util.Map; +import scorex.util.HashMap; + +public class EventRecord { + public byte phaseEnum; + public byte[] phaseData; + public byte[] eventIndex; + public byte[] eventData; + public List topics; + + public Map toMap() { + Map eventRecordMap = new HashMap(); + eventRecordMap.put("phaseEnum", this.phaseEnum); + eventRecordMap.put("phaseData", this.phaseData); + eventRecordMap.put("eventIndex", this.eventIndex); + eventRecordMap.put("eventData", this.eventData); + eventRecordMap.put("topics", this.topics); + return eventRecordMap; + } + +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/SizeDecoder.java b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/SizeDecoder.java new file mode 100644 index 00000000..2fdb6c12 --- /dev/null +++ b/javascore/bmv/eventDecoder/src/main/java/foundation/icon/btp/lib/EventDecoder/SizeDecoder.java @@ -0,0 +1,977 @@ +package foundation.icon.btp.lib.EventDecoder; + +import java.math.BigInteger; + +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.scale.ScaleReader; + +public class SizeDecoder { + public static int accountIdSize = 32; + + public static int DispatchInfo(ByteSliceInput input, int offset) { + return 10; + } + + public static int DispatchError(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + int size = 1; + input.seek(startPoint + offset); + byte dispatchError = input.takeByte(); + if ((dispatchError & 0xff) == 3) { + size += 2; + } + + if ((dispatchError & 0xff) == 0x06 || (dispatchError & 0xff) == 0x07) { + size += 1; + } + + input.seek(startPoint); + return size; + } + + public static int DispatchResult(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + int size = 1; + input.seek(startPoint + offset); + byte dispatchResultEnum = input.takeByte(); + if ((dispatchResultEnum & 0xff) == 0x01) { + byte dispatchErrorEnum = input.takeByte(); + size += 1; + if ((dispatchErrorEnum & 0xff) == 0x03) { + size += 2; + } + + if ((dispatchErrorEnum & 0xff) == 0x06 || (dispatchErrorEnum & 0xff) == 0x07) { + size += 1; + } + } + + input.seek(startPoint); + return size; + } + + public static int AccountId(ByteSliceInput input, int offset) { + return accountIdSize; + } + + public static int AccountId32(ByteSliceInput input, int offset) { + return 32; + } + + public static int EthereumAccountId(ByteSliceInput input, int offset) { + return 20; + } + + public static int Hash(ByteSliceInput input, int offset) { + return 32; + } + + public static int AccountIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int Balance(ByteSliceInput input, int offset) { + return 16; + } + + public static int BalanceOf(ByteSliceInput input, int offset) { + return 16; + } + + public static int BalanceStatus(ByteSliceInput input, int offset) { + return 1; + } + + public static int EraIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int SessionIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int Kind(ByteSliceInput input, int offset) { + return 16; + } + + public static int OpaqueTimeSlot(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + + int timeslotSize = ScaleReader.readUintCompactSize(input); + byte[] timeslot = input.take(timeslotSize); + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int AuthorityList(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int validatorSize = ScaleReader.readUintCompactSize(input); + int compactSize = input.getOffset() - startPoint - offset; + + input.seek(startPoint); + return (32 + 8 ) * validatorSize + compactSize; + } + + public static int AuthorityId(ByteSliceInput input, int offset) { + return 32; + } + + public static int Vec_IdentificationTuple(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int identificationSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i < identificationSize; i++) { + input.take(32); // validatorId + BigInteger res = ScaleReader.readCompacBigInteger(input); // totalBalance + ScaleReader.readCompacBigInteger(input); // ownBalance + long individualExposureSize = ScaleReader.readUintCompactSize(input); + for (int j = 0; j < individualExposureSize; j++) { + input.take(32); // who + ScaleReader.readCompacBigInteger(input); // value + } + } + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int PropIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int Vec_AccountId(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int accountSize = ScaleReader.readUintCompactSize(input); + int compactSize = input.getOffset() - startPoint - offset; + + input.seek(startPoint); + return accountSize*accountIdSize + compactSize; + } + + public static int ReferendumIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int VoteThreshold(ByteSliceInput input, int offset) { + return 1; + } + + public static int bool(ByteSliceInput input, int offset) { + return 1; + } + + public static int BlockNumber(ByteSliceInput input, int offset) { + return 4; + } + + public static int ProposalIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int MemberCount(ByteSliceInput input, int offset) { + return 4; + } + + public static int Vec_AccountId_Balance(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int vectorSize = ScaleReader.readUintCompactSize(input); + int compactSize = input.getOffset() - startPoint - offset; + + input.seek(startPoint); + return vectorSize*(accountIdSize + 16) + compactSize; + } + + public static int PhantomData(ByteSliceInput input, int offset) { + return 0; + } + + public static int EthereumAddress(ByteSliceInput input, int offset) { + return 20; + } + + public static int u32(ByteSliceInput input, int offset) { + return 4; + } + + public static int RegistrarIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int TaskAddress(ByteSliceInput input, int offset) { + return 8; + } + + public static int Option_Bytes(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int isHasBytes = input.takeUByte(); + if (isHasBytes > 0) { + byte[] b = ScaleReader.readBytes(input); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int ProxyType(ByteSliceInput input, int offset) { + return 1; + } + + public static int u16(ByteSliceInput input, int offset) { + return 2; + } + + public static int CallHash(ByteSliceInput input, int offset) { + return 32; + } + + public static int Timepoint(ByteSliceInput input, int offset) { + return 8; + } + + public static int BountyIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int ElectionCompute(ByteSliceInput input, int offset) { + return 1; + } + + public static int Option_ElectionCompute(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int isHasBytes = input.takeUByte(); + if (isHasBytes > 0) { + input.takeByte(); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int ActiveIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int CandidateReceipt(ByteSliceInput input, int offset) { + // descriptor: 'CandidateDescriptor', + // paraId: 'ParaId', u32 + // relayParent: 'RelayChainHash', Hash + // collatorId: 'CollatorId', H256 + // persistedValidationDataHash: 'Hash', + // povHash: 'Hash', + // erasureRoot: 'Hash', + // signature: 'CollatorSignature', 64bytes + // paraHead: 'Hash', + // validationCodeHash: 'ValidationCodeHash' Hash + // commitmentsHash: 'Hash' + return 324; + } + + public static int HeadData(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + byte[] headData = ScaleReader.readBytes(input); + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int CoreIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int GroupIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int ParaId(ByteSliceInput input, int offset) { + return 4; + } + + public static int MessageId(ByteSliceInput input, int offset) { + return 32; + } + + public static void decodeJunction(ByteSliceInput input) { + int junctionEnum = input.takeUByte(); + switch (junctionEnum) { + case 1: // Parachain: 'Compact', + ScaleReader.readCompacBigInteger(input); + break; + case 2: // AccountId32: 'AccountId32Junction', + int networkIdEnum = input.takeUByte(); + if (networkIdEnum == 1) { + ScaleReader.readBytes(input); + } + input.take(32); // accountId + break; + case 3: // AccountIndex64: 'AccountIndex64Junction', + networkIdEnum = input.takeUByte(); + if (networkIdEnum == 1) { + ScaleReader.readBytes(input); + } + ScaleReader.readCompacBigInteger(input); // index: 'Compact' + break; + case 4: // AccountId32: 'AccountId32Junction', + networkIdEnum = input.takeUByte(); + if (networkIdEnum == 1) { + ScaleReader.readBytes(input); + } + input.take(20); // index: '[u8; 20]' + break; + case 5: + input.takeByte(); // PalletInstance: 'u8', + break; + case 6: + ScaleReader.readCompacBigInteger(input); // GeneralIndex: 'Compact', + break; + case 7: + ScaleReader.readBytes(input); + break; + case 9: // Plurality: 'PluralityJunction' + int bodyIdEnum = input.takeUByte(); + if (bodyIdEnum == 1) { + ScaleReader.readBytes(input); // Named: 'Vec', + } + if (bodyIdEnum == 2) { + ScaleReader.readCompacBigInteger(input); // Index: 'Compact', + } + + int bodyPartEnum = input.takeUByte(); + if (bodyPartEnum == 1) { + ScaleReader.readCompacBigInteger(input); // Members: 'Compact', + } + if (bodyPartEnum > 1) { + ScaleReader.readCompacBigInteger(input); // nom: 'Compact', denom: 'Compact' + ScaleReader.readCompacBigInteger(input); + } + break; + default: + break; + } + } + + public static void decodeAssetInstance(ByteSliceInput input) { + int assetInstanceEnum = input.takeUByte(); + switch (assetInstanceEnum) { + case 1: + input.takeByte(); + break; + case 2: + case 3: + case 4: + case 5: + ScaleReader.readCompacBigInteger(input); + break; + case 6: + input.take(4); + break; + case 7: + input.take(8); + break; + case 8: + input.take(16); + break; + case 9: + input.take(32); + break; + case 10: + ScaleReader.readBytes(input); + break; + default: + break; + } + } + + public static void decodeMultiAsset(ByteSliceInput input) { + int multiAssetEnum = input.takeUByte(); + switch (multiAssetEnum) { + case 4: + case 5: + ScaleReader.readBytes(input); + break; + case 6: + case 7: + decodeMultiLocation(input); + break; + case 8: + ScaleReader.readBytes(input); + ScaleReader.readCompacBigInteger(input); + break; + case 9: + ScaleReader.readBytes(input); + decodeAssetInstance(input); + break; + case 10: + decodeMultiLocation(input); + ScaleReader.readCompacBigInteger(input); + break; + case 11: + decodeMultiLocation(input); + decodeAssetInstance(input); + break; + default: + break; + } + } + + public static int MultiAsset(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + input.seek(startPoint + offset); + + int multiAssetEnum = input.takeUByte(); + switch (multiAssetEnum) { + case 4: + case 5: + ScaleReader.readBytes(input); + break; + case 6: + case 7: + decodeMultiLocation(input); + break; + case 8: + ScaleReader.readBytes(input); + ScaleReader.readCompacBigInteger(input); + break; + case 9: + ScaleReader.readBytes(input); + decodeAssetInstance(input); + break; + case 10: + decodeMultiLocation(input); + ScaleReader.readCompacBigInteger(input); + break; + case 11: + decodeMultiLocation(input); + decodeAssetInstance(input); + break; + default: + break; + } + int endPoint = input.getOffset(); + + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static void decodeXcmOrder(ByteSliceInput input) { + int xcmOrderEnum = input.takeUByte(); + switch (xcmOrderEnum) { + case 1: + int multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + decodeMultiLocation(input); + break; + case 2: + case 4: + case 5: + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + decodeMultiLocation(input); + + int xcmOrderSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< xcmOrderSize; i++) { + decodeXcmOrder(input); + } + break; + case 3: + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + break; + case 6: + ScaleReader.readCompacBigInteger(input); + decodeMultiLocation(input); + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + break; + case 7: + decodeMultiAsset(input); + input.take(17); + int xcmSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< xcmSize; i++) { + decodeXcm(input); + } + break; + default: + break; + } + } + + + public static void decodeMultiLocation(ByteSliceInput input) { + int multiLocationEnum = input.takeUByte(); + for (int i = 0; i < multiLocationEnum; i++) { + decodeJunction(input); + } + } + + public static void decodeXcmResponse(ByteSliceInput input) { + int xcmResponseEnum = input.takeUByte(); + switch (xcmResponseEnum) { + case 0: + int multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + default: + break; + } + } + + public static void decodeXcm(ByteSliceInput input) { + int xcmEnum = input.takeUByte(); + switch (xcmEnum) { + case 0: + case 1: + case 2: + int multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + int xcmOrderSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< xcmOrderSize; i++) { + decodeXcmOrder(input); + } + break; + case 3: + ScaleReader.readCompacBigInteger(input); + decodeXcmResponse(input); + break; + case 4: + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + decodeMultiLocation(input); + break; + case 5: + multiAssetSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< multiAssetSize; i++) { + decodeMultiAsset(input); + } + decodeMultiLocation(input); + xcmOrderSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i< xcmOrderSize; i++) { + decodeXcmOrder(input); + } + break; + case 6: + input.takeByte(); + input.take(8); + ScaleReader.readBytes(input); + break; + case 7: + ScaleReader.readCompacBigInteger(input); + ScaleReader.readCompacBigInteger(input); + ScaleReader.readCompacBigInteger(input); + break; + case 8: + ScaleReader.readCompacBigInteger(input); + break; + case 9: + ScaleReader.readCompacBigInteger(input); + ScaleReader.readCompacBigInteger(input); + ScaleReader.readCompacBigInteger(input); + break; + case 10: + decodeMultiLocation(input); + decodeXcm(input); + default: + break; + } + } + + public static void decodeXcmError(ByteSliceInput input) { + int xcmErrorEnum = input.takeUByte(); + switch (xcmErrorEnum) { + case 11: + decodeMultiLocation(input); + decodeXcm(input); + break; + case 17: + input.take(8); + break; + default: + break; + } + } + + public static int XcmError(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int xcmErrorEnum = input.takeUByte(); + switch (xcmErrorEnum) { + case 11: + decodeMultiLocation(input); + decodeXcm(input); + break; + case 17: + input.take(8); + break; + default: + break; + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int Outcome(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int outComeEnum = input.takeUByte(); + if (outComeEnum == 0) { + input.take(8); // Weight + } + + if (outComeEnum == 1) { + input.take(8); // Weight + decodeXcmError(input); + } + + if (outComeEnum == 2) { + input.take(8); // Weight + decodeXcmError(input); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int Weight(ByteSliceInput input, int offset) { + return 8; + } + + public static int HrmpChannelId(ByteSliceInput input, int offset) { + return 8; + } + + public static int LeasePeriod(ByteSliceInput input, int offset) { + return 4; + } + + public static int SlotRange(ByteSliceInput input, int offset) { + return 1; + } + + public static int AuctionIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int OverweightIndex(ByteSliceInput input, int offset) { + return 8; + } + + public static int AssetType(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int assetTypeEnum = input.takeUByte(); + if (assetTypeEnum == 0) { + decodeMultiLocation(input); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int AssetRegistrarMetadata(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int nameSize = ScaleReader.readUintCompactSize(input); + input.take(nameSize); // name + int symbolSize = ScaleReader.readUintCompactSize(input); + input.take(symbolSize); // symbol + input.take(2); // decimals and is_fronzen + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int u128(ByteSliceInput input, int offset) { + return 16; + } + + public static int Option_Hash(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int isHasHash = input.takeUByte(); + if (isHasHash > 0) { + input.take(32); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int Bytes(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + ScaleReader.readBytes(input); + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int MultiLocation(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + decodeMultiLocation(input); + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int Xcm(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + decodeXcm(input); + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int EvmLog(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + input.seek(startPoint + offset); + + byte[] address = input.take(20); // 20 bytes address of contract + int topicSize = ScaleReader.readUintCompactSize(input); // u32 compact number of item in list + for (int i = 0; i < topicSize; i++) { + input.take(32); // 32 bytes of topic; + } + + int evmDataSize = ScaleReader.readUintCompactSize(input); // u32 compact number of bytes of evm data + byte[] evmData = input.take(evmDataSize); + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int H160(ByteSliceInput input, int offset) { + return 20; + } + + public static int U256(ByteSliceInput input, int offset) { + return 32; + } + + public static int ChainId(ByteSliceInput input, int offset) { + return 1; + } + + public static int DepositNonce(ByteSliceInput input, int offset) { + return 8; + } + + public static int ResourceId(ByteSliceInput input, int offset) { + return 32; + } + + public static int AssetId(ByteSliceInput input, int offset) { + return 4; + } + + public static int TAssetBalance(ByteSliceInput input, int offset) { + return 8; + } + + public static int H256(ByteSliceInput input, int offset) { + return 32; + } + + public static byte decodeExitError(ByteSliceInput input) { + byte error = input.takeByte(); + if ((error & 0xff) == 0x0d) { + int textSize = ScaleReader.readUintCompactSize(input); + input.take(textSize); + } + return error; + } + + public static int ExitReason(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + input.seek(startPoint + offset); + + byte exitReasonEnum = input.takeByte(); + if ((exitReasonEnum & 0xff) == 0x00) { + byte success = input.takeByte(); + } else if ((exitReasonEnum & 0xff) == 0x01) { + decodeExitError(input); + } else if ((exitReasonEnum & 0xff) == 0x02) { + byte revert = input.takeByte(); + } else if ((exitReasonEnum & 0xff) == 0x03) { + byte fatal = input.takeByte(); + if ((fatal & 0xff) == 0x02) { + decodeExitError(input); + } + + if ((fatal & 0xff) == 0x03) { + int textSize = ScaleReader.readUintCompactSize(input); + input.take(textSize); + } + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + + return endPoint - startPoint - offset; + } + + public static int RelayChainBlockNumber(ByteSliceInput input, int offset) { + return 4; + } + + public static int RoundIndex(ByteSliceInput input, int offset) { + return 4; + } + + public static int Percent(ByteSliceInput input, int offset) { + return 1; + } + + public static int Perbill(ByteSliceInput input, int offset) { + return 4; + } + + public static int RelayChainAccountId(ByteSliceInput input, int offset) { + return 32; + } + + public static int Option_AccountId(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int isHasAccountId = input.takeUByte(); + if (isHasAccountId > 0) { + input.take(accountIdSize); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int AuthorId(ByteSliceInput input, int offset) { + return 32; + } + + public static int CurrencyId(ByteSliceInput input, int offset) { + return 8; + } + + public static int CurrencyIdOf(ByteSliceInput input, int offset) { + return 8; + } + + public static int AmountOf(ByteSliceInput input, int offset) { + return 16; + } + + public static int ClassId(ByteSliceInput input, int offset) { + return 4; + } + + public static int ClassIdOf(ByteSliceInput input, int offset) { + return 4; + } + + public static int TokenId(ByteSliceInput input, int offset) { + return 8; + } + + public static int TokenIdOf(ByteSliceInput input, int offset) { + return 8; + } + + public static int u8(ByteSliceInput input, int offset) { + return 1; + } + + public static int DustHandlerType(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + int dustHandlerTypeEnum = input.takeUByte(); + if (dustHandlerTypeEnum > 0) { + input.take(accountIdSize); + } + + int endPoint = input.getOffset(); + input.seek(startPoint); + return endPoint - startPoint - offset; + } + + public static int NominatorAdded(ByteSliceInput input, int offset) { + int startPoint = input.getOffset(); + + input.seek(startPoint + offset); + + int size = 1; + byte nominatorAddedEnum = input.takeByte(); + if ((nominatorAddedEnum & 0xff) == 1) { + size += 16; + } + + input.seek(startPoint); + return size; + } + + public static int _u8_8_(ByteSliceInput input, int offset) { + return 8; + } +} \ No newline at end of file diff --git a/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/MoonbaseRelayEventDecoderScoreTest.java b/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/MoonbaseRelayEventDecoderScoreTest.java new file mode 100644 index 00000000..6753b239 --- /dev/null +++ b/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/MoonbaseRelayEventDecoderScoreTest.java @@ -0,0 +1,325 @@ +package foundation.icon.btp.eventDecoder; + +import com.iconloop.testsvc.Account; +import com.iconloop.testsvc.Score; +import com.iconloop.testsvc.ServiceManager; +import com.iconloop.testsvc.TestBase; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Disabled; + +import java.util.List; +import java.util.Map; + +import foundation.icon.btp.lib.utils.HexConverter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +// only compatible with monbase relay metadata, comment @Disabled to test event decoder of moonbase relay chain +@Disabled +class MoonbaseRelayEventDecoderScoreTest extends TestBase { + private static final ServiceManager sm = getServiceManager(); + private static final Account owner = sm.createAccount(); + private static Score eventDecoderScore; + + @BeforeAll + public static void setup() throws Exception { + eventDecoderScore = sm.deploy(owner, EventDecoderScore.class); + } + + @Test + void decodeSystemEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("18020000e703000000000000010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020001030b52e703000000000000010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702000204fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702000392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702000492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702000592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 6); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("e7030000000000000100")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("030b52e7030000000000000100")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(4).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(5).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + } + + @Test + void decodeIndicesEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c02030092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85618076e804fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020301618076e804fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020302618076e892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85618076e8")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0300")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("618076e8")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("0301")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("618076e892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0302")); + } + + @Test + void decodeBalanceEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("2002040092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4000000000000000000000028998f5804c04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040692341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702040792341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a400104fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 8); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0400")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("0401")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0402")); + + assertArrayEquals( (byte[]) eventRecords.get(7).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a4001")); + assertArrayEquals( (byte[]) eventRecords.get(7).get("eventIndex"), HexConverter.hexStringToByteArray("0407")); + } + + @Test + void decodeStakingEvent1() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("2002060049a8f36300000000000000000000017cddd07a4000000000000000000000028998f5804c04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020603000f412004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060404fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060692341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060792341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 8); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("49a8f36300000000000000000000017cddd07a4000000000000000000000028998f5804c")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0600")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("0601")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0602")); + + assertArrayEquals( (byte[]) eventRecords.get(7).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(7).get("eventIndex"), HexConverter.hexStringToByteArray("0607")); + } + + @Test + void decodeStakingEvent2() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("1002060892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060904fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060afc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4704fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702060b470d2e47fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4704fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 4); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0608")); + + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventData"), HexConverter.hexStringToByteArray("470d2e47fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventIndex"), HexConverter.hexStringToByteArray("060b")); + } + + @Test + void decodeOffencesEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("04020700696d2d6f6e6c696e653a6f66666c696e104208000000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("696d2d6f6e6c696e653a6f66666c696e1042080000")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0700")); + } + + @Test + void decodeSessionEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("04020800000f412004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("000f4120")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0800")); + } + + @Test + void decodeGrandpaEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c020a0008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020a0104fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020a0204fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0a00")); + + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("0a01")); + + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0a02")); + } + + @Test + void decodeImOnlineEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c020b0092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020b0104fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47020b020892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850b407ad0dd7c010b9f2cd2dd7c0104120b407ad0dd7c010b9f2cd2dd7c0104120492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8501017023494def5460aa2e93a0462e8792341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850b407ad0dd7c010b9f2cd2dd7c0104120b407ad0dd7c010b9f2cd2dd7c0104120492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8501017023494def5460aa2e93a0462e8704fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0b00")); + + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("0892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850b407ad0dd7c010b9f2cd2dd7c0104120b407ad0dd7c010b9f2cd2dd7c0104120492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8501017023494def5460aa2e93a0462e8792341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850b407ad0dd7c010b9f2cd2dd7c0104120b407ad0dd7c010b9f2cd2dd7c0104120492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8501017023494def5460aa2e93a0462e87")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0b02")); + } + + @Test + void decodeUtilityEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c0210000000a7e7030b5204fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702100104fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702100204fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("0000a7e7030b52")); + } + + @Test + void decodeIdentityEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("2802110092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500005d3f04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500005d3f04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500005d3f04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110600005d3f04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110792341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702110992341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 10); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1100")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1101")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1102")); + + assertArrayEquals( (byte[]) eventRecords.get(9).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(9).get("eventIndex"), HexConverter.hexStringToByteArray("1109")); + } + + @Test + void decodeRecoveryEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("1802120092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702120192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351404fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702120292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe46735147023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351404fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702120392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351404fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702120492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351404fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702120592341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 6); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1200")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1201")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe46735147023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1202")); + + assertArrayEquals( (byte[]) eventRecords.get(5).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(5).get("eventIndex"), HexConverter.hexStringToByteArray("1205")); + } + + @Test + void decodeVestingEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0802130092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a4004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702130192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 2); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a40")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1300")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1301")); + } + + @Test + void decodeSchedulerEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c021400001e51b90000a7e704fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47021401001e51b90000a7e704fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702140266674a0000000000013064656d6f63726163160000000004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("001e51b90000a7e7")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1400")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("001e51b90000a7e7")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1401")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("66674a0000000000013064656d6f637261631600000000")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1402")); + } + + @Test + void decodeSudoEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c021500010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702150192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47021502010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("0100")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1500")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1501")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("0100")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1502")); + } + + @Test + void decodeProxyEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("10021600010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702160192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351402223a04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702160292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702160392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe46735140192341e7e04fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 4); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("0100")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1600")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351402223a")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1601")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1602")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventData"), HexConverter.hexStringToByteArray("92341E7E5C46F8B32CD39F8E425D2916FBD0E5DFDB1818194EC02B66A52BFD857023494DEF5460AA2E93A0462E87ED1C5F00EA964D252C9744BC623FE46735140192341E7E")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventIndex"), HexConverter.hexStringToByteArray("1603")); + } + + @Test + void decodeMultisigEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("1002170092341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702170192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85d01400003f1200007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702170292341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85d01400003f1200007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85010004fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e4702170392341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85d01400003f1200007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8504fc4a936afd8097e18952e2df1c1f7d6eb9b6d5958ae22b7ea94ed555470d2e47"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 4); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("1700")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85d01400003f1200007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("1701")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventData"), HexConverter.hexStringToByteArray("92341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85d01400003f1200007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850100")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("1702")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventData"), HexConverter.hexStringToByteArray("92341E7E5C46F8B32CD39F8E425D2916FBD0E5DFDB1818194EC02B66A52BFD85D01400003F1200007023494DEF5460AA2E93A0462E87ED1C5F00EA964D252C9744BC623FE467351492341E7E5C46F8B32CD39F8E425D2916FBD0E5DFDB1818194EC02B66A52BFD85")); + assertArrayEquals( (byte[]) eventRecords.get(3).get("eventIndex"), HexConverter.hexStringToByteArray("1703")); + } + + @Test + void decodeCandidateBackedEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c00000000000000b814fb0a0000000002000000010000002c00e80300003eb52c89491cc4e107e32b6a66edb3698fc9960487b81e58229a1dccd3bc7a95d423dff1dbaef3ca4573bf8be0dc81037d32133bbff768f5b6409880954fda5a6bf5f9b09d615c0a98ae0a428fea8573d500d58163ac25503006696f93849668d597ac1a4699a507c68466cecaf38d1e672e0b515adc4033045f5c86aba994e931e36a4fedda6e70030d51162884c0a914f43ed30daa7af9f556511a1242a22b8af39b79b39848366a9e324761d25fa7ac8cac8070a84309b196b991689eba1db2b472dbd5add083626d18924a18ef121c9390f569ec7e4650837c6ad5190a8e13afcb4c45d42ddc022a4d1f65b10ebf2bbbcb9c4138c997ecfef323960bd482f5acd6bb417bda10d0667b2e51e50596eaa01120e0893c68de6751e3794365f213b7bd972fae17ffa90b39c7b9d0500fae0d8fae40e33c785599d21e84ad5c19e90391c0bc1d2da78eef8c03fb354a6fea7f25f06b0f38b29a060c4a71fc73849016ded81c00fc98709e979e91f4e46fd3d8a99bf090d6e49e775d29b00d92612389858a0775a5288bbb0c2c886cb7ad1d4468b5cc9c5c985df86b8b1d7601d665563eedd6990c046e6d627380268f2f2d9db24df09f7ce18c0a73ad60a621b346b579729a34fe4a7baecdf31f0466726f6e880164442b4ec52ac2017bff6ee86e398b63feac52b7b84edf557b239861fd75d9d700056e6d62730101c2f22459fd0ba639ff8d73c54c761a7ad25fdb9ff877926bf75aa27b6f1d0a5e80038d37528593f9f57b24fee32f9d9f02180cc5b35711246a0a43b51f01e486000000000000000000000100000000002039e80e00000000020000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0000")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("2c00")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("e80300003eb52c89491cc4e107e32b6a66edb3698fc9960487b81e58229a1dccd3bc7a95d423dff1dbaef3ca4573bf8be0dc81037d32133bbff768f5b6409880954fda5a6bf5f9b09d615c0a98ae0a428fea8573d500d58163ac25503006696f93849668d597ac1a4699a507c68466cecaf38d1e672e0b515adc4033045f5c86aba994e931e36a4fedda6e70030d51162884c0a914f43ed30daa7af9f556511a1242a22b8af39b79b39848366a9e324761d25fa7ac8cac8070a84309b196b991689eba1db2b472dbd5add083626d18924a18ef121c9390f569ec7e4650837c6ad5190a8e13afcb4c45d42ddc022a4d1f65b10ebf2bbbcb9c4138c997ecfef323960bd482f5acd6bb417bda10d0667b2e51e50596eaa01120e0893c68de6751e3794365f213b7bd972fae17ffa90b39c7b9d0500fae0d8fae40e33c785599d21e84ad5c19e90391c0bc1d2da78eef8c03fb354a6fea7f25f06b0f38b29a060c4a71fc73849016ded81c00fc98709e979e91f4e46fd3d8a99bf090d6e49e775d29b00d92612389858a0775a5288bbb0c2c886cb7ad1d4468b5cc9c5c985df86b8b1d7601d665563eedd6990c046e6d627380268f2f2d9db24df09f7ce18c0a73ad60a621b346b579729a34fe4a7baecdf31f0466726f6e880164442b4ec52ac2017bff6ee86e398b63feac52b7b84edf557b239861fd75d9d700056e6d62730101c2f22459fd0ba639ff8d73c54c761a7ad25fdb9ff877926bf75aa27b6f1d0a5e80038d37528593f9f57b24fee32f9d9f02180cc5b35711246a0a43b51f01e4860000000000000000")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0000")); + } + + @Test + void decodeCandidateIncludeEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0c0000000000000080ca83090000000002000000010000002c01e80300008acf9bde755569bb14b55095585998840ffc88476d4bffceb783727cb76c72e49efae44515105018d646455cfb29363abf38985fcc1411a3624ad4482fe7125db66cd25f32eaeaeb56cd88ecc7b10559305735fa7415d96d14e3550a467603fdb8a1172749841d254df4730b2e1143fe196a15cc4736ccc685a70433791ceba2150f6e6c9e0ab09704eaa10c51d641dba2c1df48a9e14e8d8e5ce89fdc5c6e294cc7a263300d5f96b46316b51954362a58880603e3874ac2cdcab13e85d77723e17becfce7ef08d8204a2f3695ff31d03e841e1ef388c804c087cad7209e5f8f429b4c2c28f986e9bfc4288fcfebc58e8731af31870fd91b3105672b960b04eb6f3f6ebe9ae1b7c2339c16ca32e30173bb5d652aec8f082718ffde64950e54933ca6ffd25ca9656d929898658e60a8847e211928d15866aefeadecffa1aa51afed0ff5a9d14d4f8939ef1a1e499bb541a8f309df45c5e003e2e241976c9cf1887020e2853d00dbb24b34c85a23142e8ba7c8f7ced9e2afc8d4803ad859f0d9fc173d59e8fe5f4994f489667cf982158beed75ed78909f8673a10a897f5336e0e7169b6b93a1d0c046e6d627380bcdad08691a234570b989a2bc146ef51a2eabb99e6ff6d655e4edc922543677d0466726f6e890c01d706212a3c44944e09f6dc8c8709496648486fc065ac00efc51dac9d3286c9d8609b4637601edc667abda9918477931ce1bbee6c4a2811176b5b27384b1ba7316bd9f7dfc18edd7eff28670b38040c98714740382df0f3b160824b15b6a57fbe3c68b68af976098229eaad1a2e6a034c96a6262b641534e2c65f073cc447f478117ef1086cd0ba1a269b3c5338545880fc27cb67c7cf71757575fa35029e0e18f91ae8aeed849f2dab9700cc93a0429a52a9a232f309dfb64b7b31802f534970032c17227ecbd80a1a93b56c213f018fc04b81c9b0ed885309fe8dd158622080deb646b9e0dba3b7f8a25357a1a24760047b57790e051fae37c3996ab554e9e3899197123784bb0a38e38dd6aa91a3e5acdc1389903d2eb02b70656bc5619163ef0982e9463c57d04be783094a92198a3650f44c8bf98b1ef513528ba182a209df08f739f3e3c20e7235bcbb47e6a8ee2466588f7eda545207213c49dce4d56648857b66567a060231f11647bcfc7cd541c1567ad9d911e4e6e1c97d6184199d1669036ca87d9e778ab87f9ef8e017f0a93c220b4170842bbf1363369e913290fe45c995031f0f63383052de6f0b4431435667a574899b1c0fb64e14fac46cba3569cdadfbf9dc7ba4945b79934854ec52d7d7813e089b69210ddcf38527b6731b197a018e75c27fd9586a5f35b4db1b2af437d3f79fece345be3fd078dfc5198ed1d879a32914780f24374207f3c48202efa11a1207b8b9f7c2612ae33575a213d18802eece677b06c19be069212416eaf60277c5ef46fea52a34c09d43d77a8a1040cc27ab139ffc22e14395c45f413bff754bd4274abe25b78e25b11334462f95872786c20bd13fe08a9e37eb49b1f5ace935fb27b19e018f167fa62a4666ece0c1bc3bc11f8c2c22f889bc208329627b06b23d54618b16767293efdc7f89e771f07454554fee0c44d36f10990532a71d113f6f62c3e0f03c0309dc58db75c60f40fb463aa39739947ae73e210b76529a35b9d3646f3a1e8347c91fc41db822fe25c67b77dcefe53846fd4422c9e8eca551ec26cfdbcbe081137ddc4cdb324292929f8a2526a12e61f1f0b11927a1752fe41a2200d7efca4255aa61eecb6bb2056e6d62730101344cff384177ba467f56e049c92ebd69451113801c9321f239478b452de4b03a112d36fdd278875e5bf2425aebdf3880d5aa5e19c162f9c15284f5fe2e6e9c860000000000000000000001000000000080b2e60e00000000020000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 3); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("0000")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventIndex"), HexConverter.hexStringToByteArray("2c01")); + assertArrayEquals( (byte[]) eventRecords.get(1).get("eventData"), HexConverter.hexStringToByteArray("e80300008acf9bde755569bb14b55095585998840ffc88476d4bffceb783727cb76c72e49efae44515105018d646455cfb29363abf38985fcc1411a3624ad4482fe7125db66cd25f32eaeaeb56cd88ecc7b10559305735fa7415d96d14e3550a467603fdb8a1172749841d254df4730b2e1143fe196a15cc4736ccc685a70433791ceba2150f6e6c9e0ab09704eaa10c51d641dba2c1df48a9e14e8d8e5ce89fdc5c6e294cc7a263300d5f96b46316b51954362a58880603e3874ac2cdcab13e85d77723e17becfce7ef08d8204a2f3695ff31d03e841e1ef388c804c087cad7209e5f8f429b4c2c28f986e9bfc4288fcfebc58e8731af31870fd91b3105672b960b04eb6f3f6ebe9ae1b7c2339c16ca32e30173bb5d652aec8f082718ffde64950e54933ca6ffd25ca9656d929898658e60a8847e211928d15866aefeadecffa1aa51afed0ff5a9d14d4f8939ef1a1e499bb541a8f309df45c5e003e2e241976c9cf1887020e2853d00dbb24b34c85a23142e8ba7c8f7ced9e2afc8d4803ad859f0d9fc173d59e8fe5f4994f489667cf982158beed75ed78909f8673a10a897f5336e0e7169b6b93a1d0c046e6d627380bcdad08691a234570b989a2bc146ef51a2eabb99e6ff6d655e4edc922543677d0466726f6e890c01d706212a3c44944e09f6dc8c8709496648486fc065ac00efc51dac9d3286c9d8609b4637601edc667abda9918477931ce1bbee6c4a2811176b5b27384b1ba7316bd9f7dfc18edd7eff28670b38040c98714740382df0f3b160824b15b6a57fbe3c68b68af976098229eaad1a2e6a034c96a6262b641534e2c65f073cc447f478117ef1086cd0ba1a269b3c5338545880fc27cb67c7cf71757575fa35029e0e18f91ae8aeed849f2dab9700cc93a0429a52a9a232f309dfb64b7b31802f534970032c17227ecbd80a1a93b56c213f018fc04b81c9b0ed885309fe8dd158622080deb646b9e0dba3b7f8a25357a1a24760047b57790e051fae37c3996ab554e9e3899197123784bb0a38e38dd6aa91a3e5acdc1389903d2eb02b70656bc5619163ef0982e9463c57d04be783094a92198a3650f44c8bf98b1ef513528ba182a209df08f739f3e3c20e7235bcbb47e6a8ee2466588f7eda545207213c49dce4d56648857b66567a060231f11647bcfc7cd541c1567ad9d911e4e6e1c97d6184199d1669036ca87d9e778ab87f9ef8e017f0a93c220b4170842bbf1363369e913290fe45c995031f0f63383052de6f0b4431435667a574899b1c0fb64e14fac46cba3569cdadfbf9dc7ba4945b79934854ec52d7d7813e089b69210ddcf38527b6731b197a018e75c27fd9586a5f35b4db1b2af437d3f79fece345be3fd078dfc5198ed1d879a32914780f24374207f3c48202efa11a1207b8b9f7c2612ae33575a213d18802eece677b06c19be069212416eaf60277c5ef46fea52a34c09d43d77a8a1040cc27ab139ffc22e14395c45f413bff754bd4274abe25b78e25b11334462f95872786c20bd13fe08a9e37eb49b1f5ace935fb27b19e018f167fa62a4666ece0c1bc3bc11f8c2c22f889bc208329627b06b23d54618b16767293efdc7f89e771f07454554fee0c44d36f10990532a71d113f6f62c3e0f03c0309dc58db75c60f40fb463aa39739947ae73e210b76529a35b9d3646f3a1e8347c91fc41db822fe25c67b77dcefe53846fd4422c9e8eca551ec26cfdbcbe081137ddc4cdb324292929f8a2526a12e61f1f0b11927a1752fe41a2200d7efca4255aa61eecb6bb2056e6d62730101344cff384177ba467f56e049c92ebd69451113801c9321f239478b452de4b03a112d36fdd278875e5bf2425aebdf3880d5aa5e19c162f9c15284f5fe2e6e9c860000000000000000")); + assertArrayEquals( (byte[]) eventRecords.get(2).get("eventIndex"), HexConverter.hexStringToByteArray("0000")); + } + + @Test + void decodeCandidateTimedOutEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0400010000002c0228080000ab6d3509b954bcc6c721339c26af7dcb201b17291b9079e1078c42b901f38919dcb8363437d401fb20903d96215f75f4b8bde76a83e879e3ff901d23b9b3ed6bc6389b1790d8839e4725f31be5852baa40b4234ffe51fa99a76186d0b28b5f47205f43667c6868fa882d17363d6b954c82453e49620c87db08ff94564f32d72f1e362ba0dbba661b69cfe13bf82d21c89fe33de6008eb647b966f1e9907322eea665a3bd8ff3b7b5d22364656f8e24e3c1a9b6d5a1c2d5eabe1de3a6a349d86701040104ec1f7c38adae4ba70d42bec9ae5d53a57152c7d9e049cc2a9425528856ad8369c7c6abbe867efe0d092b8aa8a226672ba82e4f2a581a274b227292c83cd446c9a1bd71d0ce47495a92d001a3047b17ea1afafade18e5892db4ebce47ceb81aa851dbd372cb5558c77de6b43aaead5766c9d154bb6d73a6d4756b1e6ce9022d027c40d6b35e7a3c414d3aa09b72ea723b032ac54eb1f77f099df0fad1728112c50800e17efcd4913471beddf3c2911fca03bba0a10bcdd0366b0215b8fc1086dd0287d7049d1f977b5fa6834a89b5f47a5e3a934c2e843336928860e4fa21dd1f6704080661757261201cd61e080000000005617572610101608ec121e970296dd4f88b5b63a5b319a802746fdd8b462562e48feaa810150f9b56dd4d4fb179680221c18046837d509e55e5c7616dc26f5ab2c11572ee93890900000000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("2c02")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("28080000ab6d3509b954bcc6c721339c26af7dcb201b17291b9079e1078c42b901f38919dcb8363437d401fb20903d96215f75f4b8bde76a83e879e3ff901d23b9b3ed6bc6389b1790d8839e4725f31be5852baa40b4234ffe51fa99a76186d0b28b5f47205f43667c6868fa882d17363d6b954c82453e49620c87db08ff94564f32d72f1e362ba0dbba661b69cfe13bf82d21c89fe33de6008eb647b966f1e9907322eea665a3bd8ff3b7b5d22364656f8e24e3c1a9b6d5a1c2d5eabe1de3a6a349d86701040104ec1f7c38adae4ba70d42bec9ae5d53a57152c7d9e049cc2a9425528856ad8369c7c6abbe867efe0d092b8aa8a226672ba82e4f2a581a274b227292c83cd446c9a1bd71d0ce47495a92d001a3047b17ea1afafade18e5892db4ebce47ceb81aa851dbd372cb5558c77de6b43aaead5766c9d154bb6d73a6d4756b1e6ce9022d027c40d6b35e7a3c414d3aa09b72ea723b032ac54eb1f77f099df0fad1728112c50800e17efcd4913471beddf3c2911fca03bba0a10bcdd0366b0215b8fc1086dd0287d7049d1f977b5fa6834a89b5f47a5e3a934c2e843336928860e4fa21dd1f6704080661757261201cd61e080000000005617572610101608ec121e970296dd4f88b5b63a5b319a802746fdd8b462562e48feaa810150f9b56dd4d4fb179680221c18046837d509e55e5c7616dc26f5ab2c11572ee938909000000")); + } + + @Test + void decodeUmpExecutedUpwardEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0400010000003202f57e77ed42bc08b71992f2731635636926ecc1a878c73dc1d1904a217fb310b600005ed0b20000000000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("3202")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("f57e77ed42bc08b71992f2731635636926ecc1a878c73dc1d1904a217fb310b600005ed0b200000000")); + } + + @Test + void decodeCrowdloanMemoUpdatedEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("04000200000040083857b16e5fa4d5c4ea4603ece90a1e2e8a3e603fb100ab73e0693109fae7a3132708000014576f726c6400"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("4008")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("3857b16e5fa4d5c4ea4603ece90a1e2e8a3e603fb100ab73e0693109fae7a3132708000014576f726c64")); + } + + @Test + void decodeXcmAttemptedEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("04000200000063000000ca9a3b0000000000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("6300")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("0000ca9a3b00000000")); + } + + @Test + void decodeXcmSentEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("040263010000000100a10f04060202286bee88010319755aae83cded7d2966ce2958195c5d02c6b8b625436c27cb0aae50f168c10800"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("6301")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("0000000100a10f04060202286bee88010319755aae83cded7d2966ce2958195c5d02c6b8b625436c27cb0aae50f168c108")); + } + + @Test + void decodeXcmSupportedVersionChangedEvent() { + byte[] encodedEventList = HexConverter.hexStringToByteArray("0402630d000100a10f0000000000"); + List> eventRecords = (List>) eventDecoderScore.call("decodeEvent", encodedEventList); + assertEquals(eventRecords.size(), 1); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventIndex"), HexConverter.hexStringToByteArray("630d")); + assertArrayEquals( (byte[]) eventRecords.get(0).get("eventData"), HexConverter.hexStringToByteArray("000100a10f00000000")); + } +} diff --git a/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/SizeDecoderTest.java b/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/SizeDecoderTest.java new file mode 100644 index 00000000..da65bc93 --- /dev/null +++ b/javascore/bmv/eventDecoder/src/test/java/foundation/icon/btp/eventDecoder/SizeDecoderTest.java @@ -0,0 +1,455 @@ +package foundation.icon.btp.eventDecoder; + +import org.junit.jupiter.api.Test; + +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.EventDecoder.*; +import foundation.icon.btp.lib.utils.ByteSliceInput; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class SizeDecoderTest { + @Test + void decodeDispatchError() { + byte[] encoded = HexConverter.hexStringToByteArray("0000030b52001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.DispatchError(input, 2); + assertEquals(decodedSize, 3); + } + + @Test + void decodeDispatchResult() { + byte[] encoded = HexConverter.hexStringToByteArray("00000001000100"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.DispatchResult(input, 3); + assertEquals(decodedSize, 2); + } + + @Test + void decodeOpaqueTimeSlot() { + byte[] encoded = HexConverter.hexStringToByteArray("febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e4240b0c03637b570e414d"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.OpaqueTimeSlot(input, 32); + assertEquals(decodedSize, 10); + } + + @Test + void decodeAuthorityList() { + byte[] encoded = HexConverter.hexStringToByteArray("febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e4080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.AuthorityList(input, 32); + assertEquals(decodedSize, 81); + } + + @Test + void decodeVec_IdentificationTuple() { + byte[] encoded = HexConverter.hexStringToByteArray("febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e40892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd850b407ad0dd7c010b9f2cd2dd7c010492341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8501017023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe46735140b4c80f59889020b9f2cd2dd7c0104febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e40baf80f5988902"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Vec_IdentificationTuple(input, 32); + assertEquals(decodedSize, 168); + } + + @Test + void decodeVec_AccountId() { + byte[] encoded = HexConverter.hexStringToByteArray("febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e40892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd857023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Vec_AccountId(input, 32); + assertEquals(decodedSize, 65); + } + + @Test + void decodeVec_AccountId_Balance() { + byte[] encoded = HexConverter.hexStringToByteArray("00000000000000000000017cddd07a400892341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd8500000000000000000000017cddd07a407023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe467351400000000000000000000017cddd07a400000"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Vec_AccountId_Balance(input, 16); + assertEquals(decodedSize, 97); + } + + @Test + void decodeOption_Bytes() { + byte[] encoded = HexConverter.hexStringToByteArray("00000000000000000000017cddd07a40014092341e7e5c46f8b32cd39f8e425d2916001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Option_Bytes(input, 16); + assertEquals(decodedSize, 18); + } + + @Test + void decodeHeadData() { + byte[] encoded = HexConverter.hexStringToByteArray("00000000000000000000017cddd07a404092341e7e5c46f8b32cd39f8e425d2916001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.HeadData(input, 16); + assertEquals(decodedSize, 17); + } + + @Test + void decodeJunction() { + byte[] encoded = HexConverter.hexStringToByteArray("00"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("019e9f0200"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 5); + + encoded = HexConverter.hexStringToByteArray("02007023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 34); + + encoded = HexConverter.hexStringToByteArray("0201240b0c03637b570e414d7023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 44); + + encoded = HexConverter.hexStringToByteArray("02027023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 34); + + encoded = HexConverter.hexStringToByteArray("02037023494def5460aa2e93a0462e87ed1c5f00ea964d252c9744bc623fe4673514"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 34); + + encoded = HexConverter.hexStringToByteArray("0301240b0c03637b570e414dfef353ec"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 16); + + encoded = HexConverter.hexStringToByteArray("04030b0c03637b570e414d0b0c03637b570e414d0b16"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 22); + + encoded = HexConverter.hexStringToByteArray("0563"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 2); + + encoded = HexConverter.hexStringToByteArray("060b5b30a6c6e803"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 8); + + encoded = HexConverter.hexStringToByteArray("07240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("090000"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 3); + + encoded = HexConverter.hexStringToByteArray("0901240b0c03637b570e414d00"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 13); + + encoded = HexConverter.hexStringToByteArray("0902de594b000162240100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("090302de594b001ecf7a66"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("090303de594b001ecf7a66"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("090304de594b001ecf7a66"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeJunction(input); + assertEquals(input.getOffset(), 11); + } + + @Test + void decodeAssetInstance() { + byte[] encoded = HexConverter.hexStringToByteArray("00"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("0163"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 2); + + encoded = HexConverter.hexStringToByteArray("028d01"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 3); + + encoded = HexConverter.hexStringToByteArray("051702f94b4c4d139c5f05"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("09febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e4"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 33); + + encoded = HexConverter.hexStringToByteArray("0a240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeAssetInstance(input); + assertEquals(input.getOffset(), 11); + } + + @Test + void decodeMultiLocation() { + byte[] encoded = HexConverter.hexStringToByteArray("020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiLocation(input); + assertEquals(input.getOffset(), 33); + } + + @Test + void decodeXcmOrder() { + byte[] encoded = HexConverter.hexStringToByteArray("00"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("01040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 80); + + encoded = HexConverter.hexStringToByteArray("02040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec00"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 81); + + encoded = HexConverter.hexStringToByteArray("04040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec00"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 81); + + encoded = HexConverter.hexStringToByteArray("05040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec00"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 81); + + encoded = HexConverter.hexStringToByteArray("03040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 93); + + encoded = HexConverter.hexStringToByteArray("070b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d50d6120000000000cbd41200000000000100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmOrder(input); + assertEquals(input.getOffset(), 64); + } + + @Test + void decodeMultiAsset() { + byte[] encoded = HexConverter.hexStringToByteArray("00"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("03"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("04240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 11); + + encoded = HexConverter.hexStringToByteArray("06020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 34); + + encoded = HexConverter.hexStringToByteArray("08240b0c03637b570e414d0303519c49"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 16); + + encoded = HexConverter.hexStringToByteArray("0900050323329949"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 8); + + encoded = HexConverter.hexStringToByteArray("0a020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec16d5881d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 38); + + encoded = HexConverter.hexStringToByteArray("0b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeMultiAsset(input); + assertEquals(input.getOffset(), 45); + } + + @Test + void deocdeXcmResponse() { + byte[] encoded = HexConverter.hexStringToByteArray("00040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmResponse(input); + assertEquals(input.getOffset(), 47); + } + + @Test + void decodeXcm() { + byte[] encoded = HexConverter.hexStringToByteArray("00040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d04070b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d50d6120000000000cbd41200000000000100"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 112); + + encoded = HexConverter.hexStringToByteArray("01040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d04070b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d50d6120000000000cbd41200000000000100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 112); + + encoded = HexConverter.hexStringToByteArray("02040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d04070b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d50d6120000000000cbd41200000000000100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 112); + + encoded = HexConverter.hexStringToByteArray("0303abae734900040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 53); + + encoded = HexConverter.hexStringToByteArray("04040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 80); + + encoded = HexConverter.hexStringToByteArray("05040b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec04070b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec0a240b0c03637b570e414d50d6120000000000cbd41200000000000100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 145); + + encoded = HexConverter.hexStringToByteArray("060185b95a9830120000240b0c03637b570e414d"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 20); + + encoded = HexConverter.hexStringToByteArray("078a72d71796a0790076340100"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 13); + + encoded = HexConverter.hexStringToByteArray("088a72d717"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 5); + + encoded = HexConverter.hexStringToByteArray("098a72d7178a72d7178a72d717"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcm(input); + assertEquals(input.getOffset(), 13); + } + + @Test + void decodeXcmError() { + byte[] encoded = HexConverter.hexStringToByteArray("00"); + ByteSliceInput input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmError(input); + assertEquals(input.getOffset(), 1); + + encoded = HexConverter.hexStringToByteArray("0b020301240b0c03637b570e414dfef353ec0301240b0c03637b570e414dfef353ec098a72d7178a72d7178a72d717"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmError(input); + assertEquals(input.getOffset(), 47); + + encoded = HexConverter.hexStringToByteArray("11f4e0010000000000"); + input = new ByteSliceInput(encoded); + SizeDecoder.decodeXcmError(input); + assertEquals(input.getOffset(), 9); + } + + @Test + void Outcome() { + byte[] encoded = HexConverter.hexStringToByteArray("000000000012d677007806780a7c8d03000000"); + ByteSliceInput input = new ByteSliceInput(encoded); + int size = SizeDecoder.Outcome(input, 8); + assertEquals(size, 9); + + encoded = HexConverter.hexStringToByteArray("000000000012d677017806780a7c8d030011f4e0010000000000"); + input = new ByteSliceInput(encoded); + size = SizeDecoder.Outcome(input, 8); + assertEquals(size, 18); + + encoded = HexConverter.hexStringToByteArray("000000000012d6770211f4e0010000000000"); + input = new ByteSliceInput(encoded); + size = SizeDecoder.Outcome(input, 8); + assertEquals(size, 10); + } + + @Test + void EvmLog() { + byte[] encoded = HexConverter.hexStringToByteArray("0000006392341e7e5c46f8b32cd39f8e425d2916fbd0ef1108febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e4febd97fdbc2d968df381aebe0a9211f380eaf5b5370e330d4a41f8b81000f8e4240b0c03637b570e414d001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.EvmLog(input, 4); + assertEquals(decodedSize, 95); + } + + @Test + void Bytes() { + byte[] encoded = HexConverter.hexStringToByteArray("00000000000000000000017cddd07a404092341e7e5c46f8b32cd39f8e425d2916001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Bytes(input, 16); + assertEquals(decodedSize, 17); + } + + @Test + void ExitReason() { + byte[] encoded = HexConverter.hexStringToByteArray("000000630000001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.ExitReason(input, 4); + assertEquals(decodedSize, 2); + + encoded = HexConverter.hexStringToByteArray("00000063010d304920646f6e2774206b6e6f77001e51b9"); + input = new ByteSliceInput(encoded); + decodedSize = SizeDecoder.ExitReason(input, 4); + assertEquals(decodedSize, 15); + + encoded = HexConverter.hexStringToByteArray("000000630200001e51b9"); + input = new ByteSliceInput(encoded); + decodedSize = SizeDecoder.ExitReason(input, 4); + assertEquals(decodedSize, 2); + + encoded = HexConverter.hexStringToByteArray("0000006303020d304920646f6e2774206b6e6f77001e51b9"); + input = new ByteSliceInput(encoded); + decodedSize = SizeDecoder.ExitReason(input, 4); + assertEquals(decodedSize, 16); + + encoded = HexConverter.hexStringToByteArray("000000630303304920646f6e2774206b6e6f77001e51b9"); + input = new ByteSliceInput(encoded); + decodedSize = SizeDecoder.ExitReason(input, 4); + assertEquals(decodedSize, 15); + } + + @Test + void Option_AccountId() { + byte[] encoded = HexConverter.hexStringToByteArray("000000630192341e7e5c46f8b32cd39f8e425d2916fbd0e5dfdb1818194ec02b66a52bfd85001e51b9"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.Option_AccountId(input, 4); + assertEquals(decodedSize, 33); + } + + @Test + void NominatorAdded() { + byte[] encoded = HexConverter.hexStringToByteArray("01230800"); + ByteSliceInput input = new ByteSliceInput(encoded); + int decodedSize = SizeDecoder.NominatorAdded(input, 3); + assertEquals(decodedSize, 1); + + encoded = HexConverter.hexStringToByteArray("01230801630192341e7e5c46f8b32cd39f8e425d"); + input = new ByteSliceInput(encoded); + decodedSize = SizeDecoder.NominatorAdded(input, 3); + assertEquals(decodedSize, 17); + } +} diff --git a/javascore/bmv/gradle.properties b/javascore/bmv/gradle.properties new file mode 100644 index 00000000..413f4908 --- /dev/null +++ b/javascore/bmv/gradle.properties @@ -0,0 +1 @@ +VERSION=0.1.0 diff --git a/javascore/bmv/gradle/wrapper/gradle-wrapper.jar b/javascore/bmv/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..62d4c053 Binary files /dev/null and b/javascore/bmv/gradle/wrapper/gradle-wrapper.jar differ diff --git a/javascore/bmv/gradle/wrapper/gradle-wrapper.properties b/javascore/bmv/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a4f0001d --- /dev/null +++ b/javascore/bmv/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/javascore/bmv/gradlew b/javascore/bmv/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/javascore/bmv/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/javascore/bmv/gradlew.bat b/javascore/bmv/gradlew.bat new file mode 100644 index 00000000..a9f778a7 --- /dev/null +++ b/javascore/bmv/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/javascore/bmv/helper/envSample b/javascore/bmv/helper/envSample new file mode 100644 index 00000000..cf9d8506 --- /dev/null +++ b/javascore/bmv/helper/envSample @@ -0,0 +1,19 @@ +BMC_ADDRESS=hxb6b5791be0b5ef67063b3c10b840fb81514db2fd +DST_NET_ADDRESS=0x501.pra +RELAY_ENDPOINT=wss://wss-relay.testnet.moonbeam.network +PARA_ENDPOINT=wss://moonbeam-alpha.api.onfinality.io/public-ws + +RELAY_CHAIN_OFFSET=914122 +PARA_CHAIN_OFFSET=427000 +MTA_ROOT_SIZE=16 +MTA_CACHE_SIZE=16 +MTA_IS_ALLOW_WITNESS=true + +ICON_ENDPOINT=http://localhost:9082/api/v3 +ICON_KEYSTORE_PATH=/Users/leclevietnam/mwork/btp/javascore/bmv/godWallet.json +ICON_KEYSTORE_PASSWORD=gochain +ICON_NID=3 + + +SOVEREIGN_ENDPOINT=wss://beresheet1.edgewa.re +SOVEREIGN_CHAIN_OFFSET=609000 \ No newline at end of file diff --git a/javascore/bmv/helper/package.json b/javascore/bmv/helper/package.json new file mode 100644 index 00000000..1ce74618 --- /dev/null +++ b/javascore/bmv/helper/package.json @@ -0,0 +1,20 @@ +{ + "name": "helper", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@polkadot/api": "^6.4.1", + "dotenv": "^10.0.0", + "icon-sdk-js": "^1.1.0", + "rlp": "^2.2.6", + "ts-node": "^10.1.0", + "typescript": "^4.3.5", + "urlsafe-base64": "^1.0.0" + }, + "scripts": { + "getMetaData": "yarn ts-node src/getMetaData.ts", + "getBMVInitializeParams": "yarn ts-node src/getBMVInitializeParams.ts", + "deployParaBMV": "yarn ts-node src/deployParaBMV.ts", + "deploySovereignBMV": "yarn ts-node src/deploySovereignBMV.ts" + } +} diff --git a/javascore/bmv/helper/src/deployParaBMV.ts b/javascore/bmv/helper/src/deployParaBMV.ts new file mode 100644 index 00000000..3f4c13a4 --- /dev/null +++ b/javascore/bmv/helper/src/deployParaBMV.ts @@ -0,0 +1,196 @@ +import { ApiPromise, WsProvider } from "@polkadot/api"; +import { xxhashAsHex } from "@polkadot/util-crypto"; +import * as fs from "fs"; +import * as RLP from "rlp"; +import * as URLSafeBase64 from "urlsafe-base64"; +import path from "path"; +import IconService from "icon-sdk-js"; +import { readFile, decimalToHex, convertLEtoBE, findEventIndex, deployScore, buildEventDecoder, buildParaBMV } from './util'; + +require('dotenv').config() + +async function main() { + // const wssEndpoint = "wss://rpc.polkadot.io"; // polkadot relay chain + // const wssEndpoint = "wss://kusama-rpc.polkadot.io"; // kusama relay chain + // const wssEndpoint = "wss://wss-relay.testnet.moonbeam.network"; // moonbase alpha relay chain + + // const wssEndpoint = "wss://wss.moonriver.moonbeam.network"; // moonriver parachain + // const wssEndpoint = "wss://wss.testnet.moonbeam.network"; // moonbase alpha parachain + // const wssEndpoint = "wss://icon-btp.ecl.vn:34008/"; // lecle moonbase parachain + + const bmcAddress = process.env.BMC_ADDRESS; + const netAddress = process.env.DST_NET_ADDRESS; + const relayWssEndpoint = process.env.RELAY_ENDPOINT; // wss endpoint of relay chain + const paraWssEndpoint = process.env.PARA_ENDPOINT; // wss endpoint of para chain + + const relayChainOffset = process.env.RELAY_CHAIN_OFFSET; // offset of relay chain + const paraChainOffset = process.env.PARA_CHAIN_OFFSET; // offset of para chain + const mtaRootSize = process.env.MTA_ROOT_SIZE; + const mtaCacheSize = process.env.MTA_CACHE_SIZE; + const mtaIsAllowNewerWitness = process.env.MTA_IS_ALLOW_WITNESS; + + const iconNodeUrl = process.env.ICON_ENDPOINT; + const iconNid = process.env.ICON_NID; + const iconKeyStoreFilePath = process.env.ICON_KEYSTORE_PATH; + const iconKeyStorePassword = process.env.ICON_KEYSTORE_PASSWORD; + + const relayWsProvider = new WsProvider(relayWssEndpoint); + const paraWsProvider = new WsProvider(paraWssEndpoint); + const relayApi = await ApiPromise.create({ + provider: relayWsProvider, + types: { + GrandpaAuthorities: { + version: "u8", + authorityList: "AuthorityList", + }, + }, + }); + + const iconProvider = new IconService.HttpProvider(iconNodeUrl); + const iconService = new IconService(iconProvider); + + const wallet = IconService.IconWallet.loadKeystore((await readFile(iconKeyStoreFilePath)).toString(), iconKeyStorePassword, false); + + const paraApi = await ApiPromise.create({ + provider: paraWsProvider, + types: { + RoundIndex: "u32", + }, + }); + + // get relay genesis hash + console.log(" Relay genesis hash: ", relayApi.genesisHash.toHex()); + // get relay chain name + const relayChainName = await relayApi.rpc.system.chain(); + console.log(" Relay chain name: ", JSON.stringify(relayChainName)); + + // get relay genesis hash + console.log(" Para genesis hash: ", paraApi.genesisHash.toHex()); + // get relay chain name + const paraChainName = await paraApi.rpc.system.chain(); + console.log(" Para chain name: ", JSON.stringify(paraChainName)); + + let accountIdSize = 32; + if (paraChainName.startsWith("moon") || paraChainName.startsWith("Moon")) { + accountIdSize = 20; + } + + /* + * get meta data of relay chain + */ + console.log(" Get metadata of relay chain..."); + const relayMetaData = await relayApi.rpc.state.getMetadata(); + fs.writeFileSync("./relayMetaData.json", JSON.stringify(relayMetaData, null, 2)); + + const newAuthoritiesEventIndex = findEventIndex(relayMetaData, "Grandpa", "NewAuthorities"); + const candidateIncludedEventIndex = findEventIndex(relayMetaData, "ParaInclusion", "CandidateIncluded"); + + console.log(" Build event decoder for relay chain..."); + await buildEventDecoder(path.resolve("./relayMetaData.json")); + + console.log(" Deploy relay chain event decoder..."); + const relayChainEventDecoder = await deployScore(iconService, wallet, iconNid, __dirname + '/../../eventDecoder/build/libs/eventDecoder-optimized.jar', {}); + + /* + * get meta data of para chain + */ + console.log(" Get metadata of para chain..."); + const paraMetaData = await paraApi.rpc.state.getMetadata(); + fs.writeFileSync("./paraMetaData.json", JSON.stringify(paraMetaData, null, 2)); + + const evmEventIndex = findEventIndex(paraMetaData, "EVM", "Log"); + + console.log(" Build event decoder for para chain..."); + await buildEventDecoder(path.resolve("./paraMetaData.json"), accountIdSize); + + console.log(" Deploy para chain event decoder..."); + const paraChainEventDecoder = await deployScore(iconService, wallet, iconNid, __dirname + '/../../eventDecoder/build/libs/eventDecoder-optimized.jar', {}); + + + console.log(" Build para chain BMV..."); + await buildParaBMV(); + + const relayLastBlockHash = await relayApi.rpc.chain.getBlockHash( + relayChainOffset + ); + + const paraLastBlockHash = await paraApi.rpc.chain.getBlockHash( + paraChainOffset + ); + + const grandpaAuthoritiesEncoded = await relayApi.rpc.state.getStorage( + ":grandpa_authorities", + relayLastBlockHash.toHex() + ); + + const grandpaAuthorities = await relayApi.createType( + // @ts-ignore + "GrandpaAuthorities", + String(grandpaAuthoritiesEncoded) + ); + // @ts-ignore + const validatorList = grandpaAuthorities.authorityList.map((item) => + item[0].toHex() + ); + const encoded = RLP.encode(validatorList); + const validatorListBase64Encoded = URLSafeBase64.encode(encoded); + + const grandpaPrefixHash = xxhashAsHex("Grandpa", 128); + const curentSetIdKeyHash = xxhashAsHex("CurrentSetId", 128); + const grandpaCurrentSetIdStorageKey = + grandpaPrefixHash + curentSetIdKeyHash.replace("0x", ""); + const grandPaCurrentSetIdEndcoded = await relayApi.rpc.state.getStorage( + grandpaCurrentSetIdStorageKey, + relayLastBlockHash.toHex() + ); + const grandPaCurrentSetId = await relayApi.createType( + "SetId", + String(grandPaCurrentSetIdEndcoded) + ); + + const parachainInfoPrefixHash = xxhashAsHex("ParachainInfo", 128); + const parachainIdKeyHash = xxhashAsHex("ParachainId", 128); + const parachainIdStorageKey = + parachainInfoPrefixHash + parachainIdKeyHash.replace("0x", ""); + const paraChainIdEncoded = await paraApi.rpc.state.getStorage( + parachainIdStorageKey, + paraLastBlockHash.toHex() + ); + const paraChainId = await paraApi.createType( + "u32", + String(paraChainIdEncoded) + ); + + const paraBMVDeployParams = { + bmc: bmcAddress, + net: netAddress, + mtaRootSize: decimalToHex(mtaRootSize), + mtaCacheSize: decimalToHex(mtaCacheSize), + mtaIsAllowNewerWitness: mtaIsAllowNewerWitness ? "0x1" : "0x0", + relayEventDecoderAddress: relayChainEventDecoder, + paraEventDecoderAddress: paraChainEventDecoder, + relayMtaOffset: decimalToHex(relayChainOffset), + paraMtaOffset: decimalToHex(paraChainOffset), + relayLastBlockHash: relayLastBlockHash.toHex(), + paraLastBlockHash: paraLastBlockHash.toHex(), + relayCurrentSetId: convertLEtoBE(grandPaCurrentSetId.toHex().replace("0x", "")), + paraChainId: convertLEtoBE(paraChainId.toHex().replace("0x", "")), + encodedValidators: validatorListBase64Encoded, + evmEventIndex: "0x" + decimalToHex(evmEventIndex[0]).replace("0x", "") + decimalToHex(evmEventIndex[1]).replace("0x", ""), + newAuthoritiesEventIndex: "0x" + decimalToHex(newAuthoritiesEventIndex[0]).replace("0x", "") + decimalToHex(newAuthoritiesEventIndex[1]).replace("0x", ""), + candidateIncludedEventIndex: "0x" + decimalToHex(candidateIncludedEventIndex[0]).replace("0x", "") + decimalToHex(candidateIncludedEventIndex[1]).replace("0x", ""), + } + + console.log(" Deploy para chain BMV..."); + const paraBMV = await deployScore(iconService, wallet, iconNid, __dirname + '/../../parachain/build/libs/parachain-BMV-optimized.jar', paraBMVDeployParams); + + console.log("\n --------------------- DONE ---------------------- "); + console.log("- Relay event decoder score address: ", relayChainEventDecoder); + console.log("- Para event decoder score address: ", paraChainEventDecoder); + console.log("- Para chain bmv score address: ", paraBMV); + + await relayApi.disconnect(); + await paraApi.disconnect(); +} + +main().catch(console.error); diff --git a/javascore/bmv/helper/src/deploySovereignBMV.ts b/javascore/bmv/helper/src/deploySovereignBMV.ts new file mode 100644 index 00000000..a0ab7b89 --- /dev/null +++ b/javascore/bmv/helper/src/deploySovereignBMV.ts @@ -0,0 +1,128 @@ +import { ApiPromise, WsProvider } from "@polkadot/api"; +import { xxhashAsHex } from "@polkadot/util-crypto"; +import * as fs from "fs"; +import * as RLP from "rlp"; +import * as URLSafeBase64 from "urlsafe-base64"; +import path from "path"; +import IconService from "icon-sdk-js"; +import { readFile, decimalToHex, convertLEtoBE, findEventIndex, deployScore, buildEventDecoder, buildSovereignBMV } from './util'; + +require('dotenv').config() + +async function main() { + const bmcAddress = process.env.BMC_ADDRESS; + const netAddress = process.env.DST_NET_ADDRESS; + const sovereignWssEndpoint = process.env.SOVEREIGN_ENDPOINT; + const sovereignChainOffset = process.env.SOVEREIGN_CHAIN_OFFSET; + const mtaRootSize = process.env.MTA_ROOT_SIZE; + const mtaCacheSize = process.env.MTA_CACHE_SIZE; + const mtaIsAllowNewerWitness = process.env.MTA_IS_ALLOW_WITNESS; + + const iconNodeUrl = process.env.ICON_ENDPOINT; + const iconNid = process.env.ICON_NID; + const iconKeyStoreFilePath = process.env.ICON_KEYSTORE_PATH; + const iconKeyStorePassword = process.env.ICON_KEYSTORE_PASSWORD; + + const sovereignWsProvider = new WsProvider(sovereignWssEndpoint); + const sovereignApi = await ApiPromise.create({ + provider: sovereignWsProvider, + types: { + GrandpaAuthorities: { + version: "u8", + authorityList: "AuthorityList", + }, + CurrencyIdOf: "Bytes" + }, + }); + + const iconProvider = new IconService.HttpProvider(iconNodeUrl); + const iconService = new IconService(iconProvider); + + const wallet = IconService.IconWallet.loadKeystore((await readFile(iconKeyStoreFilePath)).toString(), iconKeyStorePassword, false); + + // get relay genesis hash + console.log(" Sovereign genesis hash: ", sovereignApi.genesisHash.toHex()); + // get relay chain name + const relayChainName = await sovereignApi.rpc.system.chain(); + console.log(" Sovereign chain name: ", JSON.stringify(relayChainName)); + + /* + * get meta data of relay chain + */ + console.log(" Get metadata of Sovereign chain..."); + const sovereignMetaData = await sovereignApi.rpc.state.getMetadata(); + fs.writeFileSync("./sovereignMetaData.json", JSON.stringify(sovereignMetaData, null, 2)); + + const newAuthoritiesEventIndex = findEventIndex(sovereignMetaData, "Grandpa", "NewAuthorities"); + const evmEventIndex = findEventIndex(sovereignMetaData, "EVM", "Log"); + + console.log(" Build event decoder for sovereign chain..."); + await buildEventDecoder(path.resolve("./relayMetaData.json")); + + console.log(" Deploy relay chain event decoder..."); + const sovereignChainEventDecoder = await deployScore(iconService, wallet, iconNid, __dirname + '/../../eventDecoder/build/libs/eventDecoder-optimized.jar', {}); + + console.log(" Build sovereign chain BMV..."); + await buildSovereignBMV(); + + const sovereignLastBlockHash = await sovereignApi.rpc.chain.getBlockHash( + sovereignChainOffset + ); + + const grandpaAuthoritiesEncoded = await sovereignApi.rpc.state.getStorage( + ":grandpa_authorities", + sovereignLastBlockHash.toHex() + ); + + const grandpaAuthorities = await sovereignApi.createType( + // @ts-ignore + "GrandpaAuthorities", + String(grandpaAuthoritiesEncoded) + ); + // @ts-ignore + const validatorList = grandpaAuthorities.authorityList.map((item) => + item[0].toHex() + ); + const encoded = RLP.encode(validatorList); + const validatorListBase64Encoded = URLSafeBase64.encode(encoded); + + const grandpaPrefixHash = xxhashAsHex("GrandpaFinality", 128); + const curentSetIdKeyHash = xxhashAsHex("CurrentSetId", 128); + const grandpaCurrentSetIdStorageKey = + grandpaPrefixHash + curentSetIdKeyHash.replace("0x", ""); + const grandPaCurrentSetIdEndcoded = await sovereignApi.rpc.state.getStorage( + grandpaCurrentSetIdStorageKey, + sovereignLastBlockHash.toHex() + ); + + const grandPaCurrentSetId = await sovereignApi.createType( + "SetId", + String(grandPaCurrentSetIdEndcoded) + ); + + const paraBMVDeployParams = { + bmc: bmcAddress, + net: netAddress, + mtaRootSize: decimalToHex(mtaRootSize), + mtaCacheSize: decimalToHex(mtaCacheSize), + mtaIsAllowNewerWitness: mtaIsAllowNewerWitness ? "0x1" : "0x0", + eventDecoderAddress: sovereignChainEventDecoder, + mtaOffset: decimalToHex(sovereignChainOffset), + lastBlockHash: sovereignLastBlockHash.toHex(), + currentSetId: convertLEtoBE(grandPaCurrentSetId.toHex().replace("0x", "")), + encodedValidators: validatorListBase64Encoded, + evmEventIndex: "0x" + decimalToHex(evmEventIndex[0]).replace("0x", "") + decimalToHex(evmEventIndex[1]).replace("0x", ""), + newAuthoritiesEventIndex: "0x" + decimalToHex(newAuthoritiesEventIndex[0]).replace("0x", "") + decimalToHex(newAuthoritiesEventIndex[1]).replace("0x", "") + } + + console.log(" Deploy sovereign chain BMV..."); + const paraBMV = await deployScore(iconService, wallet, iconNid, __dirname + '/../../sovereignChain/build/libs/SovereignChain-BMV-optimized.jar', paraBMVDeployParams); + + console.log("\n --------------------- DONE ---------------------- "); + console.log("- Sovereign event decoder score address: ", sovereignChainEventDecoder); + console.log("- Para chain bmv score address: ", paraBMV); + + await sovereignApi.disconnect(); +} + +main().catch(console.error); diff --git a/javascore/bmv/helper/src/getBMVInitializeParams.ts b/javascore/bmv/helper/src/getBMVInitializeParams.ts new file mode 100644 index 00000000..6500111e --- /dev/null +++ b/javascore/bmv/helper/src/getBMVInitializeParams.ts @@ -0,0 +1,131 @@ +import { ApiPromise, WsProvider } from '@polkadot/api'; +import { xxhashAsHex } from '@polkadot/util-crypto'; +import * as fs from 'fs'; +import * as RLP from 'rlp'; +import * as URLSafeBase64 from 'urlsafe-base64'; +import { findEventIndex, decimalToHex } from './util'; + +require('dotenv').config() + +function convertLEtoBE(input) { + let result = ""; + for (let i = Math.floor(input.length / 2) - 1; i >= 0; i--) { + result += input[i * 2]; + if (input[i * 2 + 1]) { + result += input[i * 2 + 1]; + } else { + result += "0"; + } + } + + return "0x" + result.replace(/^0+/, ''); +} + +async function main() { + // const wssEndpoint = "wss://rpc.polkadot.io"; // polkadot relay chain + // const wssEndpoint = "wss://kusama-rpc.polkadot.io"; // kusama relay chain + // const wssEndpoint = "wss://wss-relay.testnet.moonbeam.network"; // moonbase alpha relay chain + + // const wssEndpoint = "wss://wss.moonriver.moonbeam.network"; // moonriver parachain + // const wssEndpoint = "wss://wss.testnet.moonbeam.network"; // moonbase alpha parachain + // const wssEndpoint = "wss://icon-btp.ecl.vn:34008/"; // lecle moonbase parachain + + const relayWssEndpoint = process.env.RELAY_ENDPOINT; // wss endpoint of relay chain + const paraWssEndpoint = process.env.PARA_ENDPOINT; // wss endpoint of para chain + + const relayChainOffset = process.env.RELAY_CHAIN_OFFSET; // offset of relay chain + const paraChainOffset = process.env.PARA_CHAIN_OFFSET; // offset of para chain + + const relayWsProvider = new WsProvider(relayWssEndpoint); + const paraWsProvider = new WsProvider(paraWssEndpoint); + const relayApi = await ApiPromise.create({ + provider: relayWsProvider, + types: { + GrandpaAuthorities: { + version: "u8", + authorityList: "AuthorityList", + } + } + }); + + const paraApi = await ApiPromise.create({ + provider: paraWsProvider, + types: { + RoundIndex: "u32", + }, + }); + + // get relay genesis hash + console.log("relay genesis hash: ", relayApi.genesisHash.toHex()); + // get relay chain name + const relayChainName = await relayApi.rpc.system.chain(); + console.log("relay chain name: ", JSON.stringify(relayChainName)); + + // get relay genesis hash + console.log("para genesis hash: ", paraApi.genesisHash.toHex()); + // get relay chain name + const paraChainName = await paraApi.rpc.system.chain(); + console.log("para chain name: ", JSON.stringify(paraChainName)); + + /* + * get meta data of relay chain + */ + console.log(" Get metadata of relay chain..."); + const relayMetaData = await relayApi.rpc.state.getMetadata(); + fs.writeFileSync("./relayMetaData.json", JSON.stringify(relayMetaData, null, 2)); + + const newAuthoritiesEventIndex = findEventIndex(relayMetaData, "Grandpa", "NewAuthorities"); + const candidateIncludedEventIndex = findEventIndex(relayMetaData, "ParaInclusion", "CandidateIncluded"); + + /* + * get meta data of para chain + */ + console.log(" Get metadata of para chain..."); + const paraMetaData = await paraApi.rpc.state.getMetadata(); + fs.writeFileSync("./paraMetaData.json", JSON.stringify(paraMetaData, null, 2)); + + const evmEventIndex = findEventIndex(paraMetaData, "EVM", "Log"); + + const relayLastBlockHash = await relayApi.rpc.chain.getBlockHash(relayChainOffset); + const paraLastBlockHash = await paraApi.rpc.chain.getBlockHash(paraChainOffset); + + const grandpaAuthoritiesEncoded = await relayApi.rpc.state.getStorage(":grandpa_authorities", relayLastBlockHash.toHex()); + // @ts-ignore + const grandpaAuthorities = await relayApi.createType("GrandpaAuthorities", String(grandpaAuthoritiesEncoded)); + // @ts-ignore + const validatorList = grandpaAuthorities.authorityList.map(item => item[0].toHex()); + const encoded = RLP.encode(validatorList); + const validatorListBase64Encoded = URLSafeBase64.encode(encoded); + + const grandpaPrefixHash = xxhashAsHex("Grandpa", 128); + const curentSetIdKeyHash = xxhashAsHex("CurrentSetId", 128); + const grandpaCurrentSetIdStorageKey = grandpaPrefixHash + curentSetIdKeyHash.replace("0x", ""); + const grandPaCurrentSetIdEndcoded = await relayApi.rpc.state.getStorage(grandpaCurrentSetIdStorageKey, relayLastBlockHash.toHex()); + const grandPaCurrentSetId = await relayApi.createType("SetId", String(grandPaCurrentSetIdEndcoded)); + + const parachainInfoPrefixHash = xxhashAsHex("ParachainInfo", 128); + const parachainIdKeyHash = xxhashAsHex("ParachainId", 128); + const parachainIdStorageKey = parachainInfoPrefixHash + parachainIdKeyHash.replace("0x", ""); + const paraChainIdEncoded = await paraApi.rpc.state.getStorage(parachainIdStorageKey, paraLastBlockHash.toHex()); + const paraChainId = await paraApi.createType("u32", String(paraChainIdEncoded)); + + const result = { + relayMtaOffset: "0x" + parseInt(relayChainOffset, 10).toString(16), + paraMtaOffset: "0x" + parseInt(paraChainOffset, 10).toString(16), + relayLastBlockHash: relayLastBlockHash.toHex(), + paraLastBlockHash: paraLastBlockHash.toHex(), + relayCurrentSetId: convertLEtoBE(grandPaCurrentSetId.toHex().replace("0x", "")), + paraChainId: convertLEtoBE(paraChainId.toHex().replace("0x", "")), + evmEventIndex: "0x" + decimalToHex(evmEventIndex[0]).replace("0x", "") + decimalToHex(evmEventIndex[1]).replace("0x", ""), + newAuthoritiesEventIndex: "0x" + decimalToHex(newAuthoritiesEventIndex[0]).replace("0x", "") + decimalToHex(newAuthoritiesEventIndex[1]).replace("0x", ""), + candidateIncludedEventIndex: "0x" + decimalToHex(candidateIncludedEventIndex[0]).replace("0x", "") + decimalToHex(candidateIncludedEventIndex[1]).replace("0x", ""), + encodedValidators: validatorListBase64Encoded, + } + + fs.writeFileSync('./BMVInitializeData.json', JSON.stringify(result, null, 2)); + + await relayApi.disconnect(); + await paraApi.disconnect(); +} + +main().catch(console.error); \ No newline at end of file diff --git a/javascore/bmv/helper/src/getMetaData.ts b/javascore/bmv/helper/src/getMetaData.ts new file mode 100644 index 00000000..4c353065 --- /dev/null +++ b/javascore/bmv/helper/src/getMetaData.ts @@ -0,0 +1,30 @@ +import { ApiPromise, WsProvider } from '@polkadot/api'; +import * as fs from 'fs'; + +async function main () { + // const wssEndpoint = "wss://rpc.polkadot.io"; // polkadot relay chain + // const wssEndpoint = "wss://kusama-rpc.polkadot.io"; // kusama relay chain + // const wssEndpoint = "wss://wss.moonriver.moonbeam.network"; // moonriver parachain + // const wssEndpoint = "wss://wss-relay.testnet.moonbeam.network"; // moonbase alpha relay chain + // const wssEndpoint = "wss://wss.testnet.moonbeam.network"; // moonbase alpha parachain + const wssEndpoint = "CHAIN_ENDPOINT"; // wss endpoint of chain + const wsProvider = new WsProvider(wssEndpoint); + const api = await ApiPromise.create({ provider: wsProvider }); + + // get genesis hash + console.log("genesis hash: ", api.genesisHash.toHex()); + // get chain name + const chain = await api.rpc.system.chain(); + console.log("chain: ", JSON.stringify(chain)); + + /* + * get meta data + */ + const metaData = await api.rpc.state.getMetadata(); + fs.writeFileSync('./metaData.json', JSON.stringify(metaData, null, 2)); + console.log("----- done ----- "); + + await api.disconnect(); +} + +main().catch(console.error); \ No newline at end of file diff --git a/javascore/bmv/helper/src/testScript.ts b/javascore/bmv/helper/src/testScript.ts new file mode 100644 index 00000000..e6d758bf --- /dev/null +++ b/javascore/bmv/helper/src/testScript.ts @@ -0,0 +1,11 @@ +import util from 'util'; +import { exec } from 'child_process'; + +const execPromise = util.promisify(exec); + +async function main () { + await execPromise(`cd .. && gradle loadMetaData -PmetaDataFilePath=/Users/leclevietnam/mwork/btp/javascore/bmv/helper/metaData.json`); + await execPromise(`cd .. && cd eventDecoder && gradle optimizedJar`); +} + +main().catch(console.error); \ No newline at end of file diff --git a/javascore/bmv/helper/src/util.ts b/javascore/bmv/helper/src/util.ts new file mode 100644 index 00000000..fb36d74e --- /dev/null +++ b/javascore/bmv/helper/src/util.ts @@ -0,0 +1,169 @@ +import * as fs from "fs"; +import util from "util"; +import { exec } from "child_process"; +import IconService from "icon-sdk-js"; + +require('dotenv').config() + +const execPromise = util.promisify(exec); +export const readFile = util.promisify(fs.readFile); + +export function sleep(ms) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +export function decimalToHex(d) { + let hex = Number(d).toString(16); + + if (hex.length % 2 != 0) { + hex = "0" + hex; + } + + return "0x" + hex; +} + +export function convertLEtoBE(input) { + let result = ""; + for (let i = Math.floor(input.length / 2) - 1; i >= 0; i--) { + result += input[i * 2]; + if (input[i * 2 + 1]) { + result += input[i * 2 + 1]; + } else { + result += "0"; + } + } + + return "0x" + result.replace(/^0+/, ""); +} + +export function findEventIndex(relayMetaData, moduleName, eventName) { + let eventIndex = []; + + const relayMetaDataJson = relayMetaData.toJSON(); + + let foundModule + // @ts-ignore + if (relayMetaDataJson.metadata.v12 || relayMetaDataJson.metadata.v13) { + const modules = (relayMetaDataJson.metadata.v12 || relayMetaDataJson.metadata.v13).modules + foundModule = modules.find((module) => module.name == moduleName); + + if (!foundModule) { + throw new Error('can not find module ' + moduleName); + } + + eventIndex.push(foundModule.index); + const secondIndex = foundModule.events.findIndex( + (e) => e.name === eventName + ); + if (secondIndex < 0) { + throw new Error("can not find " + eventName + " event in module " + moduleName); + } + // @ts-ignore + eventIndex.push(secondIndex); + return eventIndex; + } else if (relayMetaDataJson.metadata.v14) { + const modules = relayMetaDataJson.metadata.v14.pallets; + foundModule = modules.find((module) => module.name == moduleName); + + if (!foundModule) { + throw new Error('can not find module ' + moduleName); + } + + eventIndex.push(foundModule.index); + if (!foundModule.events) { + throw new Error('Module ' + moduleName + ' has no event'); + } + + const eventTypes = relayMetaDataJson.metadata.v14.lookup.types[foundModule.events.type]; + const secondIndex = eventTypes.type.def.variant.variants.findIndex( + (e) => e.name === eventName + ); + if (secondIndex < 0) { + throw new Error("can not find " + eventName + " event in module " + moduleName); + } + // @ts-ignore + eventIndex.push(secondIndex); + return eventIndex; + } + throw new Error('unsupport metadata version'); +} + +export async function deployScore( + iconService, + wallet, + nid, + contractPath, + params +) { + const { DeployTransactionBuilder } = IconService.IconBuilder; + + const iconInstallScoreAddr = "cx0000000000000000000000000000000000000000"; + + const walletAddress = wallet.getAddress(); + const stepLimit = IconService.IconConverter.toBigNumber(10000000000); + + const contractContent = await readFile(contractPath, { encoding: "hex" }); + + const txObj = new DeployTransactionBuilder() + .from(walletAddress) + .to(iconInstallScoreAddr) + .stepLimit(IconService.IconConverter.toBigNumber(13610920010)) + .nid(IconService.IconConverter.toBigNumber(nid)) + .nonce(IconService.IconConverter.toBigNumber(1)) + .version(IconService.IconConverter.toBigNumber(3)) + .timestamp(new Date().getTime() * 1000) + // @ts-ignore + .contentType("application/java") + .content("0x" + contractContent) + .params(params) + .build(); + + /* Create SignedTransaction instance */ + const signedTransaction = new IconService.SignedTransaction(txObj, wallet); + /* Send transaction. It returns transaction hash. */ + const transactionId = await iconService + .sendTransaction(signedTransaction) + .execute(); + await sleep(5000); + const transactionResult = await iconService + .getTransactionResult(transactionId) + .execute(); + if (transactionResult.failure) { + throw new Error( + "deploy score " + + contractPath + + " error: " + + JSON.stringify(transactionResult.failure) + ); + } + return transactionResult.scoreAddress; +} + +export async function buildEventDecoder(metaDataFilePath: String, accountIdSize: number = 32) { + try { + await execPromise( + `cd .. && gradle loadMetaData -PmetaDataFilePath=${metaDataFilePath} -PaccountIdSize=${accountIdSize}` + ); + await execPromise(`cd .. && cd eventDecoder && gradle optimizedJar`); + } catch (error) { + throw new Error(error.toString()); + } +} + +export async function buildParaBMV() { + try { + await execPromise(`cd .. && cd parachain && gradle optimizedJar`); + } catch (error) { + throw new Error(error.toString()); + } +} + +export async function buildSovereignBMV() { + try { + await execPromise(`cd .. && cd sovereignChain && gradle optimizedJar`); + } catch (error) { + throw new Error(error.toString()); + } + } diff --git a/javascore/bmv/helper/tsconfig.json b/javascore/bmv/helper/tsconfig.json new file mode 100644 index 00000000..4ce6b073 --- /dev/null +++ b/javascore/bmv/helper/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "paths": { + "./src/*": ["./src/*"], + "@polkadot/api/augment": ["./src/interfaces/augment-api.ts"], + "@polkadot/types/augment": ["./src/interfaces/augment-types.ts"] + }, + // some other options, whatever you want for your environment + // "target": "esnext", + // "module": "esnext", + // "jsx": "preserve", + // "declaration": true, + // "strict": false, + "noImplicitAny": false, + "strictNullChecks": false, + // "noUnusedLocals": true, + // "noImplicitReturns": false, + // "moduleResolution": "node", + // "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + // "baseUrl": ".", + "skipLibCheck": true, + // "typeRoots": [ + // "./node_modules/@types" + // ] + }, + "exclude": [ + "build/**/*", + "node_modules" + ] + } \ No newline at end of file diff --git a/javascore/bmv/helper/yarn-error.log b/javascore/bmv/helper/yarn-error.log new file mode 100644 index 00000000..2949bcf9 --- /dev/null +++ b/javascore/bmv/helper/yarn-error.log @@ -0,0 +1,754 @@ +Arguments: + /usr/local/Cellar/node/16.2.0/bin/node /usr/local/Cellar/yarn/1.22.10/libexec/bin/yarn.js getMetadata + +PATH: + /usr/local/opt/openjdk/bin:/Users/leclevietnam/.gem/ruby/2.6.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/Wireshark.app/Contents/MacOS:/Users/leclevietnam/.cargo/bin + +Yarn version: + 1.22.10 + +Node version: + 16.2.0 + +Platform: + darwin x64 + +Trace: + SyntaxError: /Users/leclevietnam/mwork/btp/javascore/bmv/helper/package.json: Unexpected token } in JSON at position 243 + at JSON.parse () + at /usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:1625:59 + at Generator.next () + at step (/usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:310:30) + at /usr/local/Cellar/yarn/1.22.10/libexec/lib/cli.js:321:13 + +npm manifest: + { + "name": "helper", + "version": "1.0.0", + "main": "index.js", + "dependencies": { + "@polkadot/api": "^4.17.1", + "ts-node": "^10.1.0", + "typescript": "^4.3.5" + }, + "scripts": { + "getMetadata": "yarn ts-node getMetadata.ts", + } + } + +yarn manifest: + No manifest + +Lockfile: + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. + # yarn lockfile v1 + + + "@babel/runtime@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + + "@polkadot/api-derive@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-4.17.1.tgz#7902ab73159f89a4f1a896ce856dd7377318d275" + integrity sha512-mgq57F1yAiZjuiA0vrR2zWidyyd+mGe7Kbs4SxVeDWLsNbLc9+eASIfX7Hch2SDHIn3CQpv6DQqJH00uDfw9Lw== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/api" "4.17.1" + "@polkadot/rpc-core" "4.17.1" + "@polkadot/types" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/util-crypto" "^6.11.1" + "@polkadot/x-rxjs" "^6.11.1" + + "@polkadot/api@4.17.1", "@polkadot/api@^4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-4.17.1.tgz#c9c8e7f5e33122aeb5b1345e43bc9579658720db" + integrity sha512-uuNIKWC+PjM+1AARRu4NLWOEudZE6DW8UOlaubx3uGhPywqPIP+HGWP2I6PqRGYKARBWxxOvca1Q7WoKzpYC8w== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/api-derive" "4.17.1" + "@polkadot/keyring" "^6.11.1" + "@polkadot/metadata" "4.17.1" + "@polkadot/rpc-core" "4.17.1" + "@polkadot/rpc-provider" "4.17.1" + "@polkadot/types" "4.17.1" + "@polkadot/types-known" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/util-crypto" "^6.11.1" + "@polkadot/x-rxjs" "^6.11.1" + eventemitter3 "^4.0.7" + + "@polkadot/keyring@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-6.11.1.tgz#2510c349c965c74cc2f108f114f1048856940604" + integrity sha512-rW8INl7pO6Dmaffd6Df1yAYCRWa2RmWQ0LGfJeA/M6seVIkI6J3opZqAd4q2Op+h9a7z4TESQGk8yggOEL+Csg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/util" "6.11.1" + "@polkadot/util-crypto" "6.11.1" + + "@polkadot/metadata@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.17.1.tgz#4da9ee5b2b816493910abfd302a50b58141ceca2" + integrity sha512-219isiCWVfbu5JxZnOPj+cV4T+S0XHS4+Jal3t3xz9y4nbgr+25Pa4KInEsJPx0u8EZAxMeiUCX3vd5U7oe72g== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/types" "4.17.1" + "@polkadot/types-known" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/util-crypto" "^6.11.1" + + "@polkadot/networks@6.11.1", "@polkadot/networks@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-6.11.1.tgz#8fd189593f6ee4f8bf64378d0aaae09e39a37d35" + integrity sha512-0C6Ha2kvr42se3Gevx6UhHzv3KnPHML0N73Amjwvdr4y0HLZ1Nfw+vcm5yqpz5gpiehqz97XqFrsPRauYdcksQ== + dependencies: + "@babel/runtime" "^7.14.6" + + "@polkadot/rpc-core@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-4.17.1.tgz#b9fa739fa98e4355fdc2b8d2b43b3a4b9d32dac4" + integrity sha512-1gqYaYuSSQsRmt3ol55jmjBP/euKyAh4PwSj94I2wu0fngK/FZwVZNDJZn/Ib68X/s38TBIgqJ6+YdUdr3z1xw== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/metadata" "4.17.1" + "@polkadot/rpc-provider" "4.17.1" + "@polkadot/types" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/x-rxjs" "^6.11.1" + + "@polkadot/rpc-provider@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-4.17.1.tgz#1f99b8365d0f76f714f613423e6a1832b5d833b3" + integrity sha512-vlU1H5mnfP0Ej8PbjcxwF9ZlT7LtcpekOKI4iYfMnfdelSUKUVyaD5PC8yRGIg9fxkorA6OM5AZs116jAl3TLA== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/types" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/util-crypto" "^6.11.1" + "@polkadot/x-fetch" "^6.11.1" + "@polkadot/x-global" "^6.11.1" + "@polkadot/x-ws" "^6.11.1" + eventemitter3 "^4.0.7" + + "@polkadot/types-known@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-4.17.1.tgz#71c18dda4967a13ec34fbbf0c4ef264e882c2688" + integrity sha512-YkOwGrO+k9aVrBR8FgYHnfJKhOfpdgC5ZRYNL/xJ9oa7lBYqPts9ENAxeBmJS/5IGeDF9f32MNyrCP2umeCXWg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/networks" "^6.11.1" + "@polkadot/types" "4.17.1" + "@polkadot/util" "^6.11.1" + + "@polkadot/types@4.17.1": + version "4.17.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-4.17.1.tgz#41d43621d53820ee930ba4036bfa8b16cf98ca6f" + integrity sha512-rjW4OFdwvFekzN3ATLibC2JPSd8AWt5YepJhmuCPdwH26r3zB8bEC6dM7YQExLVUmygVPvgXk5ffHI6RAdXBMg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/metadata" "4.17.1" + "@polkadot/util" "^6.11.1" + "@polkadot/util-crypto" "^6.11.1" + "@polkadot/x-rxjs" "^6.11.1" + + "@polkadot/util-crypto@6.11.1", "@polkadot/util-crypto@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-6.11.1.tgz#7a36acf5c8bf52541609ec0b0b2a69af295d652e" + integrity sha512-fWA1Nz17FxWJslweZS4l0Uo30WXb5mYV1KEACVzM+BSZAvG5eoiOAYX6VYZjyw6/7u53XKrWQlD83iPsg3KvZw== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/networks" "6.11.1" + "@polkadot/util" "6.11.1" + "@polkadot/wasm-crypto" "^4.0.2" + "@polkadot/x-randomvalues" "6.11.1" + base-x "^3.0.8" + base64-js "^1.5.1" + blakejs "^1.1.1" + bn.js "^4.11.9" + create-hash "^1.2.0" + elliptic "^6.5.4" + hash.js "^1.1.7" + js-sha3 "^0.8.0" + scryptsy "^2.1.0" + tweetnacl "^1.0.3" + xxhashjs "^0.2.2" + + "@polkadot/util@6.11.1", "@polkadot/util@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.11.1.tgz#8950b038ba3e6ebfc0a7ff47feeb972e81b2626c" + integrity sha512-TEdCetr9rsdUfJZqQgX/vxLuV4XU8KMoKBMJdx+JuQ5EWemIdQkEtMBdL8k8udNGbgSNiYFA6rPppATeIxAScg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-textdecoder" "6.11.1" + "@polkadot/x-textencoder" "6.11.1" + "@types/bn.js" "^4.11.6" + bn.js "^4.11.9" + camelcase "^5.3.1" + ip-regex "^4.3.0" + + "@polkadot/wasm-crypto-asmjs@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.1.2.tgz#094b3eeeb5fd39a93db177583b48454511874cfc" + integrity sha512-3Q+vVUxDAC2tXgKMM3lKzx2JW+tarDpTjkvdxIKATyi8Ek69KkUqvMyJD0VL/iFZOFZED0YDX9UU4XOJ/astlg== + dependencies: + "@babel/runtime" "^7.14.6" + + "@polkadot/wasm-crypto-wasm@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.1.2.tgz#773c78c1d65886671d3ba1d66c31afd86c93d02f" + integrity sha512-/l4IBEdQ41szHdHkuF//z1qr+XmWuLHlpBA7s9Eb221m1Fir6AKoCHoh1hp1r3v0ecZYLKvak1B225w6JAU3Fg== + dependencies: + "@babel/runtime" "^7.14.6" + + "@polkadot/wasm-crypto@^4.0.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.1.2.tgz#dead71ae5d2f7722d23aed5be2112e1732d315e9" + integrity sha512-2EKdOjIrD2xHP2rC+0G/3Qo6926nL/18vCFkd34lBd9zP9YNF2GDEtDY+zAeDIRFKe1sQHTpsKgNdYSWoV2eBg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/wasm-crypto-asmjs" "^4.1.2" + "@polkadot/wasm-crypto-wasm" "^4.1.2" + + "@polkadot/x-fetch@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-6.11.1.tgz#97d44d78ef0285eec6f6dbc4006302308ec8e24c" + integrity sha512-qJyLLnm+4SQEZ002UDz2wWnXbnnH84rIS0mLKZ5k82H4lMYY+PQflvzv6sbu463e/lgiEao+6zvWS6DSKv1Yog== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-global" "6.11.1" + "@types/node-fetch" "^2.5.10" + node-fetch "^2.6.1" + + "@polkadot/x-global@6.11.1", "@polkadot/x-global@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-6.11.1.tgz#c292b3825fea60e9b33fff1790323fc57de1ca5d" + integrity sha512-lsBK/e4KbjfieyRmnPs7bTiGbP/6EoCZz7rqD/voNS5qsJAaXgB9LR+ilubun9gK/TDpebyxgO+J19OBiQPIRw== + dependencies: + "@babel/runtime" "^7.14.6" + + "@polkadot/x-randomvalues@6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-6.11.1.tgz#f006fa250c8e82c92ccb769976a45a8e7f3df28b" + integrity sha512-2MfUfGZSOkuPt7GF5OJkPDbl4yORI64SUuKM25EGrJ22o1UyoBnPOClm9eYujLMD6BfDZRM/7bQqqoLW+NuHVw== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-global" "6.11.1" + + "@polkadot/x-rxjs@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-6.11.1.tgz#5454708b61da70eea05708611d9148fce9372498" + integrity sha512-zIciEmij7SUuXXg9g/683Irx6GogxivrQS2pgBir2DI/YZq+um52+Dqg1mqsEZt74N4KMTMnzAZAP6LJOBOMww== + dependencies: + "@babel/runtime" "^7.14.6" + rxjs "^6.6.7" + + "@polkadot/x-textdecoder@6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-6.11.1.tgz#6cc314645681cc4639085c03b65328671c7f182c" + integrity sha512-DI1Ym2lyDSS/UhnTT2e9WutukevFZ0WGpzj4eotuG2BTHN3e21uYtYTt24SlyRNMrWJf5+TkZItmZeqs1nwAfQ== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-global" "6.11.1" + + "@polkadot/x-textencoder@6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.11.1.tgz#73e89da5b91954ae380042c19314c90472f59d9e" + integrity sha512-8ipjWdEuqFo+R4Nxsc3/WW9CSEiprX4XU91a37ZyRVC4e9R1bmvClrpXmRQLVcAQyhRvG8DKOOtWbz8xM+oXKg== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-global" "6.11.1" + + "@polkadot/x-ws@^6.11.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-6.11.1.tgz#338adc7309e3a8e660fce8eb42f975426da48d10" + integrity sha512-GNu4ywrMlVi0QF6QSpKwYWMK6JRK+kadgN/zEhMoH1z5h8LwpqDLv128j5WspWbQti2teCQtridjf7t2Lzoe8Q== + dependencies: + "@babel/runtime" "^7.14.6" + "@polkadot/x-global" "6.11.1" + "@types/websocket" "^1.0.3" + websocket "^1.0.34" + + "@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + + "@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + + "@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + + "@tsconfig/node16@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" + integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + + "@types/bn.js@^4.11.6": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + + "@types/node-fetch@^2.5.10": + version "2.5.11" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.11.tgz#ce22a2e65fc8999f4dbdb7ddbbcf187d755169e4" + integrity sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + + "@types/node@*": + version "16.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.1.tgz#24691fa2b0c3ec8c0d34bfcfd495edac5593ebb4" + integrity sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA== + + "@types/websocket@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.3.tgz#49e09f939afd0ccdee4f7108d4712ec9feb0f153" + integrity sha512-ZdoTSwmDsKR7l1I8fpfQtmTI/hUwlOvE3q0iyJsp4tXU0MkdrYowimDzwxjhQvxU4qjhHLd3a6ig0OXRbLgIdw== + dependencies: + "@types/node" "*" + + arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + + asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + + base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + + base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + + blakejs@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" + integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== + + bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + + brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + + buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + + bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + + camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + + cipher-base@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + + combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + + create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + + create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + + cuint@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" + integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= + + d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + + debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + + delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + + diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + + elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + + es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + + es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + + es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + + eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + + ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + + form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + + hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + + hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + + hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + + inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + + ip-regex@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + + is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + + js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + + make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + + md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + + mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + + mime-types@^2.1.12: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + + minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + + ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + + next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + + node-fetch@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + + node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + + readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + + regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + + ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + + rxjs@^6.6.7: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + + safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + + scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + + sha.js@^2.4.0: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + + source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + + source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + + string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + + ts-node@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e" + integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA== + dependencies: + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + + tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + + tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + + type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + + type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + + typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + + typescript@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + + utf-8-validate@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1" + integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ== + dependencies: + node-gyp-build "^4.2.0" + + util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + + websocket@^1.0.34: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + + xxhashjs@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" + integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== + dependencies: + cuint "^0.2.2" + + yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + + yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/javascore/bmv/helper/yarn.lock b/javascore/bmv/helper/yarn.lock new file mode 100644 index 00000000..300d1337 --- /dev/null +++ b/javascore/bmv/helper/yarn.lock @@ -0,0 +1,852 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.15.3", "@babel/runtime@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + +"@polkadot/api-derive@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-6.4.1.tgz#5d7e1aaa18de3a806311c6a45c4ff37ea11f4ad9" + integrity sha512-RlgFEDh8LKEYYtKxacyF0Z00U/CAic/lTNvsiBHibZLRoekKdzcN9amv3Iu+giEyHIt/10NtM2+JUdmCZRgB0w== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/api" "6.4.1" + "@polkadot/rpc-core" "6.4.1" + "@polkadot/types" "6.4.1" + "@polkadot/util" "^7.5.1" + "@polkadot/util-crypto" "^7.5.1" + rxjs "^7.4.0" + +"@polkadot/api@6.4.1", "@polkadot/api@^6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-6.4.1.tgz#b969a515712a8627af225a45d5a1dd133df49579" + integrity sha512-4kUEYrnOMsoGLVqs5nWEnTMSvvZaEz4wiebqV1SsRTn4GzbQBGaaJ8B0XMi9Y1KUGtI6CGc9dQvDEwSh8p+EGQ== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/api-derive" "6.4.1" + "@polkadot/keyring" "^7.5.1" + "@polkadot/rpc-core" "6.4.1" + "@polkadot/rpc-provider" "6.4.1" + "@polkadot/types" "6.4.1" + "@polkadot/types-known" "6.4.1" + "@polkadot/util" "^7.5.1" + "@polkadot/util-crypto" "^7.5.1" + eventemitter3 "^4.0.7" + rxjs "^7.4.0" + +"@polkadot/keyring@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-7.5.1.tgz#0394d476f49f346589687611f7c273c63c9db8b2" + integrity sha512-zpgm7Z80zJrdchwkCcXynFQuWzQYh6XphoN4Z+dROtrHjFCHL2nC59gaL1iaqKLHlO/nPHarVIZqMXqluJ6KHA== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/util" "7.5.1" + "@polkadot/util-crypto" "7.5.1" + +"@polkadot/networks@7.5.1", "@polkadot/networks@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-7.5.1.tgz#9513981d65993fd02df9762840c7662eb84574c2" + integrity sha512-UaVTP9eWg+mkW+oiuxfrOyVtLuNQ0u/Rer3rdHKom+bkQPf+NPeo+3ekg7YsWoi/fJN/OYMnIIM6rbloxm0GKA== + dependencies: + "@babel/runtime" "^7.15.4" + +"@polkadot/rpc-core@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-6.4.1.tgz#dc4c63978684998af3506c0d164e0bd239e5023d" + integrity sha512-IsCzqnwKdU6Ci85kaw6tpXzLxa3N1eQvQe6AEtMVtAFY/K0I5B/GvPmsWmNqNVeyEdWI53UNtsPz/kEVT3X6Rw== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/rpc-provider" "6.4.1" + "@polkadot/types" "6.4.1" + "@polkadot/util" "^7.5.1" + rxjs "^7.4.0" + +"@polkadot/rpc-provider@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-6.4.1.tgz#8c3c8855ea3c5651e5059067a563fb80e11afdfb" + integrity sha512-p4H76uXQLeg+Vf/GGd3xXZFULYaXrsx7HWITpZZe2foY7GJPhE1+odBNqukWFBpjYniI7IT0NouWhjfxwcr5Cw== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/types" "6.4.1" + "@polkadot/util" "^7.5.1" + "@polkadot/util-crypto" "^7.5.1" + "@polkadot/x-fetch" "^7.5.1" + "@polkadot/x-global" "^7.5.1" + "@polkadot/x-ws" "^7.5.1" + eventemitter3 "^4.0.7" + +"@polkadot/types-known@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-6.4.1.tgz#aedaec8b391d3447300d33fabae274d615295c59" + integrity sha512-aN2MaVX2GiEdKWdxaF7tAFpOZIWq4l4OaflfCPrgZ8EzthqkiLixFKxdSNprdNu+rm9tF9GTDZ801sDReI9dQA== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/networks" "^7.5.1" + "@polkadot/types" "6.4.1" + "@polkadot/util" "^7.5.1" + +"@polkadot/types@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-6.4.1.tgz#d2abec76b50c5c5f4334ad429ce7b7215e1e18f4" + integrity sha512-tdlyoA5ltjCnMuBzWIPP98xSiCLYfcH4vHVrbdmzuk1ceEOmIU+TDZXBD8WBTvYLVvnW+W6ETRMCgTpP4+UpPA== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/util" "^7.5.1" + "@polkadot/util-crypto" "^7.5.1" + rxjs "^7.4.0" + +"@polkadot/util-crypto@7.5.1", "@polkadot/util-crypto@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-7.5.1.tgz#ff095262504a9f2829a41ff6b98ec3a537feb4ef" + integrity sha512-jghGTTMrGllf/TIKpWsphfXRfl0dzkn1/3PsiaFCKVLnZTmxeMv9FGH2Z6/L1XYeFheoBYmFIlKDNir45mZeiw== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/networks" "7.5.1" + "@polkadot/util" "7.5.1" + "@polkadot/wasm-crypto" "^4.2.1" + "@polkadot/x-randomvalues" "7.5.1" + base-x "^3.0.8" + base64-js "^1.5.1" + blakejs "^1.1.1" + bn.js "^4.12.0" + create-hash "^1.2.0" + ed2curve "^0.3.0" + elliptic "^6.5.4" + hash.js "^1.1.7" + js-sha3 "^0.8.0" + scryptsy "^2.1.0" + tweetnacl "^1.0.3" + xxhashjs "^0.2.2" + +"@polkadot/util@7.5.1", "@polkadot/util@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-7.5.1.tgz#aa5db65bb543173afe9db6749475b40908d98b76" + integrity sha512-6ltZyYZg/eUnyB5IBIcFpFFfhDL/ZW57Nt8CVcZmKqiKTL1kF60HmIvWky/agEV7fa00Ibd/zpANoQitEqNE0Q== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-textdecoder" "7.5.1" + "@polkadot/x-textencoder" "7.5.1" + "@types/bn.js" "^4.11.6" + bn.js "^4.12.0" + camelcase "^6.2.0" + ip-regex "^4.3.0" + +"@polkadot/wasm-crypto-asmjs@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.2.1.tgz#6b7eae1c011709f8042dfd30872a5fc5e9e021c0" + integrity sha512-ON9EBpTNDCI3QRUmuQJIegYoAcwvxDaNNA7uwKTaEEStu8LjCIbQxbt4WbOBYWI0PoUpl4iIluXdT3XZ3V3jXA== + dependencies: + "@babel/runtime" "^7.15.3" + +"@polkadot/wasm-crypto-wasm@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.2.1.tgz#2a86f9b405e7195c3f523798c6ce4afffd19737e" + integrity sha512-Rs2CKiR4D+2hKzmKBfPNYxcd2E8NfLWia0av4fgicjT9YsWIWOGQUi9AtSOfazPOR9FrjxKJy+chQxAkcfKMnQ== + dependencies: + "@babel/runtime" "^7.15.3" + +"@polkadot/wasm-crypto@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.2.1.tgz#4d09402f5ac71a90962fb58cbe4b1707772a4fb6" + integrity sha512-C/A/QnemOilRTLnM0LfhPY2N/x3ZFd1ihm9sXYyuh98CxtekSVYI9h4IJ5Jrgz5imSUHgvt9oJLqJ5GbWQV/Zg== + dependencies: + "@babel/runtime" "^7.15.3" + "@polkadot/wasm-crypto-asmjs" "^4.2.1" + "@polkadot/wasm-crypto-wasm" "^4.2.1" + +"@polkadot/x-fetch@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-7.5.1.tgz#2329bfb749ad79fb0d631f98f81bbba2332e7036" + integrity sha512-bpKNPvT9yRQ6JRlLqezl7gAZQO8GGbAwx3eq+UUz/TaY7CHjld7T6CzVlXJgrOdAonCgTz9Rp0b1n1S21hyZWQ== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-global" "7.5.1" + "@types/node-fetch" "^2.5.12" + node-fetch "^2.6.5" + +"@polkadot/x-global@7.5.1", "@polkadot/x-global@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-7.5.1.tgz#d9723585d94c07807eab416ba92bcea65d21c3d5" + integrity sha512-SM5xbMgRAf/O5GTf8g1X9gwNaPVKGhIC6qj4ieN/pHQLAS2A5WcelmoQeJhHYrcW1JKR2m3kGKMMWuLKgMVWkw== + dependencies: + "@babel/runtime" "^7.15.4" + +"@polkadot/x-randomvalues@7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-7.5.1.tgz#030dddd785b2fcf700be4273b885774361d0b4ff" + integrity sha512-qyyV8ZRif0U0Sr2DhU9h9j6AXb67ckgj+3JVPWC30bcOi0p/biu1XFR1MlSLyB6KmxwQZxJ4SKCK3eRxbGxtdw== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-global" "7.5.1" + +"@polkadot/x-textdecoder@7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-7.5.1.tgz#cfed161b94edd1721a2ab6f9f9114eee3960e2e9" + integrity sha512-dX4SL1QWmQau+gJiT/u8Vgh4DnyeGLd1MSWIg6MdYUCzmFs5tJg6Iqd94We2r9Lo22M0PJwXKlqf+3Fbhd2R9w== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-global" "7.5.1" + +"@polkadot/x-textencoder@7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-7.5.1.tgz#72d2c081c80dd30e594576d16b915a80ba77a0db" + integrity sha512-alzRtHXnydcCXK0JnmHF0W3DJMGgxGiFtGbTzvDorX6OefqjjqLYr8ALJka56xV04zsiCqAY6WqjRmu8BIBRYQ== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-global" "7.5.1" + +"@polkadot/x-ws@^7.5.1": + version "7.5.1" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-7.5.1.tgz#2d04dc84d47766e5313931b158ac538779a0d32e" + integrity sha512-+fBgPVExxUFR1WLpQls8i8GSw9zcYYEyWmGuzEvCeQ5HRYBlC0Of2coj8SNx17jKVIXVJ8Goyphs5c3Og0d7fQ== + dependencies: + "@babel/runtime" "^7.15.4" + "@polkadot/x-global" "7.5.1" + "@types/websocket" "^1.0.4" + websocket "^1.0.34" + +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" + integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + +"@types/bn.js@^4.11.6": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/node-fetch@^2.5.12": + version "2.5.12" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.12.tgz#8a6f779b1d4e60b7a57fb6fd48d84fb545b9cc66" + integrity sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "16.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.1.tgz#24691fa2b0c3ec8c0d34bfcfd495edac5593ebb4" + integrity sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA== + +"@types/websocket@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.4.tgz#1dc497280d8049a5450854dd698ee7e6ea9e60b8" + integrity sha512-qn1LkcFEKK8RPp459jkjzsfpbsx36BBt3oC3pITYtkoBw/aVX+EZFa5j3ThCRTNpLFvIMr5dSTD4RaMdilIOpA== + dependencies: + "@types/node" "*" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bindings@^1.2.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip66@^1.1.3, bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= + dependencies: + safe-buffer "^5.0.1" + +blakejs@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" + integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== + +bn.js@^4.11.1, bn.js@^4.11.3, bn.js@^4.11.9, bn.js@^4.12.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +camelcase@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" + integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cuint@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" + integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + +ed2curve@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/ed2curve/-/ed2curve-0.3.0.tgz#322b575152a45305429d546b071823a93129a05d" + integrity sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ== + dependencies: + tweetnacl "1.x.x" + +elliptic@^6.2.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +icon-sdk-js@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icon-sdk-js/-/icon-sdk-js-1.1.0.tgz#edd5bac09dad3bd0899575894c45e6011ef7e3d9" + integrity sha512-P/SUdKeUrM9fcXCuNrzix3wURUS0SJCrAKWBlyk4MKk8PiXLrl6a5RtQEfK4xOhwuIWsPXpASOsDRqqw88qDsw== + dependencies: + bignumber.js "^7.2.1" + bip66 "^1.1.5" + js-sha3 "^0.8.0" + secp256k1 "3.5.2" + utf8 "^3.0.0" + uuid "^3.3.2" + xmlhttprequest "^1.8.0" + +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ip-regex@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-types@^2.1.12: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +nan@^2.2.1: + version "2.14.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" + integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +node-fetch@^2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.5.tgz#42735537d7f080a7e5f78b6c549b7146be1742fd" + integrity sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +rxjs@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" + integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== + dependencies: + tslib "~2.1.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +scryptsy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" + integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== + +secp256k1@3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.5.2.tgz#f95f952057310722184fe9c914e6b71281f2f2ae" + integrity sha512-iin3kojdybY6NArd+UFsoTuapOF7bnJNf2UbcWXaY3z+E1sJDipl60vtzB5hbO/uquBu7z0fd4VC4Irp+xoFVQ== + dependencies: + bindings "^1.2.1" + bip66 "^1.1.3" + bn.js "^4.11.3" + create-hash "^1.1.2" + drbg.js "^1.0.1" + elliptic "^6.2.3" + nan "^2.2.1" + safe-buffer "^5.1.0" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + +ts-node@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e" + integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA== + dependencies: + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + +tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + +tweetnacl@1.x.x, tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typescript@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + +urlsafe-base64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz#23f89069a6c62f46cf3a1d3b00169cefb90be0c6" + integrity sha1-I/iQaabGL0bPOh07ABac77kL4MY= + +utf-8-validate@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1" + integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ== + dependencies: + node-gyp-build "^4.2.0" + +utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +websocket@^1.0.34: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +xmlhttprequest@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xxhashjs@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" + integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== + dependencies: + cuint "^0.2.2" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== diff --git a/javascore/bmv/icon/build.gradle b/javascore/bmv/icon/build.gradle new file mode 100644 index 00000000..452565f5 --- /dev/null +++ b/javascore/bmv/icon/build.gradle @@ -0,0 +1,52 @@ +version = '0.1.0' + +dependencies { + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + implementation project(':score-util') + implementation project(':lib') + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + + testImplementation("org.mockito:mockito-core:3.3.3") +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.bmv.icon.BTPMessageVerifier' + archivesBaseName = 'bmv-icon' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = scoreTest.url + nid = scoreTest.parseNid(scoreTest.nid) + } + } + keystore = scoreTest.default.keyStore + password = scoreTest.default.resolvedKeyPassword + parameters {[ + arg('_bmc', 'cx?'), + arg('_net', '0x?.icon'), + arg('_validators', 'hx?'), + arg('_offset', '0x?') + ] + } +} + +test { + useJUnitPlatform { + if (!integrationTest) { + excludeTags("integration") + } + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVException.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVException.java new file mode 100644 index 00000000..9f0be354 --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVException.java @@ -0,0 +1,153 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import foundation.icon.btp.lib.BTPException; + +public class BMVException extends BTPException.BMV { + + public BMVException(Code c) { + super(c, c.name()); + } + + public BMVException(Code c, String message) { + super(c, message); + } + + public static BMVException unknown(String message) { + return new BMVException(Code.Unknown, message); + } + + public static BMVException invalidMPT() { + return new BMVException(Code.InvalidMPT); + } + + public static BMVException invalidMPT(String message) { + return new BMVException(Code.InvalidMPT, message); + } + + public static BMVException invalidVotes() { + return new BMVException(Code.InvalidVotes); + } + + public static BMVException invalidVotes(String message) { + return new BMVException(Code.InvalidVotes, message); + } + + public static BMVException invalidSequence() { + return new BMVException(Code.InvalidSequence); + } + + public static BMVException invalidSequence(String message) { + return new BMVException(Code.InvalidSequence, message); + } + + public static BMVException invalidBlockUpdate() { + return new BMVException(Code.InvalidBlockUpdate); + } + + public static BMVException invalidBlockUpdate(String message) { + return new BMVException(Code.InvalidBlockUpdate, message); + } + + public static BMVException invalidBlockProof() { + return new BMVException(Code.InvalidBlockProof); + } + + public static BMVException invalidBlockProof(String message) { + return new BMVException(Code.InvalidBlockProof, message); + } + + public static BMVException invalidBlockWitness() { + return new BMVException(Code.InvalidBlockWitness); + } + + public static BMVException invalidBlockWitness(String message) { + return new BMVException(Code.InvalidBlockWitness, message); + } + + public static BMVException invalidSequenceHigher() { + return new BMVException(Code.InvalidSequenceHigher); + } + + public static BMVException invalidSequenceHigher(String message) { + return new BMVException(Code.InvalidSequenceHigher, message); + } + + public static BMVException invalidBlockUpdateHeightHigher() { + return new BMVException(Code.InvalidBlockUpdateHeightHigher); + } + + public static BMVException invalidBlockUpdateHeightHigher(String message) { + return new BMVException(Code.InvalidBlockUpdateHeightHigher, message); + } + + public static BMVException invalidBlockUpdateHeightLower() { + return new BMVException(Code.InvalidBlockUpdateHeightLower); + } + + public static BMVException invalidBlockUpdateHeightLower(String message) { + return new BMVException(Code.InvalidBlockUpdateHeightLower, message); + } + + public static BMVException invalidBlockProofHeightHigher() { + return new BMVException(Code.InvalidBlockProofHeightHigher); + } + + public static BMVException invalidBlockProofHeightHigher(String message) { + return new BMVException(Code.InvalidBlockProofHeightHigher, message); + } + + public static BMVException invalidBlockWitnessOld() { + return new BMVException(Code.InvalidBlockWitnessOld); + } + + public static BMVException invalidBlockWitnessOld(String message) { + return new BMVException(Code.InvalidBlockWitnessOld, message); + } + + //BTPException.BMV => 25 ~ 39 + public enum Code implements BTPException.Coded{ + Unknown(0), + InvalidMPT(1), + InvalidVotes(2), + InvalidSequence(3), + InvalidBlockUpdate(4), + InvalidBlockProof(5), + InvalidBlockWitness(6), + InvalidSequenceHigher(7), + InvalidBlockUpdateHeightHigher(8), + InvalidBlockUpdateHeightLower(9), + InvalidBlockProofHeightHigher(10), + InvalidBlockWitnessOld(11); + + final int code; + Code(int code){ this.code = code; } + + @Override + public int code() { return code; } + + static public Code of(int code) { + for(Code c : values()) { + if (c.code == code) { + return c; + } + } + throw new IllegalArgumentException(); + } + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVProperties.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVProperties.java new file mode 100644 index 00000000..ed2178e8 --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BMVProperties.java @@ -0,0 +1,124 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import score.*; + +public class BMVProperties { + public static final BMVProperties DEFAULT; + + static { + DEFAULT = new BMVProperties(); + } + + private Address bmc; + private String net; + private long lastHeight; + private Validators validators; + private MerkleTreeAccumulator mta; + + public Address getBmc() { + return bmc; + } + + public void setBmc(Address bmc) { + this.bmc = bmc; + } + + public String getNet() { + return net; + } + + public void setNet(String net) { + this.net = net; + } + + public long getLastHeight() { + return lastHeight; + } + + public void setLastHeight(long lastHeight) { + this.lastHeight = lastHeight; + } + + public Validators getValidators() { + return validators; + } + + public void setValidators(Validators validators) { + this.validators = validators; + } + + public MerkleTreeAccumulator getMta() { + return mta; + } + + public void setMta(MerkleTreeAccumulator mta) { + this.mta = mta; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMVProperties{"); + sb.append("bmc=").append(bmc); + sb.append(", net='").append(net).append('\''); + sb.append(", lastHeight=").append(lastHeight); + sb.append(", validators=").append(validators); + sb.append(", mta=").append(mta); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BMVProperties obj) { + obj.writeObject(writer); + } + + public static BMVProperties readObject(ObjectReader reader) { + BMVProperties obj = new BMVProperties(); + reader.beginList(); + obj.setBmc(reader.readNullable(Address.class)); + obj.setNet(reader.readNullable(String.class)); + obj.setLastHeight(reader.readLong()); + obj.setValidators(reader.readNullable(Validators.class)); + obj.setMta(reader.readNullable(MerkleTreeAccumulator.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(5); + writer.writeNullable(this.getBmc()); + writer.writeNullable(this.getNet()); + writer.write(this.getLastHeight()); + Validators validators = this.getValidators(); + writer.writeNullable(validators); + MerkleTreeAccumulator mta = this.getMta(); + writer.writeNullable(mta); + writer.end(); + } + + public static BMVProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BMVProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BMVProperties.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BTPMessageVerifier.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BTPMessageVerifier.java new file mode 100644 index 00000000..737c3ddd --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BTPMessageVerifier.java @@ -0,0 +1,292 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import foundation.icon.btp.lib.BMV; +import foundation.icon.btp.lib.BMVStatus; +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.score.util.Logger; +import foundation.icon.score.util.StringUtil; +import score.Address; +import score.Context; +import score.VarDB; +import score.annotation.External; +import scorex.util.ArrayList; +import scorex.util.Base64; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +public class BTPMessageVerifier implements BMV { + private static final Logger logger = Logger.getLogger(BTPMessageVerifier.class); + + private final VarDB properties = Context.newVarDB("properties", BMVProperties.class); + + public BTPMessageVerifier(Address _bmc, String _net, String _validators, long _offset) { + BMVProperties properties = getProperties(); + properties.setBmc(_bmc); + properties.setNet(_net); + Validators validators = Validators.fromString(_validators); + properties.setValidators(validators); + if (properties.getLastHeight() == 0) { + properties.setLastHeight(_offset); + } + if (properties.getMta() == null) { + MerkleTreeAccumulator mta = new MerkleTreeAccumulator(); + mta.setHeight(_offset); + mta.setOffset(_offset); + properties.setMta(mta); + } + setProperties(properties); + } + + static byte[] hash(byte[] bytes) { + return Context.hash("sha3-256",bytes); + } + + static Address recoverAddress(byte[] msg, byte[] sig, boolean compressed) { + byte[] publicKey = Context.recoverKey("ecdsa-secp256k1", msg, sig, compressed); + return Context.getAddressFromKey(publicKey); + } + + public BMVProperties getProperties() { + return properties.getOrDefault(BMVProperties.DEFAULT); + } + + public void setProperties(BMVProperties properties) { + this.properties.set(properties); + } + + @External + public byte[][] handleRelayMessage(String _bmc, String _prev, BigInteger _seq, String _msg) { + BTPAddress curAddr = BTPAddress.valueOf(_bmc); + BTPAddress prevAddr = BTPAddress.valueOf(_prev); + checkAccessible(curAddr, prevAddr); + + byte[] serializedMsg = Base64.getUrlDecoder().decode(_msg.getBytes()); + RelayMessage relayMessage = RelayMessage.fromBytes(serializedMsg); + BlockUpdate[] blockUpdates = relayMessage.getBlockUpdates(); + BlockProof blockProof = relayMessage.getBlockProof(); + ReceiptProof[] receiptProofs = relayMessage.getReceiptProofs(); + BlockHeader lastBlockHeader; + BMVProperties properties = getProperties(); + MerkleTreeAccumulator mta = properties.getMta(); + if (blockUpdates != null && blockUpdates.length > 0) { + Validators validators = verifyBlockUpdates(blockUpdates, mta, properties.getValidators()); + properties.setMta(mta); + if (validators != null) { + properties.setValidators(validators); + } + lastBlockHeader = blockUpdates[blockUpdates.length - 1].getBlockHeader(); + } else if (blockProof != null) { + verifyBlockProof(blockProof, mta); + lastBlockHeader = blockProof.getBlockHeader(); + } else { + throw BMVException.unknown("invalid RelayMessage, not includes BlockUpdate or BlockProof"); + } + + byte[][] ret = new byte[0][]; + if (receiptProofs != null && receiptProofs.length > 0) { + BigInteger next_seq = _seq.add(BigInteger.ONE); + List msgs = new ArrayList<>(); + if (lastBlockHeader.getResult() == null) { + throw BMVException.unknown("invalid RelayMessage, BlockHeader has not receiptHash"); + } + byte[] receiptHash = lastBlockHeader.getResult().getReceiptHash(); + for(ReceiptProof receiptProof : receiptProofs) { + Receipt receipt = proveReceiptProof(receiptProof, receiptHash); + for(EventLog eventLog : receipt.getEventLogs()) { + if(!(prevAddr.account().equals(eventLog.getAddress().toString()))) { + continue; + } + MessageEvent msgEvent = eventLog.toMessageEvent(); + if (msgEvent != null && msgEvent.getNext().equals(_bmc)) { + int compare = msgEvent.getSeq().compareTo(next_seq); + if (compare > 0) { + throw BMVException.invalidSequenceHigher( + "invalid sequence "+msgEvent.getSeq() + " expected:"+next_seq); + } else if (compare < 0) { + throw BMVException.invalidSequence( + "invalid sequence "+msgEvent.getSeq() + " expected:"+next_seq); + } else { + msgs.add(msgEvent.getMsg()); + next_seq = next_seq.add(BigInteger.ONE); + } + } + } + } + if (msgs.size() > 0) { + properties.setLastHeight(lastBlockHeader.getHeight()); + ret = new byte[msgs.size()][]; + int i = 0; + for (byte[] msg : msgs) { + ret[i] = msg; + } + } + } + + if ((blockUpdates != null && blockUpdates.length > 0) || ret.length > 0) { + setProperties(properties); + } + return ret; + } + + private Receipt proveReceiptProof(ReceiptProof receiptProof, byte[] receiptHash) { + try { + byte[] serializedReceipt = MerklePatriciaTree.prove( + receiptHash, + MerklePatriciaTree.encodeKey(receiptProof.getIndex()), + receiptProof.getProofs().getProofs()); + Receipt receipt = Receipt.fromBytes(serializedReceipt); + byte[] eventLogsHash = receipt.getEventLogsHash(); + MPTProof[] eventProofs = receiptProof.getEventProofs(); + if (eventProofs != null) { + EventLog[] eventLogs = new EventLog[eventProofs.length]; + int i=0; + for(MPTProof eventProof : eventProofs){ + byte[] serializedEventLog = MerklePatriciaTree.prove( + eventLogsHash, + MerklePatriciaTree.encodeKey(eventProof.getIndex()), + eventProof.getProofs().getProofs()); + EventLog eventLog = EventLog.fromBytes(serializedEventLog); + eventLogs[i++] = eventLog; + } + receipt.setEventLogs(eventLogs); + } + return receipt; + } catch (MerklePatriciaTree.MPTException e) { + throw BMVException.invalidMPT(e.getMessage()); + } + } + + private Validators verifyBlockUpdates(BlockUpdate[] blockUpdates, MerkleTreeAccumulator mta, Validators validators) { + boolean isValidatorsUpdate = false; + byte[] validatorHash = hash(validators.toBytes()); + for(BlockUpdate blockUpdate : blockUpdates) { + BlockHeader blockHeader = blockUpdate.getBlockHeader(); + long blockHeight = blockHeader.getHeight(); + long nextHeight = mta.getHeight() + 1; + if (nextHeight == blockHeight) { + byte[] blockHash = hash(blockHeader.toBytes()); + verifyVotes(blockUpdate.getVotes(), blockHeight, blockHash, validators); + byte[] nextValidatorHash = blockHeader.getNextValidatorHash(); + if (!(Arrays.equals(validatorHash, nextValidatorHash))) { + Validators nextValidators = blockUpdate.getNextValidators(); + if (nextValidators == null) { + throw BMVException.invalidBlockUpdate("not exists next validator"); + } + if (!(Arrays.equals(hash(nextValidators.toBytes()), nextValidatorHash))) { + throw BMVException.invalidBlockUpdate("invalid next validator hash"); + } + validators = nextValidators; + validatorHash = nextValidatorHash; + isValidatorsUpdate = true; + } + mta.add(blockHash); + } else if (nextHeight < blockHeight) { + throw BMVException.invalidBlockUpdateHeightHigher( + "invalid blockUpdate height "+blockHeight+" expected:"+nextHeight); + } else { + throw BMVException.invalidBlockUpdateHeightLower( + "invalid blockUpdate height "+blockHeight+" expected:"+nextHeight); + } + } + return isValidatorsUpdate ? validators : null; + } + + private void verifyBlockProof(BlockProof blockProof, MerkleTreeAccumulator mta) { + BlockWitness blockWitness = blockProof.getBlockWitness(); + if (blockWitness == null) { + throw BMVException.invalidBlockProof("not exists witness"); + } + BlockHeader blockHeader = blockProof.getBlockHeader(); + long blockHeight = blockHeader.getHeight(); + if (mta.getHeight() < blockHeight) { + throw BMVException.invalidBlockProofHeightHigher( + "given block height is newer "+blockHeight+" expected:"+mta.getHeight()); + } + byte[] blockHash = hash(blockHeader.toBytes()); + try { + mta.verify(blockWitness.getWitness(), blockHash, blockHeight, blockWitness.getHeight()); + } catch (MTAException.InvalidWitnessOldException e) { + throw BMVException.invalidBlockWitnessOld(e.getMessage()); + } catch (MTAException e) { + logger.println("verifyBlockProof","MTAException", e.getMessage()); + throw BMVException.invalidBlockWitness(e.getMessage()); + } + } + + private void verifyVotes(Votes votes, long blockHeight, byte[] blockHash, Validators validators) { + if (votes == null) { + logger.println("verifyVotes","invalidBlockUpdate", "not exists votes"); + throw BMVException.invalidBlockUpdate("not exists votes"); + } + VoteMessage voteMessage = new VoteMessage(); + voteMessage.setHeight(blockHeight); + voteMessage.setRound(votes.getRound()); + voteMessage.setVoteType(VoteMessage.VOTE_TYPE_PRECOMMIT); + voteMessage.setBlockId(blockHash); + voteMessage.setPartSetId(votes.getPartSetId()); + List
addresses = new ArrayList<>(); + for(Vote vote : votes.getItems()) { + voteMessage.setTimestamp(vote.getTimestamp()); + byte[] voteMessageHash = hash(voteMessage.toBytes()); + Address address = recoverAddress(voteMessageHash, vote.getSignature(), true); + if (!validators.contains(address)) { + logger.println("verifyVotes","invalidVotes", "invalid signature", + "messageHash:", StringUtil.toString(voteMessageHash), + "signature:", StringUtil.toString(vote.getSignature()), + "address:", StringUtil.toString(address.toString())); + throw BMVException.invalidVotes("invalid signature"); + } + if (addresses.contains(address)) { + logger.println("verifyVotes","invalidVotes", "duplicated vote"); + throw BMVException.invalidVotes("duplicated vote"); + } else { + addresses.add(address); + } + } + + if (addresses.size() <= (validators.getAddresses().length * 2 / 3)) { + logger.println("verifyVotes","invalidVotes", "require votes +2/3"); + throw BMVException.invalidVotes("require votes +2/3"); + } + } + + private void checkAccessible(BTPAddress curAddr, BTPAddress fromAddr) { + BMVProperties properties = getProperties(); + if (!properties.getNet().equals(fromAddr.net())) { + throw BMVException.unknown("not acceptable from"); + } else if (!Context.getCaller().equals(properties.getBmc())) { + throw BMVException.unknown("not acceptable bmc"); + } else if (!Address.fromString(curAddr.account()).equals(properties.getBmc())) { + throw BMVException.unknown("not acceptable bmc"); + } + } + + @External(readonly = true) + public BMVStatus getStatus() { + BMVProperties properties = getProperties(); + MerkleTreeAccumulator mta = properties.getMta(); + BMVStatus status = new BMVStatus(); + status.setOffset(mta.getOffset()); + status.setHeight(mta.getHeight()); + status.setLast_height(properties.getLastHeight()); + return status; + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockHeader.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockHeader.java new file mode 100644 index 00000000..72118aad --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockHeader.java @@ -0,0 +1,201 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import foundation.icon.score.util.StringUtil; +import score.*; + +public class BlockHeader { + private long version; + private long height; + private long timestamp; + private Address proposer; + private byte[] prevHash; + private byte[] voteHash; + private byte[] nextValidatorHash; + private byte[] patchTxHash; + private byte[] txHash; + private byte[] logsBloom; + private Result result; + + public long getVersion() { + return version; + } + + public void setVersion(long version) { + this.version = version; + } + + public long getHeight() { + return height; + } + + public void setHeight(long height) { + this.height = height; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public Address getProposer() { + return proposer; + } + + public void setProposer(Address proposer) { + this.proposer = proposer; + } + + public byte[] getPrevHash() { + return prevHash; + } + + public void setPrevHash(byte[] prevHash) { + this.prevHash = prevHash; + } + + public byte[] getVoteHash() { + return voteHash; + } + + public void setVoteHash(byte[] voteHash) { + this.voteHash = voteHash; + } + + public byte[] getNextValidatorHash() { + return nextValidatorHash; + } + + public void setNextValidatorHash(byte[] nextValidatorHash) { + this.nextValidatorHash = nextValidatorHash; + } + + public byte[] getPatchTxHash() { + return patchTxHash; + } + + public void setPatchTxHash(byte[] patchTxHash) { + this.patchTxHash = patchTxHash; + } + + public byte[] getTxHash() { + return txHash; + } + + public void setTxHash(byte[] txHash) { + this.txHash = txHash; + } + + public byte[] getLogsBloom() { + return logsBloom; + } + + public void setLogsBloom(byte[] logsBloom) { + this.logsBloom = logsBloom; + } + + public Result getResult() { + return result; + } + + public void setResult(Result result) { + this.result = result; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BlockHeader{"); + sb.append("version=").append(version); + sb.append(", height=").append(height); + sb.append(", timestamp=").append(timestamp); + sb.append(", proposer=").append(proposer); + sb.append(", prevHash=").append(StringUtil.bytesToHex(prevHash)); + sb.append(", voteHash=").append(StringUtil.bytesToHex(voteHash)); + sb.append(", nextValidatorHash=").append(StringUtil.bytesToHex(nextValidatorHash)); + sb.append(", patchTxHash=").append(StringUtil.bytesToHex(patchTxHash)); + sb.append(", txHash=").append(StringUtil.bytesToHex(txHash)); + sb.append(", logsBloom=").append(StringUtil.bytesToHex(logsBloom)); + sb.append(", result=").append(result); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BlockHeader obj) { + obj.writeObject(writer); + } + + public static BlockHeader readObject(ObjectReader reader) { + BlockHeader obj = new BlockHeader(); + reader.beginList(); + obj.setVersion(reader.readLong()); + obj.setHeight(reader.readLong()); + obj.setTimestamp(reader.readLong()); + obj.setProposer(reader.readNullable(Address.class)); + obj.setPrevHash(reader.readNullable(byte[].class)); + obj.setVoteHash(reader.readNullable(byte[].class)); + obj.setNextValidatorHash(reader.readNullable(byte[].class)); + obj.setPatchTxHash(reader.readNullable(byte[].class)); + obj.setTxHash(reader.readNullable(byte[].class)); + obj.setLogsBloom(reader.readNullable(byte[].class)); + byte[] resultBytes = reader.readNullable(byte[].class); + if (resultBytes != null) { + ObjectReader resultReader = Context.newByteArrayObjectReader("RLPn",resultBytes); + obj.setResult(resultReader.read(Result.class)); + } + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(11); + writer.write(this.getVersion()); + writer.write(this.getHeight()); + writer.write(this.getTimestamp()); + writer.writeNullable(this.getProposer()); + writer.writeNullable(this.getPrevHash()); + writer.writeNullable(this.getVoteHash()); + writer.writeNullable(this.getNextValidatorHash()); + writer.writeNullable(this.getPatchTxHash()); + writer.writeNullable(this.getTxHash()); + writer.writeNullable(this.getLogsBloom()); + Result result = this.getResult(); + if (result != null) { + ByteArrayObjectWriter resultWriter = Context.newByteArrayObjectWriter("RLPn"); + resultWriter.write(result); + writer.writeNullable(resultWriter.toByteArray()); + } else { + writer.writeNull(); + } + writer.end(); + } + + public static BlockHeader fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BlockHeader.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BlockHeader.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockProof.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockProof.java new file mode 100644 index 00000000..67bd90ec --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockProof.java @@ -0,0 +1,95 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class BlockProof { + private BlockHeader blockHeader; + private BlockWitness blockWitness; + + public BlockHeader getBlockHeader() { + return blockHeader; + } + + public void setBlockHeader(BlockHeader blockHeader) { + this.blockHeader = blockHeader; + } + + public BlockWitness getBlockWitness() { + return blockWitness; + } + + public void setBlockWitness(BlockWitness blockWitness) { + this.blockWitness = blockWitness; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BlockProof{"); + sb.append("blockHeader=").append(blockHeader); + sb.append(", blockWitness=").append(blockWitness); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BlockProof obj) { + obj.writeObject(writer); + } + + public static BlockProof readObject(ObjectReader reader) { + BlockProof obj = new BlockProof(); + reader.beginList(); + byte[] blockHeaderBytes = reader.readNullable(byte[].class); + if (blockHeaderBytes != null) { + ObjectReader blockHeaderReader = Context.newByteArrayObjectReader("RLPn",blockHeaderBytes); + obj.setBlockHeader(blockHeaderReader.read(BlockHeader.class)); + } + obj.setBlockWitness(reader.readNullable(BlockWitness.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + BlockHeader blockHeader = this.getBlockHeader(); + if (blockHeader != null) { + ByteArrayObjectWriter blockHeaderWriter = Context.newByteArrayObjectWriter("RLPn"); + blockHeaderWriter.write(blockHeader); + writer.writeNullable(blockHeaderWriter.toByteArray()); + } else { + writer.writeNull(); + } + BlockWitness blockWitness = this.getBlockWitness(); + writer.writeNullable(blockWitness); + writer.end(); + } + + public static BlockProof fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BlockProof.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BlockProof.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockUpdate.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockUpdate.java new file mode 100644 index 00000000..89c8c57a --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockUpdate.java @@ -0,0 +1,128 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class BlockUpdate { + private BlockHeader blockHeader; + private Votes votes; + private Validators nextValidators; + + public BlockHeader getBlockHeader() { + return blockHeader; + } + + public void setBlockHeader(BlockHeader blockHeader) { + this.blockHeader = blockHeader; + } + + public Votes getVotes() { + return votes; + } + + public void setVotes(Votes votes) { + this.votes = votes; + } + + public Validators getNextValidators() { + return nextValidators; + } + + public void setNextValidators(Validators nextValidators) { + this.nextValidators = nextValidators; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BlockUpdate{"); + sb.append("blockHeader=").append(blockHeader); + sb.append(", votes=").append(votes); + sb.append(", nextValidators=").append(nextValidators); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BlockUpdate obj) { + obj.writeObject(writer); + } + + public static BlockUpdate readObject(ObjectReader reader) { + BlockUpdate obj = new BlockUpdate(); + reader.beginList(); + byte[] blockHeaderBytes = reader.readNullable(byte[].class); + if (blockHeaderBytes != null) { + ObjectReader blockHeaderReader = Context.newByteArrayObjectReader("RLPn",blockHeaderBytes); + obj.setBlockHeader(blockHeaderReader.read(BlockHeader.class)); + } + byte[] votesBytes = reader.readNullable(byte[].class); + if (votesBytes != null) { + ObjectReader votesReader = Context.newByteArrayObjectReader("RLPn",votesBytes); + obj.setVotes(votesReader.read(Votes.class)); + } + byte[] nextValidatorsBytes = reader.readNullable(byte[].class); + if (nextValidatorsBytes != null) { + ObjectReader nextValidatorsReader = Context.newByteArrayObjectReader("RLPn",nextValidatorsBytes); + obj.setNextValidators(nextValidatorsReader.read(Validators.class)); + } + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + BlockHeader blockHeader = this.getBlockHeader(); + if (blockHeader != null) { + ByteArrayObjectWriter blockHeaderWriter = Context.newByteArrayObjectWriter("RLPn"); + blockHeaderWriter.writeNullable(blockHeader); + writer.write(blockHeaderWriter.toByteArray()); + } else { + writer.writeNull(); + } + Votes votes = this.getVotes(); + if (votes != null) { + ByteArrayObjectWriter votesWriter = Context.newByteArrayObjectWriter("RLPn"); + votesWriter.write(votes); + writer.write(votesWriter.toByteArray()); + } else { + writer.writeNull(); + } + Validators nextValidators = this.getNextValidators(); + if (nextValidators != null) { + ByteArrayObjectWriter nextValidatorsWriter = Context.newByteArrayObjectWriter("RLPn"); + nextValidatorsWriter.writeNullable(nextValidators); + writer.write(nextValidatorsWriter.toByteArray()); + } else { + writer.writeNull(); + } + writer.end(); + } + + public static BlockUpdate fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BlockUpdate.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BlockUpdate.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockWitness.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockWitness.java new file mode 100644 index 00000000..0693d912 --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/BlockWitness.java @@ -0,0 +1,109 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class BlockWitness { + private long height; + private byte[][] witness; + + public long getHeight() { + return height; + } + + public void setHeight(long height) { + this.height = height; + } + + public byte[][] getWitness() { + return witness; + } + + public void setWitness(byte[][] witness) { + this.witness = witness; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BlockWitness{"); + sb.append("height=").append(height); + sb.append(", witness=").append(StringUtil.toString(witness)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, BlockWitness obj) { + obj.writeObject(writer); + } + + public static BlockWitness readObject(ObjectReader reader) { + BlockWitness obj = new BlockWitness(); + reader.beginList(); + obj.setHeight(reader.readLong()); + if (reader.beginNullableList()) { + byte[][] witness = null; + List witnessList = new ArrayList<>(); + while(reader.hasNext()) { + witnessList.add(reader.readNullable(byte[].class)); + } + witness = new byte[witnessList.size()][]; + for(int i=0; i> 4 & 0x0F); + ret[j++] = (byte)(bytes[i] & 0x0F); + } + return ret; + } + + public static class Node { + private byte[] hash; + private byte[] nibbles; + private Node[] children; + private byte[] serialized; + private byte[] data; + + public Node() {} + + public Node(byte[] hash) { + this.hash = hash; + } + + public static Node readObject(ObjectReader reader) { + Node obj = new Node(); + reader.beginList(); + Object[] arr = new Object[17]; + int i = 0; + while(reader.hasNext()) { + try { + arr[i] = reader.readByteArray(); + } catch (IllegalStateException e) { + if (i < 16) { + arr[i] = readObject(reader); + } else { + throw new MPTException("decode failure, branchNode.data required byte[]"); + } + } + i++; + } + reader.end(); + if (i == 2) { + if (arr[0] instanceof byte[] && arr[1] instanceof byte[]) { + byte[] header = (byte[])arr[0]; + int prefix = header[0] & 0xF0; + byte[] nibbles = null; + if ((prefix & 0x10) != 0) { + nibbles = new byte[]{(byte) (header[0] & 0x0F)}; + } + obj.nibbles = bytesToNibbles(header, 1, nibbles); + if ((prefix & 0x20) != 0) { + obj.data = (byte[])arr[1]; + } else { + Node node = new Node((byte[])arr[1]); + obj.children = new Node[]{node}; + } + } else { + throw new MPTException("decode failure, required byte[]"); + } + } else if (i == 17){ + obj.children = new Node[16]; + for(int j = 0; j < 16; j++) { + if (arr[j] instanceof Node) { + obj.children[j] = (Node) arr[j]; + } else if (arr[j] instanceof byte[]) { + byte[] bytes = (byte[]) arr[j]; + if (bytes.length > 0) { + obj.children[j] = new Node(bytes); + } + } else { + throw new MPTException("decode failure, required byte[] or Node"); + } + } + obj.data = (byte[])arr[16]; + } else { + throw new MPTException("decode failure, invalid list length "+i); + } + return obj; + } + + public static Node fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return readObject(reader); + } + + public byte[] prove(byte[] nibbles, byte[][] proofs, int i) { + if (isHash()) { + byte[] serialized = proofs[i]; + byte[] hash = hash(serialized); + if (!Arrays.equals(this.hash, hash)) { + throw new MPTException("mismatch hash"); + } + Node node = Node.fromBytes(serialized); + node.hash = hash; + node.serialized = serialized; + return node.prove(nibbles, proofs, i+1); + } else if (isExtension()) { + int cnt = ArrayUtil.matchCount(this.nibbles, nibbles); + if (cnt < this.nibbles.length) { + throw new MPTException("mismatch nibbles on extension"); + } + return children[0].prove(Arrays.copyOfRange(nibbles, cnt, nibbles.length), proofs, i); + } else if (isBranch()) { + if(nibbles.length == 0) { + return data; + } else { + Node node = children[nibbles[0]]; + return node.prove(Arrays.copyOfRange(nibbles, 1, nibbles.length), proofs, i); + } + } else { + int cnt = ArrayUtil.matchCount(this.nibbles, nibbles); + if (cnt < nibbles.length) { + throw new MPTException("mismatch nibbles on leaf"); + } + return data; + } + } + + static byte[] hash(byte[] bytes) { + return Context.hash("sha3-256",bytes); + } + + private boolean isHash() { + return hash.length > 0 && serialized == null; + } + + private boolean isExtension() { + return children != null && children.length == 1; + } + + private boolean isBranch() { + return children != null && children.length == 16; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Node{"); + sb.append("hash=").append(StringUtil.toString(hash)); + sb.append(", nibbles=").append(StringUtil.toString(nibbles)); + sb.append(", children=").append(StringUtil.toString(children)); + sb.append(", serialized=").append(StringUtil.toString(serialized)); + sb.append(", data=").append(StringUtil.toString(data)); + sb.append('}'); + return sb.toString(); + } + } + +} diff --git a/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/MerkleTreeAccumulator.java b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/MerkleTreeAccumulator.java new file mode 100644 index 00000000..63ff2461 --- /dev/null +++ b/javascore/bmv/icon/src/main/java/foundation/icon/btp/bmv/icon/MerkleTreeAccumulator.java @@ -0,0 +1,421 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv.icon; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.Arrays; +import java.util.List; + +public class MerkleTreeAccumulator { + private static final int HASH_LEN = 32; + + private long height; + private byte[][] roots; + private long offset; + //optional reader.hasNext() + private Integer rootSize; + private Integer cacheSize; + private byte[][] cache; + private Boolean allowNewerWitness; + // + private Integer cacheIdx; + + public long getOffset() { + return offset; + } + + public void setOffset(long offset) { + this.offset = offset; + } + + public long getHeight() { + return height; + } + + public void setHeight(long height) { + this.height = height; + } + + public byte[][] getRoots() { + return roots; + } + + public void setRoots(byte[][] roots) { + this.roots = roots; + } + + public Integer getRootSize() { + return rootSize; + } + + public void setRootSize(Integer rootSize) { + this.rootSize = rootSize; + } + + public Integer getCacheSize() { + return cacheSize; + } + + public void setCacheSize(Integer cacheSize) { + this.cacheSize = cacheSize; + } + + public Integer getCacheIdx() { + return cacheIdx; + } + + public void setCacheIdx(Integer cacheIdx) { + this.cacheIdx = cacheIdx; + } + + public byte[][] getCache() { + return cache; + } + + public void setCache(byte[][] cache) { + this.cache = cache; + } + + public Boolean getAllowNewerWitness() { + return allowNewerWitness; + } + + public void setAllowNewerWitness(Boolean allowNewerWitness) { + this.allowNewerWitness = allowNewerWitness; + } + + public boolean isAllowNewerWitness() { + return allowNewerWitness != null && allowNewerWitness; + } + + private static byte[] concatAndHash(byte[] b1, byte[] b2) { + byte[] data = new byte[HASH_LEN * 2]; + System.arraycopy(b1, 0, data, 0, HASH_LEN); + System.arraycopy(b2, 0, data, HASH_LEN, HASH_LEN); + return Context.hash("sha3-256", data); + } + + private static void verify(byte[][] witness, int witnessLen, byte[] root, byte[] hash, long idx) { + for (int i = 0; i < witnessLen; i++) { + if (idx % 2 == 0) { + hash = concatAndHash(hash, witness[i]); + } else { + hash = concatAndHash(witness[i], hash); + } + idx = idx / 2; + } + if (!Arrays.equals(root, hash)) { + throw new MTAException("invalid witness"+ + ", root: "+StringUtil.toString(root) + ", hash: "+StringUtil.toString(hash)); + } + } + + public void verify(byte[][] witness, byte[] hash, long height, long at) { + if (this.height == at) { + byte[] root = getRoot(witness.length); + verify(witness, witness.length, root, hash, height - 1 - offset); + } else if (this.height < at) { + if (!isAllowNewerWitness()) { + throw new MTAException.InvalidWitnessNewerException("not allowed newer witness"); + } + if (this.height < height) { + throw new MTAException("given witness for newer node"); + } + int rootIdx = getRootIdxByHeight(height); + byte[] root = getRoot(rootIdx); + verify(witness, rootIdx, root, hash, height - 1 - offset); + } else { + // acc: new, wit: old + // rebuild witness is not supported, but able to verify by cache if enabled + if (isCacheEnabled() && (this.height - height - 1) < cacheSize) { + if (!hasCache(hash)) { + throw new MTAException("invalid old witness"); + } + } else { + throw new MTAException.InvalidWitnessOldException("not allowed old witness"); + } + } + } + + private int getRootIdxByHeight(long height) { + if (height <= offset) { + throw new MTAException("given height is out of range"); + } + long idx = height - 1 - offset; + int rootIdx = (roots == null ? 0 : roots.length) - 1; + while (rootIdx >= 0) { + if (roots[rootIdx] != null) { + long bitFlag = 1L << rootIdx; + if (idx < bitFlag) { + break; + } + idx -= bitFlag; + } + rootIdx--; + } + if (rootIdx < 0) { + throw new MTAException("given height is out of range"); + } + return rootIdx; + } + + private byte[] getRoot(int idx) { + if (idx < 0 || roots == null || idx >= roots.length) { + throw new MTAException("root idx is out of range"); + } else { + return roots[idx]; + } + } + + private void appendRoot(byte[] hash) { + int len = roots == null ? 0 : roots.length; + byte[][] roots = new byte[len + 1][]; + roots[len] = hash; + this.roots = roots; + } + + public boolean isRootSizeLimitEnabled() { + return rootSize != null && rootSize > 0; + } + + /** + * call after update rootSize + */ + public void ensureRoots() { + if (isRootSizeLimitEnabled() && rootSize < this.roots.length) { + byte[][] roots = new byte[rootSize][]; + int i = rootSize - 1; + int j = this.roots.length - 1; + while(i >= 0) { + roots[i--] = this.roots[j--]; + } + while(j >= 0) { + if (this.roots[j] != null) { + addOffset(j--); + } + } + this.roots = roots; + } + } + + public void add(byte[] hash) { + putCache(hash); + if (height == offset) { + appendRoot(hash); + } else { + boolean isAdded = false; + int len = roots == null ? 0 : roots.length; + int pruningIdx = (isRootSizeLimitEnabled() ? rootSize : 0) - 1; + for (int i = 0; i < len; i++) { + if (roots[i] == null) { + roots[i] = hash; + isAdded = true; + break; + } else { + if (i == pruningIdx) { + roots[i] = hash; + addOffset(i); + isAdded = true; + break; + } else { + hash = concatAndHash(roots[i], hash); + roots[i] = null; + } + } + } + if (!isAdded) { + appendRoot(hash); + } + } + height++; + } + + private void addOffset(int rootIdx) { + long offset = (long) StrictMath.pow(2, rootIdx); + this.offset += offset; + } + + public boolean isCacheEnabled() { + return cacheSize != null && cacheSize > 0; + } + + /** + * call after update cacheSize + */ + public void ensureCache() { + if (isCacheEnabled()) { + if (cache == null) { + cache = new byte[cacheSize][]; + cacheIdx = 0; + } else if (cache.length != cacheSize) { + byte[][] cache = new byte[cacheSize][]; + //copy this.cache to cache + int len = this.cache.length; + int src = this.cacheIdx; + int dst = 0; + for (int i = 0; i < len; i++) { + byte[] v = this.cache[src++]; + if (src >= len) { + src = 0; + } + if (v == null) { + continue; + } + cache[dst++] = v; + if (dst >= cacheSize) { + dst = 0; + break; + } + } + this.cache = cache; + this.cacheIdx = dst; + } + } else { + cache = null; + } + } + + private boolean hasCache(byte[] hash) { + if (isCacheEnabled()) { + for (byte[] v : cache) { + if (Arrays.equals(v, hash)) { + return true; + } + } + } + return false; + } + + private void putCache(byte[] hash) { + if (isCacheEnabled()) { + cache[cacheIdx++] = hash; + if (cacheIdx >= cache.length) { + cacheIdx = 0; + } + } + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MerkleTreeAccumulator{"); + sb.append("height=").append(height); + sb.append(", roots=").append(StringUtil.toString(roots)); + sb.append(", offset=").append(offset); + sb.append(", rootSize=").append(rootSize); + sb.append(", cacheSize=").append(cacheSize); + sb.append(", cache=").append(StringUtil.toString(cache)); + sb.append(", allowNewerWitness=").append(allowNewerWitness); + sb.append(", cacheIdx=").append(cacheIdx); + sb.append('}'); + return sb.toString(); + } + + + public static void writeObject(ObjectWriter writer, MerkleTreeAccumulator obj) { + obj.writeObject(writer); + } + + public static MerkleTreeAccumulator readObject(ObjectReader reader) { + MerkleTreeAccumulator obj = new MerkleTreeAccumulator(); + reader.beginList(); + obj.setHeight(reader.readLong()); + if (reader.beginNullableList()) { + byte[][] roots = null; + List rootsList = new ArrayList<>(); + while(reader.hasNext()) { + rootsList.add(reader.readNullable(byte[].class)); + } + roots = new byte[rootsList.size()][]; + for(int i=0; i cacheList = new ArrayList<>(); + while(reader.hasNext()) { + cacheList.add(reader.readNullable(byte[].class)); + } + cache = new byte[cacheList.size()][]; + for(int i=0; i eventProofsList = new ArrayList<>(); + while(reader.hasNext()) { + eventProofsList.add(reader.readNullable(MPTProof.class)); + } + eventProofs = new MPTProof[eventProofsList.size()]; + for(int i=0; i receiptProofsList = new ArrayList<>(); + while(reader.hasNext()) { + byte[] receiptProofsElementBytes = reader.readNullable(byte[].class); + if (receiptProofsElementBytes != null) { + ObjectReader receiptProofsElementReader = Context.newByteArrayObjectReader("RLPn",receiptProofsElementBytes); + receiptProofsList.add(receiptProofsElementReader.read(ReceiptProof.class)); + } else { + receiptProofsList.add(null); + } + } + receiptProofs = new ReceiptProof[receiptProofsList.size()]; + for(int i=0; i tokenized = StringUtil.tokenize(str, ','); + try { + Address[] addresses = new Address[tokenized.size()]; + for (int i=0; i 0) { + this.blockHeader = new BlockHeader(encodedHeader); + } else { + this.blockHeader = null; + } + } else { + this.blockHeader = null; + } + + long mtaHeight = 0; + List witnesses = new ArrayList(5); + if (rlpReader.hasNext()) { + rlpReader.beginList(); + mtaHeight = rlpReader.readLong(); + if (rlpReader.hasNext()) { + rlpReader.beginList(); + while (rlpReader.hasNext()) { + byte[] witness = rlpReader.readByteArray(); + witnesses.add(witness); + } + rlpReader.end(); + } + rlpReader.end(); + } + + rlpReader.end(); + + this.blockWitness = new BlockWitness(mtaHeight, witnesses); + } + + public void verify(SerializableMTA mta) { + if (mta.height() < this.blockHeader.getNumber()) { + Context.revert(ErrorCode.INVALID_BLOCK_PROOF_HEIGHT_HIGHER, "given block height is newer " + this.blockHeader.getNumber() + " expect: " + mta.height()); + } + + this.blockWitness.verify(mta, this.blockHeader.getHash(), this.blockHeader.getNumber()); + } + + public BlockHeader getBlockHeader() { + return this.blockHeader; + } + + public BlockWitness getBlockWitness() { + return this.blockWitness; + } + + public static BlockProof fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new BlockProof(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("BlockProof", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/blockProof/BlockWitness.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/blockProof/BlockWitness.java new file mode 100644 index 00000000..252fde77 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/blockProof/BlockWitness.java @@ -0,0 +1,37 @@ +package foundation.icon.btp.lib.blockProof; + +import java.util.List; + +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.exception.mtaException.*; +import foundation.icon.btp.lib.mta.SerializableMTA; + +import score.Context; + +public class BlockWitness { + private final long height; + private final List witness; + + public BlockWitness(long height, List witness) { + this.height = height; + this.witness = witness; + } + + public void verify(SerializableMTA mta, byte[] hash, long height) { + try { + mta.verify(this.witness, hash, height, this.height); + } catch (InvalidWitnessOldException e) { + Context.revert(ErrorCode.INVALID_BLOCK_WITNESS_OLD, e.toString()); + } catch (MTAException e) { + Context.revert(ErrorCode.INVALID_BLOCK_WITNESS, e.toString()); + } + } + + public long getHeight() { + return this.height; + } + + public List getWitness() { + return this.witness; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/btpAddress/BTPAddress.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/btpAddress/BTPAddress.java new file mode 100644 index 00000000..8a85eada --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/btpAddress/BTPAddress.java @@ -0,0 +1,79 @@ +package foundation.icon.btp.lib.btpAddress; + +public class BTPAddress { + private String protocol; + private String chain; + private String address; + private String nid; + + public BTPAddress(String protocol, String net, String address) { + this.protocol = protocol; + + if (net != null) { + int idx = net.indexOf("."); + if (idx < 0) { + throw new AssertionError("fail to parse net address"); + } + + this.nid = net.substring(0, idx); + this.chain = net.substring(idx + 1); + } + + this.address = address; + } + + public String getProtocol() { + return this.protocol; + } + + public String getChain() { + return this.chain; + } + + public String getAddress() { + return this.address; + } + + public String getNid() { + return this.nid; + } + + public String getNet() { + return this.nid + "." + this.chain; + } + + public boolean isValid() { + return this.protocol != null && this.nid != null && this.address != null && this.chain != null; + } + + public static BTPAddress fromString(String rawString) { + if (rawString == null) { + return null; + } + + char[] rawCharArray = rawString.toCharArray(); + + String protocol = null; + String net = null; + String address = null; + for (int i = 0; i < rawCharArray.length; i++) { + if (rawCharArray[i] == ':' && rawCharArray[i+1] == '/' && rawCharArray[i+2] == '/' && protocol == null) { + protocol = rawString.substring(0, i); + i = i+2; + } else if (rawCharArray[i] == '/' && net == null) { + net = rawString.substring(protocol.length() + 3, i); + address = rawString.substring(protocol.length() + 4 + net.length()); + } + } + + if (address != null && address.length() > 2 && address.charAt(0) == '0' && address.charAt(1) == 'x') { + address = address.substring(2, address.length()); + } + + return new BTPAddress(protocol, net, address); + } + + public String toString() { + return this.protocol + "://" + this.nid + "." + this.chain + "/" + this.address; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateDescriptor.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateDescriptor.java new file mode 100644 index 00000000..ccc3257d --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateDescriptor.java @@ -0,0 +1,64 @@ +package foundation.icon.btp.lib.candidateReceipt; + +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.scale.ScaleReader; + +public class CandidateDescriptor { + private long paraId; + private byte[] relayParent; + private byte[] collatorId; + private byte[] persistedValidationDataHash; + private byte[] povHash; + private byte[] erasureRoot; + private byte[] signature; + private byte[] paraHead; + private byte[] validationCodeHash; + + public CandidateDescriptor(ByteSliceInput input) { + this.paraId = ScaleReader.readU32(input); + this.relayParent = input.take(32); + this.collatorId = input.take(32); + this.persistedValidationDataHash = input.take(32); + this.povHash = input.take(32); + this.erasureRoot = input.take(32); + this.signature = input.take(64); + this.paraHead = input.take(32); + this.validationCodeHash = input.take(32); + } + + public long getParaId() { + return this.paraId; + } + + public byte[] getRelayParent() { + return this.relayParent; + } + + public byte[] getCollatorId() { + return this.collatorId; + } + + public byte[] getpPersistedValidationDataHash() { + return this.persistedValidationDataHash; + } + + public byte[] getPovHash() { + return this.povHash; + } + + public byte[] getErasureRoot() { + return this.erasureRoot; + } + + public byte[] getSignature() { + return this.signature; + } + + public byte[] getParaHead() { + return this.paraHead; + } + + public byte[] getValidationCodeHash() { + return this.validationCodeHash; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateReceipt.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateReceipt.java new file mode 100644 index 00000000..9d789938 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/candidateReceipt/CandidateReceipt.java @@ -0,0 +1,21 @@ +package foundation.icon.btp.lib.candidateReceipt; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class CandidateReceipt { + private CandidateDescriptor descriptor; + private byte[] commitmentsHash; + + public CandidateReceipt(ByteSliceInput input) { + this.descriptor = new CandidateDescriptor(input); + this.commitmentsHash = input.take(32); + } + + public CandidateDescriptor getCandidateDescriptor() { + return this.descriptor; + } + + public byte[] getCommitmentsHash() { + return this.commitmentsHash; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/CandidateIncludedEvent.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/CandidateIncludedEvent.java new file mode 100644 index 00000000..4752f518 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/CandidateIncludedEvent.java @@ -0,0 +1,36 @@ +package foundation.icon.btp.lib.event; + +import foundation.icon.btp.lib.scale.ScaleReader; +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.candidateReceipt.CandidateReceipt; + +public class CandidateIncludedEvent { + private CandidateReceipt candidateReceipt; + private byte[] headData; + private long coreIndex; + private long groupIndex; + + public CandidateIncludedEvent(byte[] eventData) { + ByteSliceInput input = new ByteSliceInput(eventData); + this.candidateReceipt = new CandidateReceipt(input); + this.headData = ScaleReader.readBytes(input); + this.coreIndex = ScaleReader.readU32(input); + this.groupIndex = ScaleReader.readU32(input); + } + + public CandidateReceipt getCandidateReceipt() { + return this.candidateReceipt; + } + + public byte[] getHeadData() { + return this.headData; + } + + public long getCoreIndex() { + return this.coreIndex; + } + + public long getGroupIndex() { + return this.groupIndex; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EVMLogEvent.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EVMLogEvent.java new file mode 100644 index 00000000..345251df --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EVMLogEvent.java @@ -0,0 +1,37 @@ +package foundation.icon.btp.lib.event; + +import foundation.icon.btp.lib.scale.ScaleReader; +import foundation.icon.btp.lib.utils.ByteSliceInput; + +import scorex.util.ArrayList; +import java.util.List; + +public class EVMLogEvent { + protected byte[] address; + protected List evmTopics = new ArrayList(3); + protected byte[] evmData; + + public EVMLogEvent(byte[] eventData) { + ByteSliceInput input = new ByteSliceInput(eventData); + this.address = input.take(20); // 20 bytes address of contract + int topicSize = ScaleReader.readUintCompactSize(input); // u32 compact number of item in list + for (int i = 0; i < topicSize; i++) { + this.evmTopics.add(input.take(32)); // 32 bytes of topic; + } + + int evmDataSize = ScaleReader.readUintCompactSize(input); // u32 compact number of bytes of evm data + this.evmData = input.take(evmDataSize); + } + + public byte[] getAddress() { + return this.address; + } + + public List getEvmTopics() { + return this.evmTopics; + } + + public byte[] getEvmEventData() { + return this.evmData; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EventRecord.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EventRecord.java new file mode 100644 index 00000000..73a7bdae --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/EventRecord.java @@ -0,0 +1,40 @@ +package foundation.icon.btp.lib.event; +import java.util.List; +import java.util.Map; +import java.math.BigInteger; + +public class EventRecord { + private byte phaseEnum; + private byte[] phaseData; + private byte[] eventIndex; + private byte[] eventData; + private List topics; + + public EventRecord(Map rawData) { + this.phaseEnum = ((BigInteger) rawData.get("phaseEnum")).toByteArray()[0]; + this.phaseData = (byte[]) rawData.get("phaseData"); + this.eventIndex = (byte[]) rawData.get("eventIndex"); + this.eventData = (byte[]) rawData.get("eventData"); + this.topics = (List) rawData.get("topics"); + } + + public byte getPhaseEnum() { + return this.phaseEnum; + } + + public byte[] getPhaseData() { + return this.phaseData; + } + + public byte[] getEventIndex() { + return this.eventIndex; + } + + public byte[] getEventData() { + return this.eventData; + } + + public List getTopics() { + return this.topics; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/NewAuthoritiesEvent.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/NewAuthoritiesEvent.java new file mode 100644 index 00000000..afd4a325 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/NewAuthoritiesEvent.java @@ -0,0 +1,25 @@ +package foundation.icon.btp.lib.event; + +import java.util.List; + +import foundation.icon.btp.lib.scale.ScaleReader; +import foundation.icon.btp.lib.utils.ByteSliceInput; + +import scorex.util.ArrayList; + +public class NewAuthoritiesEvent { + private final List validators = new ArrayList(100); + + public NewAuthoritiesEvent(byte[] eventData) { + ByteSliceInput input = new ByteSliceInput(eventData); + int validatorSize = ScaleReader.readUintCompactSize(input); + for (int i = 0; i < validatorSize; i++) { + validators.add(input.take(32)); + input.take(8); // AuthorityWeight u64 + } + } + + public List getValidators() { + return validators; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/evmEvent/BTPMessageEvmEvent.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/evmEvent/BTPMessageEvmEvent.java new file mode 100644 index 00000000..295bbddf --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/event/evmEvent/BTPMessageEvmEvent.java @@ -0,0 +1,43 @@ +package foundation.icon.btp.lib.event.evmEvent; + +import java.math.BigInteger; + +import foundation.icon.btp.lib.utils.AbiDecoder; +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class BTPMessageEvmEvent { + private final String next; + private final BigInteger seq; + private final byte[] msg; + + public BTPMessageEvmEvent(byte[] data) { + ByteSliceInput input = new ByteSliceInput(data); + BigInteger nextPosition = AbiDecoder.decodeUInt(input); + + this.seq = AbiDecoder.decodeUInt(input); + + BigInteger msgPosition = AbiDecoder.decodeUInt(input); + + if (!nextPosition.equals(BigInteger.valueOf(input.getOffset()))) { + throw new AssertionError("Message evm data invalid, next position incorrect" + nextPosition.toString() + "; expected: " + input.getOffset()); + } + this.next = AbiDecoder.decodeString(input); + + if (!msgPosition.equals(BigInteger.valueOf(input.getOffset()))) { + throw new AssertionError("Message evm data invalid, msg position incorrect : " + msgPosition.toString() + "; expected: " + input.getOffset()); + } + this.msg = AbiDecoder.decodeBytes(input); + } + + public String getNextBmc() { + return this.next; + } + + public BigInteger getSeq() { + return this.seq; + } + + public byte[] getMsg() { + return this.msg; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/RelayMessageRLPException.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/RelayMessageRLPException.java new file mode 100644 index 00000000..e4935eb8 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/RelayMessageRLPException.java @@ -0,0 +1,25 @@ +package foundation.icon.btp.lib.exception; + +@SuppressWarnings("serial") +public class RelayMessageRLPException extends Exception { + private final String scope; + private final String originalError; + public RelayMessageRLPException() { + super(); + this.scope = "unknown"; + this.originalError = "unknown"; + } + + public RelayMessageRLPException(String scope, String originalError) { + this.scope = scope; + this.originalError = originalError; + } + + public String getScope() { + return this.scope; + } + + public String getOriginalError() { + return this.originalError; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessNewerException.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessNewerException.java new file mode 100644 index 00000000..7ffa3dd2 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessNewerException.java @@ -0,0 +1,12 @@ +package foundation.icon.btp.lib.exception.mtaException; + +@SuppressWarnings("serial") +public class InvalidWitnessNewerException extends MTAException { + public InvalidWitnessNewerException() { + super("unknown"); + } + + public InvalidWitnessNewerException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessOldException.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessOldException.java new file mode 100644 index 00000000..021838b5 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/InvalidWitnessOldException.java @@ -0,0 +1,12 @@ +package foundation.icon.btp.lib.exception.mtaException; + +@SuppressWarnings("serial") +public class InvalidWitnessOldException extends MTAException { + public InvalidWitnessOldException() { + super("unknown"); + } + + public InvalidWitnessOldException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/MTAException.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/MTAException.java new file mode 100644 index 00000000..f35ffdb7 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/exception/mtaException/MTAException.java @@ -0,0 +1,23 @@ +package foundation.icon.btp.lib.exception.mtaException; + +@SuppressWarnings("serial") +public class MTAException extends Exception { + private final String message; + public MTAException() { + super(); + this.message = "unknown"; + } + + public MTAException(String message) { + super(); + this.message = message; + } + + public String getMessage() { + return this.message; + } + + public String toString() { + return message; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNode.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNode.java new file mode 100644 index 00000000..a7a3fe58 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNode.java @@ -0,0 +1,123 @@ +package foundation.icon.btp.lib.mpt; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +import score.Context; + +public class MPTNode { + private Nibbles nibbles; + private MPTNodeType type; + private byte[] data; + private byte[][] childrens; + private byte[] hash; + + public MPTNode(byte[] serialized) { + this.hash = Context.hash("blake2b-256", serialized); + byte prefix = (byte) ((serialized[0] & 0xff) >> 6); + if (prefix == 0b00) { + this.type = MPTNodeType.EMPTY; + } else { // leaf node + this.type = MPTNodeType.LEAF; + + ByteSliceInput input = new ByteSliceInput(serialized); + short nibblesCount = this.decodeNibbleSize(input); + boolean padding = nibblesCount % 2 != 0; + if (padding && (serialized[input.getOffset()] & 0b11110000) != 0) { + throw new AssertionError("invalid mpt node format"); + } + + if (nibblesCount > 0) { + short nibblesCountInByte = (short) ((nibblesCount + 1) / 2); + this.nibbles = new Nibbles(input.take(nibblesCountInByte), padding); + } + + if (prefix == 0b01) { + this.data = this.readValue(input); + } else { + short bitmap = this.readBitMap(input); + + this.type = MPTNodeType.BRANCH; + if (prefix == 0b11) { // branch node with value + this.data = this.readValue(input); + this.type = MPTNodeType.BRANCH_WITH_VALUE; + } + + this.childrens = new byte[16][]; + for (int i = 0; i < 16; i++) { + if ((bitmap & 1<> 4) | ((firstByte & 0x0f) << 4); + // int secondByteRevert = ((secondByte & 0xf0) >> 4) | ((secondByte & 0x0f) << 4); + return (short) ((secondByte << 8) | firstByte); + } + + private int readCompactSize(ByteSliceInput input) { + int i = input.takeUByte(); + byte mode = (byte) (i & 0b00000011); + if (mode == 0b00) { + return i >> 2; + } + if (mode == 0b01) { + return (i >> 2) + + (input.takeUByte() << 6); + } + if (mode == 0b10) { + return (i >> 2) + + (input.takeUByte() << 6) + + (input.takeUByte() << (6 + 8)) + + (input.takeUByte() << (6 + 2 * 8)); + } + throw new AssertionError("Mode " + mode + " is not implemented"); + } + + private byte[] readValue(ByteSliceInput input) { + int valueSize = this.readCompactSize(input); + return input.take(valueSize); + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNodeType.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNodeType.java new file mode 100644 index 00000000..39586920 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MPTNodeType.java @@ -0,0 +1,13 @@ +package foundation.icon.btp.lib.mpt; + +public enum MPTNodeType { + EMPTY((byte)0b00), + LEAF((byte)0b01), + BRANCH((byte)0b10), + BRANCH_WITH_VALUE((byte)0b11); + + private final byte type; + MPTNodeType(byte type) { + this.type = type; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MerklePatriciaTree.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MerklePatriciaTree.java new file mode 100644 index 00000000..59bac18e --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/MerklePatriciaTree.java @@ -0,0 +1,73 @@ +package foundation.icon.btp.lib.mpt; + +import scorex.util.ArrayList; + +public class MerklePatriciaTree { + public static byte[] prove(byte[] root, Nibbles provingNibbles, ArrayList proofs) { + for (int i = 0; i< proofs.size(); i++) { + MPTNode node = proofs.get(i); + if (root != null && !java.util.Arrays.equals(node.getHash(), root)) { + continue; + } + if (node.getType() == MPTNodeType.BRANCH) { + int matchNibbleSize = 0; + if (node.getNibbles() != null) { + matchNibbleSize = node.getNibbles().match(provingNibbles); + + if (matchNibbleSize < node.getNibbles().size()) { + continue; + } + } + + if (matchNibbleSize == provingNibbles.size()) { + if (node.getData() == null || node.getData().length == 0) { + continue; + } + return node.getData(); + } + + int childNodeIndex = provingNibbles.getNibble(matchNibbleSize); // next nibbles, 4 bits + byte[] childData = node.getChildrens()[childNodeIndex]; + if (childData == null) { + continue; + } + + Nibbles remainingProvingNibble = provingNibbles.createChildNibbles(matchNibbleSize + 1); + if (childData.length < 32) { // inline child node + // if (proofs.size() > 0) { // inline child node is already valid, do not need any proof here + // throw new AssertionError("MPT redundant proof"); + // } + MPTNode childNode = new MPTNode(childData); + ArrayList remainingProofs = new ArrayList(1); + remainingProofs.add(childNode); + return MerklePatriciaTree.prove(null, remainingProvingNibble, remainingProofs); + } else { // hash of child node store in branch node + if (proofs.size() == 1) { + throw new AssertionError("MPT missing proof"); + } + ArrayList remainingProofs = new ArrayList(proofs); + remainingProofs.remove(i); + return MerklePatriciaTree.prove(childData, remainingProvingNibble, remainingProofs); + } + } else if (node.getType() == MPTNodeType.LEAF) { + int matchNibbleSize = 0; + if (node.getNibbles() != null) { + matchNibbleSize = node.getNibbles().match(provingNibbles); + + if (matchNibbleSize < node.getNibbles().size()) { + throw new AssertionError("MPT mismatch nibbles on leaf node"); + } + } + + if (matchNibbleSize != provingNibbles.size()) { + throw new AssertionError("MPT redundant nibble"); + } + + return node.getData(); + } else { + throw new AssertionError("unhandle empty MPT node"); + } + } + throw new AssertionError("invalid MPT proof"); + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/Nibbles.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/Nibbles.java new file mode 100644 index 00000000..a77d18d3 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mpt/Nibbles.java @@ -0,0 +1,82 @@ +package foundation.icon.btp.lib.mpt; + +import foundation.icon.btp.lib.utils.Arrays; + +import score.Context; + +public class Nibbles { + private byte[] raw; + private final boolean hasPadding; + + public Nibbles(byte[] raw, boolean hasPadding) { + this.raw = raw; + this.hasPadding = hasPadding; + } + + public int size() { + int baseLength = this.raw.length*2; + return hasPadding ? baseLength - 1 : baseLength; + } + + public byte[] getRaw() { + return this.raw; + } + + public byte getNibble(int idx) { + if ((idx % 2) == 0) { + if (this.hasPadding) { + return (byte) (this.raw[idx/2] & 0x0f); + } else { + return (byte) ((this.raw[idx/2] & 0xf0) >> 4); + } + } else { + if (this.hasPadding) { + return (byte) ((this.raw[idx/2 + 1] & 0xf0) >> 4); + } else { + return (byte) (this.raw[idx/2] & 0x0f); + } + } + } + + public int match(Nibbles provingNibbles) { + if (this.size() == 0 || provingNibbles.size() == 0) { + return 0; + } + + if (provingNibbles.size() < this.size()) { + throw new AssertionError("proving nibble length is less than node nibble length"); + } + + for (int i = 0; i < this.size(); i++) { + if (this.getNibble(i) != provingNibbles.getNibble(i)) { + return i; + } + } + return this.size(); + } + + public Nibbles createChildNibbles(int from) { + byte[] childRaw; + boolean hasPadding = false; + if ((from % 2) == 0) { + if (this.hasPadding) { + childRaw = Arrays.copyOfRangeByte(this.raw, from/2, this.raw.length); + System.arraycopy(this.raw, from/2, childRaw, 0, + this.raw.length - from/2); + childRaw[0] = (byte) (this.raw[from/2] & 0x0f); + hasPadding = true; + } else { + childRaw = Arrays.copyOfRangeByte(this.raw, from/2, this.raw.length); + } + } else { + if (this.hasPadding) { + childRaw = Arrays.copyOfRangeByte(this.raw, from/2 + 1, this.raw.length); + } else { + childRaw = Arrays.copyOfRangeByte(this.raw, from/2, this.raw.length); + childRaw[0] = (byte) (this.raw[from/2] & 0x0f); + hasPadding = true; + } + } + return new Nibbles(childRaw, hasPadding); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MTAStatus.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MTAStatus.java new file mode 100644 index 00000000..c3017ba0 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MTAStatus.java @@ -0,0 +1,13 @@ +package foundation.icon.btp.lib.mta; +public class MTAStatus { + public long height; + public long offset; + + public MTAStatus( + long height, + long offset + ) { + this.height = height; + this.offset = offset; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MerkleTreeAccumulator.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MerkleTreeAccumulator.java new file mode 100644 index 00000000..04e5f091 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/MerkleTreeAccumulator.java @@ -0,0 +1,272 @@ +package foundation.icon.btp.lib.mta; + +import java.util.List; +import scorex.util.ArrayList; +import score.Context; + +import foundation.icon.btp.lib.exception.mtaException.*; + +public class MerkleTreeAccumulator { + private long height; + private List roots; + private long offset; + private int rootSize; + private int cacheSize; + private List caches; + private boolean isAllowNewerWitness; + private byte[] lastBlockHash; + + public MerkleTreeAccumulator( + long height, + long offset, + int rootSize, + int cacheSize, + boolean isAllowNewerWitness, + byte[] lastBlockHash, + List roots, + List caches + ) { + this.height = height; + this.offset = offset; + this.rootSize = rootSize; + this.cacheSize = cacheSize; + this.isAllowNewerWitness = isAllowNewerWitness; + this.lastBlockHash = lastBlockHash; + + if (roots != null && roots.size() > 0) { + this.roots = roots; + } else { + this.roots = new ArrayList(rootSize); + } + + if (caches != null && caches.size() > 0) { + this.caches = caches; + } else { + this.caches = new ArrayList(cacheSize); + } + + if (this.height == 0 && this.offset > 0) + this.height = this.offset; + } + + public byte[] getHash(byte[] data) { + return Context.hash("sha3-256", data); + } + + public byte[] lastBlockHash() { + return this.lastBlockHash; + } + + private byte[] getConcatenationHash(byte[] item1, byte[] item2) { + byte[] concatenation = new byte[item1.length + item2.length]; + System.arraycopy(item1, 0, concatenation, 0, item1.length); + System.arraycopy(item2, 0, concatenation, item1.length, item2.length); + return this.getHash(concatenation); + } + + public long height() { + return this.height; + } + + public long offset() { + return this.offset; + } + + public void setOffset(long offset) throws MTAException { + if (this.roots.size() > 0) { + throw new MTAException("not allow to set offset if roots is not empty"); + } + + this.offset = offset; + if (this.offset > 0 && this.height < this.offset) + this.height = this.offset; + } + + public int rootSize() { + return this.rootSize; + } + + public int cacheSize() { + return this.cacheSize; + } + + public boolean isAllowNewerWitness() { + return this.isAllowNewerWitness; + } + + public byte[] getRoot(int idx) { + return this.roots.get(idx); + } + + public List getRootList() { + return this.roots; + } + + public List getCacheList() { + return this.caches; + } + + // return null if idx out of range + public byte[] getNullableRoot(int idx) { + try { + return this.roots.get(idx); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public void setIsAllowNewerWitness(boolean allow) { + this.isAllowNewerWitness = allow; + } + + private boolean hasCache(byte[] hash) { + if (hash == null) { + for (int i = 0; i < this.caches.size(); i++) + if (this.caches.get(i) == null) + return true; + } else { + for (int i = 0; i < this.caches.size(); i++) + if (java.util.Arrays.equals(this.caches.get(i), hash)) + return true; + } + return false; + } + + private void putCache(byte[] hash) { + if (this.cacheSize > 0) { + if (this.caches.size() == this.cacheSize) { + this.caches.remove(0); + } + this.caches.add(hash); + } + } + + public byte[] getNullableCache(int idx) { + try { + return this.caches.get(idx); + } catch (IndexOutOfBoundsException e) { + return null; + } + } + + public void add(byte[] hash) { + this.putCache(hash); + this.lastBlockHash = hash; + if (this.height == 0) + this.roots.add(hash); + else if (this.roots.size() == 0) + this.roots.add(hash); + else { + byte[] root = null; + for (int i = 0; i < this.roots.size(); i++) { + byte[] currentRoot = this.roots.get(i); + if (currentRoot == null || currentRoot.length == 0) { + root = new byte[32]; + root = hash; + this.roots.set(i, root); + break; + } else { + if (0 < this.rootSize && this.rootSize <= (i + 1)) { + root = new byte[32]; + root = hash; + this.roots.set(i, root); + long offset = 1< this.height + / input height to verify < this.height + */ + private int getRootIdxByHeight(long height) throws MTAException { + long idx = height - 1 - this.offset; + int rootIdx = 0; + int i = this.roots.size(); + if (idx < 0) + throw new MTAException("given height is out of range"); + while (i > 0) { + i -= 1; + if (this.roots.get(i) == null) + continue; + int bitFlag = 1 << i; // Math.pow(2, i) + if (idx < bitFlag) { + rootIdx = i; + break; + } + idx -= bitFlag; + } + return rootIdx; + } + + private void _verify(List witness, byte[] root, byte[] hash, long idx) throws MTAException { + for (byte[] w : witness) { + if (idx % 2 == 0) { + hash = this.getConcatenationHash(hash, w); + } else { + hash = this.getConcatenationHash(w, hash); + } + idx = (int) idx/2; + } + + if (!java.util.Arrays.equals(hash, root)) + throw new MTAException("invalid witness"); + } + + public void verify(List witness, byte[] hash, long height, long at) throws MTAException, InvalidWitnessNewerException, InvalidWitnessOldException { + if (this.height == at) { + byte[] root = this.getRoot(witness.size()); + this._verify(witness, root, hash, height - 1 - this.offset); + } else if (this.height < at) { + if (!this.isAllowNewerWitness) + throw new InvalidWitnessNewerException("not allowed newer witness"); + + if (this.height < height) + throw new MTAException("given witness for newer node"); + + int rootIdx = this.getRootIdxByHeight(height); + byte[] root = this.getRoot(rootIdx); + List acceptedWitness = new ArrayList(witness); + while(acceptedWitness.size() > rootIdx) { + acceptedWitness.remove(acceptedWitness.size() - 1); + } + this._verify(acceptedWitness, root, hash, height - 1 - this.offset); + } else if (this.height > at) { + if ((this.height - height) < this.cacheSize) { + if (!this.hasCache(hash)) + throw new MTAException("invalid old witness"); + } else { + throw new InvalidWitnessOldException("not allowed old witness"); + } + } + } + + public MTAStatus getStatus() { + return new MTAStatus(this.height, this.offset); + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/SerializableMTA.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/SerializableMTA.java new file mode 100644 index 00000000..19ca0f26 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/mta/SerializableMTA.java @@ -0,0 +1,90 @@ +package foundation.icon.btp.lib.mta; + +import java.util.List; + +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +public class SerializableMTA extends MerkleTreeAccumulator { + public SerializableMTA( + long height, + long offset, + int rootSize, + int cacheSize, + boolean isAllowNewerWitness, + byte[] lastBlockHash, + List roots, + List caches + ) { + super(height, offset, rootSize, cacheSize, isAllowNewerWitness, lastBlockHash, roots, caches); + } + + public static void writeObject(ObjectWriter w, SerializableMTA t) { + w.beginList(6); + w.write(t.height()); + w.write(t.offset()); + w.write(t.rootSize()); + w.write(t.cacheSize()); + w.write(t.isAllowNewerWitness()); + w.write(t.lastBlockHash()); + + w.beginList(t.rootSize()); + for (int idx = 0; idx < t.rootSize(); idx++) { + w.writeNullable(t.getNullableRoot(idx)); + } + w.end(); + + w.beginList(t.cacheSize()); + for (int idx = 0; idx < t.cacheSize(); idx++) { + w.writeNullable(t.getNullableCache(idx)); + } + w.end(); + + w.end(); + } + + public static SerializableMTA readObject(ObjectReader r) { + r.beginList(); + long height = r.readLong(); + long offset = r.readLong(); + int rootSize = r.readInt(); + int cacheSize = r.readInt(); + boolean isAllowNewerWitness = r.readBoolean(); + byte[] lastBlockHash = r.readByteArray(); + + List roots = new ArrayList(rootSize); + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] root = r.readNullable(byte[].class); + roots.add(root); + } + r.end(); + } + + List caches = new ArrayList(cacheSize); + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] cache = r.readNullable(byte[].class); + if (cache != null && cache.length > 0) + caches.add(cache); + } + r.end(); + } + + r.end(); + + return new SerializableMTA( + height, + offset, + rootSize, + cacheSize, + isAllowNewerWitness, + lastBlockHash, + roots, + caches + ); + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleReader.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleReader.java new file mode 100644 index 00000000..1ddcc3d1 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleReader.java @@ -0,0 +1,96 @@ +package foundation.icon.btp.lib.scale; + +import java.math.BigInteger; + +import foundation.icon.btp.lib.utils.ByteSliceInput; + +public class ScaleReader { + public static int readUintCompactSize(ByteSliceInput input) { + int i = input.takeUByte(); + byte mode = (byte) (i & 0b00000011); + if (mode == 0b00) { + return i >> 2; + } + if (mode == 0b01) { + return (i >> 2) + + (input.takeUByte() << 6); + } + if (mode == 0b10) { + return (i >> 2) + + (input.takeUByte() << 6) + + (input.takeUByte() << (6 + 8)) + + (input.takeUByte() << (6 + 2 * 8)); + } + throw new AssertionError("Mode " + mode + " is not implemented"); + } + + public static byte[] readBytes(ByteSliceInput input) { + int dataSize = ScaleReader.readUintCompactSize(input); + return input.take(dataSize); + } + + public static BigInteger readCompacBigInteger(ByteSliceInput input) { + int type = input.takeUByte(); + if ((type & 0b11) != 0b11) { + input.skip(-1); + int value = ScaleReader.readUintCompactSize(input); + return BigInteger.valueOf(value); + } + int len = (type >> 2) + 4; + byte[] value = input.take(len); + //LE encoded, so need to reverse it + for (int i = 0; i < value.length / 2; i++) { + byte temp = value[i]; + value[i] = value[value.length -i -1]; + value[value.length - i - 1] = temp; + } + //unsigned, i.e. always positive, signum=1 + return new BigInteger(1, value); + } + + public static int readU8(ByteSliceInput input) { + int result = 0; + result += (int)input.takeUByte(); + return result; + } + + public static int readU16(ByteSliceInput input) { + int result = 0; + result += (int)input.takeUByte(); + result += ((int)input.takeUByte()) << 8; + return result; + } + + public static long readU32(ByteSliceInput input) { + long result = 0; + result += (long)input.takeUByte(); + result += ((long)input.takeUByte()) << 8; + result += ((long)input.takeUByte()) << (2 * 8); + result += ((long)input.takeUByte()) << (3 * 8); + return result; + } + + public static BigInteger readU64(ByteSliceInput input) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i <= 7; i++) { + result = result.add(BigInteger.valueOf(input.takeUByte()).shiftLeft(8*i)); + } + return result; + } + + public static BigInteger readU128(ByteSliceInput input) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i <= 15; i++) { + result = result.add(BigInteger.valueOf(input.takeUByte()).shiftLeft(8*i)); + } + return result; + } + + public static BigInteger readU256(ByteSliceInput input) { + BigInteger result = BigInteger.ZERO; + for (int i = 0; i <= 31; i++) { + result = result.add(BigInteger.valueOf(input.takeUByte()).shiftLeft(8*i)); + } + return result; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleWriter.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleWriter.java new file mode 100644 index 00000000..f80ceebe --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/scale/ScaleWriter.java @@ -0,0 +1,31 @@ +package foundation.icon.btp.lib.scale; + +import foundation.icon.btp.lib.utils.ByteSliceOutput; + +public class ScaleWriter { + public static void writeCompactUint(ByteSliceOutput output, int compactNumber) { + int compactSize = ScaleWriter.compactUintSize(compactNumber); + int compact = (compactNumber << 2) + compactSize - 1; + while (compactSize > 0) { + byte b = (byte) (compact & 0xff); + output.add(b); + compact >>= 8; + compactSize--; + } + } + + public static int compactUintSize(int number) { + if (number < 0) { + throw new IllegalArgumentException("Negative numbers are not supported"); + } + if (number <= 0x3f) { + return 1; + } else if (number <= 0x3fff) { + return 2; + } else if (number <= 0x3fffffff) { + return 3; + } else { + return 4; + } + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/stateProof/StateProof.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/stateProof/StateProof.java new file mode 100644 index 00000000..4062573a --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/stateProof/StateProof.java @@ -0,0 +1,57 @@ +package foundation.icon.btp.lib.stateProof; + +import foundation.icon.btp.lib.mpt.MPTNode; +import foundation.icon.btp.lib.mpt.MerklePatriciaTree; +import foundation.icon.btp.lib.mpt.Nibbles; +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import score.Context; +import score.ObjectReader; +import scorex.util.ArrayList; + +public class StateProof { + private final byte[] key; + private final ArrayList proofs = new ArrayList(5); + + public StateProof(byte[] serialized) throws RelayMessageRLPException { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + rlpReader.beginList(); + this.key = rlpReader.readByteArray(); + + rlpReader.beginList(); + while (rlpReader.hasNext()) { + MPTNode n = new MPTNode(rlpReader.readByteArray()); + this.proofs.add(n); + } + rlpReader.end(); + + rlpReader.end(); + } + + public byte[] prove(byte[] root) { + try { + Nibbles keyNibbles = new Nibbles(this.key, false); + return MerklePatriciaTree.prove(root, keyNibbles, this.proofs); + } catch (Exception | AssertionError e) { + Context.revert(ErrorCode.INVALID_MPT, e.toString()); + return null; + } + } + + public byte[] getKey() { + return this.key; + } + + public ArrayList getProofs() { + return this.proofs; + } + + public static StateProof fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new StateProof(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("StateProof ", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/AbiDecoder.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/AbiDecoder.java new file mode 100644 index 00000000..a0d294ac --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/AbiDecoder.java @@ -0,0 +1,42 @@ +package foundation.icon.btp.lib.utils; + +import java.math.BigInteger; + +import score.Context; + +public class AbiDecoder { + public static BigInteger decodeUInt(ByteSliceInput input) { + byte[] b = input.take(32); + BigInteger result = BigInteger.ZERO; + for (int i = 0; i < 32; i++) { + if (b[i] != 0) { + result = result.add(BigInteger.valueOf(b[i] & 0xff).shiftLeft(8*(31-i))); + } + } + return result; + } + + public static String decodeString(ByteSliceInput input) { + int msgLength = AbiDecoder.decodeUInt(input).intValue(); + int paddingLength = 0; + if (msgLength%32 != 0) { + paddingLength = 32 - msgLength%32; + } + + String result = new String(input.take(msgLength)); + input.take(paddingLength); // remove padding + return result; + } + + public static byte[] decodeBytes(ByteSliceInput input) { + int byteLength = AbiDecoder.decodeUInt(input).intValue(); + int paddingLength = 0; + if (byteLength%32 != 0) { + paddingLength = 32 - byteLength%32; + } + + byte[] result = input.take(byteLength); + input.take(paddingLength); // remove padding + return result; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Arrays.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Arrays.java new file mode 100644 index 00000000..50d30900 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Arrays.java @@ -0,0 +1,39 @@ +package foundation.icon.btp.lib.utils; + +import java.util.List; +import score.Context; + +public class Arrays { + private Arrays() {} + + public static byte[] copyOfRangeByte(byte[] original, int from, int to) { + int newLength = to - from; + if (newLength < 0) + throw new IllegalArgumentException(from + " > " + to); + byte[] copy = new byte[newLength]; + System.arraycopy(original, from, copy, 0, + Math.min(original.length - from, newLength)); + return copy; + } + + public static byte[][] copyOfRangeByteArray(byte[][] original, int from, int to) { + int newLength = to - from; + if (newLength < 0) + throw new IllegalArgumentException(from + " > " + to); + byte[][] copy = new byte[newLength][]; + System.arraycopy(original, from, copy, 0, + Math.min(original.length - from, newLength)); + return copy; + } + + public static boolean arrayListContains(byte[] data, List source) { + for (byte[] element : source) { + if (java.util.Arrays.equals(data, element)) { + return true; + } + } + return false; + } + + +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceInput.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceInput.java new file mode 100644 index 00000000..6e7b0cb5 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceInput.java @@ -0,0 +1,89 @@ +package foundation.icon.btp.lib.utils; + +import foundation.icon.btp.lib.utils.Arrays; + +import java.util.List; + +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class ByteSliceInput { + private byte[] input; + private int offset; + + public ByteSliceInput(byte[] input) { + this.input = input; + this.offset = 0; + } + + public int getOffset() { + return this.offset; + } + + public byte[] take(int size) { + if (this.offset + size > this.input.length) { + throw new AssertionError("byte input out of range"); + } + + byte[] data = Arrays.copyOfRangeByte(this.input, this.offset, this.offset + size); + this.offset += size; + return data; + } + + public byte takeByte() { + if (this.offset + 1 > this.input.length) { + throw new AssertionError("byte input out of range"); + } + + byte b = this.input[this.offset]; + this.offset ++; + return b; + } + + /* + * return int with the same bit pattern byte + */ + public int takeUByte() { + if (this.offset + 1 > this.input.length) { + throw new AssertionError("byte input out of range"); + } + + byte b = this.input[this.offset]; + this.offset ++; + if (b < 0) { + return 256 + (int)b; + } + return (int)b; + } + + public byte[] remain() { + if (this.offset + 1 > this.input.length) { + throw new AssertionError("byte input out of range"); + } + this.offset = this.input.length; + return Arrays.copyOfRangeByte(input, this.offset, this.input.length); + } + + public void seek(int offset) { + if (offset < 0) { + throw new IllegalArgumentException("Offset cannot be negative: " + offset); + } else if (offset >= this.input.length) { + throw new IllegalArgumentException("Offset " + offset + " must be strictly smaller than source length: " + this.input.length); + } + + this.offset = offset; + } + + public void skip(int len) { + if (len < 0 && Math.abs(len) > this.offset) { + throw new IllegalArgumentException("Position cannot be negative: " + this.offset + " " + len); + } + + if (this.offset + len >= this.input.length) { + throw new IllegalArgumentException("Offset " + offset + " must be strictly smaller than source length: " + this.input.length); + } + + this.offset += len; + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceOutput.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceOutput.java new file mode 100644 index 00000000..476dc477 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/ByteSliceOutput.java @@ -0,0 +1,75 @@ +package foundation.icon.btp.lib.utils; + +public class ByteSliceOutput { + private byte[] arr; + private int size; + private int allocationSize; + + public ByteSliceOutput(int initialSize) { + this.arr = new byte[initialSize]; + this.allocationSize = initialSize; + this.size = 0; + } + + public int size() { + return this.size; + } + + public byte[] toArray() { + if (this.size < this.allocationSize) { + byte[] original = new byte[this.size]; + this.copy(this.arr, original, 0, size); + return original; + } else { + return this.arr; + } + } + + public void add(byte b) { + if (this.size == this.allocationSize) { + this.setSize(this.allocationSize*2); + } + + this.arr[size] = b; + this.size++; + } + + public void add(int b) { + this.add( (byte) b); + } + + public void set(int idx, byte b) { + if (0 <= idx && idx < this.size) { + this.arr[idx] = b; + } else { + throw new AssertionError("idx is out of range, use add instead"); + } + } + + public void addMany(byte[] bs) { + for(byte b : bs) + this.add(b); + } + + public byte get(int idx) { + if (0 <= idx && idx < this.size) { + return this.arr[idx]; + } else { + throw new AssertionError("root idx is out of range"); + } + } + + public void setSize(int size) { + int copySize = size > this.size ? this.size : size; + byte[] newArr = new byte[size]; + copy(this.arr, newArr, 0, copySize); + this.arr = newArr; + this.allocationSize = size; + } + + private void copy(byte[] src, byte[] dst, int from, int to) { + for(int i = from; i < to; i++) { + dst[i] = src[i]; + } + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/DynamicArray.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/DynamicArray.java new file mode 100644 index 00000000..0ace558c --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/DynamicArray.java @@ -0,0 +1,123 @@ +package foundation.icon.btp.lib.utils; + +import java.util.Arrays; + +import score.Context; + +public class DynamicArray { + private byte[][] arr; + private int length; + private int allocatedSize; + + public DynamicArray(int initialSize) { + arr = new byte[initialSize][]; + allocatedSize = initialSize; + length = 0; + } + + public int length() { + return this.length; + } + + public byte[][] toArray() { + return this.arr; + } + + public int allocatedSize() { + return this.allocatedSize; + } + + public void add(byte[] hash) { + arr[length] = hash; + + if (length == this.allocatedSize) { + this.setSize(this.allocatedSize*2); + } + length++; + } + + public void addAndTruncate(byte[] hash) { + if (this.length == this.allocatedSize) { + byte[][] newArr = new byte[this.allocatedSize][]; + copy(this.arr, newArr, 1, this.length - 1); + this.arr = newArr; + this.length--; + } + this.arr[length] = hash; + this.length++; + } + + public void set(int idx, byte[] hash) { + if (0 <= idx && idx < this.length) { + this.arr[idx] = hash; + } else { + throw new AssertionError("idx is out of range, use add instead"); + } + } + + public void addMany(byte[][] hashes) { + for(byte[] hash : hashes) + this.add(hash); + } + + public byte[] get(int idx) { + if (0 <= idx && idx < this.length) { + return this.arr[idx]; + } else { + throw new AssertionError("root idx is out of range"); + } + } + + public byte[] getNullable(int idx) { + if (0 <= idx && idx < this.length) { + return this.arr[idx]; + } else { + return null; + } + } + + public void truncate(int size) { + if (size > this.length) { + throw new AssertionError("size greater than array length"); + } else { + byte[][] newArr = new byte[size][]; + copy(this.arr, newArr, size - this.length, size); + this.arr = newArr; + } + this.allocatedSize = size; + this.length = size; + } + + public void setSize(int size) { + if (size > this.length) { + byte[][] newArr = new byte[size][]; + copy(this.arr, newArr, 0, this.length); + this.arr = newArr; + } else { + shrink(size, this.arr); + this.length = size; + } + this.allocatedSize = size; + } + + public boolean contains(byte[] hash) { + if (hash == null || hash.length == 0) + return false; + for (int idx = 0; idx < this.length; idx++) + if (Arrays.equals(hash, this.arr[idx])) + return true; + return false; + } + + private void copy(byte[][] src, byte[][] dst, int from, int to) { + for(int i = from; i < to; i++) { + dst[i] = src[i]; + } + } + + private void shrink(int size, byte[][] arr) { + for(int i = size; i < this.length; i++) { + this.arr[i] = null; + } + } +} diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Hash.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Hash.java new file mode 100644 index 00000000..839ed600 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Hash.java @@ -0,0 +1,9 @@ +package foundation.icon.btp.lib.utils; + +import score.Context; + +public class Hash { + public static byte[] getHash(byte[] data) { + return Context.hash("blake2b-256", data); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/HexConverter.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/HexConverter.java new file mode 100644 index 00000000..d693e63a --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/HexConverter.java @@ -0,0 +1,25 @@ +package foundation.icon.btp.lib.utils; + +public class HexConverter { + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Signature.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Signature.java new file mode 100644 index 00000000..334f79aa --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/utils/Signature.java @@ -0,0 +1,16 @@ +package foundation.icon.btp.lib.utils; + +import java.util.Arrays; + +import score.Context; + +public class Signature { + public static boolean verify(byte[] message, byte[] signature, byte[] publicKey) { + byte[] recoverPublicKey = Context.recoverKey("ed25519", message, signature, false); + return Arrays.equals(publicKey, recoverPublicKey); + } + + public static byte[] recoverKey(byte[] message, byte[] signature) { + return Context.recoverKey("ed25519", message, signature, false); +} +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/validators/Validators.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/validators/Validators.java new file mode 100644 index 00000000..8351fb9d --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/validators/Validators.java @@ -0,0 +1,41 @@ +package foundation.icon.btp.lib.validators; + +import java.util.List; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +public class Validators { + private final List validators; + + public Validators(List validators) { + this.validators = validators; + } + + public List get() { + return this.validators; + } + + public static void writeObject(ObjectWriter w, Validators v) { + List validators = v.get(); + w.beginList(validators.size()); + + for(int i = 0; i < validators.size(); i++) { + w.write(validators.get(i)); + } + + w.end(); + } + + public static Validators readObject(ObjectReader r) { + r.beginList(); + List validators = new ArrayList(150); + while (r.hasNext()) { + byte[] v = r.readByteArray(); + validators.add(v); + } + r.end(); + + return new Validators(validators); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/ValidatorSignature.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/ValidatorSignature.java new file mode 100644 index 00000000..dddac880 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/ValidatorSignature.java @@ -0,0 +1,39 @@ +package foundation.icon.btp.lib.votes; + +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import score.ObjectReader; +import score.Context; + +public class ValidatorSignature { + private byte[] signature; + private byte[] id; + + public ValidatorSignature(byte[] serialized) { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + rlpReader.beginList(); + this.signature = rlpReader.readByteArray(); + this.id = rlpReader.readByteArray(); + rlpReader.end(); + } + + public byte[] getSignature() { + return this.signature; + } + + public byte[] getId() { + return this.id; + } + + public boolean verify(byte[] msg) { + return Context.verifySignature("ed25519", msg, this.signature, this.id); + } + + public static ValidatorSignature fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new ValidatorSignature(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("ValidatorSignature", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java new file mode 100644 index 00000000..ffcd82d3 --- /dev/null +++ b/javascore/bmv/lib/src/main/java/foundation/icon/btp/lib/votes/Votes.java @@ -0,0 +1,128 @@ +package foundation.icon.btp.lib.votes; + +import java.util.List; + +import foundation.icon.btp.lib.utils.Arrays; +import foundation.icon.btp.lib.scale.ScaleReader; +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.utils.Signature; +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import java.math.BigInteger; + +import score.ObjectReader; +import score.Context; + +import scorex.util.ArrayList; + +public class Votes { + private byte[] targetHash; + private long targetNumber; + private BigInteger round; + private BigInteger setId; + private byte[] serializedVoteMessage; + private List signatures; + + // { + // message: { + // Precommit: { + // targetHash: "HASH", + // targetNumber: "u32" + // }, + // }, + // round: u64, + // setId: u64, + // } + public Votes(byte[] serialized) throws RelayMessageRLPException { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + + rlpReader.beginList(); + this.serializedVoteMessage = rlpReader.readByteArray(); + + this.signatures = new ArrayList(100); // 100 items, each signature size 64 bytes + rlpReader.beginList(); + while (rlpReader.hasNext()) { + this.signatures.add(ValidatorSignature.fromBytes(rlpReader.readByteArray())); + } + rlpReader.end(); + + rlpReader.end(); + + ByteSliceInput input = new ByteSliceInput(this.serializedVoteMessage); + int optionPrefix = input.takeUByte(); + if (optionPrefix != 0x01) { + throw new AssertionError("invalid precommit on vote"); + } + + this.targetHash = input.take(32); + this.targetNumber = ScaleReader.readU32(input); + this.round = ScaleReader.readU64(input); + this.setId = ScaleReader.readU64(input); + } + + public void verify(long height, byte[] blockHash, List validators, BigInteger currentSetId) { + if (!java.util.Arrays.equals(targetHash, blockHash)) { + Context.revert(ErrorCode.INVALID_VOTES, "validator signature invalid block hash"); + } + + if (this.targetNumber != height) { + Context.revert(ErrorCode.INVALID_VOTES, "validator signature invalid block height"); + } + + if (!this.setId.equals(currentSetId)) { + Context.revert(ErrorCode.INVALID_VOTES, "verify signature for invalid validator set id"); + } + + List containedValidators = new ArrayList(validators.size()); + for (int i = 0; i < this.signatures.size(); i++) { + ValidatorSignature currentSignature = this.signatures.get(i); + if (!currentSignature.verify(this.serializedVoteMessage)) { + Context.revert(ErrorCode.INVALID_VOTES, "invalid signature"); + } + + if (!Arrays.arrayListContains(currentSignature.getId(), validators)) { + Context.revert(ErrorCode.INVALID_VOTES, "one of signature is not belong to validator"); + } + + if (Arrays.arrayListContains(currentSignature.getId(), containedValidators)) { + Context.revert(ErrorCode.INVALID_VOTES, "duplicated signature"); + } + + containedValidators.add(currentSignature.getId()); + } + + if (containedValidators.size() <= (validators.size() * 2/3)) { + Context.revert(ErrorCode.INVALID_VOTES, "require signature +2/3"); + } + } + + public BigInteger getSetId() { + return this.setId; + } + + public long getTargetNumber() { + return this.targetNumber; + } + + public byte[] getTargetHash() { + return this.targetHash; + } + + public List getSignatures() { + return this.signatures; + } + + public byte[] getMessage() { + return this.serializedVoteMessage; + } + + public static Votes fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new Votes(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("Votes", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/BTPMessageEvmEventTest.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/BTPMessageEvmEventTest.java new file mode 100644 index 00000000..dbfc3ef8 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/BTPMessageEvmEventTest.java @@ -0,0 +1,28 @@ +package foundation.icon.lib.btp; + +import java.math.BigInteger; + +import foundation.icon.btp.lib.event.evmEvent.BTPMessageEvmEvent; +import foundation.icon.btp.lib.utils.HexConverter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestMethodOrder(OrderAnnotation.class) +class BTPMEssageEvmEventTest { + @Test + @Order(1) + public void decodeBTPMessageEVMEvent() { + String btpMessageEventEncoded = "0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000396274703a2f2f3078332e69636f6e2f637863303130666535353934626333353131653231366136623236323565373134343161306338346362000000000000000000000000000000000000000000000000000000000000000000000000000100f8feb83a6274703a2f2f30783530312e7072612f307830444431414134323966424138333845423034316166623961383844386446306434316137313830b8396274703a2f2f3078332e69636f6e2f63786330313066653535393462633335313165323136613662323632356537313434316130633834636283626d6300b880f87e844c696e6bf877b83a6274703a2f2f30783530312e7072612f307830444431414134323966424138333845423034316166623961383844386446306434316137313830b8396274703a2f2f3078332e69636f6e2f637863303130666535353934626333353131653231366136623236323565373134343161306338346362"; + BTPMessageEvmEvent btpMessageEvmEvent = new BTPMessageEvmEvent(HexConverter.hexStringToByteArray(btpMessageEventEncoded)); + + assertTrue(btpMessageEvmEvent.getNextBmc().equals("btp://0x3.icon/cxc010fe5594bc3511e216a6b2625e71441a0c84cb")); + assertTrue(btpMessageEvmEvent.getSeq().equals(BigInteger.valueOf(1))); + assertArrayEquals(btpMessageEvmEvent.getMsg(), HexConverter.hexStringToByteArray("f8feb83a6274703a2f2f30783530312e7072612f307830444431414134323966424138333845423034316166623961383844386446306434316137313830b8396274703a2f2f3078332e69636f6e2f63786330313066653535393462633335313165323136613662323632356537313434316130633834636283626d6300b880f87e844c696e6bf877b83a6274703a2f2f30783530312e7072612f307830444431414134323966424138333845423034316166623961383844386446306434316137313830b8396274703a2f2f3078332e69636f6e2f637863303130666535353934626333353131653231366136623236323565373134343161306338346362")); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/abiDecoder.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/abiDecoder.java new file mode 100644 index 00000000..adfa162d --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/abiDecoder.java @@ -0,0 +1,31 @@ +package foundation.icon.lib.btp; + +import java.math.BigInteger; + +import foundation.icon.btp.lib.utils.AbiDecoder; +import foundation.icon.btp.lib.utils.ByteSliceInput; +import foundation.icon.btp.lib.utils.HexConverter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@TestMethodOrder(OrderAnnotation.class) +class ApiDecoder { + @Test + @Order(1) + public void decodeUint() { + String stringHex1 = "0000000000000000000000000000000000000000000000000000000000000060"; + String stringHex2 = "00000000000000000000000000000000000000000000000000000000000000a0"; + ByteSliceInput input1 = new ByteSliceInput(HexConverter.hexStringToByteArray(stringHex1)); + ByteSliceInput input2 = new ByteSliceInput(HexConverter.hexStringToByteArray(stringHex2)); + BigInteger result1 = AbiDecoder.decodeUInt(input1); + BigInteger result2 = AbiDecoder.decodeUInt(input2); + + assertTrue(result1.equals(BigInteger.valueOf(96))); + assertTrue(result2.equals(BigInteger.valueOf(160))); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/base64.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/base64.java new file mode 100644 index 00000000..5e793367 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/base64.java @@ -0,0 +1,28 @@ +package foundation.icon.lib.btp; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import scorex.util.Base64; + +@TestMethodOrder(OrderAnnotation.class) +class Base64Test { + @Test + @Order(1) + public void base64Decode() { + String encodedValidators = "-GmUqnpLQ4WT2tufQpunb35lTSIG55aU8z9RQpaOQwKBiLzXrTPuk6Q9B2iUqQHZum-tSdR7u9jGp1JszGXtcmqUQaIPVXLu_m3xHfCmLeXm0uLOaYCUxgyDn49clHUg35whC1kqp1vCdlc"; + byte[] serializedValidator = Base64.getUrlDecoder().decode(encodedValidators.getBytes()); + } + + // @Test + // @Order(1) + // public void rlpEncode() { + // int x = 0; + // ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLPn"); + // w.write(x); + // w.end(); + // ObjectReader r = Context.newByteArrayObjectReader(codec, msg.getBytes()); + // } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/blockHeader.test.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/blockHeader.test.java new file mode 100644 index 00000000..7350c423 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/blockHeader.test.java @@ -0,0 +1,27 @@ +package foundation.icon.lib.btp; + +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.blockHeader.BlockHeader; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +@TestMethodOrder(OrderAnnotation.class) +class BlockHeaderTest { + @Test + @Order(1) + public void decodeBlockHeader() { + byte[] encodedBlockHeader = HexConverter.hexStringToByteArray("661416628af29f05ff417f5dcc9aab4e051450f618b0698e15b9d97c86e59573e6ec0e007772a7e9a2569213fdf165da52c56f43448f6d1429d473bd387bb45c3f865bf6aabd6d24cc9a4885549b63ed43bf8fa1ab48f6708154338df575d15563b50cde0c046e6d627380a6676c0a755ca91225ba0bba3dd4e43a82aa3aaf516286361420ae50f790f2330466726f6e8801224a7f1fe922c176c91292b0726e1c9fc8d8d82146525d4f70acdaaa37a1d1f600056e6d6273010144b14164e5f0f9d56f3f54222eda7d4cf03fb868df7fba94d1b3f72ca50c862e395aeaa6b851e1fa4af048d7bbc71daf92d76de9ff2df33da64eee9dca53bf88"); + BlockHeader bh = new BlockHeader(encodedBlockHeader); + + // assertArrayEquals(bh.getHash(), HexConverter.hexStringToByteArray("7420e208dc04622c8061c4747692dda7d52f96b743b1ae39d95b53c5f939afa8")); + assertArrayEquals(bh.getParentHash(), HexConverter.hexStringToByteArray("661416628af29f05ff417f5dcc9aab4e051450f618b0698e15b9d97c86e59573")); + assertEquals(bh.getNumber(), 244537); + assertArrayEquals(bh.getStateRoot(), HexConverter.hexStringToByteArray("7772a7e9a2569213fdf165da52c56f43448f6d1429d473bd387bb45c3f865bf6")); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/btpAddress.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/btpAddress.java new file mode 100644 index 00000000..86e6c836 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/btpAddress.java @@ -0,0 +1,68 @@ +package foundation.icon.lib.btp; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import foundation.icon.btp.lib.btpAddress.BTPAddress; + +@TestMethodOrder(OrderAnnotation.class) +class BTPAddressTest { + @Test + @Order(1) + public void initializeBTPAdress() { + String protocol = "btp"; + String net = "0x1234.icon"; + String address = "hx3e9be7c57c769adb06dd0b4943aab9222c30d825"; + + BTPAddress btpAddr = new BTPAddress(protocol, net, address); + + assertEquals(btpAddr.getNid(), "0x1234"); + assertEquals(btpAddr.getChain(), "icon"); + assertEquals(btpAddr.getProtocol(), protocol); + assertEquals(btpAddr.getAddress(), address); + assertEquals(btpAddr.getNet(), "0x1234.icon"); + assertEquals(btpAddr.toString(), "btp://0x1234.icon/hx3e9be7c57c769adb06dd0b4943aab9222c30d825"); + } + + @Test + @Order(2) + public void initializeFromString() { + String protocol = "btp"; + String net = "0x1234.icon"; + String address = "hx3e9be7c57c769adb06dd0b4943aab9222c30d825"; + + BTPAddress btpAddr = BTPAddress.fromString("btp://0x1234.icon/hx3e9be7c57c769adb06dd0b4943aab9222c30d825"); + + assertEquals(btpAddr.getNid(), "0x1234"); + assertEquals(btpAddr.getChain(), "icon"); + assertEquals(btpAddr.getProtocol(), protocol); + assertEquals(btpAddr.getAddress(), address); + assertTrue(btpAddr.isValid()); + assertEquals(btpAddr.getNet(), "0x1234.icon"); + assertEquals(btpAddr.toString(), "btp://0x1234.icon/hx3e9be7c57c769adb06dd0b4943aab9222c30d825"); + } + + @Test + @Order(2) + public void testInvalidBTPAddress() { + String protocol = "btp"; + String net = "0x1234.icon"; + String address = "hx3e9be7c57c769adb06dd0b4943aab9222c30d825"; + + BTPAddress invalidBtpAddr1 = BTPAddress.fromString("btp://0x1234.icon"); + BTPAddress invalidBtpAddr2 = new BTPAddress(protocol, net, null); + BTPAddress invalidBtpAddr3 = new BTPAddress(null, net, null); + BTPAddress invalidBtpAddr4 = new BTPAddress(null, null, null); + BTPAddress invalidBtpAddr5 = BTPAddress.fromString("btp"); + + assertTrue(!invalidBtpAddr1.isValid()); + assertTrue(!invalidBtpAddr2.isValid()); + assertTrue(!invalidBtpAddr3.isValid()); + assertTrue(!invalidBtpAddr4.isValid()); + assertTrue(!invalidBtpAddr5.isValid()); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptNodeTest.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptNodeTest.java new file mode 100644 index 00000000..20cc13c5 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptNodeTest.java @@ -0,0 +1,163 @@ +package foundation.icon.lib.btp; + +import foundation.icon.btp.lib.mpt.MPTNode; +import foundation.icon.btp.lib.mpt.MPTNodeType; +import foundation.icon.btp.lib.utils.HexConverter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; +import score.Context; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static org.mockito.Mockito.spy; + +// @Disabled +@TestMethodOrder(OrderAnnotation.class) +class MPTNodeTest { + // @BeforeAll + // public static void setup() { + // } + + @Test + @Order(1) + public void initializedBranchNodeWithoutValueAndNibbles() { + byte[] serialized = HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f1119618088e994a9533991b66e5d837744f5be103692c64b08ea63d63abe8eaf9fb7416f8060758d475a9259fa27c831fd9ebd23bafeb33d25badffb8f86ac80f311acb593803b98133180f6aed60e8423fffa8c033c481d94e0ee9ddacc6c8d302d4cde9786806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480428917537502720167a40e6b6124a29b3769d45ab7fd5525e2801e7067dfbbac800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c80b3b0100693e0bdb4ce7a61e395a504aebc2e3fe02c8d184b4b7711182864c8aa80a4160361b01b32b8d402e9386b1f97e2c01b963aa1a1281f1802ed386892495080b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80a29da6bb7b31bd16f291157e07f2cd4de7e8daf8a2fdfcdc5abfd828110e697c8050375ff3701450c89230e01b2460aeb3de4d8b3d782d6b70ca2b7642f74450ec80b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b45117801c6d0738400fc6e6e66e28c80f63837a790bb5d58559ec858b82af37b52e4c818060ce44be0abff4ce2bb1065fac78836b7b4276fae13d916fe84c60b54533123c"); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.BRANCH); + assertEquals(mptNode.getNibbles(), null); + assertArrayEquals(mptNode.getHash(), Context.hash("blake2b-256", serialized)); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens.length, 16); + assertEquals(decodedChildrens[0].length, 32); + assertEquals(decodedChildrens[15].length, 32); + + assertTrue(mptNode.getData() == null); + } + + @Test + @Order(2) + public void initializedBranchNodeWithNibblesAndWithoutValue() { + byte[] serialized = HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f805d6230a65d4fa930e02ff40a3b457bfcf20e2b5641b6d15e9056bc438742719980eee808e9dd713935c5f10f716cb1b2952f5bff9afe639f9a89248f6544b7def44c5f0684a022a34dd8bfa2baaf44f172b710040180faf3b170fe418b88973c2286677767f3b09ed5e3fc3f79687520ce68e9b67d05808d5b41c21e2ed4daa1ade60726d08086674726fe4dc500b65f69c1ff7c97e6b58047e43c6b5ec576398a1a9eecddf19bc92bbddf9f6f6c195b347a0aeb06c7887280f7eeac1afaad9023e4811ed99731eb9cd89afd618ada2b44941d2e1d53933a704c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"); + byte[] hash = Context.hash("blake2b-256", serialized); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.BRANCH); + assertArrayEquals(mptNode.getNibbles().getRaw(), HexConverter.hexStringToByteArray("aa394eea5630e07c48ae0c9558cef7")); + assertArrayEquals(mptNode.getHash(), hash); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens.length, 16); + assertArrayEquals(decodedChildrens[0], HexConverter.hexStringToByteArray("5d6230a65d4fa930e02ff40a3b457bfcf20e2b5641b6d15e9056bc4387427199")); + assertArrayEquals(decodedChildrens[3], HexConverter.hexStringToByteArray("eee808e9dd713935c5f10f716cb1b2952f5bff9afe639f9a89248f6544b7def4")); + assertArrayEquals(decodedChildrens[5], HexConverter.hexStringToByteArray("5f0684a022a34dd8bfa2baaf44f172b7100401")); + assertArrayEquals(decodedChildrens[15], HexConverter.hexStringToByteArray("5f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74")); + + assertTrue(mptNode.getData() == null); + } + + @Test + @Order(3) + public void initializedBranchNodeWithNibblesAndValue() { + byte[] serialized = HexConverter.hexStringToByteArray("deaa394eea5630e07c48ae0c9558cef7299f1001230a00805d6230a65d4fa930e02ff40a3b457bfcf20e2b5641b6d15e9056bc438742719980eee808e9dd713935c5f10f716cb1b2952f5bff9afe639f9a89248f6544b7def44c5f0684a022a34dd8bfa2baaf44f172b710040180faf3b170fe418b88973c2286677767f3b09ed5e3fc3f79687520ce68e9b67d05808d5b41c21e2ed4daa1ade60726d08086674726fe4dc500b65f69c1ff7c97e6b58047e43c6b5ec576398a1a9eecddf19bc92bbddf9f6f6c195b347a0aeb06c7887280f7eeac1afaad9023e4811ed99731eb9cd89afd618ada2b44941d2e1d53933a704c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"); + byte[] hash = Context.hash("blake2b-256", serialized); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.BRANCH_WITH_VALUE); + assertArrayEquals(mptNode.getNibbles().getRaw(), HexConverter.hexStringToByteArray("aa394eea5630e07c48ae0c9558cef7")); + assertArrayEquals(mptNode.getHash(), hash); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens.length, 16); + assertArrayEquals(decodedChildrens[0], HexConverter.hexStringToByteArray("5d6230a65d4fa930e02ff40a3b457bfcf20e2b5641b6d15e9056bc4387427199")); + assertArrayEquals(decodedChildrens[3], HexConverter.hexStringToByteArray("eee808e9dd713935c5f10f716cb1b2952f5bff9afe639f9a89248f6544b7def4")); + assertArrayEquals(decodedChildrens[5], HexConverter.hexStringToByteArray("5f0684a022a34dd8bfa2baaf44f172b7100401")); + assertArrayEquals(decodedChildrens[15], HexConverter.hexStringToByteArray("5f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74")); + + assertArrayEquals(mptNode.getData(), HexConverter.hexStringToByteArray("01230a00")); + } + + @Test + @Order(4) + public void initializedLeafNodeWithNibbles() { + byte[] serialized = HexConverter.hexStringToByteArray("5f04abf5cb34d6244378cddbf18e849d9660000000000000000000000000000000009073a47e01000000"); + byte[] hash = Context.hash("blake2b-256", serialized); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.LEAF); + assertArrayEquals(mptNode.getNibbles().getRaw(), HexConverter.hexStringToByteArray("04abf5cb34d6244378cddbf18e849d96")); + assertArrayEquals(mptNode.getHash(), hash); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens, null); + + assertArrayEquals(mptNode.getData(), HexConverter.hexStringToByteArray("000000000000000000000000000000009073a47e01000000")); + } + + @Test + @Order(5) + public void initializedLeafNodeWithoutNibbles() { + byte[] serialized = HexConverter.hexStringToByteArray("408928450148db5df0d3ca31fc34372efe1e2bbcfef9300cb83a45caaad87d8e7de6c8d57ce9720ea305f54dddc749807e1ab8c6a1870209d0635f6246288fe412feeae511587f7a3311bf8bec0f6851639c04a1a6d68cdb3905f33abdb3b891d2102f4878801026e09dada5e8a0e437775eda1f9890e12ebae2ecb9b9aab21c4b042e77a87b9e64bfd4bb9e1afc9dcabed7bc37ce60fc9568edb5a99aad82904349be3dc29149362adbc390496e90c3b4a96352c7af91e3915344cdff06d91b36f18c4cf3805cbd5fe0c56b4b7a57d37f3747f85616be45ac5c7423630ff495e577093aa2f63b1a033477435303437cd9b13e0ec1291e35bdddcee3b59a9f3a70f66e256687872bceca9ce75e9d36aba390ffa76ce22b71fb86208ca0b81101abc7e335ffd507d6d512c13b09c27f6b1aeddea1ff0f8172ab4ed4a228226468806087a310fb642dc2c16ee4672cd922ca845d4b8fbf2b00a282c089fbfc33dfb211581f5ee0ac49d7a9e9c4b032b69627a7b23a2169d9bb733192d153cf41386ebdff471804e45e64afaf1f73de8ebbb7b1dde373276da9bac76916634252cae441cf3b39c51e2a048fdda1f135d8ac78d7153be643fc6b81f69fdab03a13b5e57c52b13ee8d39bcc65face95c2b7b134e972b934cbf32fe4ae6ed9da8ad4bb9128e997378a353cb4982156f6c5cfb09061f590ea5bcce785d283d0dacfeb3a168f481c456e580df10ad96e571e91148d149a592037f1ee1e5c0a067df6994b9c1118b9efc18db86367c3291d73c8a249ace6034c1cc68ad6537ff235ab555e2e045170da709f94e324eafd1de2a45b8ee3abe2602adc13cb0327ed5c09f4b2ff593716be0359e9ff9e9f13605e05fdbdc6b5806ac270a34f1007e33844b9aa1bf5c26199e15e983ce2ca98830bc3823f9b73bcd563a647b229bf9b1360a97a4d0b5e7b0a6bbbcc11225894363b27b4849a2e8f4df79ed6255eb59d94d0d9a189f4c2adb05edfa8edf9b167ec94cb1fdd47209ad8cba16de2df337bc105a48384a2208434858ebb7716e1b559b23d159a55af2cf17fccecd13d97aa1382d4fa82e609fc2ed9d129184f099e82996e8b0b94453fdd2e9efc5c020cbddd94cebe92bd573bf2cd4e3d7bd91d4cd11d10ace6ca356278cda0eab293b7639051c7a65571e272af8317ad37b49a7d876631aea5171d3e9311b8ee36f6654d06b39cb2ab274218ce45004eb60f57c9cee3edffab5b70391dcf6f9cf8b51bd3d55eab9ac41bd60a6d52bf31edf396b2924811927222e365a420e56ffc78b6e0a00cb03b0743d5201c0665bda06fd091844cb6b2b0b87b0720e28c2d7656dfa29a3a5f6a67aa900fdc27884379afd8af9e258145822725acb483c42ae2645045d232ff48c7a060cf058641167fc656e6d8c8941311191b8369f496fc563b4c4f7736d1cdacaee6edc9accaeb090f331397c8d4bcddaa692ee8c087e869e1611ef609f3dfa8239f98a907f0790afc246e5343f9c3f04523f8250a9e92fec802ba62f247087242511ee78b2df4e03dba3a89d0960b9c1cf924a56f2230c29cc922adbb035db398c92baee12aadaf1a20c8e654d6ac79d881772d0f20b8d7baa578f3c991834a9d09664799f87c3b100325d536570b4368f42173321dbb40b341ec242f19d6d43c2f7a78560274048ec0bb23cbf13ec6ef0603d700ac036e7dfafe6b06d3729ea80ef54fd422b94f6f9f7b51ce6de67d1d8630652d18af1ddaa776ec3602627853af7fcdebf2f356a579d535dbef0f0db5e64af49a4de047500308baaed40c71a760f100d7742eefbb85942b73710ae6bea046725caae1d618c93ffdee508fb1f58a27ca3038b772d3d3c8cabe77ccedaf7b923b90bfad2a0faa03af45fddffcb6393a811659860ffb9dfc0abca7d17665a2f61fdb013c15c2dad838a62ae10525d682f5078d32976202aadec04dd58871ac29abb72d19e2eab8453de85585467212d69b4fe07f4cedcc7d2c437b73101d86baf5a6b16f22b65487510f4d56c63172434d11c8f4f9828cab415f5cfa93f23c249c59e481b585e5fc3305b9010f45ee8787c95eae3ff4b220772c92d622fc375ebda31ec1a8da92b2dcf1d103895803df89608b92e5ce97d54359fb7d373a89c4b8fea79964def152d0023c9c2283ef7abe6337f33466e340628e66a32d1643bd6c05fcf3c3336b4755aa3dc20ce0ffd97c21ef825ff2e028dd66c08e9e958efa33d4c24c5195035866ee4e3b2f4563fdc23c5c5bca6f69ded059956910bdf8e5871455930ef3212e4b76d4c9e8eeede7f6050b46184e1485d60dbddc1e38c9687dad0e67d315cb04a296f56ab391821e6fc9d57c851c759961fa611025ba50d0dca92189e041fbd82b71a2340909b65546e2184b5f648a3a1e1bc474e76a7992dca01b0cf76ea70e9156171203f64a880ad5a25494fa8dd3fc88aa96a742ad077a8b6f7495548e9f8bb35991ebb0dd9e30352b8cb8ceb38306841de27c2244a1bda77473a4c7f807b03cba6b41df4256c5821c7f087ae3964ce9de0fe19c02b595a429b86c261f97aac5e72ca884c587d1bfbe6a01b7c2d925927edfa27d044a728a17093e0b1aa2c20e2cdbd19830f12141a2e76a36a1e3ce69fd98b9d97d88509166eb941190d3bbdcf36d12f5c07b4e343d90274f1cb14551e20a429393f789af87156d10598f77928e495902fcfda9cfd64a71224e70c289071bc2e495db071ad409d6c6f6a54d58534d63ab61f181c97fd4ee4dd6628387e3ec1024e40c1a74d493b3a259e910b87e92ac9fcc31907ba328b2478757ab787a590b516426955d072c254fe6cb3002f3b6661bbc95f7d304da167ed14feca431e7190c7d4c3f547f65c602f823ef1d2d5dab6220ec235bfedec3041e818993f9da0b69fc0b80f863f99661a1b29f7c3eaf98f6afe32c5ff45881171b4b972c9c7dbfc264978bc615352c46939c217433e31b74d54427edb5994afef4dd890c4bada7e74ba4f80e7d98984129d7d8d1fe96285b14aed5e9ac591aa021b8db8de77ee7424e372e250815c0225aa5c16e56e1c39510f22b7335da1cd1c985f4472dc46517df8a47a3e799becf3ff8ddb852ba459bdf928e5159c9ef7924b98bbc90d01495ea17e8112c146b421ebfb43d4396da76ada605199b1b649195d32141f2d9dde33400e499cfff64b086a8367ec051d4e9bf3066fd4e8f8cd30afffc1d03d125147a34f07cc72e6a85e42c2d76680c4ccfa75e343ce26dbeeefd60aeda06374e0984bcffc39c378faa954d033c8ebc6073f2c7754a27406b51b4b7befb5cd61f6ae93b526f25d8de5bc260a23558cecfac9f8af2272fe29b6ee07ec14afe70c3d71c8fd8e41300f992d8d30b767f1f974fcc050826a7de965321b5b6b5845d82224c910a04e80f80d27974512cdfd89d02b9c9c2b4640e7f81e02f99a9189747d0b7c2fe1a6abb91aeb588de9d8e6c0245cab75499679c2e521c96a2db9f7257b30a06919845c275a19df563a378fd09844c56580fc84043ea0d8b19708fcc039ea4cc6f3db2ef3e85be9152e02378e668bf19fa9984c37d8c7e5c3e64edbccef27d33ff278a66eca6acdfd2ddc411e044990e92d564073b89057482ab06547412e8d3848e15a53683782d19b393909f5b20e3a1"); + byte[] hash = Context.hash("blake2b-256", serialized); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.LEAF); + assertEquals(mptNode.getNibbles(), null); + assertArrayEquals(mptNode.getHash(), hash); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens, null); + + assertArrayEquals(mptNode.getData(), HexConverter.hexStringToByteArray("450148db5df0d3ca31fc34372efe1e2bbcfef9300cb83a45caaad87d8e7de6c8d57ce9720ea305f54dddc749807e1ab8c6a1870209d0635f6246288fe412feeae511587f7a3311bf8bec0f6851639c04a1a6d68cdb3905f33abdb3b891d2102f4878801026e09dada5e8a0e437775eda1f9890e12ebae2ecb9b9aab21c4b042e77a87b9e64bfd4bb9e1afc9dcabed7bc37ce60fc9568edb5a99aad82904349be3dc29149362adbc390496e90c3b4a96352c7af91e3915344cdff06d91b36f18c4cf3805cbd5fe0c56b4b7a57d37f3747f85616be45ac5c7423630ff495e577093aa2f63b1a033477435303437cd9b13e0ec1291e35bdddcee3b59a9f3a70f66e256687872bceca9ce75e9d36aba390ffa76ce22b71fb86208ca0b81101abc7e335ffd507d6d512c13b09c27f6b1aeddea1ff0f8172ab4ed4a228226468806087a310fb642dc2c16ee4672cd922ca845d4b8fbf2b00a282c089fbfc33dfb211581f5ee0ac49d7a9e9c4b032b69627a7b23a2169d9bb733192d153cf41386ebdff471804e45e64afaf1f73de8ebbb7b1dde373276da9bac76916634252cae441cf3b39c51e2a048fdda1f135d8ac78d7153be643fc6b81f69fdab03a13b5e57c52b13ee8d39bcc65face95c2b7b134e972b934cbf32fe4ae6ed9da8ad4bb9128e997378a353cb4982156f6c5cfb09061f590ea5bcce785d283d0dacfeb3a168f481c456e580df10ad96e571e91148d149a592037f1ee1e5c0a067df6994b9c1118b9efc18db86367c3291d73c8a249ace6034c1cc68ad6537ff235ab555e2e045170da709f94e324eafd1de2a45b8ee3abe2602adc13cb0327ed5c09f4b2ff593716be0359e9ff9e9f13605e05fdbdc6b5806ac270a34f1007e33844b9aa1bf5c26199e15e983ce2ca98830bc3823f9b73bcd563a647b229bf9b1360a97a4d0b5e7b0a6bbbcc11225894363b27b4849a2e8f4df79ed6255eb59d94d0d9a189f4c2adb05edfa8edf9b167ec94cb1fdd47209ad8cba16de2df337bc105a48384a2208434858ebb7716e1b559b23d159a55af2cf17fccecd13d97aa1382d4fa82e609fc2ed9d129184f099e82996e8b0b94453fdd2e9efc5c020cbddd94cebe92bd573bf2cd4e3d7bd91d4cd11d10ace6ca356278cda0eab293b7639051c7a65571e272af8317ad37b49a7d876631aea5171d3e9311b8ee36f6654d06b39cb2ab274218ce45004eb60f57c9cee3edffab5b70391dcf6f9cf8b51bd3d55eab9ac41bd60a6d52bf31edf396b2924811927222e365a420e56ffc78b6e0a00cb03b0743d5201c0665bda06fd091844cb6b2b0b87b0720e28c2d7656dfa29a3a5f6a67aa900fdc27884379afd8af9e258145822725acb483c42ae2645045d232ff48c7a060cf058641167fc656e6d8c8941311191b8369f496fc563b4c4f7736d1cdacaee6edc9accaeb090f331397c8d4bcddaa692ee8c087e869e1611ef609f3dfa8239f98a907f0790afc246e5343f9c3f04523f8250a9e92fec802ba62f247087242511ee78b2df4e03dba3a89d0960b9c1cf924a56f2230c29cc922adbb035db398c92baee12aadaf1a20c8e654d6ac79d881772d0f20b8d7baa578f3c991834a9d09664799f87c3b100325d536570b4368f42173321dbb40b341ec242f19d6d43c2f7a78560274048ec0bb23cbf13ec6ef0603d700ac036e7dfafe6b06d3729ea80ef54fd422b94f6f9f7b51ce6de67d1d8630652d18af1ddaa776ec3602627853af7fcdebf2f356a579d535dbef0f0db5e64af49a4de047500308baaed40c71a760f100d7742eefbb85942b73710ae6bea046725caae1d618c93ffdee508fb1f58a27ca3038b772d3d3c8cabe77ccedaf7b923b90bfad2a0faa03af45fddffcb6393a811659860ffb9dfc0abca7d17665a2f61fdb013c15c2dad838a62ae10525d682f5078d32976202aadec04dd58871ac29abb72d19e2eab8453de85585467212d69b4fe07f4cedcc7d2c437b73101d86baf5a6b16f22b65487510f4d56c63172434d11c8f4f9828cab415f5cfa93f23c249c59e481b585e5fc3305b9010f45ee8787c95eae3ff4b220772c92d622fc375ebda31ec1a8da92b2dcf1d103895803df89608b92e5ce97d54359fb7d373a89c4b8fea79964def152d0023c9c2283ef7abe6337f33466e340628e66a32d1643bd6c05fcf3c3336b4755aa3dc20ce0ffd97c21ef825ff2e028dd66c08e9e958efa33d4c24c5195035866ee4e3b2f4563fdc23c5c5bca6f69ded059956910bdf8e5871455930ef3212e4b76d4c9e8eeede7f6050b46184e1485d60dbddc1e38c9687dad0e67d315cb04a296f56ab391821e6fc9d57c851c759961fa611025ba50d0dca92189e041fbd82b71a2340909b65546e2184b5f648a3a1e1bc474e76a7992dca01b0cf76ea70e9156171203f64a880ad5a25494fa8dd3fc88aa96a742ad077a8b6f7495548e9f8bb35991ebb0dd9e30352b8cb8ceb38306841de27c2244a1bda77473a4c7f807b03cba6b41df4256c5821c7f087ae3964ce9de0fe19c02b595a429b86c261f97aac5e72ca884c587d1bfbe6a01b7c2d925927edfa27d044a728a17093e0b1aa2c20e2cdbd19830f12141a2e76a36a1e3ce69fd98b9d97d88509166eb941190d3bbdcf36d12f5c07b4e343d90274f1cb14551e20a429393f789af87156d10598f77928e495902fcfda9cfd64a71224e70c289071bc2e495db071ad409d6c6f6a54d58534d63ab61f181c97fd4ee4dd6628387e3ec1024e40c1a74d493b3a259e910b87e92ac9fcc31907ba328b2478757ab787a590b516426955d072c254fe6cb3002f3b6661bbc95f7d304da167ed14feca431e7190c7d4c3f547f65c602f823ef1d2d5dab6220ec235bfedec3041e818993f9da0b69fc0b80f863f99661a1b29f7c3eaf98f6afe32c5ff45881171b4b972c9c7dbfc264978bc615352c46939c217433e31b74d54427edb5994afef4dd890c4bada7e74ba4f80e7d98984129d7d8d1fe96285b14aed5e9ac591aa021b8db8de77ee7424e372e250815c0225aa5c16e56e1c39510f22b7335da1cd1c985f4472dc46517df8a47a3e799becf3ff8ddb852ba459bdf928e5159c9ef7924b98bbc90d01495ea17e8112c146b421ebfb43d4396da76ada605199b1b649195d32141f2d9dde33400e499cfff64b086a8367ec051d4e9bf3066fd4e8f8cd30afffc1d03d125147a34f07cc72e6a85e42c2d76680c4ccfa75e343ce26dbeeefd60aeda06374e0984bcffc39c378faa954d033c8ebc6073f2c7754a27406b51b4b7befb5cd61f6ae93b526f25d8de5bc260a23558cecfac9f8af2272fe29b6ee07ec14afe70c3d71c8fd8e41300f992d8d30b767f1f974fcc050826a7de965321b5b6b5845d82224c910a04e80f80d27974512cdfd89d02b9c9c2b4640e7f81e02f99a9189747d0b7c2fe1a6abb91aeb588de9d8e6c0245cab75499679c2e521c96a2db9f7257b30a06919845c275a19df563a378fd09844c56580fc84043ea0d8b19708fcc039ea4cc6f3db2ef3e85be9152e02378e668bf19fa9984c37d8c7e5c3e64edbccef27d33ff278a66eca6acdfd2ddc411e044990e92d564073b89057482ab06547412e8d3848e15a53683782d19b393909f5b20e3a1")); + } + + @Test + @Order(6) + public void initializedInlineLeaf() { + byte[] serialized = HexConverter.hexStringToByteArray("5f078d434d6125b40443fe11fd292d13a41003000000"); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.LEAF); + assertArrayEquals(mptNode.getNibbles().getRaw(), HexConverter.hexStringToByteArray("078d434d6125b40443fe11fd292d13a4")); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens, null); + + assertArrayEquals(mptNode.getData(), HexConverter.hexStringToByteArray("03000000")); + } + + @Test + @Order(7) + public void initializedEmptyNode() { + byte[] serialized = HexConverter.hexStringToByteArray("00"); + MPTNode mptNode = spy(new MPTNode(serialized)); + + assertEquals(mptNode.getType(), MPTNodeType.EMPTY); + assertEquals(mptNode.getNibbles(), null); + + byte[][] decodedChildrens = mptNode.getChildrens(); + assertEquals(decodedChildrens, null); + + assertArrayEquals(mptNode.getData(), null); + } + + @Test + @Order(11) + public void throwErrorIfNibblePaddingInvalid() { + byte[] serialized = HexConverter.hexStringToByteArray("9f11aa394eea5630e07c48ae0c9558cef7299f805d6230a65d4fa930e02ff40a3b457bfcf20e2b5641b6d15e9056bc438742719980eee808e9dd713935c5f10f716cb1b2952f5bff9afe639f9a89248f6544b7def44c5f0684a022a34dd8bfa2baaf44f172b710040180faf3b170fe418b88973c2286677767f3b09ed5e3fc3f79687520ce68e9b67d05808d5b41c21e2ed4daa1ade60726d08086674726fe4dc500b65f69c1ff7c97e6b58047e43c6b5ec576398a1a9eecddf19bc92bbddf9f6f6c195b347a0aeb06c7887280f7eeac1afaad9023e4811ed99731eb9cd89afd618ada2b44941d2e1d53933a704c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> new MPTNode(serialized) + ); + + assertTrue(thrown.getMessage().contains("invalid mpt node format")); + } +} diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptTest.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptTest.java new file mode 100644 index 00000000..ea749ab7 --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mptTest.java @@ -0,0 +1,167 @@ +package foundation.icon.lib.btp; + +import foundation.icon.btp.lib.mpt.Nibbles; +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.mpt.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import scorex.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// @Disabled +@TestMethodOrder(OrderAnnotation.class) +class MPTest { + @Test + @Order(1) + public void proveEventStorage() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af80f26e60648c101cd4e706c69caa588a40c5215c052e849fc410681277e12c0e5b80a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("8081048066460feb98ea680339fdf0fdca521f4f090296634456658856df27f3256d4fd1545e8d434d6125b40443fe11fd292d13a4100300000080e0d23e3681be10a9c7ca00a301e523d9519222737468ee0af39215a858266385"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d74c04000000000000002878180b00000000020000"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + byte[] provingValue = MerklePatriciaTree.prove(root, keyNibbles, proofs); + + assertArrayEquals(provingValue, HexConverter.hexStringToByteArray("04000000000000002878180b00000000020000")); + } + + @Test + @Order(2) + public void prove() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af80f26e60648c101cd4e706c69caa588a40c5215c052e849fc410681277e12c0e5b80a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("800404545ea5c1b19ab7a04f536c519aca4983ac1011094400545e98fdbe9ce6c55837576c60c7af38501001000000"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + byte[] provingValue = MerklePatriciaTree.prove(root, keyNibbles, proofs); + + assertArrayEquals(provingValue, HexConverter.hexStringToByteArray("11094400")); + } + + @Test + @Order(3) + public void proveFailMissingProof() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af80f26e60648c101cd4e706c69caa588a40c5215c052e849fc410681277e12c0e5b80a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + // proofs.add(new MPTNode(HexConverter.hexStringToByteArray("8081048066460feb98ea680339fdf0fdca521f4f090296634456658856df27f3256d4fd1545e8d434d6125b40443fe11fd292d13a4100300000080e0d23e3681be10a9c7ca00a301e523d9519222737468ee0af39215a858266385"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> MerklePatriciaTree.prove(root, keyNibbles, proofs) + ); + + assertTrue(thrown.getMessage().contains("MPT missing proof")); + } + + @Test + @Order(4) + public void proveFailMismatchNibbleOnBranch() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("800404545ea5c1b19ab7a04f536c519aca4983ac1011094400545e98fdbe9ce6c55837576c60c7af38501001000000"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af807f533d66adf5ba7dbaf627fefd249d3b24328142f5f56cbf5ca8df2ccaf595d280a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"); + byte[] root = HexConverter.hexStringToByteArray("13235466c4b6d0bccb6cfae3767a8cce52c485eb152208d1e24730cdce4058cd"); + + Nibbles keyNibbles = new Nibbles(key, false); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> MerklePatriciaTree.prove(root, keyNibbles, proofs) + ); + + assertTrue(thrown.getMessage().contains("invalid MPT proof")); + } + + @Test + @Order(5) + public void proveCanNotFindDataInBranchNode() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("800404545ea5c1b19ab7a04f536c519aca4983ac1011094400545e98fdbe9ce6c55837576c60c7af38501001000000"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af807f533d66adf5ba7dbaf627fefd249d3b24328142f5f56cbf5ca8df2ccaf595d280a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef7"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> MerklePatriciaTree.prove(root, keyNibbles, proofs) + ); + + assertTrue(thrown.getMessage().contains("invalid MPT proof")); + } + + @Test + @Order(6) + public void proveCanNotFindChildrenNode() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af80f26e60648c101cd4e706c69caa588a40c5215c052e849fc410681277e12c0e5b80a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("800404545ea5c1b19ab7a04f536c519aca4983ac1011094400545e98fdbe9ce6c55837576c60c7af38501001000000"))); + + byte[] key = HexConverter.hexStringToByteArray("25aa394eea5630e07c48ae0c9558cef7"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> MerklePatriciaTree.prove(root, keyNibbles, proofs) + ); + + assertTrue(thrown.getMessage().contains("invalid MPT proof")); + } + + @Test + @Order(7) + public void proveFailMismatchNibbleOnLeaf() { + ArrayList proofs = new ArrayList(5); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80499c808195b6400caeaaceee2debc277ceadf67ad2af2ddd02b815476ad91d303076c580fc6822539a4dd76f5bd08ebce76152f9043fe167826db92c7569e43c16d675af80f26e60648c101cd4e706c69caa588a40c5215c052e849fc410681277e12c0e5b80a108353045683e792e050af2eda268c860fbcd757d59dd5c194c6c726308459d80102c09e404e12ce2a6e71e214ea009053358e7f3de223457de265a6faab6e454803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961806fe86874420b0da66e64aceb6faf0923bcfcac83ab77a12fcaf53915fe005dbe807ea888eb72a4703a6a2049ee5f8a4b57efb9ff127c33f4fc0a1b00f36f2ec6d080415256d8937fed4618e92d39ed6a96f986e4303ca3689b33c59ade227af7909a806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480142d00923c7e321f47d9eb0918514f983f9916c6db8c952d36fa5366da20ce9a800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de80487b2dc7bef5a919013bf34dfca66bbaae571ebedbdb0eeb0c8149dfa0d3545c805ef8441d86999ada141d6e18656a9243ceb6fadf27dc3c600d65d64488232c04800d7b7c347d9d129a1c9596f6ce1b8b1adf0d408acf677571a198c3eda6ad9b6280b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f8074b8fd9e7670794f9cba3bdf1ac05b9104da6d27a42113f2f3d95a9ee90dc0298093096e5243ed57428d45ef91c5fcf8ff06179628488202e5d4864867b9674be280b911d65bff5f4ce6c0520d83d55b8ee182c7e87664f67664849276b731b4511780aa70f608bff62a7658840159c5f90224dd41de9b5a2232c2d2910e6179fb707f80a50e07dd134bf95fc2ff5676b0ec5e34d4803f0a679615405bf25cc87394fd5f"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f80da681d5efb680bef057a58ffbaa7551590425911dc0b272c9da47fcde38f3fbe807da0cb76c07e5a74b3303c95a56cf06da88afdf25c39db028498d8e8cd2287694c5f0684a022a34dd8bfa2baaf44f172b7100401802f965a5e16a4a150ab5c9bf54c8a46eb01b020f026b3f608f2f3b65a219be1e3804ce3f5738277bd4abdf453f58df9c2da09cc53dee7bff7471ed6d2ed6bd26ee180f0ce00fa24fd38f8a6f4f65268bf304215733f9ca85d708581915983a1240342807bf5646da3793f6a01641f0b3808f1eb1959dcac33a5cfcea14ae652e8cfba864c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287420706f6c6b61646f74"))); + proofs.add(new MPTNode(HexConverter.hexStringToByteArray("800404545ea5c1b19ab7a04f536c519aca4983ac1011094400545e98fdbe9ce6c55837576c60c7af38501001000000"))); + + byte[] key = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ad"); + byte[] root = HexConverter.hexStringToByteArray("c40efd7458262583b22ddcb17b434a40b802116ed24bf6cdd0dbce4face6126e"); + + Nibbles keyNibbles = new Nibbles(key, false); + + AssertionError thrown = assertThrows( + AssertionError.class, + () -> MerklePatriciaTree.prove(root, keyNibbles, proofs) + ); + + assertTrue(thrown.getMessage().contains("MPT mismatch nibbles on leaf node")); + } +} \ No newline at end of file diff --git a/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mtaTest.java b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mtaTest.java new file mode 100644 index 00000000..5cee77eb --- /dev/null +++ b/javascore/bmv/lib/src/test/java/foundation/icon/lib/btp/mtaTest.java @@ -0,0 +1,592 @@ +package foundation.icon.lib.btp; + +import java.util.List; + +import foundation.icon.btp.lib.exception.mtaException.*; +import foundation.icon.btp.lib.mta.MerkleTreeAccumulator; +import foundation.icon.btp.lib.mta.MTAStatus; +import foundation.icon.btp.lib.utils.HexConverter; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; +import scorex.util.ArrayList; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.doReturn; + +@TestMethodOrder(OrderAnnotation.class) +class MTATest { + private static MerkleTreeAccumulator mta; + private static long height = 8; + private static List roots; + private static long offset = 8; + private static int rootSize = 3; + private static int cacheSize = 10; + private static List caches; + private static byte[] lastBlockHash = HexConverter.hexStringToByteArray("fb147d7417db613d12685b9affdf6dd06bcfbba78a477a7fc9a79a21075a776a"); + private static boolean isAllowNewerWitness = true; + + public byte[] getHash(byte[] data) { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA3-256"); + return digest.digest(data); + } catch (NoSuchAlgorithmException e) { + return null; + } + } + + public byte[] getConcatenation(byte[] concatenation, byte[] item1, byte[] item2) { + System.arraycopy(item1, 0, concatenation, 0, item1.length); + System.arraycopy(item2, 0, concatenation, item1.length, item2.length); + return concatenation; + } + + @BeforeAll + public static void setup() { + mta = spy(new MerkleTreeAccumulator( + height, + offset, + rootSize, + cacheSize, + isAllowNewerWitness, + lastBlockHash, + roots, + caches + )); + } + + @Test + @Order(1) + public void initialized() { + assertEquals(mta.height(), height); + assertEquals(mta.offset(), offset); + assertEquals(mta.rootSize(), rootSize); + assertEquals(mta.cacheSize(), cacheSize); + assertEquals(mta.isAllowNewerWitness(), isAllowNewerWitness); + } + + // @Test + // @Order(2) + // public void setRootSize() { + // rootSize = 3; + // mta.setRootSize(rootSize); + // assertEquals(mta.rootSize(), rootSize); + // } + + @Test + @Order(3) + public void setOffset() throws MTAException { + offset = 10; + mta.setOffset(offset); + assertEquals(mta.offset(), offset); + assertEquals(mta.height(), offset); + height = 10; + } + + @Test + @Order(4) + public void addToMTA() { + /* add first item "dog" + * Root [ hash(dog) ] + * Data [ dog ] + */ + String item1 = "dog"; + byte[] hashItem1 = this.getHash(item1.getBytes()); + + mta.add(hashItem1); + + assertEquals(mta.height(), height + 1); + assertArrayEquals(mta.getRoot(0), hashItem1); + + /* add second item "dog" + * Root (higher item first) [ hash(dog, cat) null ] + * Data [ dog cat ] + */ + String item2 = "cat"; + byte[] hashItem2 = this.getHash(item2.getBytes()); + + byte[] concatenationHash12 = new byte[64]; + getConcatenation(concatenationHash12, hashItem1, hashItem2); + byte[] hash12 = this.getHash(concatenationHash12); + doReturn(hash12).when(mta).getHash(concatenationHash12); + + mta.add(hashItem2); + + assertEquals(mta.height(), height + 2); + assertArrayEquals(mta.getRoot(1), hash12); + assertArrayEquals(mta.getRoot(0), null); + + /* add third item "snake" + * Root [ hash(dog, cat) hash(snake) ] + * Data [ dog cat snake ] + */ + String item3 = "snake"; + byte[] hashItem3 = this.getHash(item3.getBytes()); + + mta.add(hashItem3); + + assertEquals(mta.height(), height + 3); + assertEquals(mta.offset(), offset); + assertArrayEquals(mta.getRoot(1), hash12); + assertArrayEquals(mta.getRoot(0), hashItem3); + + /* add 4th item "pig" + * Root [ hash(hash(dog, cat), hash(snake, pig)) null null ] + * Data [ dog cat snake pig ] + */ + String item4 = "pig"; + byte[] hashItem4 = this.getHash(item3.getBytes()); + + byte[] concatenationHash34 = new byte[64]; + getConcatenation(concatenationHash34, hashItem3, hashItem4); + byte[] hash34 = this.getHash(concatenationHash34); + doReturn(hash34).when(mta).getHash(concatenationHash34); + + byte[] concatenationHash1234 = new byte[64]; + getConcatenation(concatenationHash1234, hash12, hash34); + byte[] hash1234 = this.getHash(concatenationHash1234); + doReturn(hash1234).when(mta).getHash(concatenationHash1234); + + mta.add(hashItem4); + + assertEquals(mta.height(), height + 4); + assertEquals(mta.offset(), offset); + assertArrayEquals(mta.getRoot(0), null); + assertArrayEquals(mta.getRoot(1), null); + assertArrayEquals(mta.getRoot(2), hash1234); + + /* add 5th item "chicken" + * Root [ hash(hash(dog, cat), hash(snake, pig)) null H(chicken) ] + * Data [ dog cat snake pig chicken ] + */ + String item5 = "chicken"; + byte[] hashItem5 = this.getHash(item5.getBytes()); + + mta.add(hashItem5); + + assertEquals(mta.height(), height + 5); + assertEquals(mta.offset(), offset); + assertArrayEquals(mta.getRoot(0), hashItem5); + assertArrayEquals(mta.getRoot(1), null); + assertArrayEquals(mta.getRoot(2), hash1234); + + /* add 6th item "cow" + * Root [ hash(hash(dog, cat) hash(snake, pig)) H(chicken, cow) null ] + * Data [ dog cat snake pig chicken cow ] + */ + String item6 = "cow"; + byte[] hashItem6 = this.getHash(item6.getBytes()); + + byte[] concatenationHash56 = new byte[64]; + getConcatenation(concatenationHash56, hashItem5, hashItem6); + byte[] hash56 = this.getHash(concatenationHash56); + doReturn(hash56).when(mta).getHash(concatenationHash56); + + mta.add(hashItem6); + assertEquals(mta.height(), height + 6); + assertEquals(mta.offset(), offset); + assertArrayEquals(mta.getRoot(0), null); + assertArrayEquals(mta.getRoot(1), hash56); + assertArrayEquals(mta.getRoot(2), hash1234); + + /* add 7th item "fish" + * Root [ hash(hash(dog, cat), hash(snake, pig)) H(chicken, cow) H(fish) ] + * Data [ dog cat snake pig chicken cow fish ] + */ + String item7 = "fish"; + byte[] hashItem7 = this.getHash(item7.getBytes()); + + mta.add(hashItem7); + + assertEquals(mta.height(), height + 7); + assertEquals(mta.offset(), offset); + assertArrayEquals(mta.getRoot(0), hashItem7); + assertArrayEquals(mta.getRoot(1), hash56); + assertArrayEquals(mta.getRoot(2), hash1234); + } + + @Test + @Order(5) + public void addNewItemAndRootArrayIsFull() { + /* add 8th item "duck" + * Root [ hash(hash(chicken, cow), hash(fish, duch)) null null ] + * Data [ chicken cow fish duck ] + */ + String item8 = "duck"; + byte[] hashItem8 = this.getHash(item8.getBytes()); + + byte[] hashItem7 = mta.getRoot(0); + byte[] hash56 = mta.getRoot(1); + byte[] concatenationHash78 = new byte[64]; + getConcatenation(concatenationHash78, hashItem7, hashItem8); + byte[] hash78 = this.getHash(concatenationHash78); + doReturn(hash78).when(mta).getHash(concatenationHash78); + + byte[] concatenationHash5678 = new byte[64]; + getConcatenation(concatenationHash5678, hash56, hash78); + byte[] hash5678 = this.getHash(concatenationHash5678); + doReturn(hash5678).when(mta).getHash(concatenationHash5678); + + mta.add(hashItem8); + + assertEquals(mta.height(), height + 8); + assertEquals(mta.offset(), offset + 4); + assertArrayEquals(mta.getRoot(0), null); + assertArrayEquals(mta.getRoot(1), null); + assertArrayEquals(mta.getRoot(2), hash5678); + } + + @Test + @Order(6) + public void verifyWithHightEqualToAt() throws MTAException { + /* current root + * Root [ hash(hash(chicken, cow), hash(fish, duch)) null null ] + * Data [ chicken cow fish duck ] + */ + String item6 = "cow"; + byte[] hashItem6 = this.getHash(item6.getBytes()); + + String item5 = "chicken"; + byte[] hashItem5 = this.getHash(item5.getBytes()); + + String item8 = "duck"; + byte[] hashItem8 = this.getHash(item8.getBytes()); + String item7 = "fish"; + byte[] hashItem7 = this.getHash(item7.getBytes()); + byte[] concatenationHash78 = new byte[64]; + getConcatenation(concatenationHash78, hashItem7, hashItem8); + byte[] hash78 = this.getHash(concatenationHash78); + + List listWitness = new ArrayList(2); + listWitness.add(hashItem5); + listWitness.add(hash78); + // prove item 6 (cow) in tree + mta.verify(listWitness, hashItem6, 16, 18); + } + + @Test + @Order(7) + public void verifyWithHeightLessThanAt() throws MTAException { + /* contract mta, height 18, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duch)) null null ] + * Data [ chicken cow fish duck ] + */ + + /* client mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + String item6 = "cow"; + byte[] hashItem6 = this.getHash(item6.getBytes()); + + String item5 = "chicken"; + byte[] hashItem5 = this.getHash(item5.getBytes()); + + String item8 = "duck"; + byte[] hashItem8 = this.getHash(item8.getBytes()); + String item7 = "fish"; + byte[] hashItem7 = this.getHash(item7.getBytes()); + byte[] concatenationHash78 = new byte[64]; + getConcatenation(concatenationHash78, hashItem7, hashItem8); + byte[] hash78 = this.getHash(concatenationHash78); + + List listWitness = new ArrayList(2); + listWitness.add(hashItem5); + listWitness.add(hash78); + // prove item 6 (cow) in tree + mta.verify(listWitness, hashItem6, 16, 21); + } + + @Test + @Order(8) + public void verifyWithHeightLessThanAt2() throws MTAException { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + + /* client mta, height 23, offset 18 + * Root [ hash(hash(tiger, lion), hash(bird, mouse)) null H(monkey) ] + * Data [ tiger lion bird mouse monkey ] + */ + String item9 = "tiger"; + byte[] hashItem9 = this.getHash(item9.getBytes()); + String item10 = "lion"; + byte[] hashItem10 = this.getHash(item10.getBytes()); + byte[] concatenationHash9_10 = new byte[64]; + getConcatenation(concatenationHash9_10, hashItem9, hashItem10); + byte[] hash9_10 = this.getHash(concatenationHash9_10); + + String item11 = "bird"; + byte[] hashItem11 = this.getHash(item11.getBytes()); + + String item12 = "mouse"; + byte[] hashItem12 = this.getHash(item12.getBytes()); + + doReturn(hash9_10).when(mta).getHash(concatenationHash9_10); + + // add tiger + mta.add(hashItem9); + // add lion + mta.add(hashItem10); + // add bird + mta.add(hashItem11); + + List listWitness = new ArrayList(2); + listWitness.add(hashItem12); + listWitness.add(hash9_10); + // prove item 21 (bird) in tree + mta.verify(listWitness, hashItem11, 21, 23); + } + + @Test + @Order(9) + public void verifyWithHightEqualToAtFail() { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + String item6 = "cow"; + byte[] hashItem6 = this.getHash(item6.getBytes()); + + String item5 = "chicken"; + byte[] hashItem5 = this.getHash(item5.getBytes()); + + String item8 = "duck"; + byte[] hashItem8 = this.getHash(item8.getBytes()); + String item7 = "fish"; + byte[] hashItem7 = this.getHash(item7.getBytes()); + byte[] concatenationHash78 = new byte[64]; + getConcatenation(concatenationHash78, hashItem7, hashItem8); + byte[] hash78 = this.getHash(concatenationHash78); + + byte[][] witness = new byte[2][32]; + witness[0] = hashItem5; + witness[1] = hash78; + + // modified correct witness + witness[1][30] = 10; + witness[1][31] = 123; + + List listWitness = new ArrayList(2); + listWitness.add(witness[0]); + listWitness.add(witness[1]); + + // prove item 6 (cow) in tree + MTAException thrown = assertThrows( + MTAException.class, + () -> mta.verify(listWitness, hashItem6, 16, 21) + ); + + assertTrue(thrown.getMessage().contains("invalid witness")); + } + + @Test + @Order(10) + public void verifyWithHeightLessThanAtFail() { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + + /* client mta, height 23, offset 18 + * Root [ hash(hash(tiger, lion), hash(bird, mouse)) null H(monkey) ] + * Data [ tiger lion bird mouse monkey ] + */ + String item9 = "tiger"; + byte[] hashItem9 = this.getHash(item9.getBytes()); + String item10 = "lion"; + byte[] hashItem10 = this.getHash(item10.getBytes()); + + String item11 = "bird"; + byte[] hashItem11 = this.getHash(item11.getBytes()); + String item12 = "mouse"; + byte[] hashItem12 = this.getHash(item12.getBytes()); + byte[] concatenationHash11_12 = new byte[64]; + getConcatenation(concatenationHash11_12, hashItem9, hashItem10); + byte[] hash11_12 = this.getHash(concatenationHash11_12); + + byte[][] witness = new byte[2][32]; + witness[0] = hashItem9; + witness[1] = hash11_12; + + // modify witness + witness[0][30] = 15; + witness[0][31] = 111; + + List listWitness = new ArrayList(2); + listWitness.add(witness[0]); + listWitness.add(witness[1]); + + // prove item 10 (lion) in tree + MTAException thrown = assertThrows( + MTAException.class, + () -> mta.verify(listWitness, hashItem10, 20, 23) + ); + + assertTrue(thrown.getMessage().contains("invalid witness")); + } + + @Test + @Order(11) + public void throwErrorIfGivenWitnessForNewerNode() { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + + /* client mta, height 23, offset 18 + * Root [ hash(hash(tiger, lion), hash(bird, mouse)) null H(monkey) ] + * Data [ tiger lion bird mouse monkey ] + */ + String item23 = "monkey"; + byte[] hashItem23 = this.getHash(item23.getBytes()); + + // prove item 13 (monkey) in tree + MTAException thrown = assertThrows( + MTAException.class, + () -> mta.verify(new ArrayList(10), hashItem23, 23, 23) + ); + + assertTrue(thrown.getMessage().contains("given witness for newer node")); + } + + @Test + @Order(12) + public void verifyByCache() throws MTAException { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + + /* client mta, height 17, offset 10 + * Root [ hash(hash(dog, cat), hash(snake, pig)) H(chicken, cow) H(fish) ] + * Data [ dog cat snake pig chicken cow fish ] + */ + String item6 = "cow"; + byte[] hashItem6 = this.getHash(item6.getBytes()); + + String item5 = "chicken"; + byte[] hashItem5 = this.getHash(item5.getBytes()); + + byte[][] witness = new byte[1][32]; + witness[0] = hashItem5; + + List listWitness = new ArrayList(2); + listWitness.add(witness[0]); + + // prove item 6 (cow) in tree + mta.verify(listWitness, hashItem6, 16, 17); + } + + @Test + @Order(13) + public void verifyByCacheFail() { + /* contract mta, height 21, offset 14 + * Root [ hash(hash(chicken, cow), hash(fish, duck)) H(tiger, lion) H(bird) ] + * Data [ chicken cow fish duck tiger lion bird ] + */ + + /* client mta, height 17, offset 10 + * Root [ hash(hash(dog, cat), hash(snake, pig)) H(chicken, cow) H(fish) ] + * Data [ dog cat snake pig chicken cow fish ] + */ + String fakeItem = "aaa"; + byte[] hashFakeItem = this.getHash(fakeItem.getBytes()); + + byte[][] witness = new byte[1][32]; + witness[0] = hashFakeItem; + + List listWitness = new ArrayList(2); + listWitness.add(witness[0]); + + MTAException thrown = assertThrows( + MTAException.class, + () -> mta.verify(listWitness, hashFakeItem, 12, 17) + ); + + assertTrue(thrown.getMessage().contains("invalid old witness")); + } + + @Test + @Order(13) + public void verifyFailBecuaseOldWitness() { + String fakeItem = "aaa"; + byte[] hashFakeItem = this.getHash(fakeItem.getBytes()); + + byte[][] witness = new byte[1][32]; + witness[0] = hashFakeItem; + + List listWitness = new ArrayList(2); + listWitness.add(witness[0]); + + MTAException thrown = assertThrows( + MTAException.class, + () -> mta.verify(listWitness, hashFakeItem, 8, 17) + ); + + assertTrue(thrown.getMessage().contains("not allowed old witness")); + } + + @Test + @Order(14) + public void getMTAStatus() { + MTAStatus status = mta.getStatus(); + + assertEquals(status.height, 21); + assertEquals(status.offset, 14); + } + + @Test + @Order(15) + public void initializedMTAWithNullRoot() { + List roots = new ArrayList(rootSize); + List caches = new ArrayList(cacheSize); + MerkleTreeAccumulator initMta = spy(new MerkleTreeAccumulator( + height, + offset, + rootSize, + cacheSize, + isAllowNewerWitness, + lastBlockHash, + roots, + caches + )); + + assertEquals(initMta.height(), height); + } + + @Test + @Order(16) + public void initializedMTAWithNullRootItem() { + List roots = new ArrayList(rootSize); + List caches = new ArrayList(cacheSize); + roots.add(null); + roots.add(new byte[]{ 0x11, 0x22, 0x34 }); + roots.add(null); + MerkleTreeAccumulator initMta = spy(new MerkleTreeAccumulator( + height, + offset, + rootSize, + cacheSize, + isAllowNewerWitness, + lastBlockHash, + roots, + caches + )); + + assertEquals(initMta.height(), height); + } +} diff --git a/javascore/bmv/parachain/build.gradle b/javascore/bmv/parachain/build.gradle new file mode 100644 index 00000000..d357722e --- /dev/null +++ b/javascore/bmv/parachain/build.gradle @@ -0,0 +1,99 @@ +plugins { + id 'java' + id 'maven-publish' + id 'signing' +} + +sourceSets { + intTest {} +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +configurations { + intTestImplementation.extendsFrom testImplementation + intTestRuntimeOnly.extendsFrom testRuntimeOnly +} + +dependencies { + implementation 'com.github.sink772:javaee-scorex:0.5.2' + implementation project(':lib') + compileOnly files('../api-0.8.8-SNAPSHOT.jar') + + testImplementation "com.squareup.okhttp3:okhttp:3.11.0" + intTestImplementation project(':testinteg') + intTestImplementation 'foundation.icon:icon-sdk:2.0.0' + intTestImplementation 'org.web3j:core:4.8.4' + testImplementation project(':testsvc') + testImplementation 'org.mockito:mockito-core:3.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.bmv.parachain.BMV' + archivesBaseName = 'parachain-BMV' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +deployJar { + endpoints { + gangnam { + uri = 'https://sejong.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = 'http://localhost:9082/api/v3' + nid = 3 + } + } + keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : '' + password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : '' + parameters { + arg('bmc', 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd') + arg('net', '0x1234.icon') + arg('encodedValidators', '---') + arg('relayMtaOffset', '0x465459') + arg('paraMtaOffset', '0x465459') + arg('mtaRootSize', '0x10') + arg('mtaCacheSize', '0x10') + arg('mtaIsAllowNewerWitness', "0x1") + arg('relayLastBlockHash', "0xfb147d7417db613d12685b9affdf6dd06bcfbba78a477a7fc9a79a21075a776a") + arg('paraLastBlockHash', "0xfb147d7417db613d12685b9affdf6dd06bcfbba78a477a7fc9a79a21075a776a") + arg('relayEventDecoderAddress', "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd") + arg('paraEventDecoderAddress', "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd") + arg('relayCurrentSetId', "0x11") + arg('paraChainId', "0x1ed") + } +} + +test { + useJUnitPlatform() + testLogging.showStandardStreams = true + testLogging.exceptionFormat = "full" +} + +def eventDecoderBuildDirectory = project(':eventDecoder').buildDir + +task integrationTest(type: Test, dependsOn: optimizedJar) { + useJUnitPlatform() + description = 'Runs integration tests.' + group = 'verification' + + testClassesDirs = sourceSets.intTest.output.classesDirs + classpath = sourceSets.intTest.runtimeClasspath + testLogging.showStandardStreams = true + + // use the common config files + systemProperty('env.props', new File(project(':testinteg').projectDir, 'conf/env.props')) + + def prefix = 'score.path.' + systemProperty(prefix + project.name, optimizedJar.outputJarName) + systemProperty(prefix + 'MoonriverEventDecoder', """${eventDecoderBuildDirectory}/libs/MoonriverEventDecoder-optimized.jar""") + systemProperty(prefix + 'KusamaEventDecoder', """${eventDecoderBuildDirectory}/libs/KusamaEventDecoder-optimized.jar""") +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java new file mode 100644 index 00000000..cd0dfe44 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java @@ -0,0 +1,3051 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.cases; + +import foundation.icon.icx.IconService; +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.http.HttpProvider; +import foundation.icon.icx.transport.jsonrpc.RpcError; +import foundation.icon.icx.data.TransactionResult; + +import foundation.icon.test.Env; +import foundation.icon.test.TestBase; +import foundation.icon.test.TransactionHandler; +import foundation.icon.test.score.*; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; +import scorex.util.Base64; + +import score.util.Crypto; + +import static foundation.icon.test.Env.LOG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import okhttp3.OkHttpClient; +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +@TestMethodOrder(OrderAnnotation.class) +public class BMVTestScore extends TestBase { + private static final boolean DEBUG = true; + private static Address ZERO_ADDRESS; + private static TransactionHandler txHandler; + private static KeyWallet[] wallets; + private static KeyWallet ownerWallet, caller; + private static List validatorPublicKeys; + private static List validatorSecretKey; + private static BMVScore bmvScore; + private static KusamaEventDecoderScore kusamaEventDecoderScore; + private static MoonriverEventDecoderScore moonriverEventDecoderScore; + private static String bmc; + private static String destinationNet = "0x9876.edge"; + private static String sourceNet = "0x1234.icon"; + private static boolean mtaIsAllowNewerWitness = true; + private static byte[] paraLastBlockHash = HexConverter.hexStringToByteArray("08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2"); + private static byte[] relayLastBlockHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + private static String bmcBTPAddress; + private static BigInteger currentSetId = BigInteger.valueOf(123); + private static BigInteger paraChainId = BigInteger.valueOf(1001); + private static long paraMtaOffset = 10; + private static long relayMtaOffset = 100; + private static int mtaRootSize = 10; + private static int mtaCacheSize = 3; + private static List relayUpdatedBlocks = new ArrayList(10); + private static List paraUpdatedBlocks = new ArrayList(10); + + private byte[] getConcatenationHash(byte[] item1, byte[] item2) { + byte[] concatenation = new byte[item1.length + item2.length]; + System.arraycopy(item1, 0, concatenation, 0, item1.length); + System.arraycopy(item2, 0, concatenation, item1.length, item2.length); + return Crypto.sha3_256(concatenation); + } + + @BeforeAll + static void setup() throws Exception { + ZERO_ADDRESS = new Address("hx0000000000000000000000000000000000000000"); + validatorPublicKeys = new ArrayList(5); + validatorSecretKey = new ArrayList(5); + + Env.Chain chain = Env.getDefaultChain(); + OkHttpClient ohc = new OkHttpClient.Builder().build(); + IconService iconService = new IconService(new HttpProvider(ohc, chain.getEndpointURL(), 3)); + txHandler = new TransactionHandler(iconService, chain); + + // init wallets + wallets = new KeyWallet[2]; + BigInteger amount = ICX.multiply(BigInteger.valueOf(200)); + for (int i = 0; i < wallets.length; i++) { + wallets[i] = KeyWallet.create(); + txHandler.transfer(wallets[i].getAddress(), amount); + } + for (KeyWallet wallet : wallets) { + ensureIcxBalance(txHandler, wallet.getAddress(), BigInteger.ZERO, amount); + } + ownerWallet = wallets[0]; + caller = wallets[1]; + + bmc = ownerWallet.getAddress().toString(); + bmcBTPAddress = "btp://" + sourceNet + "/" + ownerWallet.getAddress().toString(); + + // initialize validators key + validatorPublicKeys.add(HexConverter.hexStringToByteArray("25aabce1a96af5c3f6a4c780b6eeeb7769bf32884f1bad285b5faa464e25c487")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("8e111029cd8a3754095d96e9d01629f7ee686198b9e8465d9c90101ed867b958")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("01573582dc1bc7474e76cd5a5cbefbcf5475284ced05cc66a45dcb65ea29b09b")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("d3e7471c8fd4cdb71e791783098794e2295f992ae6a4a27e0f893071ade31b78")); + + validatorSecretKey.add(HexConverter.hexStringToByteArray("c360fab3025db65e3d967f553ddae434382cfbb9130b6de339fd1f3283f350a025aabce1a96af5c3f6a4c780b6eeeb7769bf32884f1bad285b5faa464e25c487")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("f5880449f4eb08bfa946a6f5bb679a33f58a7f1bd93c52bb4f2e05dad27e45dc8e111029cd8a3754095d96e9d01629f7ee686198b9e8465d9c90101ed867b958")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("e8c61ebe8654dfcfc279d28eec8af09d1149bbc9ba26049354e880d479ef98f101573582dc1bc7474e76cd5a5cbefbcf5475284ced05cc66a45dcb65ea29b09b")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("8f97e18992ed2fb7b7f7e37146e2107cb950556cead04c347ad81c69b62ad51fd3e7471c8fd4cdb71e791783098794e2295f992ae6a4a27e0f893071ade31b78")); + + List listRlpValidators = new ArrayList(5); + for (byte[] validator: validatorPublicKeys) { + listRlpValidators.add(RlpString.create(validator)); + } + // RLP encode validators public key + byte[] rlpEncodeValidators = RlpEncoder.encode(new RlpList(listRlpValidators)); + // base 64 encoded validator public key + String encodedValidators = new String(Base64.getUrlEncoder().encode(rlpEncodeValidators)); + kusamaEventDecoderScore = KusamaEventDecoderScore.mustDeploy( + txHandler, + ownerWallet + ); + moonriverEventDecoderScore = MoonriverEventDecoderScore.mustDeploy( + txHandler, + ownerWallet + ); + bmvScore = BMVScore.mustDeploy( + txHandler, + ownerWallet, + bmc, + destinationNet, + encodedValidators, + relayMtaOffset, + paraMtaOffset, + mtaRootSize, + mtaCacheSize, + mtaIsAllowNewerWitness, + relayLastBlockHash, + paraLastBlockHash, + kusamaEventDecoderScore.getAddress(), + moonriverEventDecoderScore.getAddress(), + currentSetId, + paraChainId + ); + + // check contract initialized successfully + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + assertTrue(relayMtaHeight.equals(BigInteger.valueOf(relayMtaOffset))); + + BigInteger paraMtaHeight = bmvScore.paraMtaHeight(); + assertTrue(paraMtaHeight.equals(BigInteger.valueOf(paraMtaOffset))); + + List relayMtaRoot = bmvScore.relayMtaRoot(); + assertEquals(relayMtaRoot.size(), mtaRootSize); + + List paraMtaRoot = bmvScore.relayMtaRoot(); + assertEquals(paraMtaRoot.size(), mtaRootSize); + + byte[] relayMtaLastBlockHash = bmvScore.relayMtaLastBlockHash(); + assertArrayEquals(relayMtaLastBlockHash, relayLastBlockHash); + + byte[] paraMtaLastBlockHash = bmvScore.paraMtaLastBlockHash(); + assertArrayEquals(paraMtaLastBlockHash, paraLastBlockHash); + + BigInteger relayMtaOffsetResult = bmvScore.relayMtaOffset(); + assertTrue(relayMtaOffsetResult.equals(BigInteger.valueOf(relayMtaOffset))); + + BigInteger paraMtaOffsetResult = bmvScore.paraMtaOffset(); + assertTrue(paraMtaOffsetResult.equals(BigInteger.valueOf(paraMtaOffset))); + + List relayMtaCaches = bmvScore.relayMtaCaches(); + assertEquals(relayMtaCaches.size(), 0); + + List paraMtaCaches = bmvScore.paraMtaCaches(); + assertEquals(paraMtaCaches.size(), 0); + + Address bmcAddress = bmvScore.bmc(); + assertTrue(bmcAddress.toString().equals(bmc)); + + Address relayEventDecoderAddress = bmvScore.relayEventDecoder(); + assertTrue(relayEventDecoderAddress.equals(kusamaEventDecoderScore.getAddress())); + + Address paraEventDecoderAddress = bmvScore.paraEventDecoder(); + assertTrue(paraEventDecoderAddress.equals(moonriverEventDecoderScore.getAddress())); + + String netAddressResult = bmvScore.netAddress(); + assertTrue(netAddressResult.equals(destinationNet)); + + BigInteger lastHeightResult = bmvScore.lastHeight(); + assertTrue(lastHeightResult.equals(BigInteger.valueOf(paraMtaOffset))); + + List validators = bmvScore.validators(); + assertEquals(validators.size(), validatorPublicKeys.size()); + } + + /** + * --------------------------------------------------------------------------------------------- + * RECEIVING A RELAY MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: previous bmc is not belong to network that BMV handle + * Given: + * prev: btp://0xffff.eos/0x12345 + * When: + * network that BMV handle: 0x9876.edge + * Then: + * throw error: + * message: "not acceptable from" + * code: NOT_ACCEPTED_FROM_NETWORK_ERROR 38 + */ + @Test + @Order(1) + public void receivingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String invalidNetAddress = "0xffff.eos"; // valid is 0x1234.icon + String prev = "btp://" + invalidNetAddress + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_FROM_NETWORK_ERROR + ")")); + } + + /** + * + * Scenario 2: transaction caller is not bmc + * Given: + * call `handleRelayMessage` with caller "caller" + * When: + * registered bmc address: ownerWallet.getAddress() generated when setup + * Then: + * throw error: + * message: "not acceptable bmc" + * code: NOT_ACCEPTED_BMC_ADDR_ERROR 39 + */ + @Test + @Order(2) + public void receivingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(caller, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR + ")")); + } + + /** + * + * Scenario 3: bmc is invalid + * Given: + * bmc: btp://0x1234.icon/caller.getAddress() + * When: + * registered bmc address: ownerWallet.getAddress() generated when setup + * Then: + * throw error: + * message: "not acceptable bmc" + * code: NOT_ACCEPTED_BMC_ADDR_ERROR 39 + */ + @Test + @Order(3) + public void receivingScenario3() throws Exception { + String invalidBmcBTPAddress = "btp://" + sourceNet + "/" + caller.getAddress().toString(); // valid is owner address + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, invalidBmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR + ")")); + } + + /** + * --------------------------------------------------------------------------------------------- + * EXTRACTING THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: input relay message with invalid base64 format + * Given: + * msg: invalid base64 format + * When: + * + * Then: + * throw error: + * message: "decode base64 msg error: " + * code: DECODE_ERROR 37 + */ + @Test + @Order(4) + public void extractingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "abcedgef=="; // invalid base64 formart of relay message + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.DECODE_ERROR + ")")); + } + + /** + * + * Scenario 2: input relay message with invalid RLP format + * Given: + * msg: invalid RLP encoded format + * When: + * + * Then: + * throw error: + * message: "RelayMessage RLP decode error: " + * code: DECODE_ERROR 37 + */ + @Test + @Order(5) + public void extractingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List blockUpdate = new ArrayList(3); + blockUpdate.add(RlpString.create("abc")); // invalid block encode + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdate)); // invalid block update + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.DECODE_ERROR + ")")); + } + + /** + * + * Scenario 3: input relay message with empty BlockUpdate and BlockProof + * Given: + * msg: empty BlockUpdate and BlockProof + * When: + * + * Then: + * throw error: + * message: "invalid RelayMessage not exists BlockUpdate or BlockProof" + * code: BMV_ERROR 25 + */ + @Test + @Order(6) + public void extractingScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + relayMessage.add(RlpString.create("")); // empty block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * --------------------------------------------------------------------------------------------- + * VERIFYING THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: input block update without relay chain data and para block + * Given: + * msg: + * blockUpdates: [empty] + * When: + * + * Then: + * throw error: + * message: "Missing relay chain data" + * code: BMV_ERROR 25 + */ + @Test + @Order(7) + public void verifyingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // different with lastBlockHash + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List paraBlockUpdates = new ArrayList(3); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(null); + updatingBlockNumber += 1; + parentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 2: input relay chain block with invalid parent hash with relay mta last block hash + * Given: + * msg: + * relay block 101: + * parentHash: "46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55" + * When: + * relay mta height: 100 + * last block hash: "681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249" + * Then: + * throw error: + * message: "parent relay block hash does not match, parent: 46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55 current: 681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249" + * code: BMV_ERROR 25 + */ + @Test + @Order(8) + public void verifyingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // different with lastBlockHash + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encode())); + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 3: input relay chain block with invalid parent hash in blocks + * Given: + * update relay block 101, 102, 103: + * relay block 102 parent hash: "46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55" + * When: + * relay block 102 parent hash should be: block11.getHash() + * Then: + * throw error: + * message: "parent relay block hash does not match, parent: 46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55 current: block11.getHash()" + * code: BMV_ERROR 25 + * + */ + @Test + @Order(9) + public void verifyingScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); // different with lastBlockHash + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // correct is blockUpdate.getHash() + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encode())); + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 4: input relay block with height higher than current updated relay block height + * Given: + * update relay block 109, 110, 111 + * When: + * last updated relay block height of BMV: 100 + * Then: + * throw error: + * message: "invalid relay blockUpdate height: 109; expected: 101" + * code: INVALID_BLOCK_UPDATE_HEIGHT_HIGHER 33 + */ + @Test + @Order(10) + public void verifyingScenario4() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 109; // should be 101 + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encode())); + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 5: input relay block with height lower than current updated relay block height + * Given: + * update relay block 91, 92, 93 + * When: + * last updated block height of BMV: 100 + * Then: + * throw error: + * message: "invalid relay blockUpdate height: 91; expected: 101" + * code: INVALID_BLOCK_UPDATE_HEIGHT_LOWER 34 + */ + @Test + @Order(11) + public void verifyingScenario5() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 91; // should be 101 + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encode())); + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER + ")")); + } + + /** + * + * Scenario 6: update relay block without votes of validators + * Given: + * update relay block 101, 102, 103 + * relay block 103 has empty votes + * When: + * + * Then: + * throw error: + * message: "not exists votes" + * code: INVALID_BLOCK_UPDATE 29 + */ + @Test + @Order(12) + public void verifyingScenario6() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE + ")")); + } + + /** + * + * Scenario 7: update relay block with invalid vote, vote for invalid block hash + * Given: + * update relay block 101, 102, 103 + * validator sign for: + * relay block hash: "a5f2868483655605709dcbda6ce0fd21cd0386ae2f99445e052d6b1f1ae6db5b" + * When: + * validator should be sign for: + * relay block hash: block103.getHash() + * Then: + * throw error: + * message: "validator signature invalid block hash" + * code: INVALID_VOTES 27 + */ + @Test + @Order(13) + public void verifyingScenario7() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + if (i == 2) { + byte[] invalidVoteRelayBlockHash = HexConverter.hexStringToByteArray("a5f2868483655605709dcbda6ce0fd21cd0386ae2f99445e052d6b1f1ae6db5b"); + byte[] voteMessage = RelayBlockUpdate.voteMessage(invalidVoteRelayBlockHash, relayUpdatingBlockNumber, round, setId); // should not be invalidVoteRelayBlockHash but relayBlockUpdate.getHash() + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); // last block of list, update block votes + } else { + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 8: update relay block with invalid vote, vote for invalid block height + * Given: + * update relay block 101, 102, 103 + * validator sign for: + * block height 102 + * When: + * validator should be sign for: + * block height: 103 + * Then: + * throw error: + * message: "validator signature invalid block height" + * code: INVALID_VOTES 27 + */ + @Test + @Order(14) + public void verifyingScenario8() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + if (i == 2) { + long invalidVoteBlockHeight = 12; + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), invalidVoteBlockHeight, round, setId); // should not be invalidVoteRelayBlockHash but relayBlockUpdate.getHash() + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); // last block of list, update block votes + } else { + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 9: update relay block with invalid vote, vote of newer validator set id + * Given: + * update block 101, 102, 103 + * validator vote for set Id: 124 + * When: + * current validator set id stored in BMV: 123 + * Then: + * throw error: + * message: "verify signature for invalid validator set id"" + * code: INVALID_VOTES 27 + */ + @Test + @Order(15) + public void verifyingScenario9() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("124"); + BigInteger setId = new BigInteger("92"); // invalid setId + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); // should not be invalidVoteRelayBlockHash but relayBlockUpdate.getHash() + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); // last block of list, update block votes + } else { + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 10: update relay block with invalid vote, signature invalid + * Given: + * update relay block 101, 102, 103 + * validator1 has invalid signature + * When: + * + * Then: + * throw error: + * message: "invalid signature" + * code: INVALID_VOTES 27 + */ + @Test + @Order(16) + public void verifyingScenario10() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round.add(BigInteger.valueOf(1)), setId); // invalid vote message + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); // last block of list, update block votes + } else { + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 11: update relay block with invalid vote, signature not belong to validators + * Given: + * update relay block 101, 102, 103 + * block 103 is signed by key not belong to validators + * When: + * + * Then: + * throw error: + * message: "one of signature is not belong to validator" + * code: INVALID_VOTES 27 + */ + @Test + @Order(17) + public void verifyingScenario11() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + byte[] notValidatorPubicKey = HexConverter.hexStringToByteArray("793d5108b8d128792958a139660102d538c24e9fe70bd396e5c95fe451cdc222"); + byte[] notValidatorSecrectKey = HexConverter.hexStringToByteArray("b9fbcdfe9e6289fadef00225651dc6a4ae3fee160d6f86cce71150b3db691481793d5108b8d128792958a139660102d538c24e9fe70bd396e5c95fe451cdc222"); + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); + relayBlockUpdate.vote(notValidatorPubicKey, notValidatorSecrectKey, round, setId); // sign by key not belong to validator + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 12: update relay block with duplicate vote + * Given: + * update block 101, 102, 103 + * two votes for block 103 are the same + * When: + * + * Then: + * throw error: + * message: "duplicated signature" + * code: INVALID_VOTES 27 + */ + @Test + @Order(18) + public void verifyingScenario12() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); // different with lastBlockHash + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); // two same signatures + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); // two same signatures + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 13: relay block vote not enough 2/3 + * Given: + * update block 101, 102, 103 + * number of vote for block 103: 2 + * When: + * number of validators: 4 + * number of vote to finalize block should be: 3 + * Then: + * throw error: + * message: "require signature +2/3" + * code: INVALID_VOTES 27 + */ + @Test + @Order(19) + public void verifyingScenario13() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); // validator 1,2 sign block 103 + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + relayUpdatingBlockNumber += 1; + relayParentHash = relayBlockUpdate.getHash(); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 14: update relay block successfully + * Given: + * update relay block 101, 102, 103, 104, 105, 106, 107 + * number of vote for block 107: 3 + * When: + * number of validators: 4 + * current updated block height: 100 + * Then: + * update relay block to relay relay MTA caches + * update root of relay MTA + * update relay MTA last block hash + */ + @Test + @Order(20) + public void verifyingScenario14() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("681031a8a8da2d3ec9d8da3c8b3c6e4884a3654769d90f09f8e91f2d14cdd249"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long relayUpdatingBlockNumber = 101; + + List relayBlockUpdates = new ArrayList(3); + for (int i = 0; i < 7; i++) { + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, setId); + relayUpdatedBlocks.add(relayBlockUpdate); + relayParentHash = relayBlockUpdate.getHash(); + if (i == 6) { + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber, round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId);// validator 1, 2, 3 sign block 103 + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + relayUpdatingBlockNumber += 1; + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList(relayBlockUpdates)); + relayChainData.add(RlpString.create("")); // block proof + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + assertTrue(relayMtaHeight.equals(BigInteger.valueOf(relayUpdatingBlockNumber))); + + List relayMtaRoot = bmvScore.relayMtaRoot(); + assertEquals(relayMtaRoot.size(), mtaRootSize); + assertArrayEquals(relayMtaRoot.get(0), relayParentHash); + + byte[] relayMtaLastBlockHash = bmvScore.relayMtaLastBlockHash(); + assertArrayEquals(relayMtaLastBlockHash, relayParentHash); + + BigInteger relayMtaOffsetResult = bmvScore.relayMtaOffset(); + assertTrue(relayMtaOffsetResult.equals(BigInteger.valueOf(relayMtaOffset))); + + List relayMtaCaches = bmvScore.relayMtaCaches(); + assertEquals(relayMtaCaches.size(), 3); + assertArrayEquals(relayMtaCaches.get(2), relayParentHash); + + BigInteger lastHeightResult = bmvScore.lastHeight(); + assertTrue(lastHeightResult.equals(BigInteger.valueOf(paraMtaOffset))); + } + + /** + * + * Scenario 15: update relay blockProof of block that not exist in MTA + * Given: + * relay blockProof of block 108; + * When: + * current mta height: 107; + * Then: + * throw error: + * message: "given block height is newer 108; expected: 103" + * code: INVALID_BLOCK_PROOF_HEIGHT_HIGHER, 35 + */ + @Test + @Order(21) + public void verifyingScenario15() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = relayLastBlockHash.clone(); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + long relayUpdatingBlockNumber = 108; + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, currentSetId); // new block that not exist in MTA + relayBlockProof.add(RlpString.create(relayBlockUpdate.getEncodedHeader())); // block header + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(relayMtaHeight)); + List witness = new ArrayList(1); // empty witness + relayBlockWitness.add(new RlpList(witness)); + relayBlockProof.add(new RlpList(relayBlockWitness)); + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_PROOF_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 16: update relay blockProof with invalid old witness + * Given: + * relay blockProof of block 104; + * relay mta height of client: 105; + * When: + * current mta height of BMV: 107; + * mta cache size: 3 (stored block hash 107, 106, 105) + * Then: + * throw error: + * message: "not allowed old witness" + * code: INVALID_BLOCK_WITNESS_OLD, 36 + */ + @Test + @Order(22) + public void verifyingScenario16() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = relayLastBlockHash.clone(); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + relayBlockProof.add(RlpString.create(relayUpdatedBlocks.get(3).getEncodedHeader())); // block 104 + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(BigInteger.valueOf(105))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(relayUpdatedBlocks.get(5).getHash())); // block 105 hash + relayBlockWitness.add(new RlpList(witness)); + relayBlockProof.add(new RlpList(relayBlockWitness)); + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS_OLD + ")")); + } + + /** + * + * Scenario 17: update relay blockProof with invalid block header + * Given: + * relay blockProof of block 105 with incorrect data; + * relay mta height of client: 106; + * When: + * current relay mta height of BMV: 107; + * client mta height less than BMV mta height + * mta cache size: 3 (stored block hash 107, 106, 105) + * Then: + * throw error: + * message: "invalid old witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(23) + public void verifyingScenario17() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + long relayUpdatingBlockNumber = 105; + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, currentSetId); // fake block 15 updated to block proof + relayBlockProof.add(RlpString.create(relayBlockUpdate.getEncodedHeader())); // block 105 + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(BigInteger.valueOf(106))); + List witness = new ArrayList(1); // empty witness + relayBlockWitness.add(new RlpList(witness)); + witness.add(RlpString.create(relayUpdatedBlocks.get(5).getHash())); // block 105 hash + relayBlockProof.add(new RlpList(relayBlockWitness)); + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 18: update relay blockProof with invalid block witness + * Given: + * relay blockProof of block 105 that has invalid witness + * mta height of client: 107; + * When: + * current relay mta height of BMV: 107; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(24) + public void verifyingScenario18() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + long relayUpdatingBlockNumber = 105; + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + relayBlockProof.add(RlpString.create(relayUpdatedBlocks.get(4).getEncodedHeader())); // block 105 + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(bmvScore.relayMtaHeight())); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(HexConverter.hexStringToByteArray("9af0b4af1fc9a9d75dbb7e2b72e6560ae2afef30f91625b562adb9659931cc9a"))); // fake witness + witness.add(RlpString.create(HexConverter.hexStringToByteArray("679adaf23ccf7fbc51b3c59588357f0c2ec3cacba0595274c8ec5c44354ab8bc"))); + relayBlockWitness.add(new RlpList(witness)); + relayBlockProof.add(new RlpList(relayBlockWitness)); + + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 19: update relay blockProof with invalid block witness when client MTA height greater than bmv MTA height + * Given: + * blockProof of block 107 that has invalid witness + * mta height of client: 108; + * When: + * current mta height of BMV: 107; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(25) + public void verifyingScenario19() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + long relayUpdatingBlockNumber = 107; + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber, relayStateRoot, round, currentSetId); // fake block 107 updated to block proof + relayBlockProof.add(RlpString.create(relayBlockUpdate.getEncodedHeader())); + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(BigInteger.valueOf(108))); + List witness = new ArrayList(1); // empty witness + relayBlockWitness.add(new RlpList(witness)); + relayBlockProof.add(new RlpList(relayBlockWitness)); + + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + relayChainData.add(new RlpList()); // empty stateProof + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 20: invalid relay state proof + * Given: + * invalid state proof of relay block, hash of proof missmatch with stateRoot in header + * state proof hash: "e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1" + * When: + * state proof hash: "e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1" + * hash of proof not equal to stateRoot + * Then: + * throw error: + * message: "invalid MPT proof" + * code: INVALID_MPT + */ + @Test + @Order(26) + public void verifyingScenario20() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + BigInteger setId = new BigInteger("123"); + byte[] relayParentHash = relayUpdatedBlocks.get(6).getHash(); + byte[] relayStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(3); + List relayBlockUpdates = new ArrayList(1); + // new block 108 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, setId); + relayParentHash = relayBlockUpdate.getHash(); + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, setId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, setId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + List relayProofs = new ArrayList(1); + // invalid proofs + relayProofs.add(RlpString.create(HexConverter.hexStringToByteArray("80810480d7349ba81a8606735be62a4df34f5ca785ffff19b39b6cd32886a4da9d706c59545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9"))); + relayStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + relayStateProofs.add(new RlpList(relayProofs)); + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_MPT + ")")); + } + + /** + * + * Scenario 21: update new authorities list of relay chain + * Given: + * stateProof with new authorities event of relay chain + * event contains list of 4 new validators + * When: + * current validators set ID: 123 + * Then: + * update new validators list to db + * increase validator setID: 124 + */ + @Test + @Order(27) + public void verifyingScenario21() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + byte[] relayParentHash = relayUpdatedBlocks.get(6).getHash(); + + List newValidatorPublicKeys = new ArrayList(4); // new list of validators + List newValidatorSecretKey = new ArrayList(4); + newValidatorPublicKeys.add(0, HexConverter.hexStringToByteArray("2f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorPublicKeys.add(1, HexConverter.hexStringToByteArray("0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorPublicKeys.add(2, HexConverter.hexStringToByteArray("20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorPublicKeys.add(3, HexConverter.hexStringToByteArray("ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + newValidatorSecretKey.add(0, HexConverter.hexStringToByteArray("23fae2b43f110c42a1d32b2c8678d7b7f71eb7873edd0feab952efbc99d1f9d02f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorSecretKey.add(1, HexConverter.hexStringToByteArray("2b502a205ab7ff2b56a0b225fb6b58c3d78a57a8e03c80938b32e7fbc2c564cc0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorSecretKey.add(2, HexConverter.hexStringToByteArray("27b9c71dfee22bacee68a3c73c4aac3a7c95b72dbe1876095fcc2d4f96ce5bac20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorSecretKey.add(3, HexConverter.hexStringToByteArray("b68efa447ebc6ec9f31a5d24c42e4cba41ae7bcc3e7a3f64918648d9e2bf9244ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + NewAuthoritiesProof newAuthoritiesProof = new NewAuthoritiesProof(newValidatorPublicKeys); + + byte[] relayStateRoot = newAuthoritiesProof.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(3); + List relayBlockUpdates = new ArrayList(1); + // new block 108 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(newAuthoritiesProof.getEventKey())); // storage key + relayStateProofs.add(new RlpList(newAuthoritiesProof.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + List storedValidators = bmvScore.validators(); + assertEquals(storedValidators.size(), newValidatorPublicKeys.size()); + assertTrue(bmvScore.setId().equals(BigInteger.valueOf(124))); + assertArrayEquals(storedValidators.get(0), newValidatorPublicKeys.get(0)); + assertArrayEquals(storedValidators.get(1), newValidatorPublicKeys.get(1)); + assertArrayEquals(storedValidators.get(2), newValidatorPublicKeys.get(2)); + assertArrayEquals(storedValidators.get(3), newValidatorPublicKeys.get(3)); + + validatorPublicKeys = newValidatorPublicKeys; + validatorSecretKey = newValidatorSecretKey; + currentSetId = bmvScore.setId(); + } + + /** + * + * Scenario 22: ignore authorities list of relay chain that already updated + * Given: + * stateProof with new authorities event of relay chain that already updated + * When: + * current validators set ID: 124 + * Then: + * ignore that event + * no change to validator setID: 124 + */ + @Test + @Order(28) + public void verifyingScenario22() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("92"); + byte[] relayParentHash = relayUpdatedBlocks.get(6).getHash(); + byte[] block106Hash = relayUpdatedBlocks.get(5).getHash(); + byte[] block105Hash = relayUpdatedBlocks.get(4).getHash(); + byte[] block104Hash = relayUpdatedBlocks.get(3).getHash(); + byte[] block103Hash = relayUpdatedBlocks.get(2).getHash(); + byte[] block102Hash = relayUpdatedBlocks.get(1).getHash(); + byte[] block101Hash = relayUpdatedBlocks.get(0).getHash(); + + byte[] witness1 = this.getConcatenationHash(block105Hash, block106Hash); + byte[] witness20 = this.getConcatenationHash(block101Hash, block102Hash); + byte[] witness21 = this.getConcatenationHash(block103Hash, block104Hash); + byte[] witness2 = this.getConcatenationHash(witness20, witness21); + + List newValidatorPublicKeys = new ArrayList(4); // new list of validators + List newValidatorSecretKey = new ArrayList(4); + newValidatorPublicKeys.add(0, HexConverter.hexStringToByteArray("2f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorPublicKeys.add(1, HexConverter.hexStringToByteArray("0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorPublicKeys.add(2, HexConverter.hexStringToByteArray("20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorPublicKeys.add(3, HexConverter.hexStringToByteArray("ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + newValidatorSecretKey.add(0, HexConverter.hexStringToByteArray("23fae2b43f110c42a1d32b2c8678d7b7f71eb7873edd0feab952efbc99d1f9d02f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorSecretKey.add(1, HexConverter.hexStringToByteArray("2b502a205ab7ff2b56a0b225fb6b58c3d78a57a8e03c80938b32e7fbc2c564cc0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorSecretKey.add(2, HexConverter.hexStringToByteArray("27b9c71dfee22bacee68a3c73c4aac3a7c95b72dbe1876095fcc2d4f96ce5bac20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorSecretKey.add(3, HexConverter.hexStringToByteArray("b68efa447ebc6ec9f31a5d24c42e4cba41ae7bcc3e7a3f64918648d9e2bf9244ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + NewAuthoritiesProof newAuthoritiesProof = new NewAuthoritiesProof(newValidatorPublicKeys); + + byte[] relayStateRoot = newAuthoritiesProof.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight(); + + List relayChainData = new ArrayList(3); + relayChainData.add(new RlpList()); // empty block update + + List relayBlockProof = new ArrayList(2); + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayBlockProof.add(RlpString.create(relayBlockUpdate.getEncodedHeader())); + List relayBlockWitness = new ArrayList(2); + relayBlockWitness.add(RlpString.create(relayUpdatingBlockNumber)); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(relayUpdatedBlocks.get(6).getHash())); + witness.add(RlpString.create(witness1)); + witness.add(RlpString.create(witness2)); + relayBlockWitness.add(new RlpList(witness)); + relayBlockProof.add(new RlpList(relayBlockWitness)); + + relayChainData.add(RlpString.create(RlpEncoder.encode(new RlpList(relayBlockProof)))); // rlp encoded of blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(newAuthoritiesProof.getEventKey())); // storage key + relayStateProofs.add(new RlpList(newAuthoritiesProof.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(RlpEncoder.encode(new RlpList(relayChainData))); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + List storedValidators = bmvScore.validators(); + assertEquals(storedValidators.size(), newValidatorPublicKeys.size()); + assertTrue(bmvScore.setId().equals(BigInteger.valueOf(124))); + // check that validator list is not changed + assertArrayEquals(storedValidators.get(0), newValidatorPublicKeys.get(0)); + assertArrayEquals(storedValidators.get(1), newValidatorPublicKeys.get(1)); + assertArrayEquals(storedValidators.get(2), newValidatorPublicKeys.get(2)); + assertArrayEquals(storedValidators.get(3), newValidatorPublicKeys.get(3)); + + validatorPublicKeys = newValidatorPublicKeys; + validatorSecretKey = newValidatorSecretKey; + assertEquals(bmvScore.setId(), currentSetId); // check that setId is not changed + } + + /** + * + * Scenario 23: input para block update with invalid parent hash with para mta last block hash + * Given: + * msg: + * para block 11: + * parentHash: "46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55" + * When: + * para mta height: 10 + * para last block hash: "08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2" + * Then: + * throw error: + * message: "parent para block hash does not match, parent: 46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55 current: 08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2" + * code: BMV_ERROR 25 + */ + @Test + @Order(29) + public void verifyingScenario23() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + + byte[] paraParentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // different with lastBlockHash + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 24: input para block update with invalid parent hash in blocks + * Given: + * update block 11, 12, 13 + * block 12 parent hash: "e85929fd964218f6e876ab93087681ee6865d8ce86407329ea45ddedd90522dd" + * When: + * block 12 parent hash should be: block11.getHash() + * Then: + * throw error: + * message: "parent para block hash does not match, parent: e85929fd964218f6e876ab93087681ee6865d8ce86407329ea45ddedd90522dd current: block11 hash" + * code: BMV_ERROR 25 + */ + @Test + @Order(30) + public void verifyingScenario24() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber +=1; + paraParentHash = HexConverter.hexStringToByteArray("e85929fd964218f6e876ab93087681ee6865d8ce86407329ea45ddedd90522dd"); // should be paraBlockUpdate.getHash() + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 25: input para block with height higher than current updated height + * Given: + * update para block 19, 20, 21 + * When: + * last para updated block height of BMV: 10 + * Then: + * throw error: + * message: "invalid para blockUpdate height: 19; expected: 11" + * code: INVALID_BLOCK_UPDATE_HEIGHT_HIGHER 33 + */ + @Test + @Order(31) + public void verifyingScenario25() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 19; // should be 11 + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 26: input para block with height lower than current updated height + * Given: + * para update block 7, 8, 9 + * When: + * last updated para block height of BMV: 10 + * Then: + * throw error: + * message: "invalid para blockUpdate height: 7; expected: 11" + * code: INVALID_BLOCK_UPDATE_HEIGHT_LOWER 34 + */ + @Test + @Order(32) + public void verifyingScenario26() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 7; // should be 11 + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER + ")")); + } + + /** + * + * Scenario 27: input para block without relay chain data to prove that block finalized + * Given: + * para update block 11, 12, 13 + * block update 13 does not have relay chain data + * When: + * last updated para block height of BMV: 10 + * Then: + * throw error: + * message: "Missing relay chain data" + * code: BMV_ERROR 25 + */ + @Test + @Order(33) + public void verifyingScenario27() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); // no relay chain data + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 28: can not find parachain block data in relay chain data + * Given: + * para update block 11, 12, 13 + * block update 13 contain relay chain data + * relay chain data does not contain parachain block data + * When: + * last updated para block height of BMV: 10 + * Then: + * throw error: + * message: "can not find parachain data in relay chain data" + * code: BMV_ERROR 25 + */ + @Test + @Order(34) + public void verifyingScenario28() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + byte[] ramdomBlockHeader = HexConverter.hexStringToByteArray("9e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5b"); // just ramdom block hash, BMV will ignore because it not belong to current paraChainId + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(ramdomBlockHeader, paraChainId.add(BigInteger.valueOf(1))); // different paraChainId + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 109 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot, RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 29: block hash of parachain not match with data included to relay chain + * Given: + * para update block 11, 12, 13 + * block update 13 contain relay chain data + * relay chain data included block hash "9e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5b" + * When: + * last updated para block height of BMV: 10 + * block 13 hash not match "9e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5b" + * Then: + * throw error: + * message: "block hash does not match with relay chain, para block hash: block13.hash() relay inclusion: 9e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5b" + * code: BMV_ERROR 25 + */ + @Test + @Order(35) + public void verifyingScenario29() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + byte[] includedParaBlockHeader = HexConverter.hexStringToByteArray("9e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5c"); // not match with block13.hash() + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 109 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<3; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot, RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 30: update relay and para chain block successfully + * Given: + * para update block 11, 12, 13, 14, 15, 16, 17 + * block update 17 contains relay chain data + * relay chain data included block hash block17.hash() + * update relay block 109 + * When: + * last updated para block height of BMV: 10 + * last updated relay block height of BMV: 109 + * Then: + * update relay MTA + * update para MTA + */ + @Test + @Order(36) + public void verifyingScenario30() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = paraLastBlockHash.clone(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 11; + + List paraBlockUpdates = new ArrayList(3); + for (int i = 0; i<6; i++) { + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatedBlocks.add(paraBlockUpdate); + paraUpdatingBlockNumber +=1; + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + } + + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); // event that include block of parachain + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 109 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraParentHash = paraBlockUpdate.getHash(); + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + BigInteger relayMtaHeight = bmvScore.relayMtaHeight(); + assertTrue(relayMtaHeight.equals(relayUpdatingBlockNumber)); + + List relayMtaRoot = bmvScore.relayMtaRoot(); + assertEquals(relayMtaRoot.size(), mtaRootSize); + assertArrayEquals(relayMtaRoot.get(0), relayParentHash); + + byte[] relayMtaLastBlockHash = bmvScore.relayMtaLastBlockHash(); + assertArrayEquals(relayMtaLastBlockHash, relayParentHash); + + BigInteger relayMtaOffsetResult = bmvScore.relayMtaOffset(); + assertTrue(relayMtaOffsetResult.equals(BigInteger.valueOf(relayMtaOffset))); + + List relayMtaCaches = bmvScore.relayMtaCaches(); + assertEquals(relayMtaCaches.size(), 3); + assertArrayEquals(relayMtaCaches.get(2), relayParentHash); + + BigInteger lastHeightResult = bmvScore.lastHeight(); + assertTrue(lastHeightResult.equals(BigInteger.valueOf(paraMtaOffset))); + + BigInteger paraMtaHeight = bmvScore.paraMtaHeight(); + assertTrue(paraMtaHeight.equals(BigInteger.valueOf(paraUpdatingBlockNumber))); + + List paraMtaRoot = bmvScore.paraMtaRoot(); + assertEquals(paraMtaRoot.size(), mtaRootSize); + assertArrayEquals(paraMtaRoot.get(0), paraParentHash); + + byte[] paraMtaLastBlockHash = bmvScore.paraMtaLastBlockHash(); + assertArrayEquals(paraMtaLastBlockHash, paraParentHash); + + BigInteger paraMtaOffsetResult = bmvScore.paraMtaOffset(); + assertTrue(paraMtaOffsetResult.equals(BigInteger.valueOf(paraMtaOffset))); + + List paraMtaCaches = bmvScore.paraMtaCaches(); + assertEquals(paraMtaCaches.size(), 3); + assertArrayEquals(paraMtaCaches.get(2), paraParentHash); + } + + /** + * + * Scenario 31: update para blockProof of block that not exist in MTA + * Given: + * para blockProof of block 18; + * When: + * current para mta height: 17; + * Then: + * throw error: + * message: "given block height is newer 18; expected: 17" + * code: INVALID_BLOCK_PROOF_HEIGHT_HIGHER, 35 + */ + @Test + @Order(37) + public void verifyingScenario31() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + byte[] paraParentHash = paraUpdatedBlocks.get(6).getHash(); // block 17 hash + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger paraMtaHeight = bmvScore.paraMtaHeight(); + long paraUpdatingBlockNumber = 18; + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List paraBlockProof = new ArrayList(2); + ParaBlockUpdate blockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); // new block that not exist in MTA + paraBlockProof.add(RlpString.create(blockUpdate.getEncodedHeader())); // block header + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(paraUpdatingBlockNumber)); + List witness = new ArrayList(1); // empty witness + paraBlockWitness.add(new RlpList(witness)); + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_PROOF_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 32: update para blockProof with invalid old witness + * Given: + * para blockProof of block 14; + * para mta height of client: 15; + * When: + * current para mta height of BMV: 17; + * para mta cache size: 3 (stored block hash 17, 16, 15) + * Then: + * throw error: + * message: "not allowed old witness" + * code: INVALID_BLOCK_WITNESS_OLD, 36 + */ + @Test + @Order(38) + public void verifyingScenario32() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List paraBlockProof = new ArrayList(2); + paraBlockProof.add(RlpString.create(paraUpdatedBlocks.get(3).getEncodedHeader())); // block 14 + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(BigInteger.valueOf(15))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(paraUpdatedBlocks.get(5).getHash())); + paraBlockWitness.add(new RlpList(witness)); + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS_OLD + ")")); + } + + /** + * + * Scenario 33: update para blockProof with invalid block header when client MTA height less than BMV MTA height + * Given: + * para blockProof of block 15 with incorrect data; + * para mta height of client: 16; + * When: + * current para mta height of BMV: 17; + * para mta cache size: 3 (stored block hash 17, 16, 15) + * Then: + * throw error: + * message: "invalid old witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(39) + public void verifyingScenario33() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + byte[] paraParentHash = HexConverter.hexStringToByteArray("4eb5e84732c04e792a48a9c4d3b637039cff2299d8a9ebfaebfa54b437ea697c"); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 15; + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List paraBlockProof = new ArrayList(2); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); // fake block 15 updated to block proof + paraBlockProof.add(RlpString.create(paraBlockUpdate.getEncodedHeader())); // block header + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(BigInteger.valueOf(16))); + List witness = new ArrayList(1); // empty witness + paraBlockWitness.add(new RlpList(witness)); + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 34: update para blockProof with invalid block witness + * Given: + * para blockProof of block 15 that has invalid witness + * para mta height of client: 17; + * When: + * para current mta height of BMV: 17; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(40) + public void verifyingScenario34() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List paraBlockProof = new ArrayList(2); + paraBlockProof.add(RlpString.create(paraUpdatedBlocks.get(4).getEncodedHeader())); // block 15, correct data + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(BigInteger.valueOf(17))); + List witness = new ArrayList(1); // empty witness + paraBlockWitness.add(new RlpList(witness)); + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 35: update para blockProof with invalid block witness when client MTA height greater than bmv MTA height + * Given: + * para blockProof of block 17 that has invalid witness + * para mta height of client: 18; + * When: + * para current mta height of BMV: 17; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(41) + public void verifyingScenario35() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + byte[] paraParentHash = HexConverter.hexStringToByteArray("4eb5e84732c04e792a48a9c4d3b637039cff2299d8a9ebfaebfa54b437ea697c"); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = 17; + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List paraBlockProof = new ArrayList(2); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); // fake block 15 updated to block proof + paraBlockProof.add(RlpString.create(paraBlockUpdate.getEncodedHeader())); // block 17, incorrect data + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(bmvScore.paraMtaHeight().add(BigInteger.valueOf(1)))); + List witness = new ArrayList(1); // empty witness + paraBlockWitness.add(new RlpList(witness)); + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 36: invalid para state proof + * Given: + * invalid para state proof, hash of proof missmatch with stateRoot in header + * state proof hash: "", + * state root in header: "e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1" + * When: + * + * Then: + * throw error: + * message: "invalid MPT proof" + * code: INVALID_MPT + */ + @Test + @Order(42) + public void verifyingScenario36() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + byte[] paraStateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // empty block proof of para chain + + List stateProofs = new ArrayList(2); + List proofs = new ArrayList(1); + // invalid para state proof + proofs.add(RlpString.create(HexConverter.hexStringToByteArray("80810480d7349ba81a8606735be62a4df34f5ca785ffff19b39b6cd32886a4da9d706c59545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9"))); + stateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + stateProofs.add(new RlpList(proofs)); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_MPT + ")")); + } + + /** + * --------------------------------------------------------------------------------------------- + * RESPOND THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: btp message sequence higher than expected + * Given: + * btp message sequence: 120 + * + * When: + * bmc message sequence: 111 + * Then: + * throw error: + * message: "invalid sequence: 120; expected 112" + * code: INVALID_SEQUENCE_HIGHER, 32 + */ + @Test + @Order(43) + public void respondScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(120); // should be 112 + byte[] btpMessage = "testencodedmessage".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + byte[] paraStateRoot = btpEvent.getRoot(); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // empty block proof of para chain + + List paraStateProofs = new ArrayList(2); + paraStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + paraStateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(paraStateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_SEQUENCE_HIGHER + ")")); + } + + /** + * + * Scenario 2: btp message sequence lower than expected + * Given: + * btp message sequence: 105 + * + * When: + * bmc message sequence: 111 + * Then: + * throw error: + * message: "invalid sequence: 105; expected 112" + * code: INVALID_SEQUENCE, 28 + */ + @Test + @Order(44) + public void respondScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(105); // should be 112 + byte[] btpMessage = "testencodedmessage".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + byte[] paraStateRoot = btpEvent.getRoot(); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // empty block proof of para chain + + List paraStateProofs = new ArrayList(2); + paraStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + paraStateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(paraStateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_SEQUENCE + ")")); + } + + /** + * + * Scenario 3: update btp message with block update successfully + * Given: + * update block: 18 + * valid relay chain data with enough signatures + * valid state proof of relay chain and para chain + * btp message sequence: 112 + * btp message contain in block 18 + * When: + * bmc message sequence: 111 + * current block height: 17 + * Then: + * return btp message to BMC + */ + @Test + @Order(45) + public void respondScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + + String btpNextAddress = bmcBTPAddress; + BigInteger nextMessageSequence = BigInteger.valueOf(112); // current message seq is 111 + byte[] btpMessage = "testencodedmessage".getBytes(); // don't need to create about this, only ramdom data for test, BMV with return this to BMC + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, nextMessageSequence, btpMessage); + byte[] paraStateRoot = btpEvent.getRoot(); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // empty block proof of para chain + + List paraStateProofs = new ArrayList(2); + paraStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + paraStateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(paraStateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + assertTrue(bmvScore.paraMtaHeight().equals(BigInteger.valueOf(paraUpdatingBlockNumber))); + assertTrue(bmvScore.relayMtaHeight().equals(relayUpdatingBlockNumber)); + + assertTrue(bmvScore.lastHeight().equals(BigInteger.valueOf(paraUpdatingBlockNumber))); + } + + /** + * + * Scenario 4: contract log event not BMC contract of src chain + * Given: + * update block: 19 + * valid relay chain data with enough signatures + * valid state proof of relay chain and para chain + * btp message sequence: 113 + * btp message contain in block 19 + * event message extract from RelayMessage of contract cx1234567 + * When: + * BTP address of _prev: btp://0x9876.edge/08425D9Df219f93d5763c3e85204cb5B4cE33aAa + * Then: + * ignore that event log + */ + @Test + @Order(46) + public void respondScenario4() throws Exception { + String notHandlePrevBmcAddress = "ea1c4c2767b6ef9618f6338e700d2570b23f1231"; // bmc address of src chain that not match with prev input of BMC + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + BigInteger lastHeightBeforeUpdated = bmvScore.lastHeight(); // last height of para chain has btp message before updated + + String btpNextAddress = bmcBTPAddress; + BigInteger nextMessageSequence = BigInteger.valueOf(112); // current message seq is 111 + byte[] btpMessage = "testencodedmessage".getBytes(); // don't need to create about this, only ramdom data for test, BMV with return this to BMC + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(notHandlePrevBmcAddress), btpNextAddress, nextMessageSequence, btpMessage); + byte[] paraStateRoot = btpEvent.getRoot(); + + List paraBlockUpdates = new ArrayList(1); + ParaBlockUpdate paraBlockUpdate = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + + byte[] includedParaBlockHeader = paraBlockUpdate.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + relayMessage.add(RlpString.create("")); // empty block proof of para chain + + List paraStateProofs = new ArrayList(2); + paraStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + paraStateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(paraStateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + assertTrue(bmvScore.paraMtaHeight().equals(BigInteger.valueOf(paraUpdatingBlockNumber))); + assertTrue(bmvScore.relayMtaHeight().equals(relayUpdatingBlockNumber)); + + assertTrue(bmvScore.lastHeight().equals(lastHeightBeforeUpdated)); // last height should not be change, because no btp message was updated + } + + /** + * + * Scenario 5: update btp message with block proof successfully + * Given: + * update block: 20, 21 + * btp message sequence: 114 + * btp message contain in block 120 + * block proof for block 20 + * para chain data update block 111 + * candidate included event in relay chain prove that block 20 included + * When: + * current bmc message sequence: 113 + * current block height of para: 19 + * current block height of relay: 110 + * Then: + * return btp message to BMC + */ + @Test + @Order(47) + public void respondScenario5() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("112"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] paraParentHash = bmvScore.paraMtaLastBlockHash(); + long paraUpdatingBlockNumber = bmvScore.paraMtaHeight().longValue() + 1; + + String btpNextAddress = bmcBTPAddress; + BigInteger nextMessageSequence = BigInteger.valueOf(113); // current message seq is 112 + byte[] btpMessage = "testencodedmessageblock21".getBytes(); // don't need to create about this, only ramdom data for test, BMV with return this to BMC + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, nextMessageSequence, btpMessage); + byte[] paraStateRoot = btpEvent.getRoot(); + + // update block 19 and 20 of para chain + ParaBlockUpdate paraBlockUpdate19 = new ParaBlockUpdate(paraParentHash, paraUpdatingBlockNumber, paraStateRoot); + paraUpdatingBlockNumber += 1; + ParaBlockUpdate paraBlockUpdate20 = new ParaBlockUpdate(paraBlockUpdate19.getHash(), paraUpdatingBlockNumber, HexConverter.hexStringToByteArray("ce639555326f1a51548e7aa18db08e3dc0584b05d81285c6776453b78345e745")); + + // relay chain included block 20 of para chain + byte[] includedParaBlockHeader = paraBlockUpdate20.getHash(); + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(includedParaBlockHeader, paraChainId); + + byte[] relayParentHash = bmvScore.relayMtaLastBlockHash(); + byte[] relayStateRoot = candidateIncludedEvent.getRoot(); + BigInteger relayUpdatingBlockNumber = bmvScore.relayMtaHeight().add(BigInteger.valueOf(1)); + + List relayChainData = new ArrayList(1); + List relayBlockUpdates = new ArrayList(1); + // new block 110 + RelayBlockUpdate relayBlockUpdate = new RelayBlockUpdate(relayParentHash, relayUpdatingBlockNumber.longValue(), relayStateRoot, round, currentSetId); + relayParentHash = relayBlockUpdate.getHash(); + relayUpdatedBlocks.add(relayBlockUpdate); + // validators sign block to be finalized + byte[] voteMessage = RelayBlockUpdate.voteMessage(relayBlockUpdate.getHash(), relayUpdatingBlockNumber.longValue(), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + relayBlockUpdate.vote(validatorPublicKeys.get(2), validatorSecretKey.get(2), round, currentSetId); + relayBlockUpdates.add(RlpString.create(relayBlockUpdate.encodeWithVoteMessage(voteMessage))); + relayChainData.add(new RlpList(relayBlockUpdates)); + + relayChainData.add(RlpString.create("")); // empty blockProof + + List relayStateProofs = new ArrayList(2); + relayStateProofs.add(RlpString.create(candidateIncludedEvent.getEventKey())); // storage key + relayStateProofs.add(new RlpList(candidateIncludedEvent.getProof())); // MPT proof of event storage + relayChainData.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(relayStateProofs))))); + + paraBlockUpdate20.setRelayChainData(RlpEncoder.encode(new RlpList(relayChainData))); + paraUpdatedBlocks.add(paraBlockUpdate19); + paraUpdatedBlocks.add(paraBlockUpdate20); + List paraBlockUpdates = new ArrayList(2); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate19.encode())); + paraBlockUpdates.add(RlpString.create(paraBlockUpdate20.encode())); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(paraBlockUpdates)); + + List paraBlockProof = new ArrayList(2); + paraBlockProof.add(RlpString.create(paraBlockUpdate19.getEncodedHeader())); // block 19 + + List paraBlockWitness = new ArrayList(2); // empty witness + paraBlockWitness.add(RlpString.create(BigInteger.valueOf(20))); + List witness = new ArrayList(1); // empty witness + paraBlockWitness.add(new RlpList(witness)); + witness.add(RlpString.create(paraBlockUpdate20.getHash())); // block 20 hash + paraBlockProof.add(new RlpList(paraBlockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(paraBlockProof)))); // rlp encoded of blockProof + + List paraStateProofs = new ArrayList(2); + paraStateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); // storage key + paraStateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(paraStateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + assertTrue(bmvScore.paraMtaHeight().equals(BigInteger.valueOf(paraUpdatingBlockNumber))); + assertTrue(bmvScore.relayMtaHeight().equals(relayUpdatingBlockNumber)); + } +} diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java new file mode 100644 index 00000000..f0f51008 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java @@ -0,0 +1,23 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; + +import score.Context; + +public class AbiEncoder { + public static void encodeUInt(ByteSliceOutput output, long number) { + output.addMany(HexConverter.hexStringToByteArray("000000000000000000000000000000000000000000000000")); // 24 byte padding + for (int i = 7; i>=0; i--) { + output.add((byte) ((number >> (8*i)) & 0xff)); + } + } + + public static void encodeBytes(ByteSliceOutput output, byte[] data) { + int padding = (32 - data.length%32); + encodeUInt(output, data.length); + output.addMany(data); + for (int i = 0; i evmTopics = new ArrayList(3); + private final String next; + private final BigInteger seq; + private final byte[] msg; + private final List proofs = new ArrayList(5); + + public BTPEvent(String prevBmcBtpAddress, byte[] prevBmcAddress, String next, BigInteger seq, byte[] msg) { + this.next = next; + this.seq = seq; + this.msg = msg; + + if (next.length() > 64 || next.length() < 32) { + throw new AssertionError("invalid next length"); + } + + ByteSliceOutput evmData = new ByteSliceOutput(100); + AbiEncoder.encodeUInt(evmData, 96); // next position; + AbiEncoder.encodeUInt(evmData, seq.longValue()); + AbiEncoder.encodeUInt(evmData, 192); + AbiEncoder.encodeBytes(evmData, next.getBytes()); + AbiEncoder.encodeBytes(evmData, msg); + + ByteSliceOutput eventData = new ByteSliceOutput(100); + eventData.addMany(prevBmcAddress); + ScaleWriter.writeCompactUint(eventData, 1); // topic size; + eventData.addMany(HexConverter.hexStringToByteArray("37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b")); // btp event topic + ScaleWriter.writeCompactUint(eventData, evmData.size()); // evm data size + eventData.addMany(evmData.toArray()); + + ByteSliceOutput proof0 = new ByteSliceOutput(100); + ByteSliceOutput proof1 = new ByteSliceOutput(100); + ByteSliceOutput proof2 = new ByteSliceOutput(100); + ByteSliceOutput proof3 = new ByteSliceOutput(100); + ByteSliceOutput eventLeafNode = new ByteSliceOutput(100); + + eventLeafNode.addMany(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d7")); // node header + ScaleWriter.writeCompactUint(eventLeafNode, 31 + 3 + eventData.size() + 1); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("0c000000000001019a177e0000000000000000000000000000000000020000")); // other events + eventLeafNode.addMany(HexConverter.hexStringToByteArray("013300")); // evm event phase, index + eventLeafNode.addMany(eventData.toArray()); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("00")); // empty topic + proofs.add(RlpString.create(eventLeafNode.toArray())); + + proof3.addMany(HexConverter.hexStringToByteArray("80810480")); + proof3.addMany(Crypto.hash("blake2b-256", eventLeafNode.toArray())); + proof3.addMany(HexConverter.hexStringToByteArray("545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9")); + proofs.add(RlpString.create(proof3.toArray())); + + proof2.addMany(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f8043580dd17bfb375845229c0dd74fc5f9be81d5f4cf569c3ee845e3acf5271556804afcd87f2329d61d655e551a438a2720a92951e383690f5a623d245cfb5ef4444c5f0684a022a34dd8bfa2baaf44f172b710040180")); + proof2.addMany(Crypto.hash("blake2b-256", proof3.toArray())); + proof2.addMany(HexConverter.hexStringToByteArray("80fbf36a2eb1b78c879a6ee9b08a04f9f341b284ca2bec0a74f4f39b1df1386ace80586079736657bdf54edfb3d4aab0c8221717c3e1e1ad5e6beeaa7c64dbbe515e80b5a6f3ec81bb61e573624e88e3d279154a7183905d05c656f8744d5788766f614c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287820706f6c6b61646f74")); + proofs.add(RlpString.create(proof2.toArray())); + + proof1.addMany(HexConverter.hexStringToByteArray("80499c804db64d4c6dd7655a3ac8bc482884de58452960029ea5b37c03ec11e678ec9a7c801ba7abc7e25fb80c99c4bff68d0688c9af3e1ff8cfb2bd5a1754ae1b86f94d1c80")); + proof1.addMany(Crypto.hash("blake2b-256", proof2.toArray())); + proof1.addMany(HexConverter.hexStringToByteArray("804a9786b7fa887adfd76874bc414a0f8854227ea92a4739e1bccefe699449460a808310dbebe8a5a91f783843a9916605b01caa58ea3ad8abce80684686d8605f96803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80")); + proofs.add(RlpString.create(proof1.toArray())); + + proof0.addMany(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961803440cfd059e558ed0fc83a7de22ffb14d56220e3ce446d897355c85a53401afe80")); + proof0.addMany(Crypto.hash("blake2b-256", proof1.toArray())); + proof0.addMany(HexConverter.hexStringToByteArray("80ede785a30002092980072d34de5a0cc038742d403a539e486808c64f1aeeac8c806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480c7b7787db289ac25daf82f320e421dc298a601a72cea3aa9784e5700251f169d800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de805839bffac46f817f1bebedade41179b15a7d92a7542b47cb484f1c866cde44f1807d33e56d4ed9f11cb8b9f268896373ac89efc53825d1e4b97e6ff61dfa19463e80660bfa0a74da0e496ac072e6b8611312e9febca06b53ad0a59389f22efd704be80b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80d651383d73b736e843be7a54db613711ee9ee632c7af16ce2ad548ec004d22bb8062e7f9dbfb626f0f424f63b66346221223d8aaf87a6e126f49617c1d0d967d9e80bf27ea2cbcca83c65622797c4e923d04fad6e7bdd603cc19648f362c7071316780913f5f3a6fa129f8706219077741eda1cdd4b834c5c7e529270858a1324a4cf080879382ecb9b6b8a1d0b0822b9c817182231b2df2039811bb54f2e87d6641f756")); + proofs.add(RlpString.create(proof0.toArray())); + } + + public List getProof() { + return this.proofs; + } + + public byte[] getRoot() { + return Crypto.hash("blake2b-256", ((RlpString) proofs.get(4)).getBytes()); + } + + public byte[] getEventKey() { + return this.eventKey; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java new file mode 100644 index 00000000..8d5e0167 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java @@ -0,0 +1,37 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; +import java.util.List; + +import score.util.Crypto; +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class BlockVote { + private final byte[] signature; + private final byte[] validator; + + public BlockVote(byte[] voteMessage, byte[] publicKey, byte[] secretKey) { + this.validator = publicKey; + this.signature = Crypto.sign("ed25519", voteMessage, secretKey); + } + + public byte[] getSignature() { + return this.signature; + } + + public byte[] getValidator() { + return this.validator; + } + + public byte[] encode() { + List validatorSignature = new ArrayList(2); + validatorSignature.add(RlpString.create(this.signature)); + validatorSignature.add(RlpString.create(this.validator)); + return RlpEncoder.encode(new RlpList(validatorSignature)); + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java new file mode 100644 index 00000000..b1763adb --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java @@ -0,0 +1,79 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; + +import score.Context; + +public class ByteSliceOutput { + private byte[] arr; + private int size; + private int allocationSize; + + public ByteSliceOutput(int initialSize) { + this.arr = new byte[initialSize]; + this.allocationSize = initialSize; + this.size = 0; + } + + public int size() { + return this.size; + } + + public byte[] toArray() { + if (this.size < this.allocationSize) { + byte[] original = new byte[this.size]; + this.copy(this.arr, original, 0, size); + return original; + } else { + return this.arr; + } + } + + public void add(byte b) { + if (this.size == this.allocationSize) { + this.setSize(this.allocationSize*2); + } + + this.arr[size] = b; + this.size++; + } + + public void add(int b) { + this.add( (byte) b); + } + + public void set(int idx, byte b) { + if (0 <= idx && idx < this.size) { + this.arr[idx] = b; + } else { + throw new AssertionError("idx is out of range, use add instead"); + } + } + + public void addMany(byte[] bs) { + for(byte b : bs) + this.add(b); + } + + public byte get(int idx) { + if (0 <= idx && idx < this.size) { + return this.arr[idx]; + } else { + throw new AssertionError("root idx is out of range"); + } + } + + public void setSize(int size) { + int copySize = size > this.size ? this.size : size; + byte[] newArr = new byte[size]; + copy(this.arr, newArr, 0, copySize); + this.arr = newArr; + this.allocationSize = size; + } + + private void copy(byte[] src, byte[] dst, int from, int to) { + for(int i = from; i < to; i++) { + dst[i] = src[i]; + } + } +} diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/CandidateIncludedEvent.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/CandidateIncludedEvent.java new file mode 100644 index 00000000..6b69f0e3 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/CandidateIncludedEvent.java @@ -0,0 +1,74 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import scorex.util.ArrayList; + +import score.util.Crypto; +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class CandidateIncludedEvent { + private final byte[] eventKey = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + private final byte[] blockHash; + private final BigInteger paraChainId; + private final List proofs = new ArrayList(5); + + public CandidateIncludedEvent(byte[] blockHash, BigInteger paraChainId) { + this.blockHash = blockHash; + this.paraChainId = paraChainId; + ByteSliceOutput proof0 = new ByteSliceOutput(100); + ByteSliceOutput proof1 = new ByteSliceOutput(100); + ByteSliceOutput proof2 = new ByteSliceOutput(100); + ByteSliceOutput proof3 = new ByteSliceOutput(100); + ByteSliceOutput eventLeafNode = new ByteSliceOutput(100); + + eventLeafNode.addMany(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d7")); // node header + ScaleWriter.writeCompactUint(eventLeafNode, 37 + 3 + 521); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("0c00000000000000480e0d0b000000000200000001000000000080b2e60e00000000020000")); // other events + eventLeafNode.addMany(HexConverter.hexStringToByteArray("013501")); // candidate included event phase, index + ScaleWriter.writeU32(eventLeafNode, paraChainId.intValue()); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("c92a259d47bef999470af2a8a8e5f06632222121a30cbe7ec9ff6098084159339e1f7a1c522ab43821f2d09e1552bb0666422c4e3c7c70b71637b1fb0d226b5b33784de0b30bb3ce802927bc959739846bc57f38402523632bd1c7fa3c5af37f8a98944e2b042bf99a50afc1aefd206498eaddff73e8d620f1881f75672b52490cf76a450c7bd897dcbfc816cf8f765e2a2276040a58510c10e6d2b55ef1d218320447ab50d8d48b77ce75bab7e6961d6cec8eb0bf6b2683f94f455262cb5f51dd40c092b7ac9ac9b101c98e9491e0e4b91fc8decb838e409cb3ee50ee101b81")); // candidate receipt data, don't need to care about this + eventLeafNode.addMany(blockHash); // para chain block hash + eventLeafNode.addMany(HexConverter.hexStringToByteArray("20162c710c4c02ab787717f755ca3cf3b23d433a9104f711f91ca1960ec8d6bd715395bcfc1bc6b36914c29f1b730713073e39cac6e251d572cf97c37eed0ce1e902d29d646c73f07d1b0b06cb05f6ca219577178670af8f2c7bb67c29c9581f64bf52640900cd72ffccfc20c3ca3fed2303afc4e951a7271ab87b5234f69f45d469ddee250203ebb7f51842cf51fc731e8ec249d1c46736e30c69e11af191ad2174161d788808066175726120fb0611080000000005617572610101327375b17991ac0424a12a45a27d3227ef4fd2998f763255ac3d47a77467fb29161d1f7b9940082d783733b1b8029bb76791a1f0fda6e0cc3d41d15cb47686840000000025000000")); // candidate receipt data, don't need to care about this + eventLeafNode.addMany(HexConverter.hexStringToByteArray("00")); // empty topic + proofs.add(RlpString.create(eventLeafNode.toArray())); + + proof3.addMany(HexConverter.hexStringToByteArray("80810480")); + proof3.addMany(Crypto.hash("blake2b-256", eventLeafNode.toArray())); + proof3.addMany(HexConverter.hexStringToByteArray("545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9")); + proofs.add(RlpString.create(proof3.toArray())); + + proof2.addMany(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f8043580dd17bfb375845229c0dd74fc5f9be81d5f4cf569c3ee845e3acf5271556804afcd87f2329d61d655e551a438a2720a92951e383690f5a623d245cfb5ef4444c5f0684a022a34dd8bfa2baaf44f172b710040180")); + proof2.addMany(Crypto.hash("blake2b-256", proof3.toArray())); + proof2.addMany(HexConverter.hexStringToByteArray("80fbf36a2eb1b78c879a6ee9b08a04f9f341b284ca2bec0a74f4f39b1df1386ace80586079736657bdf54edfb3d4aab0c8221717c3e1e1ad5e6beeaa7c64dbbe515e80b5a6f3ec81bb61e573624e88e3d279154a7183905d05c656f8744d5788766f614c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287820706f6c6b61646f74")); + proofs.add(RlpString.create(proof2.toArray())); + + proof1.addMany(HexConverter.hexStringToByteArray("80499c804db64d4c6dd7655a3ac8bc482884de58452960029ea5b37c03ec11e678ec9a7c801ba7abc7e25fb80c99c4bff68d0688c9af3e1ff8cfb2bd5a1754ae1b86f94d1c80")); + proof1.addMany(Crypto.hash("blake2b-256", proof2.toArray())); + proof1.addMany(HexConverter.hexStringToByteArray("804a9786b7fa887adfd76874bc414a0f8854227ea92a4739e1bccefe699449460a808310dbebe8a5a91f783843a9916605b01caa58ea3ad8abce80684686d8605f96803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80")); + proofs.add(RlpString.create(proof1.toArray())); + + proof0.addMany(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961803440cfd059e558ed0fc83a7de22ffb14d56220e3ce446d897355c85a53401afe80")); + proof0.addMany(Crypto.hash("blake2b-256", proof1.toArray())); + proof0.addMany(HexConverter.hexStringToByteArray("80ede785a30002092980072d34de5a0cc038742d403a539e486808c64f1aeeac8c806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480c7b7787db289ac25daf82f320e421dc298a601a72cea3aa9784e5700251f169d800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de805839bffac46f817f1bebedade41179b15a7d92a7542b47cb484f1c866cde44f1807d33e56d4ed9f11cb8b9f268896373ac89efc53825d1e4b97e6ff61dfa19463e80660bfa0a74da0e496ac072e6b8611312e9febca06b53ad0a59389f22efd704be80b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80d651383d73b736e843be7a54db613711ee9ee632c7af16ce2ad548ec004d22bb8062e7f9dbfb626f0f424f63b66346221223d8aaf87a6e126f49617c1d0d967d9e80bf27ea2cbcca83c65622797c4e923d04fad6e7bdd603cc19648f362c7071316780913f5f3a6fa129f8706219077741eda1cdd4b834c5c7e529270858a1324a4cf080879382ecb9b6b8a1d0b0822b9c817182231b2df2039811bb54f2e87d6641f756")); + proofs.add(RlpString.create(proof0.toArray())); + } + + public List getProof() { + return this.proofs; + } + + public byte[] getRoot() { + return Crypto.hash("blake2b-256", ((RlpString) proofs.get(4)).getBytes()); + } + + public byte[] getEventKey() { + return this.eventKey; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java new file mode 100644 index 00000000..27b4c3c1 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java @@ -0,0 +1,19 @@ +package foundation.icon.test.cases; + +public class ErrorCode { + public static final int BMV_ERROR = 25; + public static final int INVALID_MPT = 26; + public static final int INVALID_VOTES = 27; + public static final int INVALID_SEQUENCE = 28; + public static final int INVALID_BLOCK_UPDATE = 29; + public static final int INVALID_BLOCK_PROOF = 30; + public static final int INVALID_BLOCK_WITNESS = 31; + public static final int INVALID_SEQUENCE_HIGHER = 32; + public static final int INVALID_BLOCK_UPDATE_HEIGHT_HIGHER = 33; + public static final int INVALID_BLOCK_UPDATE_HEIGHT_LOWER = 34; + public static final int INVALID_BLOCK_PROOF_HEIGHT_HIGHER = 35; + public static final int INVALID_BLOCK_WITNESS_OLD = 36; + public static final int DECODE_ERROR = 37; + public static final int NOT_ACCEPTED_FROM_NETWORK_ERROR=38; + public static final int NOT_ACCEPTED_BMC_ADDR_ERROR=39; +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java new file mode 100644 index 00000000..872187a0 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java @@ -0,0 +1,29 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; + +import score.Context; + +public class HexConverter { + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java new file mode 100644 index 00000000..3f5b21f1 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java @@ -0,0 +1,73 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import scorex.util.ArrayList; + +import score.util.Crypto; +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class NewAuthoritiesProof { + private final byte[] eventKey = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + private final List newValidators; + private final List proofs = new ArrayList(5); + + public NewAuthoritiesProof(List newValidators) { + this.newValidators = newValidators; + ByteSliceOutput proof0 = new ByteSliceOutput(100); + ByteSliceOutput proof1 = new ByteSliceOutput(100); + ByteSliceOutput proof2 = new ByteSliceOutput(100); + ByteSliceOutput proof3 = new ByteSliceOutput(100); + ByteSliceOutput eventLeafNode = new ByteSliceOutput(100); + + eventLeafNode.addMany(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d7")); // node header + ScaleWriter.writeCompactUint(eventLeafNode, 37 + 3 + newValidators.size()*(32 + 8) + 1 + (newValidators.size() > 63 ? 2: 1)); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("0c00000000000000480e0d0b000000000200000001000000000080b2e60e00000000020000")); // other events + eventLeafNode.addMany(HexConverter.hexStringToByteArray("010a00")); // new authorities event phase, index + ScaleWriter.writeCompactUint(eventLeafNode, newValidators.size()); + for (byte[] validator: newValidators ) { + eventLeafNode.addMany(validator); + ScaleWriter.writeU64(eventLeafNode, BigInteger.valueOf(1)); + } + eventLeafNode.addMany(HexConverter.hexStringToByteArray("00")); // empty topic + proofs.add(RlpString.create(eventLeafNode.toArray())); + + proof3.addMany(HexConverter.hexStringToByteArray("80810480")); + proof3.addMany(Crypto.hash("blake2b-256", eventLeafNode.toArray())); + proof3.addMany(HexConverter.hexStringToByteArray("545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9")); + proofs.add(RlpString.create(proof3.toArray())); + + proof2.addMany(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f8043580dd17bfb375845229c0dd74fc5f9be81d5f4cf569c3ee845e3acf5271556804afcd87f2329d61d655e551a438a2720a92951e383690f5a623d245cfb5ef4444c5f0684a022a34dd8bfa2baaf44f172b710040180")); + proof2.addMany(Crypto.hash("blake2b-256", proof3.toArray())); + proof2.addMany(HexConverter.hexStringToByteArray("80fbf36a2eb1b78c879a6ee9b08a04f9f341b284ca2bec0a74f4f39b1df1386ace80586079736657bdf54edfb3d4aab0c8221717c3e1e1ad5e6beeaa7c64dbbe515e80b5a6f3ec81bb61e573624e88e3d279154a7183905d05c656f8744d5788766f614c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287820706f6c6b61646f74")); + proofs.add(RlpString.create(proof2.toArray())); + + proof1.addMany(HexConverter.hexStringToByteArray("80499c804db64d4c6dd7655a3ac8bc482884de58452960029ea5b37c03ec11e678ec9a7c801ba7abc7e25fb80c99c4bff68d0688c9af3e1ff8cfb2bd5a1754ae1b86f94d1c80")); + proof1.addMany(Crypto.hash("blake2b-256", proof2.toArray())); + proof1.addMany(HexConverter.hexStringToByteArray("804a9786b7fa887adfd76874bc414a0f8854227ea92a4739e1bccefe699449460a808310dbebe8a5a91f783843a9916605b01caa58ea3ad8abce80684686d8605f96803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80")); + proofs.add(RlpString.create(proof1.toArray())); + + proof0.addMany(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961803440cfd059e558ed0fc83a7de22ffb14d56220e3ce446d897355c85a53401afe80")); + proof0.addMany(Crypto.hash("blake2b-256", proof1.toArray())); + proof0.addMany(HexConverter.hexStringToByteArray("80ede785a30002092980072d34de5a0cc038742d403a539e486808c64f1aeeac8c806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480c7b7787db289ac25daf82f320e421dc298a601a72cea3aa9784e5700251f169d800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de805839bffac46f817f1bebedade41179b15a7d92a7542b47cb484f1c866cde44f1807d33e56d4ed9f11cb8b9f268896373ac89efc53825d1e4b97e6ff61dfa19463e80660bfa0a74da0e496ac072e6b8611312e9febca06b53ad0a59389f22efd704be80b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80d651383d73b736e843be7a54db613711ee9ee632c7af16ce2ad548ec004d22bb8062e7f9dbfb626f0f424f63b66346221223d8aaf87a6e126f49617c1d0d967d9e80bf27ea2cbcca83c65622797c4e923d04fad6e7bdd603cc19648f362c7071316780913f5f3a6fa129f8706219077741eda1cdd4b834c5c7e529270858a1324a4cf080879382ecb9b6b8a1d0b0822b9c817182231b2df2039811bb54f2e87d6641f756")); + proofs.add(RlpString.create(proof0.toArray())); + } + + public List getProof() { + return this.proofs; + } + + public byte[] getRoot() { + return Crypto.hash("blake2b-256", ((RlpString) proofs.get(4)).getBytes()); + } + + public byte[] getEventKey() { + return this.eventKey; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ParaBlockUpdate.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ParaBlockUpdate.java new file mode 100644 index 00000000..5633280d --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ParaBlockUpdate.java @@ -0,0 +1,102 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +import score.ObjectReader; +import score.util.Crypto; + +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class ParaBlockUpdate { + private final byte[] hash; + private final byte[] parentHash; + private final long number; + private final byte[] stateRoot; + private final byte[] extrinsicsRoot; + private final byte[] digest; + private final byte[] serializedHeader; + private byte[] relayChainData; + + public ParaBlockUpdate(byte[] parentHash, long number, byte[] stateRoot, byte[] relayChainData) { + ByteSliceOutput serializedHeaderOutput = new ByteSliceOutput(32 + 4 + 32 + 32 + 1); + this.parentHash = parentHash; + serializedHeaderOutput.addMany(parentHash); + this.number = number; + ScaleWriter.writeCompactUint(serializedHeaderOutput, (int) number); + this.stateRoot = stateRoot; + serializedHeaderOutput.addMany(stateRoot); + + this.extrinsicsRoot = Crypto.hash("blake2b-256", "abc".getBytes()); + serializedHeaderOutput.addMany(extrinsicsRoot); + this.digest = HexConverter.hexStringToByteArray("080642414245b50103010000009e08cd0f000000006290af7c4a31713b4b36280943a39d8179d36e765c0614176c7c6560e515466197827f75f8c6a0c1c655dde4074710f976c86c2f07766e57063afc07efaaef01aedae58676a262cc4f7264836a17ce8f336ee49865530afbdc560d1d131c050905424142450101fcbc268c71e173ccf1cf8b87b17bcb56914eadb31b71bdd70232ca4bea023f3e501dff7a902c6485b24f7426ad941f7cc086690817d5de0183688923104d1b8d"); + serializedHeaderOutput.addMany(this.digest); + this.hash = Crypto.hash("blake2b-256", serializedHeaderOutput.toArray()); + this.serializedHeader = serializedHeaderOutput.toArray(); + + this.relayChainData = relayChainData; + } + + public ParaBlockUpdate(byte[] parentHash, long number, byte[] stateRoot) { + this(parentHash, number, stateRoot, null); + } + + public ParaBlockUpdate(byte[] relayChainData) { + this.parentHash = null; + this.number = 0; + this.stateRoot = null; + + this.extrinsicsRoot = null; + this.digest = null; + this.hash = null; + this.serializedHeader = null; + + this.relayChainData = relayChainData; + } + + public byte[] encode() { + List blockUpdate = new ArrayList(2); + if (this.serializedHeader != null) { + blockUpdate.add(RlpString.create(this.serializedHeader)); + } else { + blockUpdate.add(RlpString.create("")); + } + + if (this.relayChainData != null) { + blockUpdate.add(RlpString.create(this.relayChainData)); + } else { + blockUpdate.add(RlpString.create("")); + } + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] getEncodedHeader() { + return this.serializedHeader; + } + + public byte[] getHash() { + return this.hash; + } + + public byte[] setRelayChainData(byte[] relayChainData) { + return this.relayChainData = relayChainData; + } + + public byte[] getParentHash() { + return this.parentHash; + } + + public long getNumber() { + return this.number; + } + + public byte[] getStateRoot() { + return this.stateRoot; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/RelayBlockUpdate.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/RelayBlockUpdate.java new file mode 100644 index 00000000..c51795d1 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/RelayBlockUpdate.java @@ -0,0 +1,122 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +import score.ObjectReader; +import score.util.Crypto; + +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class RelayBlockUpdate { + private final byte[] hash; + private final byte[] parentHash; + private final long number; + private final byte[] stateRoot; + private final byte[] extrinsicsRoot; + private final byte[] digest; + private final byte[] serializedHeader; + private final List vote = new ArrayList(5); + private final BigInteger round; + private final BigInteger setId; + + public RelayBlockUpdate(byte[] parentHash, long number, byte[] stateRoot, BigInteger round, BigInteger setId) { + ByteSliceOutput serializedHeaderOutput = new ByteSliceOutput(32 + 4 + 32 + 32 + 1); + this.parentHash = parentHash; + serializedHeaderOutput.addMany(parentHash); + this.number = number; + ScaleWriter.writeCompactUint(serializedHeaderOutput, (int) number); + this.stateRoot = stateRoot; + serializedHeaderOutput.addMany(stateRoot); + + this.extrinsicsRoot = Crypto.hash("blake2b-256", "abc".getBytes()); + serializedHeaderOutput.addMany(extrinsicsRoot); + this.digest = HexConverter.hexStringToByteArray("080642414245b50103010000009e08cd0f000000006290af7c4a31713b4b36280943a39d8179d36e765c0614176c7c6560e515466197827f75f8c6a0c1c655dde4074710f976c86c2f07766e57063afc07efaaef01aedae58676a262cc4f7264836a17ce8f336ee49865530afbdc560d1d131c050905424142450101fcbc268c71e173ccf1cf8b87b17bcb56914eadb31b71bdd70232ca4bea023f3e501dff7a902c6485b24f7426ad941f7cc086690817d5de0183688923104d1b8d"); + serializedHeaderOutput.addMany(this.digest); + this.hash = Crypto.hash("blake2b-256", serializedHeaderOutput.toArray()); + this.serializedHeader = serializedHeaderOutput.toArray(); + + this.round = round; + this.setId = setId; + } + + public static byte[] voteMessage(byte[] targetHash, long targetNumber, BigInteger round, BigInteger setId) { + ByteSliceOutput serializedSignMessageOutput = new ByteSliceOutput(53); + serializedSignMessageOutput.add((byte) 0x01); + serializedSignMessageOutput.addMany(targetHash); + ScaleWriter.writeU32(serializedSignMessageOutput, (int) targetNumber); + ScaleWriter.writeU64(serializedSignMessageOutput, round); + ScaleWriter.writeU64(serializedSignMessageOutput, setId); + return serializedSignMessageOutput.toArray(); + } + + public void vote(byte[] publicKey, byte[] secretKey, BigInteger round, BigInteger setId) { + this.vote.add(new BlockVote(voteMessage(this.hash, this.number, round, setId), publicKey, secretKey)); + } + + public byte[] encode() { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + List votes = new ArrayList(2); + votes.add(RlpString.create(voteMessage(this.hash, this.number, this.round, this.setId))); + List validatorSignatures = new ArrayList(2); + for (BlockVote signature: this.vote) { + validatorSignatures.add(RlpString.create(signature.encode())); + } + votes.add(new RlpList(validatorSignatures)); + + blockUpdate.add(RlpString.create(RlpEncoder.encode(new RlpList(votes)))); + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] encodeWithoutVote() { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + blockUpdate.add(RlpString.create("")); // empty votes + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] encodeWithVoteMessage(byte[] voteMessage) { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + List votes = new ArrayList(2); + votes.add(RlpString.create(voteMessage)); + List validatorSignatures = new ArrayList(2); + for (BlockVote signature: this.vote) { + validatorSignatures.add(RlpString.create(signature.encode())); + } + votes.add(new RlpList(validatorSignatures)); + + blockUpdate.add(RlpString.create(RlpEncoder.encode(new RlpList(votes)))); + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] getEncodedHeader() { + return this.serializedHeader; + } + + public byte[] getHash() { + return this.hash; + } + + public byte[] getParentHash() { + return this.parentHash; + } + + public long getNumber() { + return this.number; + } + + public byte[] getStateRoot() { + return this.stateRoot; + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java new file mode 100644 index 00000000..0a44ef10 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java @@ -0,0 +1,58 @@ +package foundation.icon.test.cases; + +import score.Context; + +import java.math.BigInteger; +import java.util.Arrays; + +public class ScaleWriter { + public static void writeCompactUint(ByteSliceOutput output, int compactNumber) { + int compactSize = ScaleWriter.compactUintSize(compactNumber); + int compact = (compactNumber << 2) + compactSize - 1; + while (compactSize > 0) { + byte b = (byte) (compact & 0xff); + output.add(b); + compact >>= 8; + compactSize--; + } + } + + public static int compactUintSize(int number) { + if (number < 0) { + throw new IllegalArgumentException("Negative numbers are not supported"); + } + if (number <= 0x3f) { + return 1; + } else if (number <= 0x3fff) { + return 2; + } else if (number <= 0x3fffffff) { + return 3; + } else { + return 4; + } + } + + public static void writeU32(ByteSliceOutput output, int value) { + if (value < 0) { + throw new IllegalArgumentException("Negative values are not supported: " + value); + } + output.add( (byte) (value & 0xff)); + output.add( (byte) ((value >> 8) & 0xff)); + output.add( (byte) ((value >> 16) & 0xff)); + output.add( (byte) ((value >> 24) & 0xff)); + } + + public static void writeU64(ByteSliceOutput output, BigInteger value) { + if (value.compareTo(BigInteger.ZERO) < 0) { + throw new IllegalArgumentException("Negative values are not supported: " + value); + } + output.add(value.and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(8).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(16).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(24).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(32).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(40).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(48).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(56).and(BigInteger.valueOf(255)).intValue()); + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/BMVScore.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/BMVScore.java new file mode 100644 index 00000000..fabaf14f --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/BMVScore.java @@ -0,0 +1,217 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.cases.HexConverter; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionFailureException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; + +import static foundation.icon.test.Env.LOG; + +public class BMVScore extends Score { + public BMVScore(Score other) { + super(other); + } + + public static BMVScore mustDeploy( + TransactionHandler txHandler, + Wallet owner, + String bmc, + String net, + String encodedValidators, + long relayMtaOffset, + long paraMtaOffset, + int mtaRootSize, + int mtaCacheSize, + boolean mtaIsAllowNewerWitness, + byte[] relayLastBlockHash, + byte[] paraLastBlockHash, + Address relayEventDecoderAddress, + Address paraEventDecoderAddress, + BigInteger currentSetId, + BigInteger paraChainId + ) throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "BMV"); + RpcObject params = new RpcObject.Builder() + .put("bmc", new RpcValue(bmc)) + .put("net", new RpcValue(net)) + .put("encodedValidators", new RpcValue(encodedValidators)) + .put("relayMtaOffset", new RpcValue(Long.toString(relayMtaOffset))) + .put("paraMtaOffset", new RpcValue(Long.toString(paraMtaOffset))) + .put("mtaRootSize", new RpcValue(Integer.toString(mtaRootSize))) + .put("mtaCacheSize", new RpcValue(Integer.toString(mtaCacheSize))) + .put("mtaIsAllowNewerWitness", new RpcValue(mtaIsAllowNewerWitness)) + .put("relayLastBlockHash", new RpcValue(HexConverter.bytesToHex(relayLastBlockHash))) + .put("paraLastBlockHash", new RpcValue(HexConverter.bytesToHex(paraLastBlockHash))) + .put("relayEventDecoderAddress", new RpcValue(relayEventDecoderAddress)) + .put("paraEventDecoderAddress", new RpcValue(paraEventDecoderAddress)) + .put("relayCurrentSetId", new RpcValue(currentSetId.toString())) + .put("paraChainId", new RpcValue(paraChainId.toString())) + .put("evmEventIndex", new RpcValue("3300")) + .put("newAuthoritiesEventIndex", new RpcValue("0a00")) + .put("candidateIncludedEventIndex", new RpcValue("3501")) + .build(); + Score score = txHandler.deploy(owner, getFilePath("parachain"), params); + LOG.info("bmv score address = " + score.getAddress()); + LOG.infoExiting(); + return new BMVScore(score); + } + + public String paraMta() throws IOException { + return call("paraMta", null).asString(); + } + + public String relayMta() throws IOException { + return call("relayMta", null).asString(); + } + + public BigInteger paraMtaHeight() throws IOException { + return call("paraMtaHeight", null).asInteger(); + } + + public BigInteger relayMtaHeight() throws IOException { + return call("relayMtaHeight", null).asInteger(); + } + + public List paraMtaRoot() throws IOException { + List listRpcItem = call("paraMtaRoot", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public List relayMtaRoot() throws IOException { + List listRpcItem = call("relayMtaRoot", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public byte[] relayMtaLastBlockHash() throws IOException { + return call("relayMtaLastBlockHash", null).asByteArray(); + } + + public byte[] paraMtaLastBlockHash() throws IOException { + return call("paraMtaLastBlockHash", null).asByteArray(); + } + + public BigInteger paraMtaOffset() throws IOException { + return call("paraMtaOffset", null).asInteger(); + } + + public BigInteger relayMtaOffset() throws IOException { + return call("relayMtaOffset", null).asInteger(); + } + + public List paraMtaCaches() throws IOException { + List listRpcItem = call("paraMtaCaches", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public List relayMtaCaches() throws IOException { + List listRpcItem = call("relayMtaCaches", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public Address bmc() throws IOException { + return call("bmc", null).asAddress(); + } + + public Address paraEventDecoder() throws IOException { + return call("paraEventDecoder", null).asAddress(); + } + + public Address relayEventDecoder() throws IOException { + return call("relayEventDecoder", null).asAddress(); + } + + public String netAddress() throws IOException { + return call("netAddress", null).asString(); + } + + public BigInteger lastHeight() throws IOException { + return call("lastHeight", null).asInteger(); + } + + public BigInteger setId() throws IOException { + return call("setId", null).asInteger(); + } + + public List validators() throws IOException { + List listRpcItem = call("validators", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public Bytes handleRelayMessage(Wallet wallet, String bmc, String prev, BigInteger seq, String msg) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("bmc", new RpcValue(bmc)) + .put("prev", new RpcValue(prev)) + .put("seq", new RpcValue(seq)) + .put("msg", new RpcValue(msg)) + .build(); + return invoke(wallet, "handleRelayMessage", params); + } +} diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/KusamaEventDecoderScore.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/KusamaEventDecoderScore.java new file mode 100644 index 00000000..e32316bb --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/KusamaEventDecoderScore.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.cases.HexConverter; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionFailureException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; + +import static foundation.icon.test.Env.LOG; + +public class KusamaEventDecoderScore extends Score { + public KusamaEventDecoderScore(Score other) { + super(other); + } + + public static KusamaEventDecoderScore mustDeploy( + TransactionHandler txHandler, + Wallet owner + ) throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "KusamaEventDecoder"); + Score score = txHandler.deploy(owner, getFilePath("KusamaEventDecoder"), null); + LOG.info("KusamaEventDecoder score address = " + score.getAddress()); + LOG.infoExiting(); + return new KusamaEventDecoderScore(score); + } +} diff --git a/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/MoonriverEventDecoderScore.java b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/MoonriverEventDecoderScore.java new file mode 100644 index 00000000..20b0a6e4 --- /dev/null +++ b/javascore/bmv/parachain/src/intTest/java/foundation/icon/btp/score/MoonriverEventDecoderScore.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.cases.HexConverter; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionFailureException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; + +import static foundation.icon.test.Env.LOG; + +public class MoonriverEventDecoderScore extends Score { + public MoonriverEventDecoderScore(Score other) { + super(other); + } + + public static MoonriverEventDecoderScore mustDeploy( + TransactionHandler txHandler, + Wallet owner + ) throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "MoonriverEventDecoder"); + Score score = txHandler.deploy(owner, getFilePath("MoonriverEventDecoder"), null); + LOG.info("MoonriverEventDecoder score address = " + score.getAddress()); + LOG.infoExiting(); + return new MoonriverEventDecoderScore(score); + } +} diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java new file mode 100644 index 00000000..a6e7e36d --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/BMV.java @@ -0,0 +1,491 @@ +package foundation.icon.btp.bmv.parachain; + +import score.Address; +import score.Context; +import score.VarDB; +import score.ObjectReader; +import score.ByteArrayObjectWriter; + +import scorex.util.Base64; +import scorex.util.ArrayList; + +import score.annotation.External; +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.blockProof.BlockProof; +import foundation.icon.btp.lib.blockHeader.BlockHeader; +import foundation.icon.btp.lib.btpAddress.BTPAddress; +import foundation.icon.btp.lib.event.evmEvent.BTPMessageEvmEvent; +import foundation.icon.btp.bmv.parachain.lib.Constant; +import foundation.icon.btp.bmv.parachain.lib.blockUpdate.*; +import foundation.icon.btp.bmv.parachain.lib.relayChainData.RelayChainData; +import foundation.icon.btp.bmv.parachain.lib.relayMessage.RelayMessage; +import foundation.icon.btp.lib.stateProof.StateProof; +import foundation.icon.btp.lib.BMVStatus; +import foundation.icon.btp.lib.BlockVerifyResult; +import foundation.icon.btp.lib.event.EVMLogEvent; +import foundation.icon.btp.lib.event.CandidateIncludedEvent; +import foundation.icon.btp.lib.event.EventRecord; +import foundation.icon.btp.lib.event.NewAuthoritiesEvent; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import foundation.icon.btp.lib.mta.MTAStatus; +import foundation.icon.btp.lib.mta.SerializableMTA; + +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.validators.Validators; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class BMV implements IBMV { + private final VarDB
bmcDb = Context.newVarDB("bmc", Address.class); + private final VarDB netAddressDb = Context.newVarDB("netAddr", String.class); + private final VarDB lastHeightDb = Context.newVarDB("lastHeight", Long.class); + private final VarDB lastUpdateValidatorSetDb = Context.newVarDB("lastUpdateValidatorSet", Long.class); + private final VarDB paraChainIdDb = Context.newVarDB("paraId", Long.class); + private final VarDB paraMtaDb = Context.newVarDB("paraMta", SerializableMTA.class); + private final VarDB relayMtaDb = Context.newVarDB("relayMta", SerializableMTA.class); + private final VarDB validatorsDb = Context.newVarDB("validators", Validators.class); + private final VarDB
paraEventDecoderDb = Context.newVarDB("paraEventDecoder", Address.class); + private final VarDB
relayEventDecoderDb = Context.newVarDB("relayEventDecoder", Address.class); + private final VarDB relaySetIdDb = Context.newVarDB("relaySetId", BigInteger.class); + private final VarDB evmEventIndexDb = Context.newVarDB("evmEventIndex", byte[].class); + private final VarDB newAuthoritiesEventIndexDb = Context.newVarDB("newAuthoritiesEventIndex", byte[].class); + private final VarDB candidateIncludedEventIndexDb = Context.newVarDB("candidateIncludedEventIndex", byte[].class); + + public BMV( + Address bmc, + String net, + String encodedValidators, + long relayMtaOffset, + long paraMtaOffset, + int mtaRootSize, + int mtaCacheSize, + boolean mtaIsAllowNewerWitness, + byte[] relayLastBlockHash, + byte[] paraLastBlockHash, + Address relayEventDecoderAddress, + Address paraEventDecoderAddress, + long relayCurrentSetId, + long paraChainId, + byte[] evmEventIndex, + byte[] newAuthoritiesEventIndex, + byte[] candidateIncludedEventIndex + ) { + this.bmcDb.set(bmc); + this.netAddressDb.set(net); + + byte[] serializedValidator = Base64.getUrlDecoder().decode(encodedValidators.getBytes()); + + ObjectReader r = Context.newByteArrayObjectReader("RLPn", serializedValidator); + r.beginList(); + List validators = new ArrayList(150); + while (r.hasNext()) { + byte[] v = r.readByteArray(); + validators.add(v); + } + r.end(); + + Validators validatorsObject = new Validators(validators); + this.validatorsDb.set(validatorsObject); + this.relaySetIdDb.set(BigInteger.valueOf(relayCurrentSetId)); + this.lastUpdateValidatorSetDb.set(relayMtaOffset); + + SerializableMTA paraMta = new SerializableMTA(0, paraMtaOffset, mtaRootSize, mtaCacheSize, mtaIsAllowNewerWitness, paraLastBlockHash, null, null); + SerializableMTA relayMta = new SerializableMTA(0, relayMtaOffset, mtaRootSize, mtaCacheSize, mtaIsAllowNewerWitness, relayLastBlockHash, null, null); + this.lastHeightDb.set(paraMtaOffset); + this.paraMtaDb.set(paraMta); + this.relayMtaDb.set(relayMta); + + this.paraEventDecoderDb.set(paraEventDecoderAddress); + this.relayEventDecoderDb.set(relayEventDecoderAddress); + + this.paraChainIdDb.set(paraChainId); + + this.evmEventIndexDb.set(evmEventIndex); + this.newAuthoritiesEventIndexDb.set(newAuthoritiesEventIndex); + this.candidateIncludedEventIndexDb.set(candidateIncludedEventIndex); + } + + /** + * get encode of merkle tree accumulator + */ + @External(readonly=true) + public String paraMta() { + ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLPn"); + SerializableMTA.writeObject(w, this.paraMtaDb.get()); + return new String(Base64.getUrlEncoder().encode(w.toByteArray())); + } + + @External(readonly=true) + public String relayMta() { + ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLPn"); + SerializableMTA.writeObject(w, this.relayMtaDb.get()); + return new String(Base64.getUrlEncoder().encode(w.toByteArray())); + } + + /** + * get current mta height + */ + @External(readonly=true) + public long paraMtaHeight() { + return this.paraMtaDb.get().height(); + } + + @External(readonly=true) + public long relayMtaHeight() { + return this.relayMtaDb.get().height(); + } + + /** + * get list of MTA root + */ + @External(readonly=true) + public List paraMtaRoot() { + return this.paraMtaDb.get().getRootList(); + } + + @External(readonly=true) + public List relayMtaRoot() { + return this.relayMtaDb.get().getRootList(); + } + + /** + * get last block hash + */ + @External(readonly=true) + public byte[] paraMtaLastBlockHash() { + return this.paraMtaDb.get().lastBlockHash(); + } + + @External(readonly=true) + public byte[] relayMtaLastBlockHash() { + return this.relayMtaDb.get().lastBlockHash(); + } + + /** + * get current mta offset + */ + @External(readonly=true) + public long paraMtaOffset() { + return this.paraMtaDb.get().offset(); + } + + @External(readonly=true) + public long relayMtaOffset() { + return this.relayMtaDb.get().offset(); + } + + @External(readonly=true) + public List paraMtaCaches() { + return this.paraMtaDb.get().getCacheList(); + } + + @External(readonly=true) + public List relayMtaCaches() { + return this.relayMtaDb.get().getCacheList(); + } + + /** + * handle verify message from BMC and return list of event message + */ + @External + public List handleRelayMessage(String bmc, String prev, BigInteger seq, String msg) { + BTPAddress currentAddress = BTPAddress.fromString(bmc); + BTPAddress prevAddress = BTPAddress.fromString(prev); + this.checkAccessible(currentAddress, prevAddress); + + byte[] serializedMsg; + try { + serializedMsg = Base64.getUrlDecoder().decode(msg.getBytes()); + } catch (Exception e) { + Context.revert(ErrorCode.DECODE_ERROR, "decode base64 msg error: " + e.toString()); + serializedMsg = null; + } + + RelayMessage relayMessage; + try { + relayMessage = RelayMessage.fromBytes(serializedMsg); + } catch (RelayMessageRLPException e) { + Context.revert(ErrorCode.DECODE_ERROR, "RelayMessage RLP decode error: " + e.getScope() + " " + e.getOriginalError()); + relayMessage = null; + } + + if (relayMessage.getBlockUpdate().size() == 0 && relayMessage.getBlockProof() == null) { + Context.revert(ErrorCode.BMV_ERROR, "invalid RelayMessage not exists BlockUpdate and BlockProof"); + } + + BlockVerifyResult paraBlockVerifyResult = this.verifyParaChainBlock(relayMessage); + + BigInteger nextSeq = seq.add(BigInteger.valueOf(1)); + + // list of bytes message return to bmc + List bmcMsgs = new ArrayList(5); + if (paraBlockVerifyResult != null) { + byte[] evmEventIndex = this.evmEventIndexDb.get(); + for (StateProof stateProof : relayMessage.getStateProof()) { + byte[] encodedStorage = stateProof.prove(paraBlockVerifyResult.stateRoot); + if (Arrays.equals(stateProof.getKey(), Constant.EventStorageKey)) { + List> eventRecords = (List>) Context.call(this.paraEventDecoderDb.get(), "decodeEvent", encodedStorage); + for (Map rawEventRecord: eventRecords) { + EventRecord eventRecord = new EventRecord(rawEventRecord); + + // filter evm log of BMC + if (eventRecord.getEventIndex()[0] == evmEventIndex[0] && eventRecord.getEventIndex()[1] == evmEventIndex[1]) { + EVMLogEvent evmLogEvent = new EVMLogEvent(eventRecord.getEventData()); + if (evmLogEvent.getEvmTopics().size() == 0 || !Arrays.equals(evmLogEvent.getEvmTopics().get(0), Constant.MessageEventTopic)) { + continue; + } + + if (!Arrays.equals(evmLogEvent.getAddress(), HexConverter.hexStringToByteArray(prevAddress.getAddress()))) { + continue; + } + + BTPMessageEvmEvent btpMessageEvmEvent = new BTPMessageEvmEvent(evmLogEvent.getEvmEventData()); + + if (btpMessageEvmEvent.getNextBmc().equals(bmc)) { + int seqCompareRes = btpMessageEvmEvent.getSeq().compareTo(nextSeq); + if (seqCompareRes > 0) { + Context.revert(ErrorCode.INVALID_SEQUENCE_HIGHER, "invalid sequence: " + btpMessageEvmEvent.getSeq() + "; expected: " + nextSeq); + } else if (seqCompareRes < 0) { + Context.revert(ErrorCode.INVALID_SEQUENCE, "invalid sequence: " + btpMessageEvmEvent.getSeq() + "; expected: " + nextSeq); + } + bmcMsgs.add(btpMessageEvmEvent.getMsg()); + } + nextSeq = nextSeq.add(BigInteger.valueOf(1)); + } + } + } + } + } + + if (bmcMsgs.size() > 0) { + this.lastHeightDb.set(paraBlockVerifyResult.lastHeight); + } + + return bmcMsgs; + } + + /** + * get address of bmc + */ + @External(readonly=true) + public Address bmc() { + return this.bmcDb.get(); + } + + /** + * get address of event decoder + */ + @External(readonly=true) + public Address paraEventDecoder() { + return this.paraEventDecoderDb.get(); + } + + @External(readonly=true) + public Address relayEventDecoder() { + return this.relayEventDecoderDb.get(); + } + + /** + * net address that bmv verify + */ + @External(readonly=true) + public String netAddress() { + return this.netAddressDb.get(); + } + + /** + * last height + */ + @External(readonly=true) + public long lastHeight() { + return this.lastHeightDb.get(); + } + + /** + * list public keys of validators + */ + @External(readonly=true) + public List validators() { + Validators v = this.validatorsDb.get(); + return v.get(); + } + + /** + * current set id + */ + @External(readonly=true) + public BigInteger setId() { + return this.relaySetIdDb.get(); + } + + /** + * get status of BMV + */ + @External(readonly=true) + public BMVStatus getStatus() { + SerializableMTA mta = this.paraMtaDb.get(); + MTAStatus mtaStatus = mta.getStatus(); + long lastHeight = this.lastHeightDb.get(); + return new BMVStatus(mtaStatus.height, mtaStatus.offset, lastHeight); + } + + private void checkAccessible(BTPAddress currentAddress, BTPAddress prevAddress) { + if (!this.netAddressDb.get().equals(prevAddress.getNet())) { + Context.revert(ErrorCode.NOT_ACCEPTED_FROM_NETWORK_ERROR, "not acceptable from"); + } + if (!Context.getCaller().equals(this.bmcDb.get())) { + Context.revert(ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR, "not acceptable bmc"); + } + if (!Address.fromString(currentAddress.getAddress()).equals(this.bmcDb.get())) { // actualy don't need to check it + Context.revert(ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR, "not acceptable bmc"); + } + } + + private BlockVerifyResult verifyRelayChainBlock(RelayChainData relayChainData) { + byte[] stateRoot = null; + long lastHeight = 0; + SerializableMTA currentMTA = this.relayMtaDb.get(); + Validators currenValidators = this.validatorsDb.get(); + + List blockUpdates = relayChainData.getBlockUpdate(); + for(int i = 0; i < blockUpdates.size(); i++) { + long nextHeight = currentMTA.height() + 1; + BlockHeader currentBlockHeader = blockUpdates.get(i).getBlockHeader(); + if (nextHeight == currentBlockHeader.getNumber()) { + if (!Arrays.equals(currentBlockHeader.getParentHash(), currentMTA.lastBlockHash())) { + Context.revert(ErrorCode.BMV_ERROR, "parent relay block hash does not match, parent: " + HexConverter.bytesToHex(currentBlockHeader.getParentHash()) + " current: " + HexConverter.bytesToHex(currentMTA.lastBlockHash())); + } + + if (i == blockUpdates.size() - 1) { // only verify signatures of last updating block + blockUpdates.get(i).verify(currenValidators.get(), this.relaySetIdDb.get()); + } + + currentMTA.add(currentBlockHeader.getHash()); + lastHeight = nextHeight; + stateRoot = currentBlockHeader.getStateRoot(); + } else if (nextHeight < currentBlockHeader.getNumber()) { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER, "invalid relay blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } else { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER, "invalid relay blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } + } + + BlockProof blockProof = relayChainData.getBlockProof(); + if (blockProof != null) { + blockProof.verify(currentMTA); + lastHeight = blockProof.getBlockHeader().getNumber(); + stateRoot = blockProof.getBlockHeader().getStateRoot(); + } + + this.relayMtaDb.set(currentMTA); + + return new BlockVerifyResult(stateRoot, lastHeight); + } + + private BlockVerifyResult verifyParaChainBlock(RelayMessage relayMessage) { + byte[] stateRoot = null; + long lastHeight = 0; + SerializableMTA currentMTA = this.paraMtaDb.get(); + + List blockUpdates = relayMessage.getBlockUpdate(); + if (blockUpdates.size() == 1 && blockUpdates.get(0).getBlockHeader() == null) { + RelayChainData relayChainData = blockUpdates.get(0).getRelayChainData(); + if (relayChainData == null) { + Context.revert(ErrorCode.BMV_ERROR, "Missing relay chain data"); + } + BlockVerifyResult relayBlockVerifyResult = this.verifyRelayChainBlock(relayChainData); + this.verifyRelayChainState(relayChainData.getStateProof(), relayBlockVerifyResult); + } else { + for(int i = 0; i < blockUpdates.size(); i++) { + long nextHeight = currentMTA.height() + 1; + BlockHeader currentBlockHeader = blockUpdates.get(i).getBlockHeader(); + if (nextHeight == currentBlockHeader.getNumber()) { + if (!Arrays.equals(currentBlockHeader.getParentHash(), currentMTA.lastBlockHash())) { + Context.revert(ErrorCode.BMV_ERROR, "parent para block hash does not match, parent: " + HexConverter.bytesToHex(currentBlockHeader.getParentHash()) + " current: " + HexConverter.bytesToHex(currentMTA.lastBlockHash())); + } + + if (i == blockUpdates.size() - 1) { // last blockUpdate of parachain must has the same hash with ParaHead in CandidateIncludedEvent data + RelayChainData relayChainData = blockUpdates.get(i).getRelayChainData(); + if (relayChainData == null) { + Context.revert(ErrorCode.BMV_ERROR, "Missing relay chain data"); + } + BlockVerifyResult relayBlockVerifyResult = this.verifyRelayChainBlock(relayChainData); + byte[] lastIncludedParaChainBlockHash = this.verifyRelayChainState(relayChainData.getStateProof(), relayBlockVerifyResult); + if (lastIncludedParaChainBlockHash == null) { + Context.revert(ErrorCode.BMV_ERROR, "can not find parachain data in relay chain data"); + } + if (!Arrays.equals(blockUpdates.get(i).getBlockHeader().getHash(), lastIncludedParaChainBlockHash)) { + Context.revert(ErrorCode.BMV_ERROR, "block hash does not match with relay chain, para block hash: " + HexConverter.bytesToHex(blockUpdates.get(i).getBlockHeader().getHash()) + " relay inclusion: " + HexConverter.bytesToHex(lastIncludedParaChainBlockHash)); + } + } + + currentMTA.add(currentBlockHeader.getHash()); + lastHeight = nextHeight; + stateRoot = currentBlockHeader.getStateRoot(); + } else if (nextHeight < currentBlockHeader.getNumber()) { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER, "invalid para blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } else { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER, "invalid para blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } + } + } + + BlockProof blockProof = relayMessage.getBlockProof(); + if (blockProof != null) { + blockProof.verify(currentMTA); + lastHeight = blockProof.getBlockHeader().getNumber(); + stateRoot = blockProof.getBlockHeader().getStateRoot(); + } + + this.paraMtaDb.set(currentMTA); + + if (stateRoot != null && lastHeight != 0) { + return new BlockVerifyResult(stateRoot, lastHeight); + } + + return null; + } + + private byte[] verifyRelayChainState(List relayChainStateProofs, BlockVerifyResult relayChainBlockVerifyResult) { + byte[] paraIncludedHead = null; + byte[] candidateIncludedEventIndex = this.candidateIncludedEventIndexDb.get(); + byte[] newAuthoritiesEventIndex = this.newAuthoritiesEventIndexDb.get(); + for (StateProof stateProof : relayChainStateProofs) { + byte[] encodedStorage = stateProof.prove(relayChainBlockVerifyResult.stateRoot); + if (Arrays.equals(stateProof.getKey(), Constant.EventStorageKey)) { + List> eventRecords = (List>) Context.call(this.relayEventDecoderDb.get(), "decodeEvent", encodedStorage); + for (Map rawEventRecord: eventRecords) { + EventRecord eventRecord = new EventRecord(rawEventRecord); + // update new validator set + if (eventRecord.getEventIndex()[0] == newAuthoritiesEventIndex[0] && eventRecord.getEventIndex()[1] == newAuthoritiesEventIndex[1]) { + long lastUpdateValidatorSet = this.lastUpdateValidatorSetDb.get(); + if (relayChainBlockVerifyResult.lastHeight <= lastUpdateValidatorSet) { + continue; + } + NewAuthoritiesEvent newAuthoritiesEvent = new NewAuthoritiesEvent(eventRecord.getEventData()); + List newValidators = newAuthoritiesEvent.getValidators(); + Validators newValidatorsObject = new Validators(newValidators); + this.validatorsDb.set(newValidatorsObject); + this.relaySetIdDb.set(this.relaySetIdDb.get().add(BigInteger.valueOf(1))); + this.lastUpdateValidatorSetDb.set(relayChainBlockVerifyResult.lastHeight); + continue; + } + + // filter Candidate Included Event of Relay chain + if (eventRecord.getEventIndex()[0] == candidateIncludedEventIndex[0] && eventRecord.getEventIndex()[1] == candidateIncludedEventIndex[1]) { + CandidateIncludedEvent candidateIncludedEvent = new CandidateIncludedEvent(eventRecord.getEventData()); + // check inclusion of current paraChain Id and return included block hash + if (candidateIncludedEvent.getCandidateReceipt().getCandidateDescriptor().getParaId() == this.paraChainIdDb.get()) { + paraIncludedHead = candidateIncludedEvent.getCandidateReceipt().getCandidateDescriptor().getParaHead(); + } + } + } + } + } + return paraIncludedHead; + } +} diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/IBMV.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/IBMV.java new file mode 100644 index 00000000..8d5cf790 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/IBMV.java @@ -0,0 +1,115 @@ +package foundation.icon.btp.bmv.parachain; + +import foundation.icon.btp.lib.BMVStatus; + +import java.util.List; +import java.math.BigInteger; + +import score.Address; + +public interface IBMV { + /** + * get encode of merkle tree accumulator of para + */ + String paraMta(); + + /** + * get encode of merkle tree accumulator of relay + */ + String relayMta(); + + /** + * get current mta height of para + */ + long paraMtaHeight(); + + /** + * get current mta height of relay + */ + long relayMtaHeight(); + + /** + * get list of para MTA root + */ + List paraMtaRoot(); + + /** + * get list of relay MTA root + */ + List relayMtaRoot(); + + /** + * get last block hash of para + */ + byte[] paraMtaLastBlockHash(); + + /** + * get last block hash of relay + */ + byte[] relayMtaLastBlockHash(); + + /** + * get current mta offset of para + */ + long paraMtaOffset(); + + /** + * get current mta offset of relay + */ + long relayMtaOffset(); + + /** + * get current mta cache of para + */ + List paraMtaCaches(); + + /** + * get current mta cache of relay + */ + List relayMtaCaches(); + + /** + * get address of event decoder of para chain + */ + Address paraEventDecoder(); + + /** + * get address of event decoder of relay chain + */ + Address relayEventDecoder(); + + /** + * get address of bmc + */ + Address bmc(); + + /** + * net address that bmv verify + */ + String netAddress(); + + /** + * list addresses of validator + */ + List validators(); + + /** + * current validator set id + */ + BigInteger setId(); + + /** + * get status of BMV + */ + BMVStatus getStatus(); + + /** + * last height + */ + long lastHeight(); + + /** + * handle verify message from BMC and return list of event message + */ + List handleRelayMessage(String bmc, String prev, BigInteger seq, String msg); +} diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/Constant.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/Constant.java new file mode 100644 index 00000000..7f0cdb71 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/Constant.java @@ -0,0 +1,8 @@ +package foundation.icon.btp.bmv.parachain.lib; + +import foundation.icon.btp.lib.utils.HexConverter; + +public class Constant { + public static final byte[] EventStorageKey = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + public static final byte[] MessageEventTopic = HexConverter.hexStringToByteArray("37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b"); // kekak256("Message(string,uint256,bytes)"); +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/ParaBlockUpdate.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/ParaBlockUpdate.java new file mode 100644 index 00000000..00b2c207 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/ParaBlockUpdate.java @@ -0,0 +1,50 @@ +package foundation.icon.btp.bmv.parachain.lib.blockUpdate; + +import foundation.icon.btp.lib.blockHeader.BlockHeader; +import foundation.icon.btp.bmv.parachain.lib.relayChainData.RelayChainData; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; +import score.Context; +import score.ObjectReader; + +public class ParaBlockUpdate { + private final BlockHeader blockHeader; + private final RelayChainData relayChainData; + + public ParaBlockUpdate(byte[] serialized) throws RelayMessageRLPException { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + rlpReader.beginList(); + + // decode Para block header + byte[] encodedHeader = rlpReader.readNullable(byte[].class); + if (encodedHeader != null && encodedHeader.length > 0) { + this.blockHeader = new BlockHeader(encodedHeader); + } else { + this.blockHeader = null; + } + + // decode Relay chain data + byte[] encodedRelayChainData = rlpReader.readNullable(byte[].class); + if (encodedRelayChainData != null && encodedRelayChainData.length > 0) { + this.relayChainData = RelayChainData.fromBytes(encodedRelayChainData); + } else { + this.relayChainData = null; + } + rlpReader.end(); + } + + public BlockHeader getBlockHeader() { + return this.blockHeader; + } + + public RelayChainData getRelayChainData() { + return this.relayChainData; + } + + public static ParaBlockUpdate fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new ParaBlockUpdate(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("PraBlockUpdate ", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/RelayBlockUpdate.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/RelayBlockUpdate.java new file mode 100644 index 00000000..1d63cda0 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/blockUpdate/RelayBlockUpdate.java @@ -0,0 +1,63 @@ +package foundation.icon.btp.bmv.parachain.lib.blockUpdate; + +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.blockHeader.BlockHeader; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; +import foundation.icon.btp.lib.votes.Votes; + +import score.Context; +import score.ObjectReader; + +import java.math.BigInteger; +import java.util.List; + +public class RelayBlockUpdate { + private final BlockHeader blockHeader; + private final Votes votes; + + public RelayBlockUpdate(byte[] serialized) throws RelayMessageRLPException { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + rlpReader.beginList(); + + // decode Para block header + byte[] encodedHeader = rlpReader.readByteArray(); + if (encodedHeader != null && encodedHeader.length > 0) { + this.blockHeader = new BlockHeader(encodedHeader); + } else { + this.blockHeader = null; + } + + // decode list of validator's votes + byte[] encodedVotes = rlpReader.readNullable(byte[].class); + if (encodedVotes != null && encodedVotes.length > 0) { + this.votes = Votes.fromBytes(encodedVotes); + } else { + this.votes = null; + } + rlpReader.end(); + } + + public BlockHeader getBlockHeader() { + return this.blockHeader; + } + + public Votes getVotes() { + return this.votes; + } + + public void verify(List validators, BigInteger currentSetId) { + if (this.votes == null) { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE, "not exists votes"); + } + + this.votes.verify(this.blockHeader.getNumber(), this.blockHeader.getHash(), validators, currentSetId); + } + + public static RelayBlockUpdate fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new RelayBlockUpdate(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("RelayBlockUpdate ", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayChainData/RelayChainData.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayChainData/RelayChainData.java new file mode 100644 index 00000000..f6ffbcf8 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayChainData/RelayChainData.java @@ -0,0 +1,74 @@ +package foundation.icon.btp.bmv.parachain.lib.relayChainData; + +import foundation.icon.btp.lib.blockProof.BlockProof; +import foundation.icon.btp.bmv.parachain.lib.blockUpdate.*; +import foundation.icon.btp.lib.stateProof.StateProof; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import java.util.List; + +import scorex.util.ArrayList; + +import score.Context; +import score.ObjectReader; + +public class RelayChainData { + private final List blockUpdates = new ArrayList(16); + private BlockProof blockProof; + private final List stateProofs = new ArrayList(10); + + public RelayChainData(byte[] serialized) throws RelayMessageRLPException { + ObjectReader r = Context.newByteArrayObjectReader("RLPn", serialized); + r.beginList(); + + // decode list of RelayBlockUpdate + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedBlockUpdate = r.readByteArray(); + blockUpdates.add(RelayBlockUpdate.fromBytes(encodedBlockUpdate)); + } + r.end(); + } + + // decode Parachain block proof + if (r.hasNext()) { + byte[] blockProofEncoded = r.readNullable(byte[].class); + this.blockProof = (blockProofEncoded != null && blockProofEncoded.length > 0) ? BlockProof.fromBytes(blockProofEncoded) : null; + } else { + this.blockProof = null; + } + + // decode Parachain state proofs + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedStateProof = r.readByteArray(); + this.stateProofs.add(StateProof.fromBytes(encodedStateProof)); + } + r.end(); + } + + r.end(); + } + + public List getBlockUpdate() { + return this.blockUpdates; + } + + public BlockProof getBlockProof() { + return this.blockProof; + } + + public List getStateProof() { + return this.stateProofs; + } + + public static RelayChainData fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new RelayChainData(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("RelayChainData: ", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayMessage/RelayMessage.java b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayMessage/RelayMessage.java new file mode 100644 index 00000000..19875637 --- /dev/null +++ b/javascore/bmv/parachain/src/main/java/foundation/icon/btp/bmv/parachain/lib/relayMessage/RelayMessage.java @@ -0,0 +1,74 @@ +package foundation.icon.btp.bmv.parachain.lib.relayMessage; + +import foundation.icon.btp.lib.blockProof.BlockProof; +import foundation.icon.btp.bmv.parachain.lib.blockUpdate.*; +import foundation.icon.btp.lib.stateProof.StateProof; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import java.util.List; + +import scorex.util.ArrayList; + +import score.Context; +import score.ObjectReader; + +public class RelayMessage { + private final List blockUpdates = new ArrayList(16); + private BlockProof blockProof; + private final List stateProofs = new ArrayList(10); + + public RelayMessage(byte[] serialized) throws RelayMessageRLPException { + ObjectReader r = Context.newByteArrayObjectReader("RLPn", serialized); + r.beginList(); + + // decode list of Prachain BlockUpdate + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedBlockUpdate = r.readByteArray(); + blockUpdates.add(ParaBlockUpdate.fromBytes(encodedBlockUpdate)); + } + r.end(); + } + + // decode Parachain block proof + if (r.hasNext()) { + byte[] blockProofEncoded = r.readNullable(byte[].class); + this.blockProof = (blockProofEncoded != null && blockProofEncoded.length > 0) ? BlockProof.fromBytes(blockProofEncoded) : null; + } else { + this.blockProof = null; + } + + // decode Parachain state proofs + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedStateProof = r.readByteArray(); + this.stateProofs.add(StateProof.fromBytes(encodedStateProof)); + } + r.end(); + } + + r.end(); + } + + public List getBlockUpdate() { + return this.blockUpdates; + } + + public BlockProof getBlockProof() { + return this.blockProof; + } + + public List getStateProof() { + return this.stateProofs; + } + + public static RelayMessage fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new RelayMessage(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("RelayMessage", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/settings.gradle b/javascore/bmv/settings.gradle new file mode 100644 index 00000000..016e3345 --- /dev/null +++ b/javascore/bmv/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'bmv' +include 'lib', 'sovereignChain', 'parachain', 'testsvc', 'testinteg', 'eventDecoder' diff --git a/javascore/bmv/sovereignChain/build.gradle b/javascore/bmv/sovereignChain/build.gradle new file mode 100644 index 00000000..a476afd0 --- /dev/null +++ b/javascore/bmv/sovereignChain/build.gradle @@ -0,0 +1,92 @@ +plugins { + id 'java' + id 'maven-publish' + id 'signing' +} + +sourceSets { + intTest {} +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +configurations { + intTestImplementation.extendsFrom testImplementation + intTestRuntimeOnly.extendsFrom testRuntimeOnly +} + +dependencies { + implementation 'com.github.sink772:javaee-scorex:0.5.2' + implementation project(':lib') + compileOnly files('../api-0.8.8-SNAPSHOT.jar') + + testImplementation "com.squareup.okhttp3:okhttp:3.11.0" + intTestImplementation project(':testinteg') + intTestImplementation 'foundation.icon:icon-sdk:2.0.0' + intTestImplementation 'org.web3j:core:4.8.4' + testImplementation project(':testsvc') + testImplementation 'org.mockito:mockito-core:3.4.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.bmv.sovereignChain.BMV' + archivesBaseName = 'SovereignChain-BMV' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = 'http://localhost:9082/api/v3' + nid = 3 + } + } + keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : '' + password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : '' + parameters { + arg('bmc', 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd') + arg('net', '0x1234.icon') + arg('encodedValidators', '---') + arg('mtaOffset', '0x465459') + arg('mtaRootSize', '0x10') + arg('mtaCacheSize', '0x10') + arg('mtaIsAllowNewerWitness', "0x1") + arg('lastBlockHash', "0xfb147d7417db613d12685b9affdf6dd06bcfbba78a477a7fc9a79a21075a776a") + } +} + +test { + useJUnitPlatform() + testLogging.showStandardStreams = true + testLogging.exceptionFormat = "full" +} + +def eventDecoderBuildDirectory = project(':eventDecoder').buildDir + +task integrationTest(type: Test, dependsOn: optimizedJar) { + useJUnitPlatform() + description = 'Runs integration tests.' + group = 'verification' + + testClassesDirs = sourceSets.intTest.output.classesDirs + classpath = sourceSets.intTest.runtimeClasspath + testLogging.showStandardStreams = true + + // use the common config files + systemProperty('env.props', new File(project(':testinteg').projectDir, 'conf/env.props')) + + def prefix = 'score.path.' + systemProperty(prefix + project.name, optimizedJar.outputJarName) + systemProperty(prefix + 'EdgewareEventDecoderScore', """${eventDecoderBuildDirectory}/libs/EdgewareEventDecoder-optimized.jar""") +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java new file mode 100644 index 00000000..2ec4cd72 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/cases/BMVTestScore.java @@ -0,0 +1,1671 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.cases; + +import foundation.icon.icx.IconService; +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.http.HttpProvider; +import foundation.icon.icx.transport.jsonrpc.RpcError; +import foundation.icon.icx.data.TransactionResult; + +import foundation.icon.test.Env; +import foundation.icon.test.TestBase; +import foundation.icon.test.TransactionHandler; +import foundation.icon.test.score.BMVScore; +import foundation.icon.test.score.EventDecoderScore; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.List; + +import scorex.util.ArrayList; +import scorex.util.Base64; + +import score.util.Crypto; + +import static foundation.icon.test.Env.LOG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.TestMethodOrder; + +import okhttp3.OkHttpClient; +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +@TestMethodOrder(OrderAnnotation.class) +public class BMVTestScore extends TestBase { + private static final boolean DEBUG = true; + private static Address ZERO_ADDRESS; + private static TransactionHandler txHandler; + private static SecureRandom secureRandom; + private static KeyWallet[] wallets; + private static KeyWallet ownerWallet, caller; + private static List validatorPublicKeys; + private static List validatorSecretKey; + private static BMVScore bmvScore; + private static EventDecoderScore eventDecoderScore; + private static String bmc; + private static String destinationNet = "0x9876.edge"; + private static String sourceNet = "0x1234.icon"; + private static boolean mtaIsAllowNewerWitness = true; + private static byte[] lastBlockHash = HexConverter.hexStringToByteArray("08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2"); + private static String bmcBTPAddress; + private static BigInteger currentSetId = BigInteger.valueOf(123); + private static long mtaOffset = 10; + private static int mtaRootSize = 10; + private static int mtaCacheSize = 3; + private static List updatedBlocks = new ArrayList(10); + + @BeforeAll + static void setup() throws Exception { + ZERO_ADDRESS = new Address("hx0000000000000000000000000000000000000000"); + validatorPublicKeys = new ArrayList(5); + validatorSecretKey = new ArrayList(5); + + Env.Chain chain = Env.getDefaultChain(); + OkHttpClient ohc = new OkHttpClient.Builder().build(); + IconService iconService = new IconService(new HttpProvider(ohc, chain.getEndpointURL(), 3)); + txHandler = new TransactionHandler(iconService, chain); + secureRandom = new SecureRandom(); + + // init wallets + wallets = new KeyWallet[2]; + BigInteger amount = ICX.multiply(BigInteger.valueOf(100)); + for (int i = 0; i < wallets.length; i++) { + wallets[i] = KeyWallet.create(); + txHandler.transfer(wallets[i].getAddress(), amount); + } + for (KeyWallet wallet : wallets) { + ensureIcxBalance(txHandler, wallet.getAddress(), BigInteger.ZERO, amount); + } + ownerWallet = wallets[0]; + caller = wallets[1]; + + bmc = ownerWallet.getAddress().toString(); + bmcBTPAddress = "btp://" + sourceNet + "/" + ownerWallet.getAddress().toString(); + + // initialize validators key + validatorPublicKeys.add(HexConverter.hexStringToByteArray("25aabce1a96af5c3f6a4c780b6eeeb7769bf32884f1bad285b5faa464e25c487")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("8e111029cd8a3754095d96e9d01629f7ee686198b9e8465d9c90101ed867b958")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("01573582dc1bc7474e76cd5a5cbefbcf5475284ced05cc66a45dcb65ea29b09b")); + validatorPublicKeys.add(HexConverter.hexStringToByteArray("d3e7471c8fd4cdb71e791783098794e2295f992ae6a4a27e0f893071ade31b78")); + + validatorSecretKey.add(HexConverter.hexStringToByteArray("c360fab3025db65e3d967f553ddae434382cfbb9130b6de339fd1f3283f350a025aabce1a96af5c3f6a4c780b6eeeb7769bf32884f1bad285b5faa464e25c487")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("f5880449f4eb08bfa946a6f5bb679a33f58a7f1bd93c52bb4f2e05dad27e45dc8e111029cd8a3754095d96e9d01629f7ee686198b9e8465d9c90101ed867b958")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("e8c61ebe8654dfcfc279d28eec8af09d1149bbc9ba26049354e880d479ef98f101573582dc1bc7474e76cd5a5cbefbcf5475284ced05cc66a45dcb65ea29b09b")); + validatorSecretKey.add(HexConverter.hexStringToByteArray("8f97e18992ed2fb7b7f7e37146e2107cb950556cead04c347ad81c69b62ad51fd3e7471c8fd4cdb71e791783098794e2295f992ae6a4a27e0f893071ade31b78")); + + List listRlpValidators = new ArrayList(5); + for (byte[] validator: validatorPublicKeys) { + listRlpValidators.add(RlpString.create(validator)); + } + // RLP encode validators public key + byte[] rlpEncodeValidators = RlpEncoder.encode(new RlpList(listRlpValidators)); + // base 64 encoded validator public key + String encodedValidators = new String(Base64.getUrlEncoder().encode(rlpEncodeValidators)); + eventDecoderScore = EventDecoderScore.mustDeploy( + txHandler, + ownerWallet + ); + bmvScore = BMVScore.mustDeploy( + txHandler, + ownerWallet, + bmc, + destinationNet, + encodedValidators, + mtaOffset, + mtaRootSize, + mtaCacheSize, + mtaIsAllowNewerWitness, + lastBlockHash, + eventDecoderScore.getAddress(), + currentSetId + ); + + // check contract initialized successfully + BigInteger mtaHeight = bmvScore.mtaHeight(); + assertTrue(mtaHeight.equals(BigInteger.valueOf(mtaOffset))); + + List mtaRoot = bmvScore.mtaRoot(); + assertEquals(mtaRoot.size(), mtaRootSize); + + byte[] mtaLastBlockHash = bmvScore.mtaLastBlockHash(); + assertArrayEquals(mtaLastBlockHash, lastBlockHash); + + BigInteger mtaOffsetResult = bmvScore.mtaOffset(); + assertTrue(mtaOffsetResult.equals(BigInteger.valueOf(mtaOffset))); + + List mtaCaches = bmvScore.mtaCaches(); + assertEquals(mtaCaches.size(), 0); + + Address bmcAddress = bmvScore.bmc(); + assertTrue(bmcAddress.toString().equals(bmc)); + + Address eventDecoderAddress = bmvScore.eventDecoder(); + assertTrue(eventDecoderAddress.equals(eventDecoderScore.getAddress())); + + String netAddressResult = bmvScore.netAddress(); + assertTrue(netAddressResult.equals(destinationNet)); + + BigInteger lastHeightResult = bmvScore.mtaOffset(); + assertTrue(lastHeightResult.equals(BigInteger.valueOf(mtaOffset))); + + List validators = bmvScore.validators(); + assertEquals(validators.size(), validatorPublicKeys.size()); + } + + /** + * --------------------------------------------------------------------------------------------- + * RECEIVING A RELAY MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: previous bmc is not belong to network that BMV handle + * Given: + * prev: btp://0xffff.eos/0x12345 + * When: + * network that BMV handle: 0x9876.edge + * Then: + * throw error: + * message: "not acceptable from" + * code: NOT_ACCEPTED_FROM_NETWORK_ERROR 38 + */ + @Test + @Order(1) + public void receivingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String invalidNetAddress = "0xffff.eos"; // valid is 0x1234.icon + String prev = "btp://" + invalidNetAddress + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_FROM_NETWORK_ERROR + ")")); + } + + /** + * + * Scenario 2: transaction caller is not bmc + * Given: + * call `handleRelayMessage` with caller "caller" + * When: + * registered bmc address: ownerWallet.getAddress() generated when setup + * Then: + * throw error: + * message: "not acceptable bmc" + * code: NOT_ACCEPTED_BMC_ADDR_ERROR 39 + */ + @Test + @Order(2) + public void receivingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(caller, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR + ")")); + } + + /** + * + * Scenario 3: bmc is invalid + * Given: + * bmc: btp://0x1234.icon/caller.getAddress() + * When: + * registered bmc address: ownerWallet.getAddress() generated when setup + * Then: + * throw error: + * message: "not acceptable bmc" + * code: NOT_ACCEPTED_BMC_ADDR_ERROR 39 + */ + @Test + @Order(3) + public void receivingScenario3() throws Exception { + String invalidBmcBTPAddress = "btp://" + sourceNet + "/" + caller.getAddress().toString(); // valid is owner address + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "0x1234"; + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, invalidBmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR + ")")); + } + + /** + * --------------------------------------------------------------------------------------------- + * EXTRACTING THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: input relay message with invalid base64 format + * Given: + * msg: invalid base64 format + * When: + * + * Then: + * throw error: + * message: "decode base64 msg error: " + * code: DECODE_ERROR 37 + */ + @Test + @Order(4) + public void extractingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + String relayMessageEncoded = "abcedgef=="; // invalid base64 formart of relay message + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, relayMessageEncoded); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.DECODE_ERROR + ")")); + } + + /** + * + * Scenario 2: input relay message with invalid RLP format + * Given: + * msg: invalid RLP encoded format + * When: + * + * Then: + * throw error: + * message: "RelayMessage RLP decode error: " + * code: DECODE_ERROR 37 + */ + @Test + @Order(5) + public void extractingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List blockUpdate = new ArrayList(3); + blockUpdate.add(RlpString.create("abc")); // invalid block encode + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdate)); // invalid block update + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.DECODE_ERROR + ")")); + } + + /** + * + * Scenario 3: input relay message with empty BlockUpdate and BlockProof + * Given: + * msg: empty BlockUpdate and BlockProof + * When: + * + * Then: + * throw error: + * message: "invalid RelayMessage not exists BlockUpdate or BlockProof" + * code: BMV_ERROR 25 + */ + @Test + @Order(6) + public void extractingScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + relayMessage.add(RlpString.create("")); // empty block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * --------------------------------------------------------------------------------------------- + * VERIFYING THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 1: input block update with invalid parent hash with mta last block hash + * Given: + * msg: + * block 11: + * parentHash: "46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55" + * When: + * mta height: 10 + * last block hash: "08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2" + * Then: + * throw error: + * message: "parent block hash does not match, parent: 46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55 current: 08557f0ca4d319f559310f5d62b38643d0a0555b04efe3e1589f869d052ff9f2" + * code: BMV_ERROR 25 + */ + @Test + @Order(7) + public void verifyingScenario1() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // different with lastBlockHash + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 2: input block update with invalid parent hash in blocks + * Given: + * update block 11, 12, 13 + * block 12 parent hash: "46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55" + * When: + * block 12 parent hash should be: block11.getHash() + * Then: + * throw error: + * message: "parent block hash does not match, parent: 46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55 current: block11 hash" + * code: BMV_ERROR 25 + */ + @Test + @Order(9) + public void verifyingScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = HexConverter.hexStringToByteArray("46212af3be91c9eb81e0864c0158332428d4df405980a5d3042c1ba934cbaa55"); // correct is blockUpdate.getHash() + blockUpdates.add(RlpString.create(blockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.BMV_ERROR + ")")); + } + + /** + * + * Scenario 3: input block with height higher than current updated height + * Given: + * update block 19, 20, 21 + * When: + * last updated block height of BMV: 10 + * Then: + * throw error: + * message: "invalid blockUpdate height: 19; expected: 11" + * code: INVALID_BLOCK_UPDATE_HEIGHT_HIGHER 33 + */ + @Test + @Order(10) + public void verifyingScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 19; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 4: input block with height lower than current updated height + * Given: + * update block 7, 8, 9 + * When: + * last updated block height of BMV: 10 + * Then: + * throw error: + * message: "invalid blockUpdate height: 7; expected: 11" + * code: INVALID_BLOCK_UPDATE_HEIGHT_LOWER 34 + */ + @Test + @Order(11) + public void verifyingScenario4() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 7; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encode())); + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER + ")")); + } + + /** + * + * Scenario 5: update block without votes of validators + * Given: + * update block 11, 12, 13 + * block 13 has empty votes + * When: + * + * Then: + * throw error: + * message: "not exists votes" + * code: INVALID_BLOCK_UPDATE 29 + */ + @Test + @Order(12) + public void verifyingScenario5() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_UPDATE + ")")); + } + + /** + * + * Scenario 6: update block with invalid vote, vote for invalid block hash + * Given: + * update block 11, 12, 13 + * validator sign for: + * block hash: "a5f2868483655605709dcbda6ce0fd21cd0386ae2f99445e052d6b1f1ae6db5b" + * When: + * validator should be sign for: + * block hash: block13.getHash() + * Then: + * throw error: + * message: "validator signature invalid block hash" + * code: INVALID_VOTES 27 + */ + @Test + @Order(13) + public void verifyingScenario6() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)).longValue(); + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + if (i == 2) { + byte[] invalidVoteBlockHash = HexConverter.hexStringToByteArray("a5f2868483655605709dcbda6ce0fd21cd0386ae2f99445e052d6b1f1ae6db5b"); + byte[] voteMessage = BlockUpdate.voteMessage(invalidVoteBlockHash, updatingBlockNumber, round, setId); // should not be invalidVoteBlockHash but blockUpdate.getHash() + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); // update block without votes + } else { + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 7: update block with invalid vote, vote for invalid block height + * Given: + * update block 11, 12, 13 + * validator sign for: + * block height 12 + * When: + * validator should be sign for: + * block height: 13 + * Then: + * throw error: + * message: "validator signature invalid block height" + * code: INVALID_VOTES 27 + */ + @Test + @Order(14) + public void verifyingScenario7() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("92"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + if (i == 2) { + long invalidVoteBlockHeight = 12; + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), invalidVoteBlockHeight, round, setId); // should not be invalidVoteBlockHeight but updatingBlockNumber + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 8: update block with invalid vote, vote of newer validator set id + * Given: + * update block 11, 12, 13 + * vote of validator set Id: 124 + * When: + * current validator set id stored in BMV: 123 + * Then: + * throw error: + * message: "verify signature for invalid validator set id"" + * code: INVALID_VOTES 27 + */ + @Test + @Order(15) + public void verifyingScenario8() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + BigInteger setId = new BigInteger("124"); // invalid setId + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, setId); + if (i == 2) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, setId); // should not be updatingBlockNumber + 1 but updatingBlockNumber + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, setId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); // update block without votes + } else { + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 9: update block with invalid vote, signature invalid + * Given: + * update block 11, 12, 13 + * validator1 has invalid signature + * When: + * + * Then: + * throw error: + * message: "invalid signature" + * code: INVALID_VOTES 27 + */ + @Test + @Order(16) + public void verifyingScenario9() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); + if (i == 2) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round.add(BigInteger.valueOf(1)), currentSetId); // validator 1 sign with incorrect message + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); // update block without votes + } else { + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 10: update block with invalid vote, signature not belong to validators + * Given: + * update block 11, 12, 13 + * block 13 is signed by key not belong to validators + * When: + * + * Then: + * throw error: + * message: "one of signature is not belong to validator" + * code: INVALID_VOTES 27 + */ + @Test + @Order(17) + public void verifyingScenario10() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + byte[] notValidatorPubicKey = HexConverter.hexStringToByteArray("793d5108b8d128792958a139660102d538c24e9fe70bd396e5c95fe451cdc222"); + byte[] notValidatorSecrectKey = HexConverter.hexStringToByteArray("b9fbcdfe9e6289fadef00225651dc6a4ae3fee160d6f86cce71150b3db691481793d5108b8d128792958a139660102d538c24e9fe70bd396e5c95fe451cdc222"); + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); + if (i == 2) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, currentSetId); + blockUpdate.vote(notValidatorPubicKey, notValidatorSecrectKey, round, currentSetId); // sign key not belong to validator + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 11: update block with duplicate vote + * Given: + * update block 11, 12, 13 + * two votes for block 13 are the same + * When: + * + * Then: + * throw error: + * message: "duplicated signature" + * code: INVALID_VOTES 27 + */ + @Test + @Order(18) + public void verifyingScenario11() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); + if (i == 2) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // two same signatures + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // two same signatures + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); // update block without votes + } else { + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 12: block vote not enough 2/3 + * Given: + * update block 11, 12, 13 + * number of vote for block 13: 2 + * When: + * number of validators: 4 + * number of vote to finalize block should be: 3 + * Then: + * throw error: + * message: "require signature +2/3" + * code: INVALID_VOTES 27 + */ + @Test + @Order(19) + public void verifyingScenario12() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 3; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); + if (i == 2) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // just two vote, must be 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); // update block without votes + } else { + updatingBlockNumber += 1; + parentHash = blockUpdate.getHash(); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_VOTES + ")")); + } + + /** + * + * Scenario 13: update block successfully + * Given: + * update block 11, 12, 13, 14, 15, 16, 17 + * number of vote for block 17: 3 + * When: + * number of validators: 4 + * current updated block height: 10 + * Then: + * update block to MTA caches + * update root of mta + * update MTA last block hash + */ + @Test + @Order(20) + public void verifyingScenario13() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + long updatingBlockNumber = 11; + + List blockUpdates = new ArrayList(3); + for (int i = 0; i < 7; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); + updatedBlocks.add(blockUpdate); + parentHash = blockUpdate.getHash(); + if (i == 6) { + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber, round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + } else { + updatingBlockNumber += 1; + blockUpdates.add(RlpString.create(blockUpdate.encodeWithoutVote())); // update block without votes + } + } + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // block proof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + BigInteger mtaHeight = bmvScore.mtaHeight(); + assertTrue(mtaHeight.equals(BigInteger.valueOf(updatingBlockNumber))); + + List mtaRoot = bmvScore.mtaRoot(); + assertEquals(mtaRoot.size(), mtaRootSize); + assertArrayEquals(mtaRoot.get(0), parentHash); + + byte[] mtaLastBlockHash = bmvScore.mtaLastBlockHash(); + assertArrayEquals(mtaLastBlockHash, parentHash); + + BigInteger mtaOffsetResult = bmvScore.mtaOffset(); + assertTrue(mtaOffsetResult.equals(BigInteger.valueOf(mtaOffset))); + + List mtaCaches = bmvScore.mtaCaches(); + assertEquals(mtaCaches.size(), 3); + assertArrayEquals(mtaCaches.get(2), parentHash); + + BigInteger lastHeightResult = bmvScore.mtaOffset(); + assertTrue(lastHeightResult.equals(BigInteger.valueOf(mtaOffset))); + } + + /** + * + * Scenario 14: update blockProof of block that not exist in MTA + * Given: + * blockProof of block 18; + * When: + * current mta height: 17; + * Then: + * throw error: + * message: "given block height is newer 18; expected: 17" + * code: INVALID_BLOCK_PROOF_HEIGHT_HIGHER, 35 + */ + @Test + @Order(21) + public void verifyingScenario14() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger mtaHeight = bmvScore.mtaHeight(); + long updatingBlockNumber = 18; + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List blockProof = new ArrayList(2); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); // new block that not exist in MTA + blockProof.add(RlpString.create(blockUpdate.getEncodedHeader())); // block header + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(mtaHeight)); + List witness = new ArrayList(1); // empty witness + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_PROOF_HEIGHT_HIGHER + ")")); + } + + /** + * + * Scenario 15: update blockProof with invalid old witness + * Given: + * blockProof of block 14; + * mta height of client: 15; + * When: + * current mta height of BMV: 17; + * mta cache size: 3 (stored block hash 17, 16, 15) + * Then: + * throw error: + * message: "not allowed old witness" + * code: INVALID_BLOCK_WITNESS_OLD, 36 + */ + @Test + @Order(22) + public void verifyingScenario15() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = lastBlockHash.clone(); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List blockProof = new ArrayList(2); + blockProof.add(RlpString.create(updatedBlocks.get(3).getEncodedHeader())); // block 14 + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(BigInteger.valueOf(15))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(updatedBlocks.get(5).getHash())); + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // empty stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS_OLD + ")")); + } + + /** + * + * Scenario 16: update blockProof with invalid block header + * Given: + * blockProof of block 15 with incorrect data; + * mta height of client: 16; + * When: + * current mta height of BMV: 17; + * mta cache size: 3 (stored block hash 17, 16, 15) + * Then: + * throw error: + * message: "invalid old witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(23) + public void verifyingScenario16() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger mtaHeight = bmvScore.mtaHeight(); + long updatingBlockNumber = 15; + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List blockProof = new ArrayList(2); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber, stateRoot, round, currentSetId); // fake block 15 updated to block proof + blockProof.add(RlpString.create(blockUpdate.getEncodedHeader())); + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(BigInteger.valueOf(16))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(updatedBlocks.get(5).getHash())); + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 17: update blockProof with invalid block witness + * Given: + * blockProof of block 15 that has invalid witness + * mta height of client: 17; + * When: + * current mta height of BMV: 17; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(24) + public void verifyingScenario17() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger mtaHeight = bmvScore.mtaHeight(); + BigInteger updatingBlockNumber = BigInteger.valueOf(15); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List blockProof = new ArrayList(2); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); // fake block 15 updated to block proof + blockProof.add(RlpString.create(blockUpdate.getEncodedHeader())); + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(BigInteger.valueOf(17))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(HexConverter.hexStringToByteArray("9af0b4af1fc9a9d75dbb7e2b72e6560ae2afef30f91625b562adb9659931cc9a"))); // fake witness + witness.add(RlpString.create(HexConverter.hexStringToByteArray("679adaf23ccf7fbc51b3c59588357f0c2ec3cacba0595274c8ec5c44354ab8bc"))); + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 18: update blockProof with invalid block witness when client MTA height greater than bmv MTA height + * Given: + * blockProof of block 17 that has invalid witness + * mta height of client: 18; + * When: + * current mta height of BMV: 17; + * Then: + * throw error: + * message: "invalid witness" + * code: INVALID_BLOCK_WITNESS, 31 + */ + @Test + @Order(25) + public void verifyingScenario18() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = HexConverter.hexStringToByteArray("e65489f4471fe415ce09008134d496995fa3a2bd6a83470a70fd00b684c08d3a"); + byte[] stateRoot = HexConverter.hexStringToByteArray("e488e2726bec22ef07ed0c540d0a6094d41998cd845ebedb773be216d17a44a1"); + BigInteger updatingBlockNumber = BigInteger.valueOf(17); + + List relayMessage = new ArrayList(3); + relayMessage.add(new RlpList()); // empty block update + + List blockProof = new ArrayList(2); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); // fake block updated to block proof + blockProof.add(RlpString.create(blockUpdate.getEncodedHeader())); + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(BigInteger.valueOf(18))); + List witness = new ArrayList(1); // empty witness + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + relayMessage.add(new RlpList()); // stateProof + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_BLOCK_WITNESS + ")")); + } + + /** + * + * Scenario 19: invalid state proof + * Given: + * invalid state proof, hash of proof missmatch with stateRoot in header + * state proof hash: "" + * When: + * state root + * Then: + * throw error: + * message: "invalid MPT proof" + * code: INVALID_MPT + */ + @Test + @Order(26) + public void verifyingScenario19() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = updatedBlocks.get(6).getHash(); + + byte[] stateRoot = HexConverter.hexStringToByteArray("05099e0dbdde814d9bb86abc0915301fb833336890ad40761c44350846663157"); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + List proofs = new ArrayList(1); + // invalid proofs + proofs.add(RlpString.create(HexConverter.hexStringToByteArray("80810480d7349ba81a8606735be62a4df34f5ca785ffff19b39b6cd32886a4da9d706c59545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9"))); + stateProofs.add(RlpString.create(HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"))); + stateProofs.add(new RlpList(proofs)); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_MPT + ")")); + } + + /** + * + * Scenario 20: update new authorities list + * Given: + * stateProof with new authrities event + * event contains list of 4 new validators + * When: + * current validators set ID: 123 + * Then: + * update new validators list to db + * increase validator setID: 124 + */ + @Test + @Order(27) + public void verifyingScenario20() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = updatedBlocks.get(6).getHash(); + + List newValidatorPublicKeys = new ArrayList(5); + List newValidatorSecretKey = new ArrayList(5); + newValidatorPublicKeys.add(0, HexConverter.hexStringToByteArray("2f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorPublicKeys.add(1, HexConverter.hexStringToByteArray("0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorPublicKeys.add(2, HexConverter.hexStringToByteArray("20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorPublicKeys.add(3, HexConverter.hexStringToByteArray("ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + newValidatorSecretKey.add(0, HexConverter.hexStringToByteArray("23fae2b43f110c42a1d32b2c8678d7b7f71eb7873edd0feab952efbc99d1f9d02f27b7a3f5e8a73ac8e905ff237de902723e969bcc424ddbc2b718d948fc82e8")); + newValidatorSecretKey.add(1, HexConverter.hexStringToByteArray("2b502a205ab7ff2b56a0b225fb6b58c3d78a57a8e03c80938b32e7fbc2c564cc0541be4a4cfda7c722a417cd38807fa5ba9500cf3a8f5440576bf8764f3281c6")); + newValidatorSecretKey.add(2, HexConverter.hexStringToByteArray("27b9c71dfee22bacee68a3c73c4aac3a7c95b72dbe1876095fcc2d4f96ce5bac20852d791571334b261838ab84ee38aac4845e81faac2c677e108a3553b65641")); + newValidatorSecretKey.add(3, HexConverter.hexStringToByteArray("b68efa447ebc6ec9f31a5d24c42e4cba41ae7bcc3e7a3f64918648d9e2bf9244ef3a8a0271488f2fbdc787f3285aa42c4c073c2d73f9a3ee288a07471512a6e9")); + + NewAuthoritiesProof newAuthoritiesProof = new NewAuthoritiesProof(newValidatorPublicKeys); + + byte[] stateRoot = newAuthoritiesProof.getRoot(); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + updatedBlocks.add(blockUpdate); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + stateProofs.add(RlpString.create(newAuthoritiesProof.getEventKey())); + stateProofs.add(new RlpList(newAuthoritiesProof.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + + List storedValidators = bmvScore.validators(); + assertEquals(storedValidators.size(), newValidatorPublicKeys.size()); + assertArrayEquals(storedValidators.get(0), newValidatorPublicKeys.get(0)); + assertArrayEquals(storedValidators.get(1), newValidatorPublicKeys.get(1)); + assertArrayEquals(storedValidators.get(2), newValidatorPublicKeys.get(2)); + assertArrayEquals(storedValidators.get(3), newValidatorPublicKeys.get(3)); + assertTrue(bmvScore.setId().equals(BigInteger.valueOf(124))); + + validatorPublicKeys = newValidatorPublicKeys; + validatorSecretKey = newValidatorSecretKey; + currentSetId = bmvScore.setId(); + } + + /** + * --------------------------------------------------------------------------------------------- + * RESPOND THE MESSAGE FROM BMC + * --------------------------------------------------------------------------------------------- + */ + + /** + * + * Scenario 2: btp message sequence higher than expected + * Given: + * btp message sequence: 120 + * + * When: + * bmc message sequence: 111 + * Then: + * throw error: + * message: "invalid sequence: 120; expected 112" + * code: INVALID_SEQUENCE_HIGHER, 32 + */ + @Test + @Order(28) + public void respondScenario2() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = bmvScore.mtaLastBlockHash(); + + List newValidatorPublicKeys = new ArrayList(5); + List newValidatorSecretKey = new ArrayList(5); + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(120); + byte[] btpMessage = "testencodedmessage".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + + byte[] stateRoot = btpEvent.getRoot(); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + stateProofs.add(RlpString.create(btpEvent.getEventKey())); + stateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_SEQUENCE_HIGHER + ")")); + } + + /** + * + * Scenario 3: btp message sequence lower than expected + * Given: + * btp message sequence: 105 + * + * When: + * bmc message sequence: 111 + * Then: + * throw error: + * message: "invalid sequence: 105; expected 112" + * code: INVALID_SEQUENCE, 28 + */ + @Test + @Order(29) + public void respondScenario3() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = bmvScore.mtaLastBlockHash(); + + List newValidatorPublicKeys = new ArrayList(5); + List newValidatorSecretKey = new ArrayList(5); + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(105); + byte[] btpMessage = "testencodedmessage".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + + byte[] stateRoot = btpEvent.getRoot(); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + stateProofs.add(RlpString.create(btpEvent.getEventKey())); + stateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertTrue(txResult.getFailure().getMessage().equals("Reverted(" + ErrorCode.INVALID_SEQUENCE + ")")); + } + + /** + * + * Scenario 4: update btp message with block update successfully + * Given: + * update block: 19 + * btp message sequence: 112 + * btp message contain in block 19 + * When: + * bmc message sequence: 111 + * current block height: 18 + * Then: + * return btp message to BMC + */ + @Test + @Order(30) + public void respondScenario4() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = bmvScore.mtaLastBlockHash(); + + List newValidatorPublicKeys = new ArrayList(5); + List newValidatorSecretKey = new ArrayList(5); + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(112); + byte[] btpMessage = "testencodedmessagea".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + + byte[] stateRoot = btpEvent.getRoot(); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + updatedBlocks.add(blockUpdate); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + + relayMessage.add(new RlpList(blockUpdates)); + relayMessage.add(RlpString.create("")); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + stateProofs.add(RlpString.create(btpEvent.getEventKey())); + stateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + } + + /** + * + * Scenario 5: update btp message with block proof successfully + * Given: + * update block: 20, 21 + * btp message sequence: 113 + * btp message contain in block 20 + * block proof for block 20 + * When: + * bmc message sequence: 112 + * current block height: 19 + * Then: + * return btp message to BMC + */ + @Test + @Order(31) + public void respondScenario5() throws Exception { + String prevBmcAddress = "08425D9Df219f93d5763c3e85204cb5B4cE33aAa"; + String prev = "btp://" + destinationNet + "/" + prevBmcAddress; + BigInteger seq = new BigInteger("111"); + BigInteger round = new BigInteger("123"); + byte[] parentHash = bmvScore.mtaLastBlockHash(); + + List newValidatorPublicKeys = new ArrayList(5); + List newValidatorSecretKey = new ArrayList(5); + + String btpNextAddress = bmcBTPAddress; + BigInteger invalidMessageSequence = BigInteger.valueOf(112); + byte[] btpMessage = "testencodedmessagesecond".getBytes(); + BTPEvent btpEvent = new BTPEvent(prev, HexConverter.hexStringToByteArray(prevBmcAddress), btpNextAddress, invalidMessageSequence, btpMessage); + + byte[] stateRoot = btpEvent.getRoot(); + BigInteger updatingBlockNumber = bmvScore.mtaHeight().add(BigInteger.valueOf(1)); + + List relayMessage = new ArrayList(3); + List blockUpdates = new ArrayList(3); + for (int i = 0; i< 2; i++) { + BlockUpdate blockUpdate = new BlockUpdate(parentHash, updatingBlockNumber.longValue(), stateRoot, round, currentSetId); + byte[] voteMessage = BlockUpdate.voteMessage(blockUpdate.getHash(), updatingBlockNumber.longValue(), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(0), validatorSecretKey.get(0), round, currentSetId); // 3 votes, enough 2/3 * 4 = 3 votes + blockUpdate.vote(validatorPublicKeys.get(1), validatorSecretKey.get(1), round, currentSetId); + blockUpdate.vote(validatorPublicKeys.get(3), validatorSecretKey.get(3), round, currentSetId); + blockUpdates.add(RlpString.create(blockUpdate.encodeWithVoteMessage(voteMessage))); + updatedBlocks.add(blockUpdate); + updatingBlockNumber = updatingBlockNumber.add(BigInteger.valueOf(1)); + parentHash = blockUpdate.getHash(); + stateRoot = HexConverter.hexStringToByteArray("11ea2d1ee7efe3fdabe1e07a2f6af89996dfd16ba8070b6b9b980d6c1bb34502"); + } + + relayMessage.add(new RlpList(blockUpdates)); + List blockProof = new ArrayList(2); + blockProof.add(RlpString.create(updatedBlocks.get(9).getEncodedHeader())); // block 20 + + List blockWitness = new ArrayList(2); + blockWitness.add(RlpString.create(BigInteger.valueOf(21))); + List witness = new ArrayList(1); // empty witness + witness.add(RlpString.create(updatedBlocks.get(8).getHash())); // block 19 hash + blockWitness.add(new RlpList(witness)); + blockProof.add(new RlpList(blockWitness)); + relayMessage.add(RlpString.create(RlpEncoder.encode(new RlpList(blockProof)))); // rlp encoded of blockProof + + List stateProofs = new ArrayList(2); + stateProofs.add(RlpString.create(btpEvent.getEventKey())); + stateProofs.add(new RlpList(btpEvent.getProof())); + relayMessage.add(new RlpList(RlpString.create(RlpEncoder.encode(new RlpList(stateProofs))))); // stateProof with event data + + byte[] rlpEncodeRelayMessage = RlpEncoder.encode(new RlpList(relayMessage)); + String encodedBase64RelayMessage = new String(Base64.getUrlEncoder().encode(rlpEncodeRelayMessage)); + + Bytes id = bmvScore.handleRelayMessage(ownerWallet, bmcBTPAddress, prev, seq, encodedBase64RelayMessage); + TransactionResult txResult = txHandler.getResult(id); + assertSuccess(txResult); + } +} diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java new file mode 100644 index 00000000..f0f51008 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/AbiEncoder.java @@ -0,0 +1,23 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; + +import score.Context; + +public class AbiEncoder { + public static void encodeUInt(ByteSliceOutput output, long number) { + output.addMany(HexConverter.hexStringToByteArray("000000000000000000000000000000000000000000000000")); // 24 byte padding + for (int i = 7; i>=0; i--) { + output.add((byte) ((number >> (8*i)) & 0xff)); + } + } + + public static void encodeBytes(ByteSliceOutput output, byte[] data) { + int padding = (32 - data.length%32); + encodeUInt(output, data.length); + output.addMany(data); + for (int i = 0; i evmTopics = new ArrayList(3); + private final String next; + private final BigInteger seq; + private final byte[] msg; + private final List proofs = new ArrayList(5); + + public BTPEvent(String prevBmcBtpAddress, byte[] prevBmcAddress, String next, BigInteger seq, byte[] msg) { + this.next = next; + this.seq = seq; + this.msg = msg; + + if (next.length() > 64 || next.length() < 32) { + throw new AssertionError("invalid next length"); + } + + ByteSliceOutput evmData = new ByteSliceOutput(100); + AbiEncoder.encodeUInt(evmData, 96); // next position; + AbiEncoder.encodeUInt(evmData, seq.longValue()); + AbiEncoder.encodeUInt(evmData, 192); + AbiEncoder.encodeBytes(evmData, next.getBytes()); + AbiEncoder.encodeBytes(evmData, msg); + + ByteSliceOutput eventData = new ByteSliceOutput(100); + eventData.addMany(prevBmcAddress); + ScaleWriter.writeCompactUint(eventData, 1); // topic size; + eventData.addMany(HexConverter.hexStringToByteArray("37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b")); // btp event topic + ScaleWriter.writeCompactUint(eventData, evmData.size()); // evm data size + eventData.addMany(evmData.toArray()); + + ByteSliceOutput proof0 = new ByteSliceOutput(100); + ByteSliceOutput proof1 = new ByteSliceOutput(100); + ByteSliceOutput proof2 = new ByteSliceOutput(100); + ByteSliceOutput proof3 = new ByteSliceOutput(100); + ByteSliceOutput eventLeafNode = new ByteSliceOutput(100); + + eventLeafNode.addMany(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d7")); // node header + ScaleWriter.writeCompactUint(eventLeafNode, 31 + 3 + eventData.size() + 1); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("10020c010002090037080000000000000000000020df0e0b00000000020000")); // other events + eventLeafNode.addMany(HexConverter.hexStringToByteArray("012200")); // evm event phase, index + eventLeafNode.addMany(eventData.toArray()); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("00")); // empty topic + proofs.add(RlpString.create(eventLeafNode.toArray())); + + proof3.addMany(HexConverter.hexStringToByteArray("80810480")); + proof3.addMany(Crypto.hash("blake2b-256", eventLeafNode.toArray())); + proof3.addMany(HexConverter.hexStringToByteArray("545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9")); + proofs.add(RlpString.create(proof3.toArray())); + + proof2.addMany(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f8043580dd17bfb375845229c0dd74fc5f9be81d5f4cf569c3ee845e3acf5271556804afcd87f2329d61d655e551a438a2720a92951e383690f5a623d245cfb5ef4444c5f0684a022a34dd8bfa2baaf44f172b710040180")); + proof2.addMany(Crypto.hash("blake2b-256", proof3.toArray())); + proof2.addMany(HexConverter.hexStringToByteArray("80fbf36a2eb1b78c879a6ee9b08a04f9f341b284ca2bec0a74f4f39b1df1386ace80586079736657bdf54edfb3d4aab0c8221717c3e1e1ad5e6beeaa7c64dbbe515e80b5a6f3ec81bb61e573624e88e3d279154a7183905d05c656f8744d5788766f614c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287820706f6c6b61646f74")); + proofs.add(RlpString.create(proof2.toArray())); + + proof1.addMany(HexConverter.hexStringToByteArray("80499c804db64d4c6dd7655a3ac8bc482884de58452960029ea5b37c03ec11e678ec9a7c801ba7abc7e25fb80c99c4bff68d0688c9af3e1ff8cfb2bd5a1754ae1b86f94d1c80")); + proof1.addMany(Crypto.hash("blake2b-256", proof2.toArray())); + proof1.addMany(HexConverter.hexStringToByteArray("804a9786b7fa887adfd76874bc414a0f8854227ea92a4739e1bccefe699449460a808310dbebe8a5a91f783843a9916605b01caa58ea3ad8abce80684686d8605f96803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80")); + proofs.add(RlpString.create(proof1.toArray())); + + proof0.addMany(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961803440cfd059e558ed0fc83a7de22ffb14d56220e3ce446d897355c85a53401afe80")); + proof0.addMany(Crypto.hash("blake2b-256", proof1.toArray())); + proof0.addMany(HexConverter.hexStringToByteArray("80ede785a30002092980072d34de5a0cc038742d403a539e486808c64f1aeeac8c806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480c7b7787db289ac25daf82f320e421dc298a601a72cea3aa9784e5700251f169d800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de805839bffac46f817f1bebedade41179b15a7d92a7542b47cb484f1c866cde44f1807d33e56d4ed9f11cb8b9f268896373ac89efc53825d1e4b97e6ff61dfa19463e80660bfa0a74da0e496ac072e6b8611312e9febca06b53ad0a59389f22efd704be80b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80d651383d73b736e843be7a54db613711ee9ee632c7af16ce2ad548ec004d22bb8062e7f9dbfb626f0f424f63b66346221223d8aaf87a6e126f49617c1d0d967d9e80bf27ea2cbcca83c65622797c4e923d04fad6e7bdd603cc19648f362c7071316780913f5f3a6fa129f8706219077741eda1cdd4b834c5c7e529270858a1324a4cf080879382ecb9b6b8a1d0b0822b9c817182231b2df2039811bb54f2e87d6641f756")); + proofs.add(RlpString.create(proof0.toArray())); + } + + public List getProof() { + return this.proofs; + } + + public byte[] getRoot() { + return Crypto.hash("blake2b-256", ((RlpString) proofs.get(4)).getBytes()); + } + + public byte[] getEventKey() { + return this.eventKey; + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockUpdate.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockUpdate.java new file mode 100644 index 00000000..599b9f20 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockUpdate.java @@ -0,0 +1,122 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +import score.ObjectReader; +import score.util.Crypto; + +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class BlockUpdate { + private final byte[] hash; + private final byte[] parentHash; + private final long number; + private final byte[] stateRoot; + private final byte[] extrinsicsRoot; + private final byte[] digest; + private final byte[] serializedHeader; + private final List vote = new ArrayList(5); + private final BigInteger round; + private final BigInteger setId; + + public BlockUpdate(byte[] parentHash, long number, byte[] stateRoot, BigInteger round, BigInteger setId) { + ByteSliceOutput serializedHeaderOutput = new ByteSliceOutput(32 + 4 + 32 + 32 + 1); + this.parentHash = parentHash; + serializedHeaderOutput.addMany(parentHash); + this.number = number; + ScaleWriter.writeCompactUint(serializedHeaderOutput, (int) number); + this.stateRoot = stateRoot; + serializedHeaderOutput.addMany(stateRoot); + + this.extrinsicsRoot = Crypto.hash("blake2b-256", "abc".getBytes()); + serializedHeaderOutput.addMany(extrinsicsRoot); + this.digest = HexConverter.hexStringToByteArray("080642414245b50103010000009e08cd0f000000006290af7c4a31713b4b36280943a39d8179d36e765c0614176c7c6560e515466197827f75f8c6a0c1c655dde4074710f976c86c2f07766e57063afc07efaaef01aedae58676a262cc4f7264836a17ce8f336ee49865530afbdc560d1d131c050905424142450101fcbc268c71e173ccf1cf8b87b17bcb56914eadb31b71bdd70232ca4bea023f3e501dff7a902c6485b24f7426ad941f7cc086690817d5de0183688923104d1b8d"); + serializedHeaderOutput.addMany(this.digest); + this.hash = Crypto.hash("blake2b-256", serializedHeaderOutput.toArray()); + this.serializedHeader = serializedHeaderOutput.toArray(); + + this.round = round; + this.setId = setId; + } + + public static byte[] voteMessage(byte[] targetHash, long targetNumber, BigInteger round, BigInteger setId) { + ByteSliceOutput serializedSignMessageOutput = new ByteSliceOutput(53); + serializedSignMessageOutput.add((byte) 0x01); + serializedSignMessageOutput.addMany(targetHash); + ScaleWriter.writeU32(serializedSignMessageOutput, (int) targetNumber); + ScaleWriter.writeU64(serializedSignMessageOutput, round); + ScaleWriter.writeU64(serializedSignMessageOutput, setId); + return serializedSignMessageOutput.toArray(); + } + + public void vote(byte[] publicKey, byte[] secretKey, BigInteger round, BigInteger setId) { + this.vote.add(new BlockVote(voteMessage(this.hash, this.number, round, setId), publicKey, secretKey)); + } + + public byte[] encode() { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + List votes = new ArrayList(2); + votes.add(RlpString.create(voteMessage(this.hash, this.number, this.round, this.setId))); + List validatorSignatures = new ArrayList(2); + for (BlockVote signature: this.vote) { + validatorSignatures.add(RlpString.create(signature.encode())); + } + votes.add(new RlpList(validatorSignatures)); + + blockUpdate.add(RlpString.create(RlpEncoder.encode(new RlpList(votes)))); + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] encodeWithoutVote() { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + blockUpdate.add(RlpString.create("")); // empty votes + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] encodeWithVoteMessage(byte[] voteMessage) { + List blockUpdate = new ArrayList(2); + blockUpdate.add(RlpString.create(this.serializedHeader)); + + List votes = new ArrayList(2); + votes.add(RlpString.create(voteMessage)); + List validatorSignatures = new ArrayList(2); + for (BlockVote signature: this.vote) { + validatorSignatures.add(RlpString.create(signature.encode())); + } + votes.add(new RlpList(validatorSignatures)); + + blockUpdate.add(RlpString.create(RlpEncoder.encode(new RlpList(votes)))); + return RlpEncoder.encode(new RlpList(blockUpdate)); + } + + public byte[] getEncodedHeader() { + return this.serializedHeader; + } + + public byte[] getHash() { + return this.hash; + } + + public byte[] getParentHash() { + return this.parentHash; + } + + public long getNumber() { + return this.number; + } + + public byte[] getStateRoot() { + return this.stateRoot; + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java new file mode 100644 index 00000000..8d5e0167 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/BlockVote.java @@ -0,0 +1,37 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; +import java.util.List; + +import score.util.Crypto; +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class BlockVote { + private final byte[] signature; + private final byte[] validator; + + public BlockVote(byte[] voteMessage, byte[] publicKey, byte[] secretKey) { + this.validator = publicKey; + this.signature = Crypto.sign("ed25519", voteMessage, secretKey); + } + + public byte[] getSignature() { + return this.signature; + } + + public byte[] getValidator() { + return this.validator; + } + + public byte[] encode() { + List validatorSignature = new ArrayList(2); + validatorSignature.add(RlpString.create(this.signature)); + validatorSignature.add(RlpString.create(this.validator)); + return RlpEncoder.encode(new RlpList(validatorSignature)); + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java new file mode 100644 index 00000000..b1763adb --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ByteSliceOutput.java @@ -0,0 +1,79 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; + +import score.Context; + +public class ByteSliceOutput { + private byte[] arr; + private int size; + private int allocationSize; + + public ByteSliceOutput(int initialSize) { + this.arr = new byte[initialSize]; + this.allocationSize = initialSize; + this.size = 0; + } + + public int size() { + return this.size; + } + + public byte[] toArray() { + if (this.size < this.allocationSize) { + byte[] original = new byte[this.size]; + this.copy(this.arr, original, 0, size); + return original; + } else { + return this.arr; + } + } + + public void add(byte b) { + if (this.size == this.allocationSize) { + this.setSize(this.allocationSize*2); + } + + this.arr[size] = b; + this.size++; + } + + public void add(int b) { + this.add( (byte) b); + } + + public void set(int idx, byte b) { + if (0 <= idx && idx < this.size) { + this.arr[idx] = b; + } else { + throw new AssertionError("idx is out of range, use add instead"); + } + } + + public void addMany(byte[] bs) { + for(byte b : bs) + this.add(b); + } + + public byte get(int idx) { + if (0 <= idx && idx < this.size) { + return this.arr[idx]; + } else { + throw new AssertionError("root idx is out of range"); + } + } + + public void setSize(int size) { + int copySize = size > this.size ? this.size : size; + byte[] newArr = new byte[size]; + copy(this.arr, newArr, 0, copySize); + this.arr = newArr; + this.allocationSize = size; + } + + private void copy(byte[] src, byte[] dst, int from, int to) { + for(int i = from; i < to; i++) { + dst[i] = src[i]; + } + } +} diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java new file mode 100644 index 00000000..27b4c3c1 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ErrorCode.java @@ -0,0 +1,19 @@ +package foundation.icon.test.cases; + +public class ErrorCode { + public static final int BMV_ERROR = 25; + public static final int INVALID_MPT = 26; + public static final int INVALID_VOTES = 27; + public static final int INVALID_SEQUENCE = 28; + public static final int INVALID_BLOCK_UPDATE = 29; + public static final int INVALID_BLOCK_PROOF = 30; + public static final int INVALID_BLOCK_WITNESS = 31; + public static final int INVALID_SEQUENCE_HIGHER = 32; + public static final int INVALID_BLOCK_UPDATE_HEIGHT_HIGHER = 33; + public static final int INVALID_BLOCK_UPDATE_HEIGHT_LOWER = 34; + public static final int INVALID_BLOCK_PROOF_HEIGHT_HIGHER = 35; + public static final int INVALID_BLOCK_WITNESS_OLD = 36; + public static final int DECODE_ERROR = 37; + public static final int NOT_ACCEPTED_FROM_NETWORK_ERROR=38; + public static final int NOT_ACCEPTED_BMC_ADDR_ERROR=39; +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java new file mode 100644 index 00000000..872187a0 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/HexConverter.java @@ -0,0 +1,29 @@ +package foundation.icon.test.cases; + +import java.util.Arrays; + +import score.Context; + +public class HexConverter { + private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java new file mode 100644 index 00000000..95bae0b0 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/NewAuthoritiesProof.java @@ -0,0 +1,73 @@ +package foundation.icon.test.cases; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import scorex.util.ArrayList; + +import score.util.Crypto; +import scorex.util.ArrayList; + +import org.web3j.rlp.RlpEncoder; +import org.web3j.rlp.RlpString; +import org.web3j.rlp.RlpList; +import org.web3j.rlp.RlpType; + +public class NewAuthoritiesProof { + private final byte[] eventKey = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + private final List newValidators; + private final List proofs = new ArrayList(5); + + public NewAuthoritiesProof(List newValidators) { + this.newValidators = newValidators; + ByteSliceOutput proof0 = new ByteSliceOutput(100); + ByteSliceOutput proof1 = new ByteSliceOutput(100); + ByteSliceOutput proof2 = new ByteSliceOutput(100); + ByteSliceOutput proof3 = new ByteSliceOutput(100); + ByteSliceOutput eventLeafNode = new ByteSliceOutput(100); + + eventLeafNode.addMany(HexConverter.hexStringToByteArray("5ed41e5e16056765bc8461851072c9d7")); // node header + ScaleWriter.writeCompactUint(eventLeafNode, 31 + 3 + newValidators.size()*(32 + 8) + 1 + (newValidators.size() > 63 ? 2: 1)); + eventLeafNode.addMany(HexConverter.hexStringToByteArray("10020c010002090037080000000000000000000020df0e0b00000000020000")); // other events + eventLeafNode.addMany(HexConverter.hexStringToByteArray("010e00")); // new authorities event phase, index + ScaleWriter.writeCompactUint(eventLeafNode, newValidators.size()); + for (byte[] validator: newValidators ) { + eventLeafNode.addMany(validator); + ScaleWriter.writeU64(eventLeafNode, BigInteger.valueOf(1)); + } + eventLeafNode.addMany(HexConverter.hexStringToByteArray("00")); // empty topic + proofs.add(RlpString.create(eventLeafNode.toArray())); + + proof3.addMany(HexConverter.hexStringToByteArray("80810480")); + proof3.addMany(Crypto.hash("blake2b-256", eventLeafNode.toArray())); + proof3.addMany(HexConverter.hexStringToByteArray("545e8d434d6125b40443fe11fd292d13a4100300000080985a2c32d72399aa5d04da503ead1ff1a15007afe9b119dec3aa53528d9948c9")); + proofs.add(RlpString.create(proof3.toArray())); + + proof2.addMany(HexConverter.hexStringToByteArray("9eaa394eea5630e07c48ae0c9558cef7299f8043580dd17bfb375845229c0dd74fc5f9be81d5f4cf569c3ee845e3acf5271556804afcd87f2329d61d655e551a438a2720a92951e383690f5a623d245cfb5ef4444c5f0684a022a34dd8bfa2baaf44f172b710040180")); + proof2.addMany(Crypto.hash("blake2b-256", proof3.toArray())); + proof2.addMany(HexConverter.hexStringToByteArray("80fbf36a2eb1b78c879a6ee9b08a04f9f341b284ca2bec0a74f4f39b1df1386ace80586079736657bdf54edfb3d4aab0c8221717c3e1e1ad5e6beeaa7c64dbbe515e80b5a6f3ec81bb61e573624e88e3d279154a7183905d05c656f8744d5788766f614c5f021aab032aaa6e946ca50ad39ab666030401705f09cce9c888469bb1a0dceaa129672ef8287820706f6c6b61646f74")); + proofs.add(RlpString.create(proof2.toArray())); + + proof1.addMany(HexConverter.hexStringToByteArray("80499c804db64d4c6dd7655a3ac8bc482884de58452960029ea5b37c03ec11e678ec9a7c801ba7abc7e25fb80c99c4bff68d0688c9af3e1ff8cfb2bd5a1754ae1b86f94d1c80")); + proof1.addMany(Crypto.hash("blake2b-256", proof2.toArray())); + proof1.addMany(HexConverter.hexStringToByteArray("804a9786b7fa887adfd76874bc414a0f8854227ea92a4739e1bccefe699449460a808310dbebe8a5a91f783843a9916605b01caa58ea3ad8abce80684686d8605f96803f6f047a1cb4d78714bcb64168513cca61d3120e8c0f5c30affb7393620f84b480623848e735dd707e95d569e06cbca4c4437a925d0887bfd1ce67b035d44aec80")); + proofs.add(RlpString.create(proof1.toArray())); + + proof0.addMany(HexConverter.hexStringToByteArray("80ffff801ee4115c4d5894ff9b5e6bb83611e2a8a8503482fe35a3f086d500fd1f111961803440cfd059e558ed0fc83a7de22ffb14d56220e3ce446d897355c85a53401afe80")); + proof0.addMany(Crypto.hash("blake2b-256", proof1.toArray())); + proof0.addMany(HexConverter.hexStringToByteArray("80ede785a30002092980072d34de5a0cc038742d403a539e486808c64f1aeeac8c806af5c4f477504b627b1af9c106960aefe0e0a0632386b542897157486e0145a480c7b7787db289ac25daf82f320e421dc298a601a72cea3aa9784e5700251f169d800eb754c27d6302344f80fc4f785eae09c7c6acf58ee0ebddbd2f1755eb37a7de805839bffac46f817f1bebedade41179b15a7d92a7542b47cb484f1c866cde44f1807d33e56d4ed9f11cb8b9f268896373ac89efc53825d1e4b97e6ff61dfa19463e80660bfa0a74da0e496ac072e6b8611312e9febca06b53ad0a59389f22efd704be80b54561a0ca6f56c484ed434f3cdd323985e74088f456e6c32ed47bd65dd7269f80d651383d73b736e843be7a54db613711ee9ee632c7af16ce2ad548ec004d22bb8062e7f9dbfb626f0f424f63b66346221223d8aaf87a6e126f49617c1d0d967d9e80bf27ea2cbcca83c65622797c4e923d04fad6e7bdd603cc19648f362c7071316780913f5f3a6fa129f8706219077741eda1cdd4b834c5c7e529270858a1324a4cf080879382ecb9b6b8a1d0b0822b9c817182231b2df2039811bb54f2e87d6641f756")); + proofs.add(RlpString.create(proof0.toArray())); + } + + public List getProof() { + return this.proofs; + } + + public byte[] getRoot() { + return Crypto.hash("blake2b-256", ((RlpString) proofs.get(4)).getBytes()); + } + + public byte[] getEventKey() { + return this.eventKey; + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java new file mode 100644 index 00000000..0a44ef10 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/lib/ScaleWriter.java @@ -0,0 +1,58 @@ +package foundation.icon.test.cases; + +import score.Context; + +import java.math.BigInteger; +import java.util.Arrays; + +public class ScaleWriter { + public static void writeCompactUint(ByteSliceOutput output, int compactNumber) { + int compactSize = ScaleWriter.compactUintSize(compactNumber); + int compact = (compactNumber << 2) + compactSize - 1; + while (compactSize > 0) { + byte b = (byte) (compact & 0xff); + output.add(b); + compact >>= 8; + compactSize--; + } + } + + public static int compactUintSize(int number) { + if (number < 0) { + throw new IllegalArgumentException("Negative numbers are not supported"); + } + if (number <= 0x3f) { + return 1; + } else if (number <= 0x3fff) { + return 2; + } else if (number <= 0x3fffffff) { + return 3; + } else { + return 4; + } + } + + public static void writeU32(ByteSliceOutput output, int value) { + if (value < 0) { + throw new IllegalArgumentException("Negative values are not supported: " + value); + } + output.add( (byte) (value & 0xff)); + output.add( (byte) ((value >> 8) & 0xff)); + output.add( (byte) ((value >> 16) & 0xff)); + output.add( (byte) ((value >> 24) & 0xff)); + } + + public static void writeU64(ByteSliceOutput output, BigInteger value) { + if (value.compareTo(BigInteger.ZERO) < 0) { + throw new IllegalArgumentException("Negative values are not supported: " + value); + } + output.add(value.and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(8).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(16).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(24).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(32).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(40).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(48).and(BigInteger.valueOf(255)).intValue()); + output.add(value.shiftRight(56).and(BigInteger.valueOf(255)).intValue()); + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/BMVScore.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/BMVScore.java new file mode 100644 index 00000000..600c69d8 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/BMVScore.java @@ -0,0 +1,162 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.cases.HexConverter; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionFailureException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; + +import static foundation.icon.test.Env.LOG; + +public class BMVScore extends Score { + public BMVScore(Score other) { + super(other); + } + + public static BMVScore mustDeploy( + TransactionHandler txHandler, + Wallet owner, + String bmc, + String net, + String encodedValidators, + long mtaOffset, + int mtaRootSize, + int mtaCacheSize, + boolean mtaIsAllowNewerWitness, + byte[] lastBlockHash, + Address eventDecoderAddress, + BigInteger currentSetId + ) throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "BMV"); + RpcObject params = new RpcObject.Builder() + .put("bmc", new RpcValue(bmc)) + .put("net", new RpcValue(net)) + .put("encodedValidators", new RpcValue(encodedValidators)) + .put("mtaOffset", new RpcValue(Long.toString(mtaOffset))) + .put("mtaRootSize", new RpcValue(Integer.toString(mtaRootSize))) + .put("mtaCacheSize", new RpcValue(Integer.toString(mtaCacheSize))) + .put("mtaIsAllowNewerWitness", new RpcValue(mtaIsAllowNewerWitness)) + .put("lastBlockHash", new RpcValue(HexConverter.bytesToHex(lastBlockHash))) + .put("eventDecoderAddress", new RpcValue(eventDecoderAddress)) + .put("currentSetId", new RpcValue(currentSetId.toString())) + .put("newAuthoritiesEvent", new RpcValue("0e00")) + .put("evmEventIndex", new RpcValue("2200")) + .build(); + Score score = txHandler.deploy(owner, getFilePath("sovereignChain"), params); + LOG.info("bmv score address = " + score.getAddress()); + LOG.infoExiting(); + return new BMVScore(score); + } + + public String mta() throws IOException { + return call("mta", null).asString(); + } + + public BigInteger mtaHeight() throws IOException { + return call("mtaHeight", null).asInteger(); + } + + public List mtaRoot() throws IOException { + List listRpcItem = call("mtaRoot", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public byte[] mtaLastBlockHash() throws IOException { + return call("mtaLastBlockHash", null).asByteArray(); + } + + public BigInteger mtaOffset() throws IOException { + return call("mtaOffset", null).asInteger(); + } + + public List mtaCaches() throws IOException { + List listRpcItem = call("mtaCaches", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public Address bmc() throws IOException { + return call("bmc", null).asAddress(); + } + + public Address eventDecoder() throws IOException { + return call("eventDecoder", null).asAddress(); + } + + public String netAddress() throws IOException { + return call("netAddress", null).asString(); + } + + public BigInteger lastHeight() throws IOException { + return call("lastHeight", null).asInteger(); + } + + public BigInteger setId() throws IOException { + return call("setId", null).asInteger(); + } + + public List validators() throws IOException { + List listRpcItem = call("validators", null).asArray().asList(); + List result = new ArrayList(listRpcItem.size()); + for (RpcItem rpcItem : listRpcItem) { + if (rpcItem.isNull()) { + result.add(null); + } else { + result.add(rpcItem.asByteArray()); + } + } + return result; + } + + public Bytes handleRelayMessage(Wallet wallet, String bmc, String prev, BigInteger seq, String msg) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("bmc", new RpcValue(bmc)) + .put("prev", new RpcValue(prev)) + .put("seq", new RpcValue(seq)) + .put("msg", new RpcValue(msg)) + .build(); + return invoke(wallet, "handleRelayMessage", params); + } +} diff --git a/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/EventDecoderScore.java b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/EventDecoderScore.java new file mode 100644 index 00000000..8e5b919d --- /dev/null +++ b/javascore/bmv/sovereignChain/src/intTest/java/foundation/icon/btp/score/EventDecoderScore.java @@ -0,0 +1,53 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.cases.HexConverter; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionFailureException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import scorex.util.ArrayList; + +import static foundation.icon.test.Env.LOG; + +public class EventDecoderScore extends Score { + public EventDecoderScore(Score other) { + super(other); + } + + public static EventDecoderScore mustDeploy( + TransactionHandler txHandler, + Wallet owner + ) throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "EventDecoder"); + Score score = txHandler.deploy(owner, getFilePath("EdgewareEventDecoderScore"), null); + LOG.info("eventDecoder score address = " + score.getAddress()); + LOG.infoExiting(); + return new EventDecoderScore(score); + } +} diff --git a/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/BMV.java b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/BMV.java new file mode 100644 index 00000000..b70a5f9b --- /dev/null +++ b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/BMV.java @@ -0,0 +1,339 @@ +package foundation.icon.btp.bmv.sovereignChain; + +import score.Address; +import score.Context; +import score.VarDB; +import score.ObjectReader; +import score.ByteArrayObjectWriter; + +import scorex.util.Base64; +import scorex.util.ArrayList; + +import score.annotation.External; +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.blockHeader.BlockHeader; +import foundation.icon.btp.lib.blockProof.BlockProof; +import foundation.icon.btp.lib.btpAddress.BTPAddress; +import foundation.icon.btp.lib.event.evmEvent.BTPMessageEvmEvent; +import foundation.icon.btp.bmv.sovereignChain.lib.Constant; +import foundation.icon.btp.bmv.sovereignChain.lib.blockUpdate.BlockUpdate; +import foundation.icon.btp.bmv.sovereignChain.lib.relayMessage.RelayMessage; +import foundation.icon.btp.lib.stateProof.StateProof; +import foundation.icon.btp.lib.BMVStatus; +import foundation.icon.btp.lib.BlockVerifyResult; +import foundation.icon.btp.lib.event.EVMLogEvent; +import foundation.icon.btp.lib.event.EventRecord; +import foundation.icon.btp.lib.event.NewAuthoritiesEvent; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import foundation.icon.btp.lib.mta.MTAStatus; +import foundation.icon.btp.lib.mta.SerializableMTA; + +import foundation.icon.btp.lib.utils.HexConverter; +import foundation.icon.btp.lib.validators.Validators; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class BMV implements IBMV { + private final VarDB
bmcDb = Context.newVarDB("bmc", Address.class); + private final VarDB netAddressDb = Context.newVarDB("netAddr", String.class); + private final VarDB lastHeightDb = Context.newVarDB("lastHeight", Long.class); + private final VarDB mtaDb = Context.newVarDB("mta", SerializableMTA.class); + private final VarDB validatorsDb = Context.newVarDB("validators", Validators.class); + private final VarDB
eventDecoderDb = Context.newVarDB("eventDecoder", Address.class); + private final VarDB setIdDb = Context.newVarDB("setId", BigInteger.class); + private final VarDB newAuthoritiesEventIndexDb = Context.newVarDB("newAuthoritiesEventIndex", byte[].class); + private final VarDB evmEventIndexDb = Context.newVarDB("evmEventIndex", byte[].class); + + public BMV( + Address bmc, + String net, + String encodedValidators, + long mtaOffset, + int mtaRootSize, + int mtaCacheSize, + boolean mtaIsAllowNewerWitness, + byte[] lastBlockHash, + Address eventDecoderAddress, + long currentSetId, + byte[] newAuthoritiesEventIndex, + byte[] evmEventIndex + ) { + this.bmcDb.set(bmc); + this.netAddressDb.set(net); + + byte[] serializedValidator = Base64.getUrlDecoder().decode(encodedValidators.getBytes()); + + ObjectReader r = Context.newByteArrayObjectReader("RLPn", serializedValidator); + r.beginList(); + List validators = new ArrayList(150); + while (r.hasNext()) { + byte[] v = r.readByteArray(); + validators.add(v); + } + r.end(); + + Validators validatorsObject = new Validators(validators); + this.validatorsDb.set(validatorsObject); + this.setIdDb.set(BigInteger.valueOf(currentSetId)); + + SerializableMTA mta = new SerializableMTA(0, mtaOffset, mtaRootSize, mtaCacheSize, mtaIsAllowNewerWitness, lastBlockHash, null, null); + this.lastHeightDb.set(mtaOffset); + this.mtaDb.set(mta); + + this.eventDecoderDb.set(eventDecoderAddress); + this.evmEventIndexDb.set(evmEventIndex); + this.newAuthoritiesEventIndexDb.set(newAuthoritiesEventIndex); + } + + /** + * get encode of merkle tree accumulator + */ + @External(readonly=true) + public String mta() { + ByteArrayObjectWriter w = Context.newByteArrayObjectWriter("RLPn"); + SerializableMTA.writeObject(w, this.mtaDb.get()); + return new String(Base64.getUrlEncoder().encode(w.toByteArray())); + } + + /** + * get current mta height + */ + @External(readonly=true) + public long mtaHeight() { + return this.mtaDb.get().height(); + } + + /** + * get current mta height + */ + @External(readonly=true) + public List mtaRoot() { + return this.mtaDb.get().getRootList(); + } + + /** + * get last block hash + */ + @External(readonly=true) + public byte[] mtaLastBlockHash() { + return this.mtaDb.get().lastBlockHash(); + } + + /** + * get current mta offset + */ + @External(readonly=true) + public long mtaOffset() { + return this.mtaDb.get().offset(); + } + + @External(readonly=true) + public List mtaCaches() { + return this.mtaDb.get().getCacheList(); + } + + /** + * handle verify message from BMC and return list of event message + */ + @External + public List handleRelayMessage(String bmc, String prev, BigInteger seq, String msg) { + BTPAddress currentAddress = BTPAddress.fromString(bmc); + BTPAddress prevAddress = BTPAddress.fromString(prev); + this.checkAccessible(currentAddress, prevAddress); + + byte[] serializedMsg; + try { + serializedMsg = Base64.getUrlDecoder().decode(msg.getBytes()); + } catch (Exception e) { + Context.revert(ErrorCode.DECODE_ERROR, "decode base64 msg error: " + e.toString()); + serializedMsg = null; + } + + RelayMessage relayMessage; + try { + relayMessage = RelayMessage.fromBytes(serializedMsg); + } catch (RelayMessageRLPException e) { + Context.revert(ErrorCode.DECODE_ERROR, "RelayMessage RLP decode error: " + e.getScope() + " " + e.getOriginalError()); + relayMessage = null; + } + + if (relayMessage.getBlockUpdate().size() == 0 && relayMessage.getBlockProof() == null) { + Context.revert(ErrorCode.BMV_ERROR, "invalid RelayMessage not exists BlockUpdate or BlockProof"); + } + + BlockVerifyResult blockVerifyResult = this.verifyBlock(relayMessage); + + BigInteger nextSeq = seq.add(BigInteger.valueOf(1)); + + // list of bytes message return to bmc + List bmcMsgs = new ArrayList(5); + byte[] newAuthoritiesEventIndex = this.newAuthoritiesEventIndexDb.get(); + byte[] evmEventIndex = this.evmEventIndexDb.get(); + for (StateProof stateProof : relayMessage.getStateProof()) { + byte[] encodedStorage = stateProof.prove(blockVerifyResult.stateRoot); + if (Arrays.equals(stateProof.getKey(), Constant.EventStorageKey)) { + List> eventRecords = (List>) Context.call(this.eventDecoderDb.get(), "decodeEvent", encodedStorage); + for (Map rawEventRecord: eventRecords) { + // update new validator set + EventRecord eventRecord = new EventRecord(rawEventRecord); + if (eventRecord.getEventIndex()[0] == newAuthoritiesEventIndex[0] && eventRecord.getEventIndex()[1] == newAuthoritiesEventIndex[1]) { + NewAuthoritiesEvent newAuthoritiesEvent = new NewAuthoritiesEvent(eventRecord.getEventData()); + List newValidators = newAuthoritiesEvent.getValidators(); + Validators newValidatorsObject = new Validators(newValidators); + this.validatorsDb.set(newValidatorsObject); + this.setIdDb.set(this.setIdDb.get().add(BigInteger.valueOf(1))); + continue; + } + + // filter evm log of BMC + if (eventRecord.getEventIndex()[0] == evmEventIndex[0] && eventRecord.getEventIndex()[1] == evmEventIndex[1]) { + EVMLogEvent evmLogEvent = new EVMLogEvent(eventRecord.getEventData()); + if (evmLogEvent.getEvmTopics().size() == 0 || !Arrays.equals(evmLogEvent.getEvmTopics().get(0), Constant.MessageEventTopic)) { + continue; + } + + if (!Arrays.equals(evmLogEvent.getAddress(), HexConverter.hexStringToByteArray(prevAddress.getAddress()))) { + continue; + } + + BTPMessageEvmEvent btpMessageEvmEvent = new BTPMessageEvmEvent(evmLogEvent.getEvmEventData()); + + if (btpMessageEvmEvent.getNextBmc().equals(bmc)) { + int seqCompareRes = btpMessageEvmEvent.getSeq().compareTo(nextSeq); + if (seqCompareRes > 0) { + Context.revert(ErrorCode.INVALID_SEQUENCE_HIGHER, "invalid sequence: " + btpMessageEvmEvent.getSeq() + "; expected: " + nextSeq); + } else if (seqCompareRes < 0) { + Context.revert(ErrorCode.INVALID_SEQUENCE, "invalid sequence: " + btpMessageEvmEvent.getSeq() + "; expected: " + nextSeq); + } + bmcMsgs.add(btpMessageEvmEvent.getMsg()); + } + nextSeq = seq.add(BigInteger.valueOf(1)); + } + } + } + } + + if (bmcMsgs.size() > 0) { + this.lastHeightDb.set(blockVerifyResult.lastHeight); + } + + return bmcMsgs; + } + + /** + * get address of bmc + */ + @External(readonly=true) + public Address bmc() { + return this.bmcDb.get(); + } + + /** + * get address of eventDecoder + */ + @External(readonly=true) + public Address eventDecoder() { + return this.eventDecoderDb.get(); + } + + /** + * net address that bmv verify + */ + @External(readonly=true) + public String netAddress() { + return this.netAddressDb.get(); + } + + /** + * last height + */ + @External(readonly=true) + public long lastHeight() { + return this.lastHeightDb.get(); + } + + /** + * list public keys of validators + */ + @External(readonly=true) + public List validators() { + Validators v = this.validatorsDb.get(); + return v.get(); + } + + /** + * current set id + */ + @External(readonly=true) + public BigInteger setId() { + return this.setIdDb.get(); + } + + /** + * get status of BMV + */ + @External(readonly=true) + public BMVStatus getStatus() { + SerializableMTA mta = this.mtaDb.get(); + MTAStatus mtaStatus = mta.getStatus(); + long lastHeight = this.lastHeightDb.get(); + return new BMVStatus(mtaStatus.height, mtaStatus.offset, lastHeight); + } + + private void checkAccessible(BTPAddress currentAddress, BTPAddress prevAddress) { + if (!this.netAddressDb.get().equals(prevAddress.getNet())) { + Context.revert(ErrorCode.NOT_ACCEPTED_FROM_NETWORK_ERROR, "not acceptable from"); + } + if (!Context.getCaller().equals(this.bmcDb.get())) { + Context.revert(ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR, "not acceptable bmc"); + } + if (!Address.fromString(currentAddress.getAddress()).equals(this.bmcDb.get())) { // actualy don't need to check it + Context.revert(ErrorCode.NOT_ACCEPTED_BMC_ADDR_ERROR, "not acceptable bmc"); + } + } + + private BlockVerifyResult verifyBlock(RelayMessage relayMessage) { + byte[] stateRoot = null; + long lastHeight = 0; + SerializableMTA currentMTA = this.mtaDb.get(); + Validators currenValidators = this.validatorsDb.get(); + + List blockUpdates = relayMessage.getBlockUpdate(); + for(int i = 0; i < blockUpdates.size(); i++) { + long nextHeight = currentMTA.height() + 1; + BlockHeader currentBlockHeader = blockUpdates.get(i).getBlockHeader(); + if (nextHeight == currentBlockHeader.getNumber()) { + if (!Arrays.equals(currentBlockHeader.getParentHash(), currentMTA.lastBlockHash())) { + Context.revert(ErrorCode.BMV_ERROR, "parent block hash does not match, parent: " + HexConverter.bytesToHex(currentBlockHeader.getParentHash()) + " current: " + HexConverter.bytesToHex(currentMTA.lastBlockHash())); + } + + if (i == blockUpdates.size() - 1) { // only verify signatures of last updating block + blockUpdates.get(i).verify(currenValidators.get(), this.setIdDb.get()); + } + + currentMTA.add(currentBlockHeader.getHash()); + lastHeight = nextHeight; + stateRoot = currentBlockHeader.getStateRoot(); + } else if (nextHeight < currentBlockHeader.getNumber()) { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_HIGHER, "invalid blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } else { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE_HEIGHT_LOWER, "invalid blockUpdate height: " + currentBlockHeader.getNumber() + "; expected: " + nextHeight); + } + } + + BlockProof blockProof = relayMessage.getBlockProof(); + if (blockProof != null) { + blockProof.verify(currentMTA); + lastHeight = blockProof.getBlockHeader().getNumber(); + stateRoot = blockProof.getBlockHeader().getStateRoot(); + } + + this.mtaDb.set(currentMTA); + + return new BlockVerifyResult(stateRoot, lastHeight); + } +} diff --git a/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/IBMV.java b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/IBMV.java new file mode 100644 index 00000000..97082018 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/IBMV.java @@ -0,0 +1,80 @@ +package foundation.icon.btp.bmv.sovereignChain; + +import foundation.icon.btp.lib.BMVStatus; + +import score.Address; + +import java.util.List; +import java.math.BigInteger; + +public interface IBMV { + /** + * get encode of merkle tree accumulator + */ + String mta(); + + /** + * get current mta height + */ + long mtaHeight(); + + /** + * get current mta height + */ + List mtaRoot(); + + /** + * get last block hash + */ + byte[] mtaLastBlockHash(); + + /** + * get current mta offset + */ + long mtaOffset(); + + /** + * get current mta caches + */ + List mtaCaches(); + + /** + * get address of bmc + */ + Address bmc(); + + /** + * net address that bmv verify + */ + String netAddress(); + + /** + * list addresses of validator + */ + List validators(); + + /** + * get status of BMV + */ + BMVStatus getStatus(); + + /** + * get address of eventDecoder + */ + Address eventDecoder(); + + /** + * last height + */ + long lastHeight(); + + /** + * current set id + */ + BigInteger setId(); + + /** + * handle verify message from BMC and return list of event message + */ + List handleRelayMessage(String bmc, String prev, BigInteger seq, String msg); +} diff --git a/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/Constant.java b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/Constant.java new file mode 100644 index 00000000..8189c897 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/Constant.java @@ -0,0 +1,8 @@ +package foundation.icon.btp.bmv.sovereignChain.lib; + +import foundation.icon.btp.lib.utils.HexConverter; + +public class Constant { + public static final byte[] EventStorageKey = HexConverter.hexStringToByteArray("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"); + public static final byte[] MessageEventTopic = HexConverter.hexStringToByteArray("37be353f216cf7e33639101fd610c542e6a0c0109173fa1c1d8b04d34edb7c1b"); // kekak256("Message(string,uint256,bytes)"); +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/blockUpdate/BlockUpdate.java b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/blockUpdate/BlockUpdate.java new file mode 100644 index 00000000..ef0d7801 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/blockUpdate/BlockUpdate.java @@ -0,0 +1,58 @@ +package foundation.icon.btp.bmv.sovereignChain.lib.blockUpdate; + +import foundation.icon.btp.lib.ErrorCode; +import foundation.icon.btp.lib.blockHeader.BlockHeader; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; +import foundation.icon.btp.lib.votes.Votes; +import score.Context; +import score.ObjectReader; + +import java.math.BigInteger; +import java.util.List; + +public class BlockUpdate { + private final BlockHeader blockHeader; + private final Votes votes; + + public BlockUpdate(byte[] serialized) throws RelayMessageRLPException { + ObjectReader rlpReader = Context.newByteArrayObjectReader("RLPn", serialized); + rlpReader.beginList(); + byte[] encodedHeader = rlpReader.readByteArray(); + if (encodedHeader != null && encodedHeader.length > 0) { + this.blockHeader = new BlockHeader(encodedHeader); + } else { + this.blockHeader = null; + } + byte[] encodedVotes = rlpReader.readNullable(byte[].class); + if (encodedVotes != null && encodedVotes.length > 0) { + this.votes = Votes.fromBytes(encodedVotes); + } else { + this.votes = null; + } + rlpReader.end(); + } + + public BlockHeader getBlockHeader() { + return this.blockHeader; + } + + public Votes getVotes() { + return this.votes; + } + + public void verify(List validators, BigInteger currentSetId) { + if (this.votes == null) { + Context.revert(ErrorCode.INVALID_BLOCK_UPDATE, "not exists votes"); + } + + this.votes.verify(this.blockHeader.getNumber(), this.blockHeader.getHash(), validators, currentSetId); + } + + public static BlockUpdate fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new BlockUpdate(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("BlockUpdate ", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/relayMessage/RelayMessage.java b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/relayMessage/RelayMessage.java new file mode 100644 index 00000000..0b472d30 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/main/java/foundation/icon/btp/bmv/sovereignChain/lib/relayMessage/RelayMessage.java @@ -0,0 +1,71 @@ +package foundation.icon.btp.bmv.sovereignChain.lib.relayMessage; + +import foundation.icon.btp.lib.blockProof.BlockProof; +import foundation.icon.btp.bmv.sovereignChain.lib.blockUpdate.BlockUpdate; +import foundation.icon.btp.lib.stateProof.StateProof; +import foundation.icon.btp.lib.exception.RelayMessageRLPException; + +import java.util.List; + +import scorex.util.ArrayList; + +import score.Context; +import score.ObjectReader; + +public class RelayMessage { + private final List blockUpdates = new ArrayList(16); + private BlockProof blockProof; + private final List stateProofs = new ArrayList(10); + + public RelayMessage(byte[] serialized) throws RelayMessageRLPException { + ObjectReader r = Context.newByteArrayObjectReader("RLPn", serialized); + r.beginList(); + + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedBlockUpdate = r.readByteArray(); + blockUpdates.add(BlockUpdate.fromBytes(encodedBlockUpdate)); + } + r.end(); + } + + if (r.hasNext()) { + byte[] blockProofEncoded = r.readNullable(byte[].class); + this.blockProof = (blockProofEncoded != null && blockProofEncoded.length > 0) ? BlockProof.fromBytes(blockProofEncoded) : null; + } else { + this.blockProof = null; + } + + if (r.hasNext()) { + r.beginList(); + while (r.hasNext()) { + byte[] encodedStateProof = r.readByteArray(); + this.stateProofs.add(StateProof.fromBytes(encodedStateProof)); + } + r.end(); + } + + r.end(); + } + + public List getBlockUpdate() { + return this.blockUpdates; + } + + public BlockProof getBlockProof() { + return this.blockProof; + } + + public List getStateProof() { + return this.stateProofs; + } + + public static RelayMessage fromBytes(byte[] serialized) throws RelayMessageRLPException { + try { + return new RelayMessage(serialized); + } catch (IllegalStateException | UnsupportedOperationException | IllegalArgumentException e) { + throw new RelayMessageRLPException("RelayMessage", e.toString()); + } + } +} \ No newline at end of file diff --git a/javascore/bmv/sovereignChain/src/test/java/foundation/icon/btp/bmv/BMVTest.java b/javascore/bmv/sovereignChain/src/test/java/foundation/icon/btp/bmv/BMVTest.java new file mode 100644 index 00000000..7733ac67 --- /dev/null +++ b/javascore/bmv/sovereignChain/src/test/java/foundation/icon/btp/bmv/BMVTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.bmv; + +import com.iconloop.testsvc.Account; +import com.iconloop.testsvc.Score; +import com.iconloop.testsvc.ServiceManager; +import com.iconloop.testsvc.TestBase; + +class BMVTest extends TestBase { + private static final ServiceManager sm = getServiceManager(); + private static final Account owner = sm.createAccount(); + + // TODO deploy contract bmc + private static final Account bmc = sm.createAccount(); + private static final String network = "sovereignchain"; + // TODO create list edgeware validator address + private static final String validators = "hxc00a6d2d1e9ee0686704e0b6eec75d0f2c095b39,hx3e9be7c57c769adb06dd0b4943aab9222c30d825"; + private static Score bmvScore; + + // @BeforeAll + // public static void setup() throws Exception { + // bmvScore = sm.deploy(owner, BMV.class, bmc.getAddress(), network, validators, 10, 10, 10, true); + // } + + // @Test + // void mta() { + // // TODO implement this + // assertEquals("pending", bmvScore.call("mta")); + // } + + // @Test + // void getBmcAddress() { + // assertEquals(bmc.getAddress(), bmvScore.call("bmc")); + // } + + // @Test + // void getNetAddress() { + // assertEquals(network, bmvScore.call("netAddress")); + // } + + // @Test + // void getListValidators() { + // assertEquals(validators, bmvScore.call("validators")); + // } + + // // TODO test getStatus readonly + // @Test + // void getStatus() { + // long a = 0; + // BMVStatus status = (BMVStatus) bmvScore.call("getStatus"); + // // assertEquals(status.height, 11); + // } +} diff --git a/javascore/bmv/testinteg/build.gradle b/javascore/bmv/testinteg/build.gradle new file mode 100644 index 00000000..b9e8aac2 --- /dev/null +++ b/javascore/bmv/testinteg/build.gradle @@ -0,0 +1,15 @@ +apply plugin: 'java-library' + +optimizedJar.enabled = false + +dependencies { + implementation 'foundation.icon:icon-sdk:2.0.0' + implementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' +} + +jar { + doFirst { + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } + } + exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA' +} diff --git a/javascore/bmv/testinteg/conf/env.props b/javascore/bmv/testinteg/conf/env.props new file mode 100644 index 00000000..80aebffc --- /dev/null +++ b/javascore/bmv/testinteg/conf/env.props @@ -0,0 +1,5 @@ +node.url=http://localhost:9082 + +chain.nid=0x3 +chain.godWallet=godWallet.json +chain.godPassword=gochain diff --git a/javascore/bmv/testinteg/conf/godWallet.json b/javascore/bmv/testinteg/conf/godWallet.json new file mode 100644 index 00000000..2a611aa3 --- /dev/null +++ b/javascore/bmv/testinteg/conf/godWallet.json @@ -0,0 +1,22 @@ +{ + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } +} diff --git a/javascore/bmv/testinteg/library-2.0.0.jar b/javascore/bmv/testinteg/library-2.0.0.jar new file mode 100644 index 00000000..c5d59fdd Binary files /dev/null and b/javascore/bmv/testinteg/library-2.0.0.jar differ diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Constants.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Constants.java new file mode 100644 index 00000000..30058d13 --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Constants.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.data.Address; + +import java.math.BigInteger; + +public class Constants { + public static final BigInteger STATUS_SUCCESS = BigInteger.ONE; + public static final BigInteger STATUS_FAILURE = BigInteger.ZERO; + + public static final BigInteger DEFAULT_STEPS = BigInteger.valueOf(100000000); + public static final long DEFAULT_WAITING_TIME = 7000; + + public static final Address ZERO_ADDRESS = + new Address("cx0000000000000000000000000000000000000000"); + public static final Address TREASURY_ADDRESS = + new Address("hx1000000000000000000000000000000000000000"); + + public static final String CONTENT_TYPE_PYTHON = "application/zip"; + public static final String CONTENT_TYPE_JAVA = "application/java"; +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Env.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Env.java new file mode 100644 index 00000000..3436e02b --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Env.java @@ -0,0 +1,103 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.crypto.KeystoreException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Properties; + +public class Env { + public static final Log LOG = Log.getGlobal(); + private static Chain chain; + + static { + String envFile = System.getProperty("env.props", "conf/env.props"); + Properties props = new Properties(); + try { + LOG.info("Using env.props: " + envFile); + FileInputStream fis = new FileInputStream(envFile); + props.load(fis); + fis.close(); + } catch (IOException e) { + System.err.printf("'%s' does not exist\n", envFile); + throw new IllegalArgumentException(e.getMessage()); + } + String confPath = Path.of(envFile).getParent().toString() + "/"; + readProperties(props, confPath); + } + + private static void readProperties(Properties props, String confPath) { + String chainName = "chain"; + String nid = props.getProperty(chainName + ".nid"); + if (nid == null) { + throw new IllegalArgumentException("nid not found"); + } + String godWalletPath = confPath + props.getProperty(chainName + ".godWallet"); + String godPassword = props.getProperty(chainName + ".godPassword"); + KeyWallet godWallet; + try { + godWallet = readWalletFromFile(godWalletPath, godPassword); + } catch (IOException e) { + throw new IllegalArgumentException(e.getMessage()); + } + String nodeName = "node"; + String url = props.getProperty(nodeName + ".url"); + if (url == null) { + throw new IllegalArgumentException("node url not found"); + } + chain = new Chain(Integer.parseInt(nid.substring(2), 16), godWallet, url); + } + + private static KeyWallet readWalletFromFile(String path, String password) throws IOException { + try { + File file = new File(path); + return KeyWallet.load(password, file); + } catch (KeystoreException e) { + e.printStackTrace(); + throw new IOException("Key load failed!"); + } + } + + public static Chain getDefaultChain() { + if (chain == null) { + throw new AssertionError("Chain not found"); + } + return chain; + } + + public static class Chain { + public final int networkId; + public final Wallet godWallet; + private final String nodeUrl; + + public Chain(int networkId, Wallet godWallet, String url) { + this.networkId = networkId; + this.godWallet = godWallet; + this.nodeUrl = url; + } + + public String getEndpointURL() { + return this.nodeUrl; + } + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Log.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Log.java new file mode 100644 index 00000000..b48cc6da --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/Log.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import java.util.EmptyStackException; +import java.util.Stack; + +public class Log { + private static final String[] PREFIX_LEVELS = {null, "[S]", "[W]", null, null}; + private static final String PREFIX_STEP_IN = "--> "; + private static final String PREFIX_STEP_OUT = "<-- "; + private static final String DEPTH_STRING = " "; + + private static final int LEVEL_START = 0; + public static final int LEVEL_NONE = LEVEL_START; + public static final int LEVEL_SEVERE = LEVEL_NONE + 1; + public static final int LEVEL_WARNING = LEVEL_SEVERE + 1; + public static final int LEVEL_INFO = LEVEL_WARNING + 1; + public static final int LEVEL_DEBUG = LEVEL_INFO + 1; + private static final int LEVEL_END = LEVEL_DEBUG; + + private int level = LEVEL_INFO; + private Stack frames = new Stack<>(); + + public static Log getGlobal() { + return new Log(); + } + + public void setLevel(int newLevel) { + if (newLevel >= LEVEL_START && newLevel <= LEVEL_END) { + level = newLevel; + } + } + + private boolean isLoggable(int level) { + return this.level >= level && level > LEVEL_START; + } + + public void info(String msg) { + log(LEVEL_INFO, msg); + } + + public void warning(String msg) { + log(LEVEL_WARNING, msg); + } + + public void severe(String msg) { + log(LEVEL_SEVERE, msg); + } + + public void infoEntering(String taskName, String msg) { + if (taskName == null) { + taskName = ""; + } + if (msg == null) { + msg = ""; + } + StringBuilder buf = new StringBuilder(5 + taskName.length() + msg.length()); + buf.append(PREFIX_STEP_IN).append(taskName); + if (msg.length() > 0) { + buf.append(": ").append(msg); + } + log(LEVEL_INFO, buf.toString()); + frames.push(taskName); + } + + public void infoEntering(String taskName) { + infoEntering(taskName, null); + } + + public void infoExiting(String msg) { + if (msg == null) { + msg = ""; + } + try { + String taskName = frames.pop(); + StringBuilder buf = new StringBuilder(5 + taskName.length() + msg.length()); + buf.append(PREFIX_STEP_OUT).append(taskName); + if (msg.length() > 0) { + buf.append(": ").append(msg); + } + log(LEVEL_INFO, buf.toString()); + } catch (EmptyStackException e) { + log(LEVEL_WARNING, "(INVALID) Exiting without no entering" + msg); + } + } + + public void infoExiting() { + infoExiting(null); + } + + public void debug(String msg) { + log(LEVEL_DEBUG, msg); + } + + public void log(int level, String msg) { + if (msg != null && isLoggable(level)) { + if (PREFIX_LEVELS[level] != null || !frames.empty()) { + StringBuilder buf = new StringBuilder(msg.length() + frames.size() * 3 + 3); + for (int i = frames.size(); i > 0; i--) { + buf.append(DEPTH_STRING); + } + if (PREFIX_LEVELS[level] != null) { + buf.append(PREFIX_LEVELS[level]); + } + buf.append(msg); + msg = buf.toString(); + } + System.out.println(msg); + } + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/ResultTimeoutException.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/ResultTimeoutException.java new file mode 100644 index 00000000..9f4c6f0f --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/ResultTimeoutException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.data.Bytes; + +public class ResultTimeoutException extends Exception { + Bytes txHash; + + public ResultTimeoutException() { + super(); + } + + public ResultTimeoutException(String message) { + super(message); + } + + public ResultTimeoutException(Bytes txHash) { + super("Timeout. txHash=" + txHash); + this.txHash = txHash; + } + + public Bytes getTxHash() { + return this.txHash; + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TestBase.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TestBase.java new file mode 100644 index 00000000..0bfe3a2e --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TestBase.java @@ -0,0 +1,97 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.TransactionResult; +import org.opentest4j.AssertionFailedError; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import static foundation.icon.test.Env.LOG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestBase { + protected static final BigInteger ICX = BigInteger.TEN.pow(18); + + protected static void assertSuccess(TransactionResult result) { + assertStatus(Constants.STATUS_SUCCESS, result); + } + + protected static void assertFailure(TransactionResult result) { + assertStatus(Constants.STATUS_FAILURE, result); + LOG.info("Expected " + result.getFailure()); + } + + protected static void assertStatus(BigInteger status, TransactionResult result) { + try { + assertEquals(status, result.getStatus()); + } catch (AssertionFailedError e) { + LOG.info("Assertion Failed: result=" + result); + fail(e.getMessage()); + } + } + + protected static void transferAndCheckResult(TransactionHandler txHandler, Address to, BigInteger amount) + throws IOException, ResultTimeoutException { + Bytes txHash = txHandler.transfer(to, amount); + assertSuccess(txHandler.getResult(txHash)); + } + + protected static void transferAndCheckResult(TransactionHandler txHandler, Address[] addresses, BigInteger amount) + throws IOException, ResultTimeoutException { + List hashes = new ArrayList<>(); + for (Address to : addresses) { + hashes.add(txHandler.transfer(to, amount)); + } + for (Bytes hash : hashes) { + assertSuccess(txHandler.getResult(hash)); + } + } + + protected static void ensureIcxBalance(TransactionHandler txHandler, Address address, + BigInteger oldVal, BigInteger newVal) throws Exception { + long limitTime = System.currentTimeMillis() + Constants.DEFAULT_WAITING_TIME; + while (true) { + BigInteger icxBalance = txHandler.getBalance(address); + String msg = "ICX balance of " + address + ": " + icxBalance; + if (icxBalance.equals(oldVal)) { + if (limitTime < System.currentTimeMillis()) { + throw new ResultTimeoutException(); + } + try { + // wait until block confirmation + LOG.debug(msg + "; Retry in 1 sec."); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else if (icxBalance.equals(newVal)) { + LOG.info(msg); + break; + } else { + throw new IOException(String.format("ICX balance mismatch: expected <%s>, but was <%s>", + newVal, icxBalance)); + } + } + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionFailureException.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionFailureException.java new file mode 100644 index 00000000..bd3a8887 --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionFailureException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.data.TransactionResult; + +import java.math.BigInteger; + +public class TransactionFailureException extends Exception { + private final TransactionResult.Failure failure; + + public TransactionFailureException(TransactionResult.Failure failure) { + this.failure = failure; + } + + @Override + public String toString() { + return this.failure.toString(); + } + + public BigInteger getCode() { + return this.failure.getCode(); + } + + public String getMessage() { + return this.failure.getMessage(); + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionHandler.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionHandler.java new file mode 100644 index 00000000..92d999cd --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/TransactionHandler.java @@ -0,0 +1,201 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test; + +import foundation.icon.icx.Call; +import foundation.icon.icx.IconService; +import foundation.icon.icx.SignedTransaction; +import foundation.icon.icx.Transaction; +import foundation.icon.icx.TransactionBuilder; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.ConfirmedTransaction; +import foundation.icon.icx.data.ScoreApi; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcError; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.test.score.ChainScore; +import foundation.icon.test.score.Score; +import foundation.icon.test.util.ZipFile; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static foundation.icon.test.Env.LOG; + +public class TransactionHandler { + private final IconService iconService; + private final Env.Chain chain; + + public TransactionHandler(IconService iconService, Env.Chain chain) { + this.iconService = iconService; + this.chain = chain; + } + + public Score deploy(Wallet owner, String scorePath, RpcObject params) + throws IOException, ResultTimeoutException, TransactionFailureException { + return deploy(owner, scorePath, params, null); + } + + public Score deploy(Wallet owner, String scorePath, RpcObject params, BigInteger steps) + throws IOException, ResultTimeoutException, TransactionFailureException { + return deploy(owner, scorePath, Constants.ZERO_ADDRESS, params, steps); + } + + public Score deploy(Wallet owner, String scorePath, Address to, RpcObject params, BigInteger steps) + throws IOException, ResultTimeoutException, TransactionFailureException { + if (scorePath.endsWith(".jar")) { + byte[] data = Files.readAllBytes(Path.of(scorePath)); + return getScore(doDeploy(owner, data, to, params, steps, Constants.CONTENT_TYPE_JAVA)); + } else { + byte[] data = ZipFile.zipContent(scorePath); + return getScore(doDeploy(owner, data, to, params, steps, Constants.CONTENT_TYPE_PYTHON)); + } + } + + private Bytes doDeploy(Wallet owner, byte[] content, Address to, RpcObject params, + BigInteger steps, String contentType) throws IOException { + Transaction transaction = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(owner.getAddress()) + .to(to) + .deploy(contentType, content) + .params(params) + .build(); + if (steps == null) { + steps = estimateStep(transaction); + } + SignedTransaction signedTransaction = new SignedTransaction(transaction, owner, steps); + return iconService.sendTransaction(signedTransaction).execute(); + } + + public Score getScore(Bytes txHash) + throws IOException, ResultTimeoutException, TransactionFailureException { + TransactionResult result = getResult(txHash, Constants.DEFAULT_WAITING_TIME); + if (!Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new TransactionFailureException(result.getFailure()); + } + return new Score(this, new Address(result.getScoreAddress())); + } + + public Env.Chain getChain() { + return this.chain; + } + + public BigInteger getNetworkId() { + return BigInteger.valueOf(chain.networkId); + } + + public BigInteger getBalance(Address address) throws IOException { + return iconService.getBalance(address).execute(); + } + + public List getScoreApi(Address scoreAddress) throws IOException { + return iconService.getScoreApi(scoreAddress).execute(); + } + + public BigInteger estimateStep(Transaction transaction) throws IOException { + try { + return iconService.estimateStep(transaction).execute(); + } catch (RpcError e) { + LOG.info("estimateStep failed(" + e.getCode() + ", " + e.getMessage() + "); use default steps."); + return Constants.DEFAULT_STEPS.multiply(BigInteger.TWO); + } + } + + public RpcItem call(Call call) throws IOException { + return this.iconService.call(call).execute(); + } + + public Bytes invoke(Wallet wallet, Transaction tx, BigInteger steps) throws IOException { + if (steps == null) { + steps = estimateStep(tx); + } + return this.iconService.sendTransaction(new SignedTransaction(tx, wallet, steps)).execute(); + } + + public TransactionResult getResult(Bytes txHash) + throws IOException, ResultTimeoutException { + return getResult(txHash, Constants.DEFAULT_WAITING_TIME); + } + + public TransactionResult getResult(Bytes txHash, long waiting) + throws IOException, ResultTimeoutException { + long limitTime = System.currentTimeMillis() + waiting; + while (true) { + try { + return iconService.getTransactionResult(txHash).execute(); + } catch (RpcError e) { + if (e.getCode() == -31002 /* pending */ + || e.getCode() == -31003 /* executing */ + || e.getCode() == -31004 /* not found */) { + if (limitTime < System.currentTimeMillis()) { + throw new ResultTimeoutException(txHash); + } + try { + // wait until block confirmation + LOG.debug("RpcError: code(" + e.getCode() + ") message(" + e.getMessage() + "); Retry in 1 sec."); + Thread.sleep(1000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + continue; + } + LOG.warning("RpcError: code(" + e.getCode() + ") message(" + e.getMessage() + "); Retry in 1 sec."); + throw e; + } + } + } + + public Bytes transfer(Address to, BigInteger amount) throws IOException { + return transfer(chain.godWallet, to, amount); + } + + public Bytes transfer(Wallet owner, Address to, BigInteger amount) throws IOException { + return transfer(owner, to, amount, null); + } + + public Bytes transfer(Wallet owner, Address to, BigInteger amount, BigInteger steps) throws IOException { + Transaction transaction = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(owner.getAddress()) + .to(to) + .value(amount) + .build(); + if (steps == null) { + steps = estimateStep(transaction).add(BigInteger.valueOf(10000)); + } + SignedTransaction signedTransaction = new SignedTransaction(transaction, owner, steps); + return iconService.sendTransaction(signedTransaction).execute(); + } + + public void refundAll(Wallet owner) throws IOException { + BigInteger stepPrice = new ChainScore(this).getStepPrice(); + BigInteger remaining = getBalance(owner.getAddress()); + BigInteger fee = Constants.DEFAULT_STEPS.multiply(stepPrice); + transfer(owner, chain.godWallet.getAddress(), remaining.subtract(fee), Constants.DEFAULT_STEPS); + } + + public ConfirmedTransaction getTransaction(Bytes txHash) throws IOException { + return iconService.getTransaction(txHash).execute(); + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/ChainScore.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/ChainScore.java new file mode 100644 index 00000000..e9f025f5 --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/ChainScore.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.test.Constants; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; + +public class ChainScore extends Score { + + public ChainScore(TransactionHandler txHandler) { + super(txHandler, Constants.ZERO_ADDRESS); + } + + public BigInteger getStepPrice() throws IOException { + return call("getStepPrice", null).asInteger(); + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/Score.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/Score.java new file mode 100644 index 00000000..d4230f81 --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/score/Score.java @@ -0,0 +1,164 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.score; + +import foundation.icon.icx.Call; +import foundation.icon.icx.Transaction; +import foundation.icon.icx.TransactionBuilder; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.data.TransactionResult.EventLog; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.test.Constants; +import foundation.icon.test.ResultTimeoutException; +import foundation.icon.test.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +public class Score { + private final TransactionHandler txHandler; + private final Address address; + + public Score(TransactionHandler txHandler, Address scoreAddress) { + this.txHandler = txHandler; + this.address = scoreAddress; + } + + public Score(Score other) { + this(other.txHandler, other.address); + } + + public static String getFilePath(String pkgName) { + String key = "score.path." + pkgName; + String path = System.getProperty(key); + if (path == null) { + throw new IllegalArgumentException("No such property: " + key); + } + return path; + } + + protected static EventLog findEventLog(TransactionResult result, Address scoreAddress, String funcSig) { + List eventLogs = result.getEventLogs(); + for (EventLog event : eventLogs) { + if (event.getScoreAddress().equals(scoreAddress.toString())) { + String signature = event.getIndexed().get(0).asString(); + if (funcSig.equals(signature)) { + return event; + } + } + } + return null; + } + + public RpcItem call(String method, RpcObject params) + throws IOException { + if (params == null) { + params = new RpcObject.Builder().build(); + } + Call call = new Call.Builder() + .to(getAddress()) + .method(method) + .params(params) + .build(); + return this.txHandler.call(call); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params) throws IOException { + return invoke(wallet, method, params, BigInteger.ZERO, null); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params, + BigInteger value, BigInteger steps) throws IOException { + return invoke(wallet, method, params, value, steps, null, null); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params, BigInteger value, + BigInteger steps, BigInteger timestamp, BigInteger nonce) throws IOException { + Transaction tx = getTransaction(wallet, method, params, value, timestamp, nonce); + return this.txHandler.invoke(wallet, tx, steps); + } + + private Transaction getTransaction(Wallet wallet, String method, RpcObject params, BigInteger value, + BigInteger timestamp, BigInteger nonce) { + TransactionBuilder.Builder builder = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(wallet.getAddress()) + .to(getAddress()); + + if ((value != null) && value.bitLength() != 0) { + builder.value(value); + } + if ((timestamp != null) && timestamp.bitLength() != 0) { + builder.timestamp(timestamp); + } + if (nonce != null) { + builder.nonce(nonce); + } + + Transaction tx; + if (params != null) { + tx = builder.call(method).params(params).build(); + } else { + tx = builder.call(method).build(); + } + return tx; + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params) + throws ResultTimeoutException, IOException { + return invokeAndWaitResult(wallet, method, params, null, null); + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params, + BigInteger steps) + throws ResultTimeoutException, IOException { + return invokeAndWaitResult(wallet, method, params, null, steps); + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params, + BigInteger value, BigInteger steps) + throws ResultTimeoutException, IOException { + Bytes txHash = this.invoke(wallet, method, params, value, steps); + return getResult(txHash); + } + + public TransactionResult getResult(Bytes txHash) throws ResultTimeoutException, IOException { + return getResult(txHash, Constants.DEFAULT_WAITING_TIME); + } + + public TransactionResult getResult(Bytes txHash, long waiting) throws ResultTimeoutException, IOException { + return this.txHandler.getResult(txHash, waiting); + } + + public Address getAddress() { + return this.address; + } + + public BigInteger getNetworkId() { + return txHandler.getNetworkId(); + } + + @Override + public String toString() { + return "SCORE(" + getAddress().toString() + ")"; + } +} diff --git a/javascore/bmv/testinteg/src/main/java/foundation/icon/test/util/ZipFile.java b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/util/ZipFile.java new file mode 100644 index 00000000..8f351df6 --- /dev/null +++ b/javascore/bmv/testinteg/src/main/java/foundation/icon/test/util/ZipFile.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.test.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class ZipFile { + public static byte[] zipContent(String path) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(outputStream); + recursiveZip(new File(path), null, zos); + zos.close(); + outputStream.close(); + return outputStream.toByteArray(); + } + + private static void recursiveZip(File source, String zipPath, ZipOutputStream zos) throws IOException { + if (source.isHidden()) { + return; + } + if (source.isDirectory()) { + String dir = source.getName(); + if (!dir.endsWith(File.separator)) { + dir = dir + File.separator; + } + zos.putNextEntry(new ZipEntry(dir)); + zos.closeEntry(); + File[] files = source.listFiles(); + if (files == null) { + return; + } + String path = zipPath == null ? dir : zipPath + dir; + for (File file : files) { + recursiveZip(file, path, zos); + } + } else { + ZipEntry ze = new ZipEntry(zipPath + source.getName()); + zos.putNextEntry(ze); + zos.write(Files.readAllBytes(source.toPath())); + zos.closeEntry(); + } + } +} diff --git a/javascore/bmv/testsvc/build.gradle b/javascore/bmv/testsvc/build.gradle new file mode 100644 index 00000000..28b1f7c1 --- /dev/null +++ b/javascore/bmv/testsvc/build.gradle @@ -0,0 +1,17 @@ +plugins { + id 'java-library' +} + +optimizedJar.enabled = false + +dependencies { + implementation 'org.bouncycastle:bcprov-jdk15on:1.60' + implementation 'org.msgpack:msgpack-core:0.8.17' + implementation 'org.slf4j:slf4j-api:1.7.26' + implementation 'org.ow2.asm:asm:7.0' + implementation 'org.ow2.asm:asm-commons:7.0' + implementation 'org.ow2.asm:asm-tree:7.0' + implementation 'org.ow2.asm:asm-util:7.0' + + compile files('./bmv/api-0.8.8-SNAPSHOT.jar') +} diff --git a/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Account.java b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Account.java new file mode 100644 index 00000000..99123c73 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Account.java @@ -0,0 +1,85 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iconloop.testsvc; + +import score.Address; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +public class Account { + private static final Map accounts = new HashMap<>(); + + private final Address address; + private final Map balances = new HashMap<>(); + + public static Account newExternalAccount(int seed) { + var acct = new Account(0, seed); + accounts.put(acct.getAddress(), acct); + return acct; + } + + public static Account newScoreAccount(int seed) { + var acct = new Account(1, seed); + accounts.put(acct.getAddress(), acct); + return acct; + } + + public static Account getAccount(Address address) { + return accounts.get(address); + } + + private Account(int type, int seed) { + var ba = new byte[Address.LENGTH]; + ba[0] = (byte) type; + var index = ba.length - 1; + ba[index--] = (byte) seed; + ba[index--] = (byte) (seed >> 8); + ba[index--] = (byte) (seed >> 16); + ba[index] = (byte) (seed >> 24); + this.address = new Address(ba); + } + + public Address getAddress() { + return address; + } + + public void addBalance(String symbol, BigInteger value) { + balances.put(symbol, getBalance(symbol).add(value)); + } + + public void subtractBalance(String symbol, BigInteger value) { + balances.put(symbol, getBalance(symbol).subtract(value)); + } + + public BigInteger getBalance(String symbol) { + return balances.getOrDefault(symbol, BigInteger.ZERO); + } + + public BigInteger getBalance() { + return getBalance("ICX"); + } + + @Override + public String toString() { + return "Account{" + + "address=" + address + + ", balances=" + balances + + '}'; + } +} diff --git a/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Score.java b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Score.java new file mode 100644 index 00000000..f2e6328d --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/Score.java @@ -0,0 +1,86 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iconloop.testsvc; + +import score.Address; + +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; + +public class Score extends TestBase { + private static final ServiceManager sm = getServiceManager(); + + private final Account score; + private final Account owner; + private Object instance; + + public Score(Account score, Account owner) { + this.score = score; + this.owner = owner; + } + + public Account getAccount() { + return this.score; + } + + public Address getAddress() { + return this.score.getAddress(); + } + + public Account getOwner() { + return this.owner; + } + + public void setInstance(Object newInstance) { + this.instance = newInstance; + } + + public Object getInstance() { + return this.instance; + } + + public Object call(String method, Object... params) { + return call(null, true, BigInteger.ZERO, method, params); + } + + public void invoke(Account from, String method, Object... params) { + sm.getBlock().increase(); + call(from, false, BigInteger.ZERO, method, params); + } + + Object call(Account from, boolean readonly, BigInteger value, String method, Object... params) { + sm.pushFrame(from, this.score, readonly, method, value); + Class[] paramClasses = new Class[params.length]; + for (int i = 0; i < params.length; i++) { + Class type = params[i].getClass(); + paramClasses[i] = (type == Boolean.class) ? Boolean.TYPE : type; + } + try { + Class clazz = instance.getClass(); + var m = clazz.getMethod(method, paramClasses); + return m.invoke(instance, params); + } catch (NoSuchMethodException | IllegalAccessException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } catch (InvocationTargetException e) { + e.printStackTrace(); + throw new AssertionError(e.getTargetException().getMessage()); + } finally { + sm.popFrame(); + } + } +} diff --git a/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/ServiceManager.java b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/ServiceManager.java new file mode 100644 index 00000000..d8e3f868 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/ServiceManager.java @@ -0,0 +1,223 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iconloop.testsvc; + +import score.Address; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.Stack; + +public class ServiceManager { + private static final BigInteger ICX = BigInteger.TEN.pow(18); + + private final Stack contexts = new Stack<>(); + private final Map, Score> classScoreMap = new HashMap<>(); + private final Map addressScoreMap = new HashMap<>(); + private final Map storageMap = new HashMap<>(); + private int nextCount = 1; + + public Score deploy(Account owner, Class mainClass, Object... params) throws Exception { + getBlock().increase(); + var score = new Score(Account.newScoreAccount(nextCount++), owner); + classScoreMap.put(mainClass, score); + addressScoreMap.put(score.getAddress(), score); + pushFrame(owner, score.getAccount(), false, "", BigInteger.ZERO); + try { + Constructor[] ctor = mainClass.getConstructors(); + if (ctor.length != 1) { + // User SCORE should only have one public constructor + throw new AssertionError("multiple public constructors found"); + } + score.setInstance(ctor[0].newInstance(params)); + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + throw e; + } finally { + popFrame(); + } + return score; + } + + public Account createAccount() { + return createAccount(0); + } + + public Account createAccount(int initialIcx) { + var acct = Account.newExternalAccount(nextCount++); + acct.addBalance("ICX", ICX.multiply(BigInteger.valueOf(initialIcx))); + return acct; + } + + public Address getOwner() { + var address = getCurrentFrame().to.getAddress(); + return getScoreFromAddress(address).getOwner().getAddress(); + } + + public Address getOrigin() { + return getFirstFrame().from.getAddress(); + } + + public Address getCaller() { + return getCurrentFrame().from.getAddress(); + } + + public Address getAddress() { + return getCurrentFrame().to.getAddress(); + } + + private Score getScoreFromClass(Class caller) { + var score = classScoreMap.get(caller); + if (score == null) { + throw new IllegalStateException(caller.getName() + " not found"); + } + return score; + } + + private Score getScoreFromAddress(Address target) { + var score = addressScoreMap.get(target); + if (score == null) { + throw new IllegalStateException("ScoreNotFound"); + } + return score; + } + + public Object call(Account from, BigInteger value, Address targetAddress, String method, Object... params) { + Score score = getScoreFromAddress(targetAddress); + return score.call(from, false, value, method, params); + } + + public Object call(Class caller, BigInteger value, Address targetAddress, String method, Object... params) { + Score from = getScoreFromClass(caller); + if ("fallback".equals(method) || "".equals(method)) { + transfer(from.getAccount(), targetAddress, value); + return null; + } else { + return call(from.getAccount(), value, targetAddress, method, params); + } + } + + public void transfer(Account from, Address targetAddress, BigInteger value) { + getBlock().increase(); + var fromBalance = from.getBalance(); + if (fromBalance.compareTo(value) < 0) { + throw new IllegalStateException("OutOfBalance"); + } + var to = Account.getAccount(targetAddress); + if (to == null) { + throw new IllegalStateException("NoAccount"); + } + from.subtractBalance("ICX", value); + to.addBalance("ICX", value); + if (targetAddress.isContract()) { + call(from, value, targetAddress, "fallback"); + } + } + + public void putStorage(String key, Object value) { + storageMap.put(getAddress().toString() + key, value); + } + + public Object getStorage(String key) { + return storageMap.get(getAddress().toString() + key); + } + + public static class Block { + private static Block sInstance; + + private long height; + private long timestamp; + + public Block(long height, long timestamp) { + this.height = height; + this.timestamp = timestamp; + } + + public static Block getInstance() { + if (sInstance == null) { + Random rand = new Random(); + sInstance = new Block(rand.nextInt(1000), System.nanoTime() * 1000); + } + return sInstance; + } + + public long getHeight() { + return height; + } + + public long getTimestamp() { + return timestamp; + } + + public void increase() { + increase(1); + } + + public void increase(long delta) { + height += delta; + timestamp = System.nanoTime() * 1000; + } + } + + public Block getBlock() { + return Block.getInstance(); + } + + public static class Frame { + Account from; + Account to; + String method; + boolean readonly; + BigInteger value; + + public Frame(Account from, Account to, boolean readonly, String method, BigInteger value) { + this.from = from; + this.to = to; + this.readonly = readonly; + this.method = method; + this.value = value; + } + + public boolean isReadonly() { + return readonly; + } + + public BigInteger getValue() { + return value; + } + } + + protected void pushFrame(Account from, Account to, boolean readonly, String method, BigInteger value) { + contexts.push(new Frame(from, to, readonly, method, value)); + } + + protected void popFrame() { + contexts.pop(); + } + + public Frame getCurrentFrame() { + return contexts.peek(); + } + + public Frame getFirstFrame() { + return contexts.firstElement(); + } +} diff --git a/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/TestBase.java b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/TestBase.java new file mode 100644 index 00000000..7955884d --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/com/iconloop/testsvc/TestBase.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iconloop.testsvc; + +import java.math.BigInteger; + +public class TestBase { + protected static final BigInteger ICX = BigInteger.TEN.pow(18); + + private static ServiceManager sInstance; + + protected static ServiceManager getServiceManager() { + if (sInstance == null) { + sInstance = new ServiceManager(); + } + return sInstance; + } +} diff --git a/javascore/bmv/testsvc/src/main/java/impl/Crypto.java b/javascore/bmv/testsvc/src/main/java/impl/Crypto.java new file mode 100644 index 00000000..a6be91d1 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/impl/Crypto.java @@ -0,0 +1,200 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.util; + +import score.util.xxhash.XxHash; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9IntegerConverter; +import org.bouncycastle.crypto.digests.Blake2bDigest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.jcajce.provider.digest.Blake2b; +import org.bouncycastle.math.ec.ECAlgorithms; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve; +import org.bouncycastle.math.ec.rfc8032.Ed25519; +import org.bouncycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class Crypto { + public static byte[] sha3_256(byte[] msg) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA3-256"); + return digest.digest(msg); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e.toString()); + } + } + + public static byte[] sha256(byte[] msg) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + return digest.digest(msg); + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e.toString()); + } + } + + static void require(boolean cond, String msg) { + if (!cond) { + throw new IllegalArgumentException(msg); + } + } + + public static byte[] hash(String alg, byte[] msg) { + switch (alg) { + case "sha-256": + return sha256(msg); + case "sha3-256": + return sha3_256(msg); + case "xxhash-128": + return XxHash.hash128(msg); + case "blake2b-128": { + var digest = new Blake2bDigest(128); + digest.update(msg, 0, msg.length); + var res = new byte[128/8]; + digest.doFinal(res, 0); + return res; + } + case "blake2b-256": { + var digest = new Blake2bDigest(256); + digest.update(msg, 0, msg.length); + var res = new byte[256/8]; + digest.doFinal(res, 0); + return res; + } + } + throw new IllegalArgumentException("Unsupported algorithm " + alg); + } + + public static byte[] sign(String alg, byte[] msg, byte[] sk) { + byte[] sig = new byte[64]; + switch (alg) { + case "ed25519": { + Ed25519.sign(sk, 0, msg, 0, msg.length, sig, 0); + return sig; + } + } + throw new IllegalArgumentException("Unsupported algorithm " + alg); + } + + public static boolean verifySignature(String alg, byte[] msg, byte[] sig, byte[] pk) { + switch (alg) { + case "ed25519": { + require(sig.length == Ed25519.SIGNATURE_SIZE, "invalid signature length"); + require(pk.length == Ed25519.PUBLIC_KEY_SIZE, "invalid public key length"); + return Ed25519.verify(sig, 0, pk, 0, msg, 0, msg.length); + } + case "ecdsa-secp256k1": { + require(msg.length == 32, "the length of message must be 32"); + require(sig.length == 65, "the length of signature must be 65"); + require(pk.length == 33 || pk.length == 65, "invalid public key length"); + var recovered = recoverKey(msg, sig, pk.length==33); + return Arrays.equals(recovered, pk); + } + } + throw new IllegalArgumentException("Unsupported algorithm " + alg); + } + + public static byte[] recoverKey(String alg, byte[] msg, byte[] sig, boolean compressed) { + switch (alg) { + case "ecdsa-secp256k1": { + require(msg.length == 32, "the length of msgHash must be 32"); + require(sig.length == 65, "the length of signature must be 65"); + return recoverKey(msg, sig, compressed); + } + } + throw new IllegalArgumentException("Unsupported algorithm " + alg); + } + + public static byte[] recoverKey(byte[] msgHash, byte[] signature, boolean compressed) { + BigInteger r = BigIntegers.fromUnsignedByteArray(signature, 0, 32); + BigInteger s = BigIntegers.fromUnsignedByteArray(signature, 32, 32); + return recoverFromSignature(signature[64], r, s, msgHash, compressed); + } + + public static byte[] getAddressBytesFromKey(byte[] pubKey) { + checkArgument(pubKey.length == 33 || pubKey.length == 65, "Invalid key length"); + byte[] uncompressed; + if (pubKey.length == 33) { + uncompressed = uncompressKey(pubKey); + } else { + uncompressed = pubKey; + } + byte[] hash = sha3_256(Arrays.copyOfRange(uncompressed, 1, uncompressed.length)); + byte[] address = new byte[21]; + System.arraycopy(hash, hash.length - 20, address, 1, 20); + return address; + } + + private final static X9ECParameters curveParams = CustomNamedCurves.getByName("secp256k1"); + private final static ECDomainParameters curve = new ECDomainParameters( + curveParams.getCurve(), curveParams.getG(), curveParams.getN(), curveParams.getH()); + + private static byte[] uncompressKey(byte[] compKey) { + ECPoint point = curve.getCurve().decodePoint(compKey); + byte[] x = point.getXCoord().getEncoded(); + byte[] y = point.getYCoord().getEncoded(); + byte[] key = new byte[x.length + y.length + 1]; + key[0] = 0x04; + System.arraycopy(x, 0, key, 1, x.length); + System.arraycopy(y, 0, key, x.length + 1, y.length); + return key; + } + + private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { + X9IntegerConverter x9 = new X9IntegerConverter(); + byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(curve.getCurve())); + compEnc[0] = (byte) (yBit ? 0x03 : 0x02); + return curve.getCurve().decodePoint(compEnc); + } + + private static byte[] recoverFromSignature(int recId, BigInteger r, BigInteger s, byte[] message, boolean compressed) { + checkArgument(recId >= 0, "recId must be positive"); + checkArgument(r.signum() >= 0, "r must be positive"); + checkArgument(s.signum() >= 0, "s must be positive"); + + BigInteger n = curve.getN(); + BigInteger i = BigInteger.valueOf((long) recId / 2); + BigInteger x = r.add(i.multiply(n)); + BigInteger prime = SecP256K1Curve.q; + if (x.compareTo(prime) >= 0) { + return null; + } + ECPoint ecPoint = decompressKey(x, (recId & 1) == 1); + if (!ecPoint.multiply(n).isInfinity()) { + return null; + } + BigInteger e = new BigInteger(1, message); + BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n); + BigInteger rInv = r.modInverse(n); + BigInteger srInv = rInv.multiply(s).mod(n); + BigInteger eInvrInv = rInv.multiply(eInv).mod(n); + ECPoint q = ECAlgorithms.sumOfTwoMultiplies(curve.getG(), eInvrInv, ecPoint, srInv); + return q.getEncoded(compressed); + } + + private static void checkArgument(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + } +} diff --git a/javascore/bmv/testsvc/src/main/java/impl/xxhash/Access.java b/javascore/bmv/testsvc/src/main/java/impl/xxhash/Access.java new file mode 100644 index 00000000..f479fa86 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/impl/xxhash/Access.java @@ -0,0 +1,259 @@ +/* + * Copyright 2021 ICON Foundation + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.util.xxhash; + +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.BIG_ENDIAN; +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +/** + * Strategy of reading bytes, defines the abstraction of {@code T} class instances as ordered byte + * sequence. All {@code getXXX(input, offset)} should be consistent to each other in terms of + * ordered byte sequence each {@code T} instance represents. For example, if some {@code + * Access} implementation returns {@link ByteOrder#LITTLE_ENDIAN} on {@link #byteOrder(Object) + * byteOrder(input)} call, the following expressions should always have the same value: + *
    + *
  • {@code getLong(input, 0)}
  • + *
  • {@code getUnsignedInt(input, 0) | (getUnsignedInt(input, 4) << 32)}
  • + *
  • {@code getUnsignedInt(input, 0) |
    + *    ((long) getUnsignedShort(input, 4) << 32) |
    + *    ((long) getUnsignedByte(input, 6) << 48) |
    + *    ((long) getUnsignedByte(input, 7) << 56)}
  • + *
  • And so on
  • + *
+ * + *

{@code getXXX(input, offset)} methods could throw unchecked exceptions when requested bytes + * range is outside of the bounds of the byte sequence, represented by the given {@code input}. + * However, they could omit checks for better performance. + * + *

Only {@link #getByte(Object, long)} and {@link #byteOrder(Object)} methods are abstract in + * this class, so implementing them is sufficient for valid {@code Access} instance, but for + * efficiency your should override other methods. + * + *

{@code Access} API is designed for inputs, that actually represent byte sequences that lay + * continuously in memory. Theoretically {@code Access} strategy could be implemented for + * non-continuous byte sequences, or abstractions which aren't actually present in memory as they + * are accessed, but this should be awkward, and hashing using such {@code Access} is expected to + * be slow. + * + * @param the type of the object to access + */ +public abstract class Access { + /** + * Constructor for use in subclasses. + */ + protected Access() {} + + /** + * Reads {@code [offset, offset + 7]} bytes of the byte sequence represented by the given + * {@code input} as a single {@code long} value. + * + * @param input the object to access + * @param offset offset to the first byte to read within the byte sequence represented + * by the given object + * @return eight bytes as a {@code long} value, in {@linkplain #byteOrder(Object) the expected + * order} + */ + public long getLong(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedInt(input, offset) | (getUnsignedInt(input, offset + 4L) << 32); + } else { + return getUnsignedInt(input, offset + 4L) | (getUnsignedInt(input, offset) << 32); + } + } + + /** + * Shortcut for {@code getInt(input, offset) & 0xFFFFFFFFL}. Could be implemented more + * efficiently. + * + * @param input the object to access + * @param offset offset to the first byte to read within the byte sequence represented + * by the given object + * @return four bytes as an unsigned int value, in {@linkplain #byteOrder(Object) the expected + * order} + */ + public long getUnsignedInt(T input, long offset) { + return ((long) getInt(input, offset)) & 0xFFFFFFFFL; + } + + /** + * Reads {@code [offset, offset + 3]} bytes of the byte sequence represented by the given + * {@code input} as a single {@code int} value. + * + * @param input the object to access + * @param offset offset to the first byte to read within the byte sequence represented + * by the given object + * @return four bytes as an {@code int} value, in {@linkplain #byteOrder(Object) the expected + * order} + */ + public int getInt(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedShort(input, offset) | (getUnsignedShort(input, offset + 2L) << 16); + } else { + return getUnsignedShort(input, offset + 2L) | (getUnsignedShort(input, offset) << 16); + } + } + + /** + * Shortcut for {@code getShort(input, offset) & 0xFFFF}. Could be implemented more + * efficiently. + * + * @param input the object to access + * @param offset offset to the first byte to read within the byte sequence represented + * by the given object + * @return two bytes as an unsigned short value, in {@linkplain #byteOrder(Object) the expected + * order} + */ + public int getUnsignedShort(T input, long offset) { + if (byteOrder(input) == LITTLE_ENDIAN) { + return getUnsignedByte(input, offset) | (getUnsignedByte(input, offset + 1L) << 8); + } else { + return getUnsignedByte(input, offset + 1L) | (getUnsignedByte(input, offset) << 8); + } + } + + /** + * Reads {@code [offset, offset + 1]} bytes of the byte sequence represented by the given + * {@code input} as a single {@code short} value, returned widened to {@code int}. + * + * @param input the object to access + * @param offset offset to the first byte to read within the byte sequence represented + * by the given object + * @return two bytes as a {@code short} value, in {@linkplain #byteOrder(Object) the expected + * order}, widened to {@code int} + */ + public int getShort(T input, long offset) { + return (int) (short) getUnsignedShort(input, offset); + } + + /** + * Shortcut for {@code getByte(input, offset) & 0xFF}. Could be implemented more efficiently. + * + * @param input the object to access + * @param offset offset to the byte to read within the byte sequence represented + * by the given object + * @return a byte by the given {@code offset}, interpreted as unsigned + */ + public int getUnsignedByte(T input, long offset) { + return getByte(input, offset) & 0xFF; + } + + /** + * Reads a single byte at the given {@code offset} in the byte sequence represented by the given + * {@code input}, returned widened to {@code int}. + * + * @param input the object to access + * @param offset offset to the byte to read within the byte sequence represented + * by the given object + * @return a byte by the given {@code offset}, widened to {@code int} + */ + public abstract int getByte(T input, long offset); + + // short names + public long i64(final T input, final long offset) { return getLong(input, offset); } + public long u32(final T input, final long offset) { return getUnsignedInt(input, offset); } + public int i32(final T input, final long offset) { return getInt(input, offset); } + public int u16(final T input, final long offset) { return getUnsignedShort(input, offset); } + public int i16(final T input, final long offset) { return getShort(input, offset); } + public int u8(final T input, final long offset) { return getUnsignedByte(input, offset); } + public int i8(final T input, final long offset) { return getByte(input, offset); } + + /** + * The byte order in which all multi-byte {@code getXXX()} reads from the given {@code input} + * are performed. + * + * @param input the accessed object + * @return the byte order of all multi-byte reads from the given {@code input} + */ + public abstract ByteOrder byteOrder(T input); + + /** + * Get {@code this} or the reversed access object for reading the input as fixed + * byte order of {@code byteOrder}. + * + * @param input the accessed object + * @param byteOrder the byte order to be used for reading the {@code input} + * @return a {@code Access} object which will read the {@code input} with the + * byte order of {@code byteOrder}. + */ + public Access byteOrder(final T input, final ByteOrder byteOrder) { + return byteOrder(input) == byteOrder ? this : reverseAccess(); + } + + /** + * Get the {@code Access} object with a different byte order. This method should + * always return a fixed reference. + */ + protected abstract Access reverseAccess(); + + /** + * Get or create the reverse byte order {@code Access} object for {@code access}. + */ + static Access newDefaultReverseAccess(final Access access) { + return access instanceof ReverseAccess + ? access.reverseAccess() + : new ReverseAccess(access); + } + + /** + * The default reverse byte order delegating {@code Access} class. + */ + private static class ReverseAccess extends Access { + final Access access; + private ReverseAccess(final Access access) { + this.access = access; + } + @Override + public long getLong(final T input, final long offset) { + return Long.reverseBytes(access.getLong(input, offset)); + } + @Override + public long getUnsignedInt(final T input, final long offset) { + return Long.reverseBytes(access.getUnsignedInt(input, offset)) >>> 32; + } + @Override + public int getInt(final T input, final long offset) { + return Integer.reverseBytes(access.getInt(input, offset)); + } + @Override + public int getUnsignedShort(final T input, final long offset) { + return Integer.reverseBytes(access.getUnsignedShort(input, offset)) >>> 16; + } + @Override + public int getShort(final T input, final long offset) { + return Integer.reverseBytes(access.getShort(input, offset)) >> 16; + } + @Override + public int getUnsignedByte(final T input, final long offset) { + return access.getUnsignedByte(input, offset); + } + @Override + public int getByte(final T input, final long offset) { + return access.getByte(input, offset); + } + @Override + public ByteOrder byteOrder(final T input) { + return LITTLE_ENDIAN == access.byteOrder(input) ? BIG_ENDIAN : LITTLE_ENDIAN; + } + @Override + protected Access reverseAccess() { + return access; + } + } +} \ No newline at end of file diff --git a/javascore/bmv/testsvc/src/main/java/impl/xxhash/LongHashFunction.java b/javascore/bmv/testsvc/src/main/java/impl/xxhash/LongHashFunction.java new file mode 100644 index 00000000..822f686e --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/impl/xxhash/LongHashFunction.java @@ -0,0 +1,107 @@ +/* + * Copyright 2021 ICON Foundation + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.util.xxhash; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Hash function producing {@code long}-valued result from byte sequences of any length and + * a plenty of different sources which "feels like byte sequences". Except {@link + * #hashBytes(byte[])}, {@link #hashBytes(ByteBuffer)} (with their "sliced" versions) and + * {@link #hashMemory(long, long)} methods, which actually accept byte sequences, notion of byte + * sequence is defined as follows: + *

    + *
  • For methods accepting arrays of Java primitives, {@code String}s and + * {@code StringBuilder}s, byte sequence is how the input's bytes are actually lay in memory. + *
  • + *
  • For methods accepting single primitive values, byte sequence is how this primitive + * would be put into memory with {@link ByteOrder#nativeOrder() native} byte order, or + * equivalently, {@code hashXxx(primitive)} has always the same result as {@code + * hashXxxs(new xxx[] {primitive})}, where "xxx" is any Java primitive type name.
  • + *
  • For {@link #hash(Object, Access, long, long)} method byte sequence abstraction + * is defined by the given {@link Access} strategy to the given object.
  • + *
+ * + *

Hash function implementation could either produce equal results for equal input on platforms + * with different {@link ByteOrder}, favoring one byte order in terms of performance, or different + * results, but performing equally good. This choice should be explicitly documented for all + * {@code LongHashFunction} implementations. + * + *

Subclassing

+ * To implement a specific hash function algorithm, this class should be subclassed. Only methods + * that accept single primitives, {@link #hashVoid()} and {@link #hash(Object, Access, long, long)} + * should be implemented; other have default implementations which in the end delegate to + * {@link #hash(Object, Access, long, long)} abstract method. + * + *

Notes about how exactly methods with default implementations are implemented in doc comments + * are given for information and could be changed at any moment. However, it could hardly cause + * any issues with subclassing, except probably little performance degradation. Methods documented + * as "shortcuts" could either delegate to the referenced method or delegate directly to the method + * to which the referenced method delegates. + * + *

{@code LongHashFunction} implementations shouldn't assume that {@code Access} strategies + * do defensive checks, and access only bytes within the requested range. + */ +public abstract class LongHashFunction implements Serializable { + private static final long serialVersionUID = 0L; + /** + * Returns a hash function implementing xxHash + * algorithm with the given seed value. This implementation produces equal results for equal + * input on platforms with different {@link ByteOrder}, but is slower on big-endian platforms + * than on little-endian. + * + * @see #xx() + */ + public static LongHashFunction xx(long seed) { + return XxHash.asLongHashFunctionWithSeed(seed); + } + + /** + * Constructor for use in subclasses. + */ + protected LongHashFunction() {} + + /** + * Returns the hash code for {@code len} continuous bytes of the given {@code input} object, + * starting from the given offset. The abstraction of input as ordered byte sequence and + * "offset within the input" is defined by the given {@code access} strategy. + * + *

This method doesn't promise to throw a {@code RuntimeException} if {@code + * [off, off + len - 1]} subsequence exceeds the bounds of the bytes sequence, defined by {@code + * access} strategy for the given {@code input}, so use this method with caution. + * + * @param input the object to read bytes from + * @param access access which defines the abstraction of the given input + * as ordered byte sequence + * @param off offset to the first byte of the subsequence to hash + * @param len length of the subsequence to hash + * @param the type of the input + * @return hash code for the specified bytes subsequence + */ + public abstract long hash(T input, Access access, long off, long len); + + private long unsafeHash(Object input, long off, long len) { + return hash(input, UnsafeAccess.INSTANCE, off, len); + } + + public long hashBytes(byte[] input) { + return unsafeHash(input, UnsafeAccess.BYTE_BASE, input.length); + } +} diff --git a/javascore/bmv/testsvc/src/main/java/impl/xxhash/UnsafeAccess.java b/javascore/bmv/testsvc/src/main/java/impl/xxhash/UnsafeAccess.java new file mode 100644 index 00000000..dffa894e --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/impl/xxhash/UnsafeAccess.java @@ -0,0 +1,165 @@ +/* + * Copyright 2021 ICON Foundation + * Copyright 2014 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.util.xxhash; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; +import static java.nio.ByteOrder.nativeOrder; + +class UnsafeAccess extends Access { + static final boolean NATIVE_LITTLE_ENDIAN = nativeOrder() == LITTLE_ENDIAN; + static long unsignedInt(int i) { + return i & 0xFFFFFFFFL; + } + + static int unsignedShort(int s) { + return s & 0xFFFF; + } + + static int unsignedByte(int b) { + return b & 0xFF; + } + + static final UnsafeAccess INSTANCE; + private static final Access INSTANCE_NON_NATIVE; + + // for test only + static final UnsafeAccess OLD_INSTANCE = NATIVE_LITTLE_ENDIAN + ? new OldUnsafeAccessLittleEndian() + : new OldUnsafeAccessBigEndian(); + + static final Unsafe UNSAFE; + + static final long BOOLEAN_BASE; + static final long BYTE_BASE; + static final long CHAR_BASE; + static final long SHORT_BASE; + static final long INT_BASE; + static final long LONG_BASE; + + static final byte TRUE_BYTE_VALUE; + static final byte FALSE_BYTE_VALUE; + + static { + try { + Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + UNSAFE = (Unsafe) theUnsafe.get(null); + + BOOLEAN_BASE = UNSAFE.arrayBaseOffset(boolean[].class); + BYTE_BASE = UNSAFE.arrayBaseOffset(byte[].class); + CHAR_BASE = UNSAFE.arrayBaseOffset(char[].class); + SHORT_BASE = UNSAFE.arrayBaseOffset(short[].class); + INT_BASE = UNSAFE.arrayBaseOffset(int[].class); + LONG_BASE = UNSAFE.arrayBaseOffset(long[].class); + + TRUE_BYTE_VALUE = (byte)UNSAFE.getInt(new boolean[] {true, true, true, true}, + BOOLEAN_BASE); + FALSE_BYTE_VALUE = (byte)UNSAFE.getInt(new boolean[] {false, false, false, false}, + BOOLEAN_BASE); + } catch (final Exception e) { + throw new AssertionError(e); + } + + boolean hasGetByte = true; + try { + UNSAFE.getByte(new byte[1], BYTE_BASE); + } catch (final Throwable ignore) { + // Unsafe in pre-Nougat Android does not have getByte(), fall back to workround + hasGetByte = false; + } + + INSTANCE = hasGetByte ? new UnsafeAccess() : OLD_INSTANCE; + INSTANCE_NON_NATIVE = Access.newDefaultReverseAccess(INSTANCE); + } + + private UnsafeAccess() {} + + @Override + public long getLong(Object input, long offset) { + return UNSAFE.getLong(input, offset); + } + + @Override + public long getUnsignedInt(Object input, long offset) { + return unsignedInt(getInt(input, offset)); + } + + @Override + public int getInt(Object input, long offset) { + return UNSAFE.getInt(input, offset); + } + + @Override + public int getUnsignedShort(Object input, long offset) { + return unsignedShort(getShort(input, offset)); + } + + @Override + public int getShort(Object input, long offset) { + return UNSAFE.getShort(input, offset); + } + + @Override + public int getUnsignedByte(Object input, long offset) { + return unsignedByte(getByte(input, offset)); + } + + @Override + public int getByte(Object input, long offset) { + return UNSAFE.getByte(input, offset); + } + + @Override + public ByteOrder byteOrder(Object input) { + return ByteOrder.nativeOrder(); + } + + @Override + protected Access reverseAccess() { + return INSTANCE_NON_NATIVE; + } + + private static class OldUnsafeAccessLittleEndian extends UnsafeAccess { + @Override + public int getShort(final Object input, final long offset) { + return UNSAFE.getInt(input, offset - 2) >> 16; + } + + @Override + public int getByte(final Object input, final long offset) { + return UNSAFE.getInt(input, offset - 3) >> 24; + } + } + + private static class OldUnsafeAccessBigEndian extends UnsafeAccess { + @Override + public int getShort(final Object input, final long offset) { + return (int)(short)UNSAFE.getInt(input, offset - 2); + } + + @Override + public int getByte(final Object input, final long offset) { + return (int)(byte)UNSAFE.getInt(input, offset - 3); + } + } +} diff --git a/javascore/bmv/testsvc/src/main/java/impl/xxhash/XxHash.java b/javascore/bmv/testsvc/src/main/java/impl/xxhash/XxHash.java new file mode 100644 index 00000000..3186cab5 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/impl/xxhash/XxHash.java @@ -0,0 +1,183 @@ +/* + * Copyright 2021 ICON Foundation + * Copyright 2015 Higher Frequency Trading http://www.higherfrequencytrading.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.util.xxhash; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +/** + * Adapted version of xxHash implementation from https://github.com/Cyan4973/xxHash. + * This implementation provides endian-independant hash values, but it's slower on big-endian platforms. + */ +public class XxHash { + // Primes if treated as unsigned + private static final long P1 = -7046029288634856825L; + private static final long P2 = -4417276706812531889L; + private static final long P3 = 1609587929392839161L; + private static final long P4 = -8796714831421723037L; + private static final long P5 = 2870177450012600261L; + + public static byte[] hash128(byte[] msg) { + var buf = ByteBuffer.allocate(16); + ByteOrder oder = buf.order(); + buf.order(ByteOrder.LITTLE_ENDIAN); + buf.asLongBuffer() + .put(LongHashFunction.xx(0).hashBytes(msg)) + .put(LongHashFunction.xx(1).hashBytes(msg)); + buf.position(buf.position() + 16); + buf.order(oder); + return buf.array(); + } + + static long xxHash64(long seed, T input, Access access, long off, long length) { + long hash; + long remaining = length; + + if (remaining >= 32) { + long v1 = seed + P1 + P2; + long v2 = seed + P2; + long v3 = seed; + long v4 = seed - P1; + + do { + v1 += access.i64(input, off) * P2; + v1 = Long.rotateLeft(v1, 31); + v1 *= P1; + + v2 += access.i64(input, off + 8) * P2; + v2 = Long.rotateLeft(v2, 31); + v2 *= P1; + + v3 += access.i64(input, off + 16) * P2; + v3 = Long.rotateLeft(v3, 31); + + + v4 += access.i64(input, off + 24) * P2; + v4 = Long.rotateLeft(v4, 31); + v4 *= P1; + + off += 32; + remaining -= 32; + } while (remaining >= 32); + + hash = Long.rotateLeft(v1, 1) + + Long.rotateLeft(v2, 7) + + Long.rotateLeft(v3, 12) + + Long.rotateLeft(v4, 18); + + v1 *= P2; + v1 = Long.rotateLeft(v1, 31); + v1 *= P1; + hash ^= v1; + hash = hash * P1 + P4; + + v2 *= P2; + v2 = Long.rotateLeft(v2, 31); + v2 *= P1; + hash ^= v2; + hash = hash * P1 + P4; + + v3 *= P2; + v3 = Long.rotateLeft(v3, 31); + v3 *= P1; + hash ^= v3; + hash = hash * P1 + P4; + + v4 *= P2; + v4 = Long.rotateLeft(v4, 31); + v4 *= P1; + hash ^= v4; + hash = hash * P1 + P4; + } else { + hash = seed + P5; + } + + hash += length; + + while (remaining >= 8) { + long k1 = access.i64(input, off); + k1 *= P2; + k1 = Long.rotateLeft(k1, 31); + k1 *= P1; + hash ^= k1; + hash = Long.rotateLeft(hash, 27) * P1 + P4; + off += 8; + remaining -= 8; + } + + if (remaining >= 4) { + hash ^= access.u32(input, off) * P1; + hash = Long.rotateLeft(hash, 23) * P2 + P3; + off += 4; + remaining -= 4; + } + + while (remaining != 0) { + hash ^= access.u8(input, off) * P5; + hash = Long.rotateLeft(hash, 11) * P1; + --remaining; + ++off; + } + + return finalize(hash); + } + + private static long finalize(long hash) { + hash ^= hash >>> 33; + hash *= P2; + hash ^= hash >>> 29; + hash *= P3; + hash ^= hash >>> 32; + return hash; + } + + private static class AsLongHashFunction extends LongHashFunction { + private static final long serialVersionUID = 0L; + + public long seed() { + return 0L; + } + + @Override + public long hash(T input, Access access, long off, long len) { + long seed = seed(); + return XxHash.xxHash64(seed, input, access.byteOrder(input, LITTLE_ENDIAN), off, len); + } + } + + static LongHashFunction asLongHashFunctionWithSeed(long seed) { + return new AsLongHashFunctionSeeded(seed); + } + + private static class AsLongHashFunctionSeeded extends AsLongHashFunction { + private static final long serialVersionUID = 0L; + + private final long seed; + + private AsLongHashFunctionSeeded(long seed) { + this.seed = seed; + } + + @Override + public long seed() { + return seed; + } + } +} diff --git a/javascore/bmv/testsvc/src/main/java/score/Address.java b/javascore/bmv/testsvc/src/main/java/score/Address.java new file mode 100644 index 00000000..c5842c3e --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/Address.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019 ICON Foundation + * Copyright (c) 2018 Aion Foundation https://aion.network/ + */ + +package score; + +/** + * Represents an address of account in the ICON Network. + */ +public class Address { + + /** + * The length of an address. + */ + public static final int LENGTH = 21; + + /** + * Creates an address with the contents of the given raw byte array. + * + * @param raw a byte array + * @throws NullPointerException if the input byte array is null + * @throws IllegalArgumentException if the input byte array length is invalid + */ + public Address(byte[] raw) throws IllegalArgumentException { + } + + /** + * Creates an address from the hex string format. + * + * @param str a hex string that represents an Address + * @return the resulting address + * @throws NullPointerException if the input string is null + * @throws IllegalArgumentException if the input string format or length is invalid + */ + public static Address fromString(String str) { + return null; + } + + /** + * Returns true if and only if this address represents a contract address. + * + * @return true if this address represents a contract address, false otherwise + */ + public boolean isContract() { + return false; + } + + /** + * Converts this address to a new byte array. + * + * @return a newly allocated byte array that represents this address + */ + public byte[] toByteArray() { + return null; + } + + /** + * Returns a hash code for this address. + * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + return 0; + } + + /** + * Compares this address to the specified object. + * + * @param obj the object to compare this address against + * @return true if the given object represents an {@code Address} equivalent to this address, false otherwise + */ + @Override + public boolean equals(Object obj) { + return false; + } + + /** + * Returns a string representation of this address. + * + * @return a string representation of this object + */ + @Override + public String toString() { + return null; + } +} diff --git a/javascore/bmv/testsvc/src/main/java/score/ArrayDB.java b/javascore/bmv/testsvc/src/main/java/score/ArrayDB.java new file mode 100644 index 00000000..8da98f45 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/ArrayDB.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * An array DB holds a sequence of values. + * @param Element type. It shall be readable and writable class. + * @see ObjectReader + * @see ObjectWriter + */ +public interface ArrayDB { + /** + * Adds a value at the end of the array DB. + * @param value new value + */ + void add(E value); + + /** + * Sets value of the specified index. + * @param index index + * @param value new value + * @throws IllegalArgumentException if index is out of range. + */ + void set(int index, E value); + + /** + * Removes last element of the array DB. + * @throws IllegalStateException if array DB has zero elements. + */ + void removeLast(); + + /** + * Returns the element at the specified position in the array DB. + * @param index index of element + * @return the element at the specified position in the array DB. + * @throws IllegalArgumentException if index is out of range. + */ + E get(int index); + + /** + * Returns number of elements in this array DB. + * @return number of elements in this array DB. + */ + int size(); + + /** + * Pops last element of the array DB. + * @return last element of array DB + * @throws IllegalStateException if array DB has zero elements. + */ + E pop(); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/BranchDB.java b/javascore/bmv/testsvc/src/main/java/score/BranchDB.java new file mode 100644 index 00000000..51bdc229 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/BranchDB.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * A branch DB is a hash from keys to sub-DBs. + * @param Key type. K shall be String, byte array, Address, + * Byte, Short, Integer, Long, Character or BigInteger. + * @param Value type. V shall be VarDB, DictDB, ArrayDB or BranchDB. + */ +public interface BranchDB { + /** + * Returns sub-DB for the key. + * + * @param key key for sub-DB. + * @return sub-DB. + */ + V at(K key); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/ByteArrayObjectWriter.java b/javascore/bmv/testsvc/src/main/java/score/ByteArrayObjectWriter.java new file mode 100644 index 00000000..b8a4b564 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/ByteArrayObjectWriter.java @@ -0,0 +1,21 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +public interface ByteArrayObjectWriter extends ObjectWriter { + byte[] toByteArray(); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/Context.java b/javascore/bmv/testsvc/src/main/java/score/Context.java new file mode 100644 index 00000000..6f03d6ac --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/Context.java @@ -0,0 +1,481 @@ +/* + * Copyright 2019 ICON Foundation + * Copyright (c) 2018 Aion Foundation https://aion.network/ + */ + +package score; + +import score.util.Crypto; +import java.math.BigInteger; + +/** + * Every SCORE has an associated Context which allows the application to interface + * with the environment the SCORE is running. + *

+ * Typically, it includes the transaction and block context, and other blockchain functionality. + */ +public final class Context { + + private Context() { + } + + //=================== + // Transaction + //=================== + + /** + * Returns the hash of the transaction. + * + * @return the transaction hash + */ + public static byte[] getTransactionHash() { + return null; + } + + /** + * Returns the transaction index in a block. + * + * @return the transaction index + */ + public static int getTransactionIndex() { + return 0; + } + + /** + * Returns the timestamp of a transaction request. + * + * @return the transaction timestamp + */ + public static long getTransactionTimestamp() { + return 0L; + } + + /** + * Returns the nonce of a transaction request. + * + * @return the transaction nonce + */ + public static BigInteger getTransactionNonce() { + return BigInteger.ZERO; + } + + /** + * Returns the address of the currently-running SCORE. + * + * @return an address + */ + public static Address getAddress() { + return null; + } + + /** + * Returns the caller's address. + * Note that the caller and the origin may be the same but differ in cross-calls: the origin is the sender + * of the "first" invocation in the chain while the caller is whoever directly called the current SCORE. + * + * @return an address + */ + public static Address getCaller() { + return null; + } + + /** + * Returns the originator's address. + * Note that the caller and the origin may be the same but differ in cross-calls: the origin is the sender + * of the "first" invocation in the chain while the caller is whoever directly called the current SCORE. + * Also, the origin never has associated code. + * + * @return an address + */ + public static Address getOrigin() { + return null; + } + + /** + * Returns the address of the account who initially deployed the contract. + * + * @return an address + */ + public static Address getOwner() { + return null; + } + + /** + * Returns the value being transferred to this SCORE. + * + * @return the value in loop (1 ICX == 10^18 loop) + */ + public static BigInteger getValue() { + return BigInteger.ZERO; + } + + //=================== + // Block + //=================== + + /** + * Returns the block timestamp. + * + * @return the timestamp of the current block in microseconds + */ + public static long getBlockTimestamp() { + return 0L; + } + + /** + * Returns the block height. + * + * @return the height of the block, in which the transaction is included + */ + public static long getBlockHeight() { + return 0L; + } + + //=================== + // Storage + //=================== + + /** + * Returns the balance of an account. + * + * @param address the account address + * @return the account balance, or 0 if the account does not exist + * @throws IllegalArgumentException if the address is invalid, e.g. NULL address + */ + public static BigInteger getBalance(Address address) throws IllegalArgumentException { + return BigInteger.ZERO; + } + + //=================== + // System + //=================== + + /** + * Calls the method of the given account address with the value. + * + * @param return type + * @param cls class of return type + * @param value the value in loop to transfer + * @param targetAddress the account address + * @param method method + * @param params parameters + * @return the invocation result + * @throws IllegalArgumentException if the arguments are invalid, e.g. insufficient balance, NULL address + * @throws RevertedException if call target reverts the newly created frame + * @throws UserRevertedException if call target reverts the newly created frame by calling {@link Context#revert} + */ + public static T call(Class cls, BigInteger value, + Address targetAddress, String method, Object... params) { + return null; + } + + /** + * Calls the method of the given account address with the value. + * + * @param value the value in loop to transfer + * @param targetAddress the account address + * @param method method + * @param params parameters + * @return the invocation result + * @throws IllegalArgumentException if the arguments are invalid, e.g. insufficient balance, NULL address + * @throws RevertedException if call target reverts the newly created frame + * @throws UserRevertedException if call target reverts the newly created frame by calling {@link Context#revert} + */ + public static Object call(BigInteger value, + Address targetAddress, String method, Object... params) { + return null; + } + + /** + * Calls the method of the account designated by the targetAddress. + * + * @param return type + * @param cls class of return type + * @param targetAddress the account address + * @param method method + * @param params parameters + * @return the invocation result + * @throws IllegalArgumentException if the arguments are invalid, e.g. insufficient balance, NULL address + * @throws RevertedException if call target reverts the newly created frame + * @throws UserRevertedException if call target reverts the newly created frame by calling {@link Context#revert} + */ + public static T call(Class cls, Address targetAddress, String method, + Object... params) { + return null; + } + + /** + * Calls the method of the account designated by the targetAddress. + * + * @param targetAddress the account address + * @param method method + * @param params parameters + * @return the invocation result + * @throws IllegalArgumentException if the arguments are invalid, e.g. insufficient balance, NULL address + * @throws RevertedException if call target reverts the newly created frame + * @throws UserRevertedException if call target reverts the newly created frame by calling {@link Context#revert} + */ + public static Object call(Address targetAddress, String method, Object... params) { + return null; + } + + /** + * Transfers the value to the given target address from this SCORE's account. + * + * @param targetAddress the account address + * @param value the value in loop to transfer + * @throws IllegalArgumentException if the arguments are invalid, e.g. insufficient balance, NULL address + */ + public static void transfer(Address targetAddress, BigInteger value) { + } + + /** + * Deploys a SCORE with the given byte streams. + * + * @param content the byte streams of the SCORE + * @param params parameters + * @return the newly created SCORE address + * @throws IllegalArgumentException if the arguments are invalid, e.g. corrupted content, etc. + */ + public static Address deploy(byte[] content, Object... params) { + return null; + } + + /** + * Deploys a SCORE with the given byte streams to the target address. + * + * @param targetAddress the SCORE address that is to be updated + * @param content the byte streams of the SCORE + * @param params parameters + * @return the target SCORE address + * @throws IllegalArgumentException if the arguments are invalid, e.g. corrupted content, NULL address + */ + public static Address deploy(Address targetAddress, byte[] content, Object... params) { + return null; + } + + /** + * Stops the current execution and rolls back all state changes. + * In case of cross-calls, {@link UserRevertedException} would be raised to the caller + * with the given code and message data. + * + * @param code an arbitrary user-defined code + * @param message a message to be delivered to the caller + */ + public static void revert(int code, String message) { + } + + /** + * Stops the current execution and rolls back all state changes. + * + * @param code an arbitrary user-defined code + * @see #revert(int, String) + */ + public static void revert(int code) { + } + + /** + * Stops the current execution and rolls back all state changes. + * + * @param message a message + * @see #revert(int, String) + */ + public static void revert(String message) { + } + + /** + * Stops the current execution and rolls back all state changes. + * This is equivalent to {@code revert(0)}. + * + * @see #revert(int) + */ + public static void revert() { + } + + /** + * Checks that the provided condition is true and if it is false, triggers a revert. + *

+ * In other words, if {@code condition == true}, this method does nothing, + * otherwise it is equivalent to calling {@link Context#revert()}. + * + * @param condition the condition that is required to be {@code true}. + */ + public static void require(boolean condition) { + } + + /** + * Prints a message, for debugging purpose. + * + * @param message the message to print + */ + public static void println(String message) { + System.out.println(message); + } + + /** + * Returns hash value of the given message. + * @param alg hash algorithm. One of sha-256, sha3-256, xxhash-128, + * blake2b-128 and blake2b-256. + * @param msg message + * @return hash value + * @throws IllegalArgumentException if the algorithm is unsupported. + */ + public static byte[] hash(String alg, byte[] msg) { + return Crypto.hash(alg, msg); + } + + /** + * Returns {@code true} if the given signature for the given message by + * the given public key is correct. + * @param alg signature algorithm. One of ed25519 and ecdsa-secp256k1 + * @param msg message + * @param sig signature + * @param pubKey public key + * @return {@code true} if the given signature for the given message by + * the given public key is correct. + * @throws IllegalArgumentException if the algorithm is unsupported. + */ + public static boolean verifySignature(String alg, byte[] msg, byte[] sig, byte[] pubKey) { + return Crypto.verifySignature(alg, msg, sig, pubKey); + } + + /** + * Recovers the public key from the message and the recoverable signature. + * @param alg signature algorithm. ecdsa-secp256k1 is supported. + * @param msg message + * @param sig signature + * @param compressed the type of public key to be returned + * @return the public key recovered from message and signature + * @throws IllegalArgumentException if the algorithm is unsupported. + */ + public static byte[] recoverKey(String alg, byte[] msg, byte[] sig, boolean compressed) { + return Crypto.recoverKey(alg, msg, sig, compressed); + } + + /** + * Returns the address that is associated with the given public key. + * + * @param pubKey a byte array that represents the public key + * @return the address that is associated with the public key + */ + public static Address getAddressFromKey(byte[] pubKey) { + return null; + } + + //=================== + // Fee Sharing + //=================== + + /** + * Returns the current fee sharing proportion of the SCORE. + * 100 means the SCORE will pay 100% of transaction fees on behalf of the transaction sender. + * + * @return the current fee sharing proportion that the SCORE will pay (0 to 100) + */ + public static int getFeeSharingProportion() { + return 0; + } + + /** + * Sets the proportion of transaction fees that the SCORE will pay. + * {@code proportion} should be between 0 to 100. + * If this method is invoked multiple times, the last proportion value will be used. + * + * @param proportion the desired proportion of transaction fees that the SCORE will pay + * @throws IllegalArgumentException if the proportion is not between 0 to 100 + */ + public static void setFeeSharingProportion(int proportion) { + } + + //=================== + // Collection DB + //=================== + + /** + * Returns a new branch DB. + * + * @param id DB ID + * @param leafValueClass class of leaf value. For example, a branch DB of + * type {@code BranchDB>} has + * Boolean.class as its leaf value class. + * @param key type + * @param sub-DB type + * @return new branch DB + * @see BranchDB + */ + public static BranchDB newBranchDB(String id, Class leafValueClass) { + return null; + } + + /** + * Returns a new dictionary DB. + * + * @param id DB ID + * @param valueClass class of {@code V} + * @param key type + * @param value type + * @return new dictionary DB + * @see DictDB + */ + public static DictDB newDictDB(String id, Class valueClass) { + return null; + } + + /** + * Returns a new array DB. + * + * @param id DB ID + * @param valueClass class of {@code E} + * @param element type + * @return new array DB + * @see ArrayDB + */ + public static ArrayDB newArrayDB(String id, Class valueClass) { + return null; + } + + /** + * Returns a new variable DB. + * + * @param id DB ID + * @param valueClass class of {@code E} + * @param variable type + * @return new variable DB + * @see ArrayDB + */ + public static VarDB newVarDB(String id, Class valueClass) { + return null; + } + + /** + * Records a log on the blockchain. It is recommended to use + * {@link score.annotation.EventLog} annotation rather than this method directly. + * + * @param indexed indexed data + * @param data extra data + */ + public static void logEvent(Object[] indexed, Object[] data) { + } + /** + * Returns a new object reader reading from a byte array. + * @param codec codec. Currently "RLPn" is supported. + * @param byteArray byte array. + * @return object reader. + * @throws IllegalArgumentException if the codec is unsupported. + */ + public static ObjectReader newByteArrayObjectReader(String codec, + byte[] byteArray) { + return null; + } + + /** + * Returns a new object writer writing to a byte array. + * @param codec codec. Currently "RLPn" is supported. + * @return byte array object writer + * @throws IllegalArgumentException if the codec is unsupported. + */ + public static ByteArrayObjectWriter newByteArrayObjectWriter( + String codec) { + return null; + } +} \ No newline at end of file diff --git a/javascore/bmv/testsvc/src/main/java/score/DictDB.java b/javascore/bmv/testsvc/src/main/java/score/DictDB.java new file mode 100644 index 00000000..5db04b87 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/DictDB.java @@ -0,0 +1,53 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * A dictionary DB is a hash from key to value. + * Only values of the dictionary DB is recorded in the DB. + * Keys are not recorded. + * @param Key type. It shall be String, byte array, Address, + * Byte, Short, Integer, Long, Character or BigInteger. + * @param Value type. It shall be readable and writable class. + * @see ObjectReader + * @see ObjectWriter + */ +public interface DictDB { + /** + * Sets a value for a key + * @param key key + * @param value value for the key + */ + void set(K key, V value); + + /** + * Returns the value for a key + * @param key key + * @return the value for a key + */ + V get(K key); + + /** + * Returns the value for a key or {@code defaultValue} if the value is + * {@code null}. + * @param key key + * @param defaultValue default value + * @return the value for a key or {@code defaultValue} if the value is + * {@code null}. + */ + V getOrDefault(K key, V defaultValue); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/ObjectReader.java b/javascore/bmv/testsvc/src/main/java/score/ObjectReader.java new file mode 100644 index 00000000..883a02ca --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/ObjectReader.java @@ -0,0 +1,472 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +import java.math.BigInteger; + +/** + * Interface for object read. + * + *

Common specification for object reader and object writer is specified in + * {@link ObjectWriter}. It is recommended to read object writer documentation + * first before you read this documentation. + * + *

An object is readable if the object is a builtin object or its class + * has the following method. + *

+ *  public static UserClass readObject(ObjectReader r)
+ * 
+ * + *

A simple object is read by corresponding read method. + * The following is an example of reading simple objects. + *

+ *      str = objectReader.readString();
+ *      i = objectReader.readInt();
+ * 
+ * + *

A list is read by a {@link #beginList} call followed by calls for reading + * zero or more its elements followed by a {@link #end} call. + * + *

A map is read by a {@link #beginMap} call followed by calls for reading + * zero or more its elements followed by a {@link #end} call. + * + *

You can read a custom object indirectly if its class has the following + * method. + *

+ *  public static UserClass readObject(ObjectReader r)
+ * 
+ * When you read a custom object of this class, the {@code readObject} method + * is called. In the method, you must read one equivalent builtin object. + * It is error to read no object or two or more objects. The method may + * read on list with multiple elements. + * + *

If an exception is thrown during any read call, the reader becomes + * invalidated. An invalidated reader fails any method. + * + * @see ObjectWriter + */ +public interface ObjectReader { + /** + * Reads a boolean. + * + * @return the boolean read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + boolean readBoolean(); + + /** + * Reads a byte. + * + * @return the byte read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + byte readByte(); + + /** + * Reads a short. + * + * @return the short read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + short readShort(); + + /** + * Reads a character. + * + * @return the char read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + char readChar(); + + /** + * Reads an integer. + * + * @return the int read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + int readInt(); + + /** + * Reads a float. + * + * @return the float read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + float readFloat(); + + /** + * Reads a long. + * + * @return the long read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + long readLong(); + + /** + * Reads a double. + * + * @return the double read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + double readDouble(); + + /** + * Reads a big integer. + * + * @return the big integer read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + BigInteger readBigInteger(); + + /** + * Reads a string. + * + * @return the string read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + String readString(); + + /** + * Reads a byte array. + * + * @return the byte array read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + byte[] readByteArray(); + + /** + * Reads an address. + * + * @return the address read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + Address readAddress(); + + /** + * Reads an object of the class {@code c}. + * + * @param type of object to be read + * @param c Class of object to be read. It shall be one of + * {@link Boolean}, {@link Byte}, {@link Short}, {@link Character}, + * {@link Integer}, {@link Float}, {@link Long}, {@link Double}, + * {@link BigInteger}, {@link String}, byte array, {@link Address}, + * or a custom class with the following method: + *

+     * public static UserClass readObject(ObjectReader r)
+     * 
+ * @return the object read. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code readObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + */ + T read(Class c); + + /** + * Reads an object or returns default object if there is no next object. + * + * @param type of object to be read + * @param c class of object to be read. + * @param def the default object. + * @return the object read or default object. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of type mismatch, corrupted stream + * or invalidated reader. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code readObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + * @see #hasNext + */ + T readOrDefault(Class c, T def); + + /** + * Reads a nullable object. + * + * @param type of object to be read + * @param c class of object to be read. + * @return read object or null. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code readObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + */ + T readNullable(Class c); + + /** + * Reads a nullable object or returns default object if there is no next + * object. + * + * @param type of object to be read + * @param c class of object to be read. + * @param def the default object. + * @return read object or null if an object or null is read. Default object + * if there is no more item in current list or map. + * @throws IllegalStateException If this reader cannot read the + * given type of object because of type mismatch, corrupted stream + * or invalidated reader. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code readObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #read(Class) + * @see #hasNext + */ + T readNullableOrDefault(Class c, T def); + + /** + * Reads a list header and begins a list. + * + *

If a list was successfully begun, a read operation reads an element + * of the list in writing order. After reading zero or more elements, the + * caller must call {@link #end} to end list. It is not required to read all + * elements before an {@link #end} call. + * + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + */ + void beginList(); + + /** + * Reads a nullable list header. If the reader reads a list header, a list + * is begun and {@code true} is returned. If the reader reads null, no list + * is begun and {@code false} is returned. + * + *

If a list was successfully begun, a read operation reads an element + * of the list in writing order. After reading zero or more elements, the + * caller must call {@link #end} to end list. It is not required to read all + * elements before an {@link #end} call. + * + * @return true if a list header is read or false if null is read + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #beginList + */ + boolean beginNullableList(); + + /** + * Reads a map header and begins a map. + * + *

If a map was successfully begun, elements of map can be read. + * A map element consist of a key and its value and can be read by + * two separate reads and elements are read in writing order. For example, + * The first read operation after beginning of a map reads the key of the + * first map element and the next read operation reads it value and the next + * read operation reads the key of the second element, and so on. + * After reading keys and values, the caller must call {@link #end} to end + * map. It is not required to read all elements before an {@link #end} call. + * + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + */ + void beginMap(); + + /** + * Reads a nullable map header. If the reader reads a map header, a map is + * begun and {@code true} is returned. If the reader reads null, no map is + * begun and {@code false} is returned. + * + *

If a map was successfully begun, elements of map can be read. + * A map element consist of a key and its value and can be read by + * two separate reads and elements are read in writing order. For example, + * The first read operation after beginning of a map reads the key of the + * first map element and the next read operation reads it value and the next + * read operation reads the key of the second element, and so on. + * After reading keys and values, the caller must call {@link #end} to end + * map. It is not required to read all elements before an {@link #end} call. + * + * @return true if a map header is read or false if null is read + * + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + * @see #beginMap + */ + boolean beginNullableMap(); + + /** + * Returns true if this reader has next object to read. + * If the reader is reading a container, this method returns true if the top + * most container has more object to read, + * returns false otherwise. + * + * @return true if this reader has next object to read. + * @throws IllegalStateException if this reader was already invalidated. + */ + boolean hasNext(); + + /** + * Ends the current container. Unread elements of the current container + * are all skipped. + * + * @throws IllegalStateException if this end is imbalanced. + */ + void end(); + + /** + * Skips an element of the current container. + * + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + */ + void skip(); + + /** + * Skips elements of the current container. + * + * @param count the count. + * + * @throws IllegalStateException If this reader cannot read the + * given type of object because of end of stream, end of list, + * end of map, type mismatch, corrupted stream or invalidated + * reader. + * @throws UnsupportedOperationException If this reader cannot read an + * object because the object is too long (for example, 2^32 bytes + * or longer byte array). + */ + void skip(int count); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/ObjectWriter.java b/javascore/bmv/testsvc/src/main/java/score/ObjectWriter.java new file mode 100644 index 00000000..26f98caf --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/ObjectWriter.java @@ -0,0 +1,384 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +import java.math.BigInteger; + +/** + * Interface for object write. + * + *

An object is writable if the object is a builtin object or its class + * has the following method. + *

+ *  public static void writeObject(ObjectWriter w, UserClass obj)
+ * 
+ * + *

A builtin object is a simple object or a container object. A simple object + * is + * a {@link Boolean}, a {@link Byte}, a {@link Short}, a {@link Character}, + * a {@link Integer}, a {@link Float}, a {@link Long}, a {@link Double}, + * a {@link BigInteger}, a {@link String}, byte array or an {@link Address}. + * They are written by corresponding write methods. The following is an + * example of writing simple objects. + *

+ *      objectWriter.writeString("a string");
+ *      objectWriter.writeInt(0);
+ * 
+ * + *

A container object is a list or a map. A container may have another + * container as its element. + * + *

A list has zero or more builtin objects as its elements. A list is written + * by a {@link #beginList(int)} call followed by calls for writings of zero or + * more its elements followed by a {@link #end} call. For example, the following + * method writes a list of two elements. + *

+ *      objectWriter.beginList(2);
+ *          objectWriter.writeInt(0);
+ *          objectWriter.beginList(0);
+ *          objectWriter.end();
+ *      objectWriter.end();
+ * 
+ * + *

A map has zero or more pairs of objects as its elements. A map is written + * by a {@link #beginMap(int)} call followed by calls for writings of zero or + * more its elements followed by a {@link #end} call. For example, the following + * method writes a map of one element. + *

+ *      objectWriter.beginMap(1);
+ *          objectWriter.writeString("key");
+ *          objectWriter.writeString("value");
+ *      objectWriter.end();
+ * 
+ * + *

You can write a custom object indirectly if its class has the following + * method. + *

+ *  public static void writeObject(ObjectWriter w, UserClass obj)
+ * 
+ * When you write a custom object, the + * {@code writeObject} method is called. In the method, you must + * write one equivalent builtin object. It is error to write no object or + * two or more objects. The method may write one list with multiple elements. + * For example, the following is error. + *
+ *  public static void writeObject(ObjectWriter w, UserClass obj) {
+ *      w.writeString(obj.name);
+ *      w.writeString(obj.description);
+ *  }
+ * 
+ * Instead, write a list with multiple elements if you want to write multiple + * objects. + *
+ *  public static void writeObject(ObjectWriter w, UserClass obj) {
+ *      w.beginList(2);
+ *          w.writeString(obj.name);
+ *          w.writeString(obj.description);
+ *      w.end();
+ *  }
+ * 
+ * + *

You can write an object as a non-nullable type object or nullable type + * object. A non-nullable type object is always not null. A nullable object may + * be null or non-null. A write method writes an object as non-nullable type + * unless the document specifies the method writes a nullable type object. + * For example, {@link #write(String)} writes non-nullable type object. + * {@link #writeNullable(Object)} and {@link #writeNull()} methods + * write a nullable type object. {@link #beginNullableList(int)} begins a + * nullable list. + * + *

Nullable-ness shall be preserved during write and read. If a non-nullable + * object is written, the object shall be read as a non-nullable object and + * shall not be read as a nullable object later. If a nullable object is + * written, the object shall be read as a nullable object and shall not be read + * a non-nullable object later. For example, if an object is written as the + * following: + * + *

+ *     Integer i = getInteger();
+ *     objectWriter.writeNullable(i);
+ * 
+ *

+ * It is error to read the object as the following: + *

+ *     int i = objectReader.readInt();
+ * 
+ *

+ * Instead, the object shall be read as the following: + *

+ *     Integer i = objectReader.readNullable(Integer.class);
+ * 
+ * + *

If an exception is thrown during any write call, the writer becomes + * invalidated. An invalidated writer fails any method. + * + * @see ObjectReader + */ +public interface ObjectWriter { + /** + * Writes a boolean. + * + * @param v a boolean value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(boolean v); + + /** + * Writes a byte. + * + * @param v a byte value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(byte v); + + /** + * Writes a short. + * + * @param v a short value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(short v); + + /** + * Writes a character. + * + * @param v a character value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(char v); + + /** + * Writes a integer. + * + * @param v an integer value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(int v); + + /** + * Writes a float. + * + * @param v a float value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(float v); + + /** + * Writes a long. + * + * @param v a long value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(long v); + + /** + * Writes a double. + * + * @param v a double value. + * @throws IllegalStateException if this writer is invalidated one. + */ + void write(double v); + + /** + * Writes a big integer. + * + * @param v a big integer. + * @throws IllegalStateException if this writer is invalidated one. + * @throws NullPointerException If {@code v} is {@code null}. + */ + void write(BigInteger v); + + /** + * Writes a string. + * + * @param v a string. + * @throws IllegalStateException if this writer is invalidated one. + * @throws NullPointerException If {@code v} is {@code null}. + */ + void write(String v); + + /** + * Writes a byte array. + * + * @param v a byte array. + * @throws IllegalStateException if this writer is invalidated one. + * @throws NullPointerException If {@code v} is {@code null}. + */ + void write(byte[] v); + + /** + * Writes an address. + * + * @param v an address. + * @throws IllegalStateException if this writer is invalidated one. + * @throws NullPointerException If {@code v} is {@code null}. + */ + void write(Address v); + + /** + * Writes an object. The class of the object shall be + * {@link Boolean}, {@link Byte}, {@link Short}, {@link Character}, + * {@link Integer}, {@link Float}, {@link Long}, {@link Double}, + * {@link BigInteger}, {@link String}, byte array, {@link Address}, + * or a custom class with the following method. + * + *

+     * public static void writeObject(ObjectWriter w, UserClass obj)
+     * 
+ * + * @param v object to be written. + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws NullPointerException If {@code v} is {@code null}. + */ + void write(Object v); + + /** + * Writes a nullable object. + * + * @param v object to be written. + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @see #write(Object) + */ + void writeNullable(Object v); + + /** + * Writes objects. + * + * @param v objects to be written. + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @throws NullPointerException If {@code v} is {@code null}. + * @see #write(Object) + */ + void write(Object... v); + + /** + * Writes nullable objects. + * + * @param v objects to be written. + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @see #write(Object) + */ + void writeNullable(Object... v); + + /** + * Writes a null. + * + * @throws IllegalStateException if this writer is invalidated one. + */ + void writeNull(); + + /** + * Writes a list header and begins a list. + *

+ * A following write operation writes an element of list. When all elements + * are written you must call {@link #end} to end the current list. + * + * @param l number of elements. + * @throws IllegalStateException if this writer is invalidated one. + * @see ObjectWriter + */ + void beginList(int l); + + /** + * Writes a nullable list header and begins a list. + *

+ * A following write operation writes an element of list. When all elements + * are written you must call {@link #end} to end the current list. + * + * @param l number of elements. + * @throws IllegalStateException if this writer is invalidated one. + * @see ObjectWriter + */ + void beginNullableList(int l); + + /** + * Writes a list. The operation writes a list header and writes elements + * and ends the list. + * + * @param v list elements + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @see ObjectWriter + */ + void writeListOf(Object... v); + + /** + * Writes a list of nullable. The operation writes a list header and writes + * elements and ends the list. + * + * @param v list elements + * @throws IllegalStateException if this writer is invalidated one. + * @throws IllegalArgumentException If the object is not a simple object + * and correct {@code writeObject} method is not available or + * the method threw {@link Throwable} which is not an + * {@link RuntimeException}. + * @see ObjectWriter + */ + void writeListOfNullable(Object... v); + + /** + * Writes a map header and begins a map. + *

+ * A following write operation writes an element of map. When all elements + * are written you must call {@link #end} to end the current map. + * + * @param l number of elements. + * @throws IllegalStateException if this writer is invalidated one. + * @see ObjectWriter + */ + void beginMap(int l); + + /** + * Writes a nullable map header and begins a map. + *

+ * A following write operation writes an element of map. When all elements + * are written you must call {@link #end} to end the current map. + * + * @param l number of elements. + * @throws IllegalStateException if this writer is invalidated one. + * @see ObjectWriter + */ + void beginNullableMap(int l); + + /** + * Ends the current container. + * + * @throws IllegalStateException if this writer is invalidated one or + * if this end is imbalanced. + * @see ObjectWriter + */ + void end(); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/RevertedException.java b/javascore/bmv/testsvc/src/main/java/score/RevertedException.java new file mode 100644 index 00000000..3d704eb4 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/RevertedException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * Signals a failure of an inter-contract call. + */ +public class RevertedException extends RuntimeException { + /** + * Constructs a new exception. + */ + public RevertedException() { + super(); + } + + /** + * Constructs a new exception. + * @param message message + */ + public RevertedException(String message) { + super(message); + } + + /** + * Constructs a new exception. + * @param message message + * @param cause cause + */ + public RevertedException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception. + * @param cause cause + */ + public RevertedException(Throwable cause) { + super(cause); + } +} diff --git a/javascore/bmv/testsvc/src/main/java/score/UserRevertException.java b/javascore/bmv/testsvc/src/main/java/score/UserRevertException.java new file mode 100644 index 00000000..b7d4c7f9 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/UserRevertException.java @@ -0,0 +1,53 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * Signals failure of an {@link score.annotation.External} method. If an + * external method is called by an external transaction or an internal + * transaction and the method throws an exception of this class or subclass of + * this class during the execution, the transaction fails and the code returned + * by {@link #getCode} is used as user failure code of the transaction. If the + * code is out of range, the code is clamped. + * + *

User may extend this class and override {@link #getCode} method. + */ +public class UserRevertException extends RuntimeException { + public UserRevertException() { + } + + public UserRevertException(String message) { + super(message); + } + + public UserRevertException(String message, Throwable cause) { + super(message, cause); + } + + public UserRevertException(Throwable cause) { + super(cause); + } + + /** + * Returns error code. Subclass may override this method to change failure + * code. Default implementation returns {@code 0}. + * @return error code. + */ + public int getCode() { + return 0; + } +} diff --git a/javascore/bmv/testsvc/src/main/java/score/UserRevertedException.java b/javascore/bmv/testsvc/src/main/java/score/UserRevertedException.java new file mode 100644 index 00000000..d1311c5f --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/UserRevertedException.java @@ -0,0 +1,108 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * Signals a manual reversion from a score. + */ +public class UserRevertedException extends RevertedException { + // NOTE: the following codes should be matched with {@code foundation.icon.ee.types.Status} + private static final int Start = 32; + private static final int End = 1000 - Start; + + private int statusCode; + + /** + * Constructs a new exception + */ + public UserRevertedException() { + super(); + } + + /** + * Constructs a new exception + * @param message message + */ + public UserRevertedException(String message) { + super(message); + } + + /** + * Constructs a new exception + * @param message message + * @param cause cause + */ + public UserRevertedException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs a new exception + * @param cause cause + */ + public UserRevertedException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new exception + * @param code reversion code defined by score + */ + public UserRevertedException(int code) { + super(); + statusCode = code; + } + + /** + * Constructs a new exception + * @param code reversion code defined by score + * @param message message + */ + public UserRevertedException(int code, String message) { + super(message); + statusCode = code; + } + + /** + * Constructs a new exception + * @param code reversion code defined by score + * @param message message + * @param cause cause + */ + public UserRevertedException(int code, String message, Throwable cause) { + super(message, cause); + statusCode = code; + } + + /** + * Constructs a new exception + * @param code reversion code defined by score + * @param cause cause + */ + public UserRevertedException(int code, Throwable cause) { + super(cause); + statusCode = code; + } + + /** + * Returns reversion code. + * @return reversion code. + */ + public int getCode() { + return statusCode; + } +} diff --git a/javascore/bmv/testsvc/src/main/java/score/VarDB.java b/javascore/bmv/testsvc/src/main/java/score/VarDB.java new file mode 100644 index 00000000..f9ded9db --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/VarDB.java @@ -0,0 +1,46 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score; + +/** + * A variable DB holds one value. + * @param Variable type. It shall be readable and writable class. + * @see ObjectReader + * @see ObjectWriter + */ +public interface VarDB { + /** + * Sets value. + * @param value new value + */ + void set(E value); + + /** + * Returns the current value. + * @return current value + */ + E get(); + + /** + * Returns the current value or {@code defaultValue} if the current value + * is {@code null}. + * @param defaultValue default value + * @return the current value or {@code defaultValue} if the current value + * is {@code null}. + */ + E getOrDefault(E defaultValue); +} diff --git a/javascore/bmv/testsvc/src/main/java/score/annotation/EventLog.java b/javascore/bmv/testsvc/src/main/java/score/annotation/EventLog.java new file mode 100644 index 00000000..e8e38776 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/annotation/EventLog.java @@ -0,0 +1,41 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to record logs in its TxResult as {@code eventLogs}. + * + *

If the value of an element, named {@code indexed}, is set, the designated number of parameters + * of the applied method declaration will be indexed in the order and included in the Bloom filter. + * Indexed parameters and non-indexed parameters are separately stored in the TxResult. + * At most 3 parameters can be indexed, and the value of {@code indexed} cannot exceed the number of parameters. + * Possible data types for method parameters are {@code int}, {@code boolean}, {@code byte[]}, + * {@code BigInteger}, {@code String}, and {@code Address}. + * + *

It is recommended to declare a method without a implementation body. + * Even if the applied method has the body, it does not be executed in runtime. + */ +@Target(ElementType.METHOD) +public @interface EventLog { + /** + * The number of indexed parameters of the applied method declaration (maximum 3). + */ + int indexed() default 0; +} diff --git a/javascore/bmv/testsvc/src/main/java/score/annotation/External.java b/javascore/bmv/testsvc/src/main/java/score/annotation/External.java new file mode 100644 index 00000000..5cba8dd8 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/annotation/External.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to indicate whether the method will be exposed externally. + * + *

In order for a method to be called from outside the contract (EOA or another contract), + * it needs to be annotated as {@code External}. + * The annotated methods will be registered on the exportable API list. + * Any attempt to call a non-external method from outside the contract will fail. + * + *

If the {@code readonly} element is specified and its value is {@code true}, i.e., {@code @External(readonly=true)}, + * the method will have read-only access to the state DB. + * + *

NOTE: The special method, named {@code fallback}, cannot be annotated with {@code @External}. + * (i.e., {@code fallback} method cannot be specified in the transaction message as a callee method, + * and it can only be called via plain ICX transfer message.) + */ +@Target(ElementType.METHOD) +public @interface External { + /** + * The method will have read-only access to the state DB if this value is {@code true}. + */ + boolean readonly() default false; +} diff --git a/javascore/bmv/testsvc/src/main/java/score/annotation/Keep.java b/javascore/bmv/testsvc/src/main/java/score/annotation/Keep.java new file mode 100644 index 00000000..7f85daa3 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/annotation/Keep.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Denotes that the element should not be removed when the code is optimized by + * tool kit. + */ +@Target({ElementType.METHOD, ElementType.FIELD}) +public @interface Keep { +} diff --git a/javascore/bmv/testsvc/src/main/java/score/annotation/Optional.java b/javascore/bmv/testsvc/src/main/java/score/annotation/Optional.java new file mode 100644 index 00000000..53b9ff62 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/annotation/Optional.java @@ -0,0 +1,36 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to indicate whether the method parameter is optional. + * + *

If a method parameter is annotated with this {@code Optional}, the parameter can be omitted + * in the transaction message. + * If optional parameters were omitted when the external method is called, the value of optional parameters + * would be their zero values. + * The zero value is: + * 0 for numeric types, + * false for the boolean type, and + * null for Object types. + */ +@Target(ElementType.PARAMETER) +public @interface Optional { +} diff --git a/javascore/bmv/testsvc/src/main/java/score/annotation/Payable.java b/javascore/bmv/testsvc/src/main/java/score/annotation/Payable.java new file mode 100644 index 00000000..808c2588 --- /dev/null +++ b/javascore/bmv/testsvc/src/main/java/score/annotation/Payable.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package score.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * Annotation that can be used to indicate whether the method can receive ICX coins. + * + *

If this annotation is applied to the external method, the method can receive the incoming ICX coins + * designated in the transaction message and process further works for it. + * Users can get the value of transferred ICX coins by using {@link score.Context#getValue()}. + * + *

If ICX coins were passed to a non-payable method, that transaction would fail. + * + *

NOTE: The special method, named {@code fallback}, is invoked whenever the contract receives + * plain ICX coins without data. However, if the {@code fallback} method was not annotated with {@code @Payable}, + * it would not be listed on the SCORE APIs and could not be called as well. + */ +@Target(ElementType.METHOD) +public @interface Payable { +} diff --git a/javascore/bmv_without_relaychain.tar.gz b/javascore/bmv_without_relaychain.tar.gz new file mode 100644 index 00000000..75d7eca9 Binary files /dev/null and b/javascore/bmv_without_relaychain.tar.gz differ diff --git a/javascore/build.gradle b/javascore/build.gradle new file mode 100644 index 00000000..532aff7b --- /dev/null +++ b/javascore/build.gradle @@ -0,0 +1,99 @@ +import java.nio.file.Files + +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath 'foundation.icon:gradle-javaee-plugin:0.7.8' + } +} + +configurations.all { + resolutionStrategy.cacheChangingModulesFor 1, 'minutes' +} + +subprojects { + repositories { + mavenLocal() + mavenCentral() + } + + apply plugin: 'java' + apply plugin: 'foundation.icon.javaee' + + java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + // need to add this option to retrieve formal parameter names + compileJava { + options.compilerArgs += ['-parameters'] + } + + ext { + javaeeVersion = getProperty("javaee.version") + scorexVersion = getProperty("scorex.version") + javaeeUnittestVersion = getProperty("javaee-unittest.version") + scoreClientVersion = getProperty("score-client.version") + iconsdkVersion = getProperty("iconsdk.version") + jacksonVersion = getProperty("jackson.version") + debugJar = Boolean.parseBoolean(getProperty("debugJar").toString()) + integrationTest = Boolean.parseBoolean(getProperty("integrationTest").toString()) + } + + def loadProperties = { + def prefix = 'score-test.' + def props = project.getProperties() + def scoreTest = new HashMap() + def keySet = ['keyStore','keyPassword','keySecret'] + props.each { + if (it.key.startsWith(prefix)) { + def splited = it.key.split("\\.") + if (splited.length == 3) { + def user = splited[1] + def key = splited[2] + if (keySet.contains(key)) { + def obj = scoreTest.getOrDefault(user, new HashMap()) + obj.put(key, it.value.toString()) + scoreTest.put(user, obj) + } + } + } + } + scoreTest.each { + def resolvedKeyPassword = it.value.get('keyPassword') + if (resolvedKeyPassword == null) { + if (it.value.containsKey('keySecret')) { + resolvedKeyPassword = Files.readString( + java.nio.file.Path.of(it.value.get('keySecret').toString())) + } + it.value.put('resolvedKeyPassword', resolvedKeyPassword) + } + } + + scoreTest.put('url', props.getOrDefault(prefix + 'url', 'http://localhost:9082/api/v3')) + scoreTest.put('nid', props.getOrDefault(prefix + 'nid', 3)) + scoreTest.put('parseNid', { Object v -> + if (v instanceof String) { + return v.startsWith("0x") ? + Integer.parseInt(v.substring(2), 16) : + Integer.parseInt(v) + } else { + return v as int + } + }) + + if (!scoreTest.containsKey("default")) { + scoreTest.put("default", Map.of("keyStore","","keySecret","","resolvedKeyPassword","")) + } + if (!scoreTest.containsKey("tester")) { + scoreTest.put("tester", Map.of("keyStore","","keySecret","","resolvedKeyPassword","")) + } + + ext['scoreTest'] = scoreTest + } + loadProperties() +} \ No newline at end of file diff --git a/javascore/fee_aggregation/CPFTreasury.zip b/javascore/fee_aggregation/CPFTreasury.zip new file mode 100644 index 00000000..dc4d3d17 Binary files /dev/null and b/javascore/fee_aggregation/CPFTreasury.zip differ diff --git a/javascore/fee_aggregation/README.md b/javascore/fee_aggregation/README.md new file mode 100644 index 00000000..029e3ac0 --- /dev/null +++ b/javascore/fee_aggregation/README.md @@ -0,0 +1,73 @@ +## FeeAggregation + + +### Requirements +- Install [ICON Local Node](https://github.com/icon-project/goloop/blob/master/doc/gochain_icon_local_node_guide.md) + +### Build +``` +$ ./gradlew build +$ ./gradlew optimizedJar +``` +### Deploy contract +``` +./gradlew deployToLocal -PkeystoreName= -PkeystorePass= +``` + +### Tests +``` +$ ./gradlew test +``` + +### Integration Tests +**NOTE!!** +You should update the config file before run integration tests +- With gochain, default: +``` +# config/env.props +node.url=http://localhost:9082/api/v3 + +chain.nid=0x03 +chain.godWallet=godWallet.json +chain.godPassword=gochain +``` +```shell +# godWallet.json +{ + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } +} +``` + +- With goloop, config: +``` +node.url=http://localhost:9080/api/v3/{channel} + +chain.nid={{ nid in channel config }} +chain.godWallet=godWallet.json # need update follow by channel config +chain.godPassword={{ password in channel config }} +``` + +**Run integration test** +``` +$ ./gradlew integrationTest +``` + diff --git a/javascore/fee_aggregation/SampleMultiToken.zip b/javascore/fee_aggregation/SampleMultiToken.zip new file mode 100644 index 00000000..e7a90ec9 Binary files /dev/null and b/javascore/fee_aggregation/SampleMultiToken.zip differ diff --git a/javascore/fee_aggregation/bandProtocol.zip b/javascore/fee_aggregation/bandProtocol.zip new file mode 100644 index 00000000..89a3a9be Binary files /dev/null and b/javascore/fee_aggregation/bandProtocol.zip differ diff --git a/javascore/fee_aggregation/build.gradle b/javascore/fee_aggregation/build.gradle new file mode 100644 index 00000000..ae2031c8 --- /dev/null +++ b/javascore/fee_aggregation/build.gradle @@ -0,0 +1,113 @@ +buildscript { + repositories { + mavenCentral() + maven { + url 'https://oss.jfrog.org/artifactory/oss-snapshot-local' + } + } + dependencies { + classpath 'foundation.icon:gradle-javaee-plugin:0.7.8' + } +} + +apply plugin: 'java' +apply plugin: 'java-gradle-plugin' +apply plugin: 'foundation.icon.javaee' + +group 'foundation.icon.btp' +version '1.0' + +// for integration tests +sourceSets { + intTest {} +} + +configurations { + intTestImplementation.extendsFrom testImplementation + intTestRuntimeOnly.extendsFrom testRuntimeOnly +} + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} + +compileJava { + options.compilerArgs += ['-parameters'] +} + +repositories { + mavenCentral() + maven { + url 'https://oss.jfrog.org/artifactory/oss-snapshot-local' + } +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.FeeAggregationSCORE' +} + +gradlePlugin { + automatedPublishing = false + plugins { + javaeePlugin { + id = 'foundation.icon.javaee' + implementationClass = 'foundation.icon.gradle.plugins.javaee.JavaeePlugin' + } + } +} + +dependencies { + compileOnly 'foundation.icon:javaee-api:0.8.9' + implementation 'com.github.sink772:javaee-scorex:0.5.1' + implementation 'com.github.sink772:javaee-tokens:0.5.4' + implementation 'com.github.sink772:minimal-json:0.9.6' + + implementation 'foundation.icon:icon-sdk:2.0.0' + testImplementation 'org.mockito:mockito-core:3.3.3' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' + + intTestImplementation 'foundation.icon:icon-sdk:2.0.0' + intTestImplementation 'com.squareup.okhttp3:okhttp:3.11.0' +} + +deployJar { + endpoints { + gangnam { + uri = 'https://sejong.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = 'http://localhost:9080/api/v3/icon' + nid = 3 + } + } + keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : '' + password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : '' + parameters { + arg('_cps_address', 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd') + } +} + +test { + useJUnitPlatform() +} + +task integrationTest(type: Test) { + useJUnitPlatform() + description = 'Runs integration tests.' + group = 'verification' + + testClassesDirs = sourceSets.intTest.output.classesDirs + classpath = sourceSets.intTest.runtimeClasspath + testLogging.showStandardStreams = true + + // use the common config files + systemProperty('env.props', new File('conf/env.props')) + + def prefix = 'score.path.' + systemProperty(prefix + project.name, optimizedJar.outputJarName) +} + + diff --git a/javascore/fee_aggregation/conf/env.props b/javascore/fee_aggregation/conf/env.props new file mode 100644 index 00000000..d866164d --- /dev/null +++ b/javascore/fee_aggregation/conf/env.props @@ -0,0 +1,5 @@ +node.url=http://localhost:9082/api/v3 + +chain.nid=0x03 +chain.godWallet=godWallet.json +chain.godPassword=gochain diff --git a/javascore/fee_aggregation/conf/godWallet.json b/javascore/fee_aggregation/conf/godWallet.json new file mode 100644 index 00000000..978fea42 --- /dev/null +++ b/javascore/fee_aggregation/conf/godWallet.json @@ -0,0 +1,22 @@ +{ + "address": "hxb6b5791be0b5ef67063b3c10b840fb81514db2fd", + "id": "87323a66-289a-4ce2-88e4-00278deb5b84", + "version": 3, + "coinType": "icx", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "069e46aaefae8f1c1f840d6b09144999" + }, + "ciphertext": "f35ff7cf4f5759cb0878088d0887574a896f7f0fc2a73898d88be1fe52977dbd", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 65536, + "r": 8, + "p": 1, + "salt": "0fc9c3b24cdb8175" + }, + "mac": "1ef4ff51fdee8d4de9cf59e160da049eb0099eb691510994f5eca492f56c817a" + } +} \ No newline at end of file diff --git a/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.jar b/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..62d4c053 Binary files /dev/null and b/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.jar differ diff --git a/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.properties b/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..a4f0001d --- /dev/null +++ b/javascore/fee_aggregation/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/javascore/fee_aggregation/gradlew b/javascore/fee_aggregation/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/javascore/fee_aggregation/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/javascore/fee_aggregation/gradlew.bat b/javascore/fee_aggregation/gradlew.bat new file mode 100644 index 00000000..a9f778a7 --- /dev/null +++ b/javascore/fee_aggregation/gradlew.bat @@ -0,0 +1,104 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/javascore/fee_aggregation/sample-token-0.2.0-optimized.jar b/javascore/fee_aggregation/sample-token-0.2.0-optimized.jar new file mode 100644 index 00000000..eb645ebd Binary files /dev/null and b/javascore/fee_aggregation/sample-token-0.2.0-optimized.jar differ diff --git a/javascore/fee_aggregation/settings.gradle b/javascore/fee_aggregation/settings.gradle new file mode 100644 index 00000000..8fb52033 --- /dev/null +++ b/javascore/fee_aggregation/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'fee-aggregation-system' + diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Constants.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Constants.java new file mode 100644 index 00000000..a66f4ca6 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Constants.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.icx.data.Address; + +import java.math.BigInteger; + +public class Constants { + public static final BigInteger STATUS_SUCCESS = BigInteger.ONE; + public static final BigInteger STATUS_FAILURE = BigInteger.ZERO; + + public static final BigInteger DEFAULT_STEPS = BigInteger.valueOf(100000); + public static final long DEFAULT_WAITING_TIME = 7000; + + public static final Address ZERO_ADDRESS = + new Address("cx0000000000000000000000000000000000000000"); + public static final Address TREASURY_ADDRESS = + new Address("hx1000000000000000000000000000000000000000"); + + public static final String CONTENT_TYPE_PYTHON = "application/zip"; + public static final String CONTENT_TYPE_JAVA = "application/java"; +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Env.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Env.java new file mode 100644 index 00000000..25b089bd --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Env.java @@ -0,0 +1,103 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.crypto.KeystoreException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Properties; + +public class Env { + public static final Log LOG = Log.getGlobal(); + private static Chain chain; + + static { + String envFile = System.getProperty("env.props", "conf/env.props"); + Properties props = new Properties(); + try { + LOG.info("Using env.props: " + envFile); + FileInputStream fis = new FileInputStream(envFile); + props.load(fis); + fis.close(); + } catch (IOException e) { + System.err.printf("'%s' does not exist\n", envFile); + throw new IllegalArgumentException(e.getMessage()); + } + String confPath = Path.of(envFile).getParent().toString() + "/"; + readProperties(props, confPath); + } + + private static void readProperties(Properties props, String confPath) { + String chainName = "chain"; + String nid = props.getProperty(chainName + ".nid"); + if (nid == null) { + throw new IllegalArgumentException("nid not found"); + } + String godWalletPath = confPath + props.getProperty(chainName + ".godWallet"); + String godPassword = props.getProperty(chainName + ".godPassword"); + KeyWallet godWallet; + try { + godWallet = readWalletFromFile(godWalletPath, godPassword); + } catch (IOException e) { + throw new IllegalArgumentException(e.getMessage()); + } + String nodeName = "node"; + String url = props.getProperty(nodeName + ".url"); + if (url == null) { + throw new IllegalArgumentException("node url not found"); + } + chain = new Chain(Integer.parseInt(nid.substring(2), 16), godWallet, url); + } + + private static KeyWallet readWalletFromFile(String path, String password) throws IOException { + try { + File file = new File(path); + return KeyWallet.load(password, file); + } catch (KeystoreException e) { + e.printStackTrace(); + throw new IOException("Key load failed!"); + } + } + + public static Chain getDefaultChain() { + if (chain == null) { + throw new AssertionError("Chain not found"); + } + return chain; + } + + public static class Chain { + public final int networkId; + public final Wallet godWallet; + private final String nodeUrl; + + public Chain(int networkId, Wallet godWallet, String url) { + this.networkId = networkId; + this.godWallet = godWallet; + this.nodeUrl = url; + } + + public String getEndpointURL() { + return this.nodeUrl; + } + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Log.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Log.java new file mode 100644 index 00000000..9de3c036 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/Log.java @@ -0,0 +1,126 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import java.util.EmptyStackException; +import java.util.Stack; + +public class Log { + private static final String[] PREFIX_LEVELS = {null, "[S]", "[W]", null, null}; + private static final String PREFIX_STEP_IN = "--> "; + private static final String PREFIX_STEP_OUT = "<-- "; + private static final String DEPTH_STRING = " "; + + private static final int LEVEL_START = 0; + public static final int LEVEL_NONE = LEVEL_START; + public static final int LEVEL_SEVERE = LEVEL_NONE + 1; + public static final int LEVEL_WARNING = LEVEL_SEVERE + 1; + public static final int LEVEL_INFO = LEVEL_WARNING + 1; + public static final int LEVEL_DEBUG = LEVEL_INFO + 1; + private static final int LEVEL_END = LEVEL_DEBUG; + + private int level = LEVEL_INFO; + private Stack frames = new Stack<>(); + + public static Log getGlobal() { + return new Log(); + } + + public void setLevel(int newLevel) { + if (newLevel >= LEVEL_START && newLevel <= LEVEL_END) { + level = newLevel; + } + } + + private boolean isLoggable(int level) { + return this.level >= level && level > LEVEL_START; + } + + public void info(String msg) { + log(LEVEL_INFO, msg); + } + + public void warning(String msg) { + log(LEVEL_WARNING, msg); + } + + public void severe(String msg) { + log(LEVEL_SEVERE, msg); + } + + public void infoEntering(String taskName, String msg) { + if (taskName == null) { + taskName = ""; + } + if (msg == null) { + msg = ""; + } + StringBuilder buf = new StringBuilder(5 + taskName.length() + msg.length()); + buf.append(PREFIX_STEP_IN).append(taskName); + if (msg.length() > 0) { + buf.append(": ").append(msg); + } + log(LEVEL_INFO, buf.toString()); + frames.push(taskName); + } + + public void infoEntering(String taskName) { + infoEntering(taskName, null); + } + + public void infoExiting(String msg) { + if (msg == null) { + msg = ""; + } + try { + String taskName = frames.pop(); + StringBuilder buf = new StringBuilder(5 + taskName.length() + msg.length()); + buf.append(PREFIX_STEP_OUT).append(taskName); + if (msg.length() > 0) { + buf.append(": ").append(msg); + } + log(LEVEL_INFO, buf.toString()); + } catch (EmptyStackException e) { + log(LEVEL_WARNING, "(INVALID) Exiting without no entering" + msg); + } + } + + public void infoExiting() { + infoExiting(null); + } + + public void debug(String msg) { + log(LEVEL_DEBUG, msg); + } + + public void log(int level, String msg) { + if (msg != null && isLoggable(level)) { + if (PREFIX_LEVELS[level] != null || !frames.empty()) { + StringBuilder buf = new StringBuilder(msg.length() + frames.size() * 3 + 3); + for (int i = frames.size(); i > 0; i--) { + buf.append(DEPTH_STRING); + } + if (PREFIX_LEVELS[level] != null) { + buf.append(PREFIX_LEVELS[level]); + } + buf.append(msg); + msg = buf.toString(); + } + System.out.println(msg); + } + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/ResultTimeoutException.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/ResultTimeoutException.java new file mode 100644 index 00000000..b3a19884 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/ResultTimeoutException.java @@ -0,0 +1,40 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.icx.data.Bytes; + +public class ResultTimeoutException extends Exception { + Bytes txHash; + + public ResultTimeoutException() { + super(); + } + + public ResultTimeoutException(String message) { + super(message); + } + + public ResultTimeoutException(Bytes txHash) { + super("Timeout. txHash=" + txHash); + this.txHash = txHash; + } + + public Bytes getTxHash() { + return this.txHash; + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TestBase.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TestBase.java new file mode 100644 index 00000000..0c3c29b3 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TestBase.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.TransactionResult; +import org.opentest4j.AssertionFailedError; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class TestBase { + protected static final BigInteger ICX = BigInteger.TEN.pow(18); + + protected static void assertSuccess(TransactionResult result) { + assertStatus(Constants.STATUS_SUCCESS, result); + } + + protected static void assertFailure(TransactionResult result) { + assertStatus(Constants.STATUS_FAILURE, result); + Env.LOG.info("Expected " + result.getFailure()); + } + + protected static void assertStatus(BigInteger status, TransactionResult result) { + try { + assertEquals(status, result.getStatus()); + } catch (AssertionFailedError e) { + Env.LOG.info("Assertion Failed: result=" + result); + fail(e.getMessage()); + } + } + + protected static void transferAndCheckResult(TransactionHandler txHandler, Address to, BigInteger amount) + throws IOException, ResultTimeoutException { + Bytes txHash = txHandler.transfer(to, amount); + assertSuccess(txHandler.getResult(txHash)); + } + + protected static void transferAndCheckResult(TransactionHandler txHandler, Address[] addresses, BigInteger amount) + throws IOException, ResultTimeoutException { + List hashes = new ArrayList<>(); + for (Address to : addresses) { + hashes.add(txHandler.transfer(to, amount)); + } + for (Bytes hash : hashes) { + assertSuccess(txHandler.getResult(hash)); + } + } + + protected static void ensureIcxBalance(TransactionHandler txHandler, Address address, + BigInteger oldVal, BigInteger newVal) throws Exception { + long limitTime = System.currentTimeMillis() + Constants.DEFAULT_WAITING_TIME; + while (true) { + BigInteger icxBalance = txHandler.getBalance(address); + String msg = "ICX balance of " + address + ": " + icxBalance; + if (icxBalance.equals(oldVal)) { + if (limitTime < System.currentTimeMillis()) { + throw new ResultTimeoutException(); + } + try { + // wait until block confirmation + Env.LOG.debug(msg + "; Retry in 1 sec."); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else if (icxBalance.equals(newVal)) { + Env.LOG.info(msg); + break; + } else { + throw new IOException(String.format("ICX balance mismatch: expected <%s>, but was <%s>", + newVal, icxBalance)); + } + } + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionFailureException.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionFailureException.java new file mode 100644 index 00000000..1d5668b3 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionFailureException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.icx.data.TransactionResult; + +import java.math.BigInteger; + +public class TransactionFailureException extends Exception { + private final TransactionResult.Failure failure; + + public TransactionFailureException(TransactionResult.Failure failure) { + this.failure = failure; + } + + @Override + public String toString() { + return this.failure.toString(); + } + + public BigInteger getCode() { + return this.failure.getCode(); + } + + public String getMessage() { + return this.failure.getMessage(); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionHandler.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionHandler.java new file mode 100644 index 00000000..32408e55 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/TransactionHandler.java @@ -0,0 +1,200 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp; + +import foundation.icon.btp.score.ChainScore; +import foundation.icon.btp.score.Score; +import foundation.icon.icx.Call; +import foundation.icon.icx.IconService; +import foundation.icon.icx.SignedTransaction; +import foundation.icon.icx.Transaction; +import foundation.icon.icx.TransactionBuilder; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.ConfirmedTransaction; +import foundation.icon.icx.data.ScoreApi; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcError; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.btp.util.ZipFile; +import foundation.icon.icx.transport.jsonrpc.RpcValue; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class TransactionHandler { + private final IconService iconService; + private final Env.Chain chain; + + public TransactionHandler(IconService iconService, Env.Chain chain) { + this.iconService = iconService; + this.chain = chain; + } + + public Score deploy(Wallet owner, String scorePath, RpcObject params) + throws IOException, ResultTimeoutException, TransactionFailureException { + return deploy(owner, scorePath, params, null); + } + + public Score deploy(Wallet owner, String scorePath, RpcObject params, BigInteger steps) + throws IOException, ResultTimeoutException, TransactionFailureException { + return deploy(owner, scorePath, Constants.ZERO_ADDRESS, params, steps); + } + + public Score deploy(Wallet owner, String scorePath, Address to, RpcObject params, BigInteger steps) + throws IOException, ResultTimeoutException, TransactionFailureException { + byte[] data = Files.readAllBytes(Path.of(scorePath)); + if (scorePath.endsWith(".jar")) { + return getScore(doDeploy(owner, data, to, params, steps, Constants.CONTENT_TYPE_JAVA)); + } else { +// byte[] data = ZipFile.zipContent(scorePath); + return getScore(doDeploy(owner, data, to, params, steps, Constants.CONTENT_TYPE_PYTHON)); + } + } + + private Bytes doDeploy(Wallet owner, byte[] content, Address to, RpcObject params, + BigInteger steps, String contentType) throws IOException { + Transaction transaction = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(owner.getAddress()) + .to(to) + .deploy(contentType, content) + .params(params) + .build(); + if (steps == null) { + steps = estimateStep(transaction); + } + SignedTransaction signedTransaction = new SignedTransaction(transaction, owner, steps); + return iconService.sendTransaction(signedTransaction).execute(); + } + + public Score getScore(Bytes txHash) + throws IOException, ResultTimeoutException, TransactionFailureException { + TransactionResult result = getResult(txHash, Constants.DEFAULT_WAITING_TIME); + if (!Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new TransactionFailureException(result.getFailure()); + } + return new Score(this, new Address(result.getScoreAddress())); + } + + public Env.Chain getChain() { + return this.chain; + } + + public BigInteger getNetworkId() { + return BigInteger.valueOf(chain.networkId); + } + + public BigInteger getBalance(Address address) throws IOException { + return iconService.getBalance(address).execute(); + } + + public List getScoreApi(Address scoreAddress) throws IOException { + return iconService.getScoreApi(scoreAddress).execute(); + } + + public BigInteger estimateStep(Transaction transaction) throws IOException { + try { + return iconService.estimateStep(transaction).execute(); + } catch (RpcError e) { + Env.LOG.info("estimateStep failed(" + e.getCode() + ", " + e.getMessage() + "); use default steps."); + return (Constants.DEFAULT_STEPS.multiply(BigInteger.TWO)).add(BigInteger.valueOf(10000)); + } + } + + public RpcItem call(Call call) throws IOException { + return this.iconService.call(call).execute(); + } + + public Bytes invoke(Wallet wallet, Transaction tx, BigInteger steps) throws IOException { + if (steps == null) { + steps = estimateStep(tx); + } + return this.iconService.sendTransaction(new SignedTransaction(tx, wallet, steps)).execute(); + } + + public TransactionResult getResult(Bytes txHash) + throws IOException, ResultTimeoutException { + return getResult(txHash, Constants.DEFAULT_WAITING_TIME); + } + + public TransactionResult getResult(Bytes txHash, long waiting) + throws IOException, ResultTimeoutException { + long limitTime = System.currentTimeMillis() + waiting; + while (true) { + try { + return iconService.getTransactionResult(txHash).execute(); + } catch (RpcError e) { + if (e.getCode() == -31002 /* pending */ + || e.getCode() == -31003 /* executing */ + || e.getCode() == -31004 /* not found */) { + if (limitTime < System.currentTimeMillis()) { + throw new ResultTimeoutException(txHash); + } + try { + // wait until block confirmation + Env.LOG.debug("RpcError: code(" + e.getCode() + ") message(" + e.getMessage() + "); Retry in 1 sec."); + Thread.sleep(1000); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + continue; + } + Env.LOG.warning("RpcError: code(" + e.getCode() + ") message(" + e.getMessage() + "); Retry in 1 sec."); + throw e; + } + } + } + + public Bytes transfer(Address to, BigInteger amount) throws IOException { + return transfer(chain.godWallet, to, amount); + } + + public Bytes transfer(Wallet owner, Address to, BigInteger amount) throws IOException { + return transfer(owner, to, amount, Constants.DEFAULT_STEPS.multiply(BigInteger.TWO)); + } + + public Bytes transfer(Wallet owner, Address to, BigInteger amount, BigInteger steps) throws IOException { + Transaction transaction = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(owner.getAddress()) + .to(to) + .value(amount) + .build(); + if (steps == null) { + steps = estimateStep(transaction).add(BigInteger.valueOf(10000)); + } + SignedTransaction signedTransaction = new SignedTransaction(transaction, owner, steps); + return iconService.sendTransaction(signedTransaction).execute(); + } + + public void refundAll(Wallet owner) throws IOException { + BigInteger stepPrice = new ChainScore(this).getStepPrice(); + BigInteger remaining = getBalance(owner.getAddress()); + BigInteger fee = Constants.DEFAULT_STEPS.multiply(stepPrice); + transfer(owner, chain.godWallet.getAddress(), remaining.subtract(fee), Constants.DEFAULT_STEPS); + } + + public ConfirmedTransaction getTransaction(Bytes txHash) throws IOException { + return iconService.getTransaction(txHash).execute(); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/cases/FeeAggregationSCORETest.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/cases/FeeAggregationSCORETest.java new file mode 100644 index 00000000..ab6ce30d --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/cases/FeeAggregationSCORETest.java @@ -0,0 +1,527 @@ +package foundation.icon.btp.cases; + +import foundation.icon.btp.*; +import foundation.icon.btp.score.BandProtocolScore; +import foundation.icon.btp.score.FeeAggregationScore; +import foundation.icon.btp.score.SampleMultiTokenScore; +import foundation.icon.btp.score.SampleTokenScore; +import foundation.icon.icx.IconService; +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.http.HttpProvider; +import okhttp3.OkHttpClient; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static foundation.icon.btp.Env.LOG; +import static org.junit.jupiter.api.Assertions.*; + +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; +import java.util.Date; + +public class FeeAggregationSCORETest extends TestBase { + private static TransactionHandler txHandler; + private static KeyWallet[] wallets; + private static KeyWallet ownerWallet; + private static SampleTokenScore sampleTokenScore; + private static SampleMultiTokenScore irc31TokenScore; + private static FeeAggregationScore feeAggregationScore; + private static BandProtocolScore bandProtocolScore; + private static IconService iconService; + + @BeforeAll + static void setup() throws Exception { + Env.Chain chain = Env.getDefaultChain(); + OkHttpClient ohc = new OkHttpClient.Builder().build(); + iconService = new IconService(new HttpProvider(ohc, chain.getEndpointURL())); + txHandler = new TransactionHandler(iconService, chain); + + // init wallets + wallets = new KeyWallet[6]; + BigInteger amount = ICX.multiply(BigInteger.valueOf(300)); + for (int i = 0; i < wallets.length; i++) { + wallets[i] = KeyWallet.create(); + txHandler.transfer(wallets[i].getAddress(), amount); + } + for (KeyWallet wallet : wallets) { + ensureIcxBalance(txHandler, wallet.getAddress(), BigInteger.ZERO, amount); + } + ownerWallet = wallets[0]; + + // Deploy token SCORE + BigInteger sampleTokenDecimals = BigInteger.valueOf(18); + BigInteger initialSupply = BigInteger.valueOf(1000); + sampleTokenScore = SampleTokenScore.mustDeploy(txHandler, ownerWallet, "SampleToken", + sampleTokenDecimals, initialSupply); + + // Deploy multi-token SCORE && mint token + BigInteger irc31Decimals = BigInteger.valueOf(10); + irc31TokenScore = SampleMultiTokenScore.mustDeploy(txHandler, ownerWallet, irc31Decimals); + irc31TokenScore.mintToken(ownerWallet); + + bandProtocolScore = BandProtocolScore.mustDeploy(txHandler, ownerWallet); + + // Deploy FAS + feeAggregationScore = FeeAggregationScore.mustDeploy(txHandler, ownerWallet, bandProtocolScore.getAddress()); + + // prepare + LOG.infoEntering("transfer token", "10 " + sampleTokenScore.name() + " to FeeAggregationSCORE"); + TransactionResult resultOfTransfer10TokenToFAS = sampleTokenScore.transfer(ownerWallet, feeAggregationScore.getAddress(), ICX.multiply(BigInteger.TEN)); + sampleTokenScore.ensureTransfer(resultOfTransfer10TokenToFAS, ownerWallet.getAddress(), feeAggregationScore.getAddress(), ICX.multiply(BigInteger.TEN), null); + LOG.infoExiting(); + + LOG.infoEntering("transfer multi-token", "100 " + SampleMultiTokenScore.ID + " to FeeAggregationSCORE"); + TransactionResult resultOfTransfer100000MultiTokenToFAS = irc31TokenScore.transfer(ownerWallet, ownerWallet.getAddress(), feeAggregationScore.getAddress(), SampleMultiTokenScore.ID, irc31TokenScore.unit().multiply(BigInteger.valueOf(100)), null); + irc31TokenScore.ensureTransfer(resultOfTransfer100000MultiTokenToFAS, ownerWallet.getAddress(), feeAggregationScore.getAddress(), SampleMultiTokenScore.ID, irc31TokenScore.unit().multiply(BigInteger.valueOf(100)), null); + LOG.infoExiting(); + + LOG.infoEntering("call", "registerIRC2() - register " + sampleTokenScore.name()); + feeAggregationScore.ensureRegisterIRC2Success(ownerWallet, sampleTokenScore.name(), sampleTokenScore.getAddress()); + LOG.infoExiting(); + + LOG.infoEntering("call", "registerIRC31() - register IRC31 Token " + SampleMultiTokenScore.NAME); + feeAggregationScore.ensureRegisterIRC31Success(ownerWallet, SampleMultiTokenScore.NAME, irc31TokenScore.getAddress(), SampleMultiTokenScore.ID, irc31Decimals); + LOG.infoExiting(); + + LOG.infoEntering("transact", "setDuration() - set duration to 30s " + SampleMultiTokenScore.NAME); + feeAggregationScore.ensureSetDurationSuccess(ownerWallet, BigInteger.valueOf(1000*1000*30)); // 100s + LOG.infoExiting(); + + LOG.infoEntering("transact", "relay() - set rate of " + sampleTokenScore.name() + " to 100 ICX"); + bandProtocolScore.relay(ownerWallet, "[\"" + sampleTokenScore.name() + "\",\"ICX\",\"USDC\"]", "[100000000000000, 1000000000000,1000000000]", "[1633508543,1633508543,1633508543]", "[5684902,5684904,5684902]"); + LOG.infoExiting(); + } + + @AfterAll + static void shutdown() throws Exception { + for (KeyWallet wallet : wallets) { + txHandler.refundAll(wallet); + } + } + + /** + * Scenario 1: if User submits a deposit for SampleToken for the first time + * + * Given: + * - There are 10 SampleToken in the Fee Aggregation system + * - UserA has 200 ICX + * When: + * - UserA send the request to deposit for 10 SampleToken by 100 ICX to the Fee Aggregation system + * Then: + * - UserA deposit 100 ICX to the Fee Aggregation system for 10 SampleToken + * - Start an syndication for 10 SampleToken + */ + @Test + void scenario1Test() throws Exception { + KeyWallet userA = wallets[1]; + BigInteger depositAmount = ICX.multiply(BigInteger.valueOf(100)); + BigInteger tokenAmount = ICX.multiply(BigInteger.TEN); + + // UserA bid 100 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserA deposit 100 ICX to bid 10 " + sampleTokenScore.name()); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userA, sampleTokenScore.name(), depositAmount); + // UserA remain less than (200 - 100) ICX because of transaction fee + BigInteger balanceA = txHandler.getBalance(userA.getAddress()); + assertTrue(balanceA.compareTo(ICX.multiply(BigInteger.valueOf(200))) < 0); + feeAggregationScore.ensureSyndicationInfo(userA, sampleTokenScore.name(), tokenAmount, depositAmount); + feeAggregationScore.ensureDepositAmount(sampleTokenScore.name(), depositAmount, userA.getAddress()); + LOG.infoExiting(); + } + + /** + * Scenario 2: if User submits a deposit for SampleToken which syndication is active + * + * Given: + * - 10 SampleToken have been deposited by UserA with 100 ICX + * - UserB has 200 ICX + * When: + * - UserB sends the request to deposit for 10 SampleToken by 150 ICX to the Fee Aggregation system + * Then: + * - Syndication now has two shareholders + * - UserA deposit 100 ICX + * - UserB deposit 150 ICX + * - totalDeposited 250 ICX + */ + @Test + void scenario2Test() throws Exception { + KeyWallet userB = wallets[2]; + BigInteger tokenAmount = ICX.multiply(BigInteger.TEN); + BigInteger depositAmount = ICX.multiply(BigInteger.valueOf(150)); + + // UserB bid 150 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserB deposit 150 ICX to bid 10 " + sampleTokenScore.name()); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userB, sampleTokenScore.name(), depositAmount); + feeAggregationScore.ensureSyndicationInfo(userB, sampleTokenScore.name(), tokenAmount, ICX.multiply(BigInteger.valueOf(250))); + // UserB remain less than (300 - 150) ICX + BigInteger balanceB = txHandler.getBalance(userB.getAddress()); + assertNotEquals(balanceB.compareTo(ICX.multiply(BigInteger.valueOf(150))), 1); + feeAggregationScore.ensureDepositAmount(sampleTokenScore.name(), depositAmount, userB.getAddress()); + LOG.infoExiting(); + } + + /** + * Scenario 3: if User submits a additional deposit for SampleToken + * + * Given: + * - There are 10 SampleToken in the Fee Aggregation system + * - UserA has deposited 100 ICX + * When: + * - UserA send the request to deposit additional 60 ICX for 10 SampleToken to the Fee Aggregation system + * Then: + * - UserA total deposit 200 ICX to the Fee Aggregation system for 10 SampleToken + */ + @Test + void scenario3Test() throws Exception { + KeyWallet userA = wallets[1]; + KeyWallet userB = wallets[2]; + BigInteger depositAmount = ICX.multiply(BigInteger.valueOf(60)); + BigInteger tokenAmount = ICX.multiply(BigInteger.TEN); + BigInteger currentUserADeposited = feeAggregationScore.getDepositAmount(sampleTokenScore.name(), userA.getAddress()); + BigInteger currentUserBDeposited = feeAggregationScore.getDepositAmount(sampleTokenScore.name(), userB.getAddress()); + + // UserA bid 100 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserA deposit 100 ICX to bid 10 " + sampleTokenScore.name()); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userA, sampleTokenScore.name(), depositAmount); + // UserA remain less than (200 - 60) ICX because of transaction fee + BigInteger balanceA = txHandler.getBalance(userA.getAddress()); + assertTrue(balanceA.compareTo(ICX.multiply(BigInteger.valueOf(140))) < 0); + feeAggregationScore.ensureSyndicationInfo(userA, sampleTokenScore.name(), tokenAmount, depositAmount.add(currentUserADeposited).add(currentUserBDeposited)); + feeAggregationScore.ensureDepositAmount(sampleTokenScore.name(), depositAmount.add(currentUserADeposited), userA.getAddress()); + LOG.infoExiting(); + } + + /** + * Scenario 4: User deposit for syndication that has been ended + * + * Given: + * - Current SampleToken amount of contract 30 + * When: + * - Current syndication has 10 SampleToken and two share holders + * - UserA has deposited 160 ICX + * - UserB has deposited 150 ICX + * - Current SampleToken ICX price = 100.000.000.000.000/1.000.000.000.000 = 1/100 + * - SampleToken/ICX price with discount 10% = 1/90 + * - Total price of SampleToken in this syndication 100*90 = 9000 ICX + * - UserC send the request to deposit 100 ICX to the Fee Aggregation system to SampleToken + * Then: + * - Finished current syndication + * - Add share of SampleToken to UserA and UserB in state + * - UserA has 160/90 = 1.7777777777778 SampleToken + * - UserB has 150/110 = 1.666666666667 SampleToken + * - Create new syndication for SampleToken with token amount is (30 - 10) + */ + @Test + void scenario4Test() throws Exception { + KeyWallet userA = wallets[1]; + KeyWallet userB = wallets[2]; + KeyWallet userC = wallets[3]; + BigInteger depositAmount = ICX.multiply(BigInteger.valueOf(100)); + BigInteger currentUserADeposited = feeAggregationScore.getDepositAmount(sampleTokenScore.name(), userA.getAddress()); + BigInteger currentUserBDeposited = feeAggregationScore.getDepositAmount(sampleTokenScore.name(), userB.getAddress()); + BigInteger currentSampleTokenIcxRate = bandProtocolScore.getReferenceData(sampleTokenScore.name(), "ICX"); + BigInteger currentSampleTokenIcxRateWithDiscount = currentSampleTokenIcxRate.divide(BigInteger.valueOf(100)).multiply(BigInteger.valueOf(90)); + + BigInteger currentSyndicationEndTime = feeAggregationScore.getEndTime(sampleTokenScore.name()); + + // prepare + LOG.infoEntering("transfer token", "20 " + sampleTokenScore.name() + " to FeeAggregationSCORE"); + TransactionResult resultOfTransfer10TokenToFAS = sampleTokenScore.transfer(ownerWallet, feeAggregationScore.getAddress(), ICX.multiply(BigInteger.valueOf(20))); + sampleTokenScore.ensureTransfer(resultOfTransfer10TokenToFAS, ownerWallet.getAddress(), feeAggregationScore.getAddress(), ICX.multiply(BigInteger.valueOf(20)), null); + LOG.infoExiting(); + + BigInteger userASampleTokenShare = currentUserADeposited.multiply(ICX).divide(currentSampleTokenIcxRateWithDiscount); + BigInteger userBSampleTokenShare = currentUserBDeposited.multiply(ICX).divide(currentSampleTokenIcxRateWithDiscount); + + // wait until current syndication end + while(currentSyndicationEndTime.longValue() > (new Date().getTime()*1000 - 3000000)) + TimeUnit.SECONDS.sleep(1); + + + // UserA bid 100 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserA deposit 100 ICX to bid 10 " + sampleTokenScore.name()); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userC, sampleTokenScore.name(), depositAmount); + // UserA remain less than (300 - 100) ICX because of transaction fee + BigInteger balanceC = txHandler.getBalance(userC.getAddress()); + assertTrue(balanceC.compareTo(ICX.multiply(BigInteger.valueOf(200))) < 0); + feeAggregationScore.ensureSyndicationInfo(userC, sampleTokenScore.name(), ICX.multiply(BigInteger.valueOf(20)), depositAmount); + feeAggregationScore.ensureDepositAmount(sampleTokenScore.name(), depositAmount, userC.getAddress()); + LOG.infoExiting(); + + feeAggregationScore.ensureTokenBalance(sampleTokenScore.name(), userASampleTokenShare, userA.getAddress()); + feeAggregationScore.ensureTokenBalance(sampleTokenScore.name(), userBSampleTokenShare, userB.getAddress()); + } + + /** + * Scenario 5: Distribute token for by percent of token that deposited if total deposited greater than token value + * + * Given: + * - Current SecondSampleToken amount of contract 100 + * - ICX SecondSampleToken rate 1/10 == 10/100 + * - SecondSampleToken/ICX price with discount 10% = 9/100 + * - Total price of SecondSampleToken in this syndication 100*(9/100) = 9 ICX + * When: + * - UserA has deposited 20 ICX + * - UserB has deposited 50 ICX + * - Total deposited 70 ICX + * - Transfer additional 200 SecondSampleToken to Fee Aggregation + * - UserC send the request to deposit 10 ICX to the Fee Aggregation system to SecondSampleToken + * Then: + * - Finished current syndication of SecondSampleToken + * - Add share of SampleToken to UserA and UserB in state + * - UserA has (20/70)*100 = 28.5714285714 SampleMultiToken + * - UserB has (50/70)*100 = 71.4285714285 SampleMultiToken + * - Update ICX remaining for UserA and UserB + * - UserA remain (20 - 28.5714285700*(9/100)) = 17.42857143 ICX + * - UserB remain (50 - 71.4285714300*(9/100)) = 43.57142857 ICX + * - Create new syndication for SecondSampleToken with token amount is 200 + */ + @Test + void scenario5Test() throws Exception { + LOG.infoEntering("transact", "setDuration() - set duration to 10s "); + feeAggregationScore.ensureSetDurationSuccess(ownerWallet, BigInteger.valueOf(1000*1000*10)); // 10s + LOG.infoExiting(); + + LOG.infoEntering("transact", "relay() - set rate of " + SampleMultiTokenScore.NAME + " to 1/100 ICX"); + bandProtocolScore.relay(ownerWallet, "[\"" + SampleMultiTokenScore.NAME + "\",\"ICX\",\"USDC\"]", "[10000000000, 100000000000, 1000000000]", "[1633508543, 1633508543, 1633508543]", "[5684902, 5684904, 5684902]"); + LOG.infoExiting(); + + KeyWallet userA = wallets[1]; + KeyWallet userB = wallets[2]; + KeyWallet userC = wallets[3]; + BigInteger userADeposited = ICX.multiply(BigInteger.valueOf(20)); + BigInteger userBDeposited = ICX.multiply(BigInteger.valueOf(50)); + BigInteger totalDeposited = userADeposited.add(userBDeposited); + BigInteger tokenAmount = irc31TokenScore.unit().multiply(BigInteger.valueOf(100)); + BigInteger currentSampleMultiTokenScoreIcxRate = bandProtocolScore.getReferenceData(SampleMultiTokenScore.NAME, "ICX"); + + // UserA bid 20 ICX for 100 SecondSampleToken + LOG.infoEntering("transact", "deposit() - UserA deposit 20 ICX for 100 " + SampleMultiTokenScore.NAME); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userA, SampleMultiTokenScore.NAME, userADeposited); + feeAggregationScore.ensureSyndicationInfo(userA, SampleMultiTokenScore.NAME, tokenAmount, userADeposited); + feeAggregationScore.ensureDepositAmount(SampleMultiTokenScore.NAME, userADeposited, userA.getAddress()); + + // UserA bid 20 ICX for 100 SecondSampleToken + LOG.infoEntering("transact", "deposit() - UserB deposit 50 ICX for 100 " + SampleMultiTokenScore.NAME); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userB, SampleMultiTokenScore.NAME, userBDeposited); + feeAggregationScore.ensureSyndicationInfo(userB, SampleMultiTokenScore.NAME, tokenAmount, totalDeposited); + feeAggregationScore.ensureDepositAmount(SampleMultiTokenScore.NAME, userBDeposited, userB.getAddress()); + + BigInteger currentSyndicationEndTime = feeAggregationScore.getEndTime(SampleMultiTokenScore.NAME); + + LOG.infoEntering("transfer multi-token", "200 " + SampleMultiTokenScore.NAME + " to FeeAggregationSCORE"); + TransactionResult resultOfTransfer100000MultiTokenToFAS = irc31TokenScore.transfer(ownerWallet, ownerWallet.getAddress(), feeAggregationScore.getAddress(), SampleMultiTokenScore.ID, irc31TokenScore.unit().multiply(BigInteger.valueOf(200)), null); + irc31TokenScore.ensureTransfer(resultOfTransfer100000MultiTokenToFAS, ownerWallet.getAddress(), feeAggregationScore.getAddress(), SampleMultiTokenScore.ID, irc31TokenScore.unit().multiply(BigInteger.valueOf(200)), null); + LOG.infoExiting(); + + BigInteger userASampleMultiTokenShare = new BigInteger("285714285714"); + BigInteger userBSampleMultiTokenShare = new BigInteger("714285714285"); + + // wait until current syndication end + while(currentSyndicationEndTime.longValue() > (new Date().getTime()*1000 - 3000000)) + TimeUnit.SECONDS.sleep(1); + + + // UserA bid 100 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserC deposit 10 ICX for 200 " + SampleMultiTokenScore.NAME); + feeAggregationScore.ensureDepositIcxSuccess(txHandler, userC, SampleMultiTokenScore.NAME, ICX.multiply(BigInteger.valueOf(10))); + feeAggregationScore.ensureSyndicationInfo(userC, SampleMultiTokenScore.NAME, irc31TokenScore.unit().multiply(BigInteger.valueOf(200)), ICX.multiply(BigInteger.valueOf(10))); + feeAggregationScore.ensureDepositAmount(SampleMultiTokenScore.NAME, ICX.multiply(BigInteger.valueOf(10)), userC.getAddress()); + LOG.infoExiting(); + + feeAggregationScore.ensureTokenBalance(SampleMultiTokenScore.NAME, userASampleMultiTokenShare, userA.getAddress()); + feeAggregationScore.ensureTokenBalance(SampleMultiTokenScore.NAME, userBSampleMultiTokenShare, userB.getAddress()); + feeAggregationScore.ensureRefundableBalance(new BigInteger("17428571428574000000"), userA.getAddress()); + feeAggregationScore.ensureRefundableBalance(new BigInteger("43571428571435000000"), userB.getAddress()); + } + + /** + * Scenario 6: if User submits a deposit for SampleTokenA which Fee Aggregation system does not register + * + * Given: + * - Fee Aggregation System does not register SampleTokenA + * - UserD has 100 ICX + * When: + * - UserD sends the request to deposit for SampleTokenA by 100 ICX to the Fee Aggregation system + * Then: + * - Transaction Revert + */ + @Test + void scenario6Test() throws Exception { + KeyWallet userD = wallets[4]; + String tokenNameNotRegisted = "SampleTokenA"; + + // UserC bid 100 ICX for 10 SampleToken + LOG.infoEntering("transact", "deposit() - UserC deposit 100 ICX to bid 10 " + tokenNameNotRegisted); + feeAggregationScore.ensureDepositIcxFailed(txHandler, userD, tokenNameNotRegisted, ICX.multiply(BigInteger.valueOf(100)), 57); + LOG.infoExiting("Transaction Revert"); + } + + /** + * Scenario 7: if User submits a deposit for amount SampleTokenB equal 0 in the Fee Aggregation system + * + * Given: + * - SampleTokenB balance is 0 in Fee Aggregation System + * - UserD has 100 ICX + * When: + * - UserD sends the request to deposit for SampleTokenB by 100 ICX to the Fee Aggregation system + * Then: + * - Transaction Revert + */ + @Test + void scenario7Test() throws Exception { + KeyWallet userD = wallets[4]; + String tokenNameB = "SampleTokenB"; + BigInteger tokenBId = BigInteger.valueOf(999999999); + BigInteger tokenBDecimals = BigInteger.valueOf(8); + LOG.infoEntering("call", "registerIRC2() - register " + tokenNameB); + feeAggregationScore.ensureRegisterIRC31Success(ownerWallet, tokenNameB, irc31TokenScore.getAddress(), tokenBId, tokenBDecimals); + LOG.infoExiting(); + + // UserC bid 100 ICX for 0 SampleTokenB + LOG.infoEntering("transact", "deposit() - UserC deposit 100 ICX to bid 0 " + tokenNameB); + feeAggregationScore.ensureDepositIcxFailed(txHandler, userD, tokenNameB, ICX.multiply(BigInteger.valueOf(100)), 61); + LOG.infoExiting("Transaction Revert"); + } + + /** + * Scenario 8: if User withdraw their refundable ICX token + * + * Given: + * - UserA refundable balance 17428571428574000000 ICX + * When: + * - UserA call Fee Aggregation to withdraw ICX token + * Then: + * - Set UserA refundable balance to 0 ICX + * - Transfer 17428571428574000000 ICX to UserA + */ + @Test + void scenario8Test() throws Exception { + KeyWallet userA = wallets[1]; + BigInteger currentFaIcxBalance = FeeAggregationSCORETest.iconService.getBalance(feeAggregationScore.getAddress()).execute(); + BigInteger currentUserAIcxBalance = FeeAggregationSCORETest.iconService.getBalance(userA.getAddress()).execute(); + BigInteger currentUserARefundableBalance = feeAggregationScore.getRefundableBalance(userA.getAddress()); + LOG.infoEntering("call", "withdrawal() " + userA.getAddress().toString()); + TransactionResult withdrawTransactionResult = feeAggregationScore.ensureWithdrawSuccess(userA); + LOG.infoExiting(); + + BigInteger withdrawTransactionFeeInIcx = withdrawTransactionResult.getStepPrice().multiply(withdrawTransactionResult.getStepUsed()); + + BigInteger userAIcxBalanceAferWithdrew = FeeAggregationSCORETest.iconService.getBalance(userA.getAddress()).execute(); + BigInteger userARefundableBalanceAfterWithdrew = feeAggregationScore.getRefundableBalance(userA.getAddress()); + BigInteger faIcxBalanceAfterWithdrew = FeeAggregationSCORETest.iconService.getBalance(feeAggregationScore.getAddress()).execute(); + + assertTrue(userAIcxBalanceAferWithdrew.equals(currentUserAIcxBalance.add(currentUserARefundableBalance).subtract(withdrawTransactionFeeInIcx))); + assertTrue(userARefundableBalanceAfterWithdrew.equals(BigInteger.ZERO)); + assertTrue(faIcxBalanceAfterWithdrew.equals(currentFaIcxBalance.subtract(currentUserARefundableBalance))); + } + + /** + * Scenario 9: if User withdraw their refundable ICX token with avaialable refundable token is ZERO + * + * Given: + * - UserA refundable balance 0 ICX + * When: + * - UserA call Fee Aggregation to withdraw ICX token + * Then: + * - Transaction revert NOT_FOUND_BALANCE code 62 + */ + @Test + void scenario9Test() throws Exception { + KeyWallet userA = wallets[1]; + + BigInteger currentFaIcxBalance = FeeAggregationSCORETest.iconService.getBalance(feeAggregationScore.getAddress()).execute(); + BigInteger currentUserAIcxBalance = FeeAggregationSCORETest.iconService.getBalance(userA.getAddress()).execute(); + BigInteger currentUserARefundableBalance = feeAggregationScore.getRefundableBalance(userA.getAddress()); + assertTrue(currentUserARefundableBalance.equals(BigInteger.ZERO)); + + LOG.infoEntering("call", "withdrawal() " + userA.getAddress().toString()); + TransactionResult withdrawTransactionResult = feeAggregationScore.ensureWithdrawFailed(userA, 62); + LOG.infoExiting(); + + BigInteger withdrawTransactionFeeInIcx = withdrawTransactionResult.getStepPrice().multiply(withdrawTransactionResult.getStepUsed()); + + BigInteger userAIcxBalanceAferWithdrew = FeeAggregationSCORETest.iconService.getBalance(userA.getAddress()).execute(); + BigInteger faIcxBalanceAfterWithdrew = FeeAggregationSCORETest.iconService.getBalance(feeAggregationScore.getAddress()).execute(); + + assertTrue(userAIcxBalanceAferWithdrew.equals(currentUserAIcxBalance.subtract(withdrawTransactionFeeInIcx))); + assertTrue(faIcxBalanceAfterWithdrew.equals(currentFaIcxBalance)); + } + + /** + * Scenario 10: if User withdraw their token that not exist + * + * Given: + * - "SampleTokenA" not exist in FA; + * When: + * - UserA call Fee Aggregation to claim SampleTokenA token + * Then: + * - Transaction revert INVALID_TOKEN_NAME code 57 + */ + @Test + void scenario10Test() throws Exception { + KeyWallet userA = wallets[1]; + + String claimmingToken = "SampleTokenA"; + + LOG.infoEntering("call", "claim() token: " + claimmingToken + " user: "+ userA.getAddress().toString()); + feeAggregationScore.ensureClaimFailed(userA, claimmingToken, 57); + } + + /** + * Scenario 11: if User withdraw their token that they has no balance + * + * Given: + * - UserC has no balance of token "SampleToken"; + * When: + * - UserC call Fee Aggregation to claim SampleToken token + * Then: + * - Transaction revert NOT_FOUND_BALANCE code 62 + */ + @Test + void scenario11Test() throws Exception { + KeyWallet userC = wallets[3]; + + String claimmingToken = sampleTokenScore.name(); + BigInteger currentFaSampleTokenBalance = sampleTokenScore.balanceOf(feeAggregationScore.getAddress()); + + LOG.infoEntering("call", "claim() token: " + claimmingToken + " user: "+ userC.getAddress().toString()); + feeAggregationScore.ensureClaimFailed(userC, claimmingToken, 62); + + BigInteger faSampleTokenBalanceAfterClaimed = sampleTokenScore.balanceOf(feeAggregationScore.getAddress()); + assertTrue(faSampleTokenBalanceAfterClaimed.equals(currentFaSampleTokenBalance)); + } + + /** + * Scenario 12: if User withdraw their token successfully + * + * Given: + * - UserA has 28.5714285714 SampleMultiToken + * When: + * - UserA call Fee Aggregation to claim SampleMultiToken token + * Then: + * - Transfer 28.5714285714 SampleMultiToken for userA + * - Set SampleMultiToken balance of userA to ZERO + * - Update lockedBalance of SampleMultiToken of FA + */ + @Test + void scenario12Test() throws Exception { + KeyWallet userA = wallets[1]; + + String claimmingToken = SampleMultiTokenScore.NAME; + BigInteger currentFaSampleTokenBalance = irc31TokenScore.balanceOf(feeAggregationScore.getAddress(), SampleMultiTokenScore.ID); + BigInteger currentUserASampleTokenBalance = irc31TokenScore.balanceOf(userA.getAddress(), SampleMultiTokenScore.ID); + BigInteger currentUserASampleTokenClaimableBalance = feeAggregationScore.getTokenBalance(userA.getAddress(), claimmingToken); + + LOG.infoEntering("call", "claim() token: " + claimmingToken + " user: "+ userA.getAddress().toString()); + feeAggregationScore.ensureClaimSuccess(userA, claimmingToken); + + BigInteger faSampleTokenBalanceAfterClaimed = irc31TokenScore.balanceOf(feeAggregationScore.getAddress(), SampleMultiTokenScore.ID); + BigInteger userASampleTokenBalanceAfterClaimed = irc31TokenScore.balanceOf(userA.getAddress(), SampleMultiTokenScore.ID); + BigInteger userASampleTokenClaimableBalanceAfterClaimed = feeAggregationScore.getTokenBalance(userA.getAddress(), claimmingToken); + assertTrue(faSampleTokenBalanceAfterClaimed.equals(currentFaSampleTokenBalance.subtract(currentUserASampleTokenClaimableBalance))); + assertTrue(userASampleTokenBalanceAfterClaimed.equals(currentUserASampleTokenBalance.add(currentUserASampleTokenClaimableBalance))); + assertTrue(userASampleTokenClaimableBalanceAfterClaimed.equals(BigInteger.ZERO)); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/BandProtocolScore.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/BandProtocolScore.java new file mode 100644 index 00000000..30f781e6 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/BandProtocolScore.java @@ -0,0 +1,80 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.score; + +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.btp.ResultTimeoutException; +import foundation.icon.btp.TransactionFailureException; +import foundation.icon.btp.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; + +import static foundation.icon.btp.Env.LOG; + +public class BandProtocolScore extends Score { + public final static String NAME = "MySampleToken"; + + public static BandProtocolScore mustDeploy(TransactionHandler txHandler, Wallet wallet) + throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "SampleToken"); + Score score = txHandler.deploy(wallet, "bandProtocol.zip", null); + LOG.info("scoreAddr = " + score.getAddress()); + LOG.infoExiting(); + return new BandProtocolScore(score); + } + + public BandProtocolScore(Score other) { + super(other); + } + + public BigInteger getRefData(String symbol) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("_symbol", new RpcValue(symbol)) + .build(); + return call("get_ref_data", params).asObject().getItem("rate").asInteger(); + } + + public BigInteger getReferenceData(String base, String quote) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("_base", new RpcValue(base)) + .put("_quote", new RpcValue(quote)) + .build(); + return call("get_reference_data", params).asObject().getItem("rate").asInteger(); + } + + public Object getReferenceDataBulk(String bases, String quotes) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("_bases", new RpcValue(bases)) + .put("_quotes", new RpcValue(quotes)) + .build(); + return call("get_reference_data_bulk", params).asObject(); + } + + public TransactionResult relay(Wallet wallet, String symbols, String rates, String resolveTimes, String requestIds) + throws IOException, ResultTimeoutException { + RpcObject params = new RpcObject.Builder() + .put("_symbols", new RpcValue(symbols)) + .put("_rates", new RpcValue(rates)) + .put("_resolve_times", new RpcValue(resolveTimes)) + .put("_request_ids", new RpcValue(requestIds)).build(); + return this.invokeAndWaitResult(wallet, "relay", params); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/ChainScore.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/ChainScore.java new file mode 100644 index 00000000..4568e7d9 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/ChainScore.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 ICONLOOP Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.score; + +import foundation.icon.btp.Constants; +import foundation.icon.btp.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; + +public class ChainScore extends Score { + + public ChainScore(TransactionHandler txHandler) { + super(txHandler, Constants.ZERO_ADDRESS); + } + + public BigInteger getStepPrice() throws IOException { + return call("getStepPrice", null).asInteger(); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/FeeAggregationScore.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/FeeAggregationScore.java new file mode 100644 index 00000000..14bd445c --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/FeeAggregationScore.java @@ -0,0 +1,259 @@ +package foundation.icon.btp.score; + +import foundation.icon.btp.*; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +import static foundation.icon.btp.Env.LOG; + +public class FeeAggregationScore extends Score { + + public static FeeAggregationScore mustDeploy(TransactionHandler txHandler, Wallet wallet, Address bandProtocolAddress) + throws ResultTimeoutException, TransactionFailureException, IOException, AssertionError { + // Deploy CPS + Score cps = txHandler.deploy(wallet, "CPFTreasury.zip", null); + + LOG.infoEntering("deploy", "Fee Aggregation SCORE"); + RpcObject params = new RpcObject.Builder() + .put("_cps_address", new RpcValue(cps.getAddress())) + .put("_band_protocol_address", new RpcValue(bandProtocolAddress)) + .build(); + Score score = txHandler.deploy(wallet, getFilePath("fee-aggregation-system"), params); + LOG.info("scoreAddr = " + score.getAddress()); + LOG.infoExiting(); + return new FeeAggregationScore(score); + } + + public FeeAggregationScore(Score other) { + super(other); + } + + public void ensureRegisterIRC2Success(Wallet wallet, String tokenName, Address tokenAddress) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .put("_tokenAddress", new RpcValue(tokenAddress)) + .build(); + + invokeAndWaitResult(wallet, "registerIRC2", params); + + List tokens = call("tokens", null).asArray().asList(); + for (RpcItem token : tokens) { + if (token.asObject().getItem("name").asString().equals(tokenName)) { + return; + } + } + throw new IOException("ensureRegisterSuccess failed."); + } + + public void ensureRegisterIRC31Success(Wallet wallet, String tokenName, Address tokenAddress, BigInteger id, BigInteger decimal) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .put("_tokenAddress", new RpcValue(tokenAddress)) + .put("_tokenId", new RpcValue(id)) + .put("_decimal", new RpcValue(decimal)) + .build(); + + invokeAndWaitResult(wallet, "registerIRC31", params); + + List tokens = call("tokens", null).asArray().asList(); + for (RpcItem token : tokens) { + if (token.asObject().getItem("name").asString().equals(tokenName)) { + return; + } + } + throw new IOException("ensureRegisterIRC2Success failed."); + } + + public void ensureDepositIcxSuccess(TransactionHandler txHandler, Wallet wallet, String tokenName, BigInteger amount) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .build(); + + TransactionResult result = invokeAndWaitResult(wallet, "deposit", params, amount, null); + if (!Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new TransactionFailureException(result.getFailure()); + } + if (findEventLog(result, getAddress(), "SyndicationStart(int,str,int,Address,int,int)") == null && findEventLog(result, getAddress(), "DepositInfo(int,str,Address,int)") == null) { + throw new TransactionFailureException(result.getFailure()); + } + } + + public void ensureDepositIcxFailed(TransactionHandler txHandler, Wallet wallet, String tokenName, BigInteger amount, int errorCode) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .build(); + + TransactionResult result = invokeAndWaitResult(wallet, "deposit", params, amount, null); + if (Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new AssertionError("Expect to fail but success"); + } + + if (!result.getFailure().getMessage().equals("Reverted(" + errorCode + ")")) { + throw new AssertionError("Expect error code to be " + errorCode + " but receive " + result.getFailure().getMessage()); + } + } + + public void ensureDepositAmount(String tokenName, BigInteger amount, Address depositor) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .put("_depositor", new RpcValue(depositor)) + .build(); + BigInteger depositAmount = call("getDepositedAmount", params).asInteger(); + if (depositAmount.compareTo(amount) != 0) { + throw new AssertionError("Deposit amount are not equal to expected"); + } + } + + public void ensureTokenBalance(String tokenName, BigInteger expected, Address address) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .put("_address", new RpcValue(address)) + .build(); + BigInteger tokenBalance = call("getTokenBalance", params).asInteger(); + if (tokenBalance.compareTo(expected) != 0) { + throw new AssertionError("Token balance of " + address.toString() + " expected: " + expected.toString() + " but received: " + tokenBalance.toString()); + } + } + + public void ensureRefundableBalance(BigInteger expected, Address address) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_address", new RpcValue(address)) + .build(); + BigInteger tokenBalance = call("getRefundableBalance", params).asInteger(); + if (tokenBalance.compareTo(expected) != 0) { + throw new AssertionError("Refundable balance of " + address.toString() + " expected: " + expected.toString() + " but received: " + tokenBalance.toString()); + } + } + + public void ensureSyndicationInfo(Wallet wallet, String tokenName, BigInteger tokenAmount, BigInteger totalDeposited) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .build(); + + RpcObject syndication = call("getCurrentSyndication", params).asObject(); + if (!syndication.getItem("_totalDeposited").asInteger().equals(totalDeposited)) { + throw new IOException("ensureAuctionInfo failed due to not equal totalDeposited."); + } + + if (!syndication.getItem("_tokenAmount").asInteger().equals(tokenAmount)) { + throw new IOException("ensureAuctionInfo failed due to not equal tokenAmount."); + } + + return; + } + + public void ensureSetDurationSuccess(Wallet wallet, BigInteger duration) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_duration", new RpcValue(duration)) + .build(); + + TransactionResult result = invokeAndWaitResult(wallet, "setDurationTime", params, null); + if (!result.getStatus().equals(BigInteger.ONE)) { + throw new IOException("ensureSetDurationSuccess failed"); + }; + + RpcItem durationTime = call("getDurationTime", new RpcObject.Builder().build()); + if (!durationTime.asInteger().equals(duration)) { + throw new IOException("State data duration time not changed"); + }; + } + + public TransactionResult ensureWithdrawSuccess(Wallet wallet) throws Exception { + RpcObject params = new RpcObject.Builder().build(); + + TransactionResult result = invokeAndWaitResult(wallet, "withdrawal", params, null); + if (!result.getStatus().equals(BigInteger.ONE)) { + throw new IOException("ensureWithdrawSuccess failed"); + }; + return result; + } + + public TransactionResult ensureWithdrawFailed(Wallet wallet, int errorCode) throws Exception { + RpcObject params = new RpcObject.Builder().build(); + + TransactionResult result = invokeAndWaitResult(wallet, "withdrawal", params, null); + if (Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new AssertionError("Expect to fail but success"); + } + + if (!result.getFailure().getMessage().equals("Reverted(" + errorCode + ")")) { + throw new AssertionError("Expect error code to be " + errorCode + " but receive " + result.getFailure().getMessage()); + } + + return result; + } + + public TransactionResult ensureClaimFailed(Wallet wallet, String tokenName, int errorCode) throws Exception { + RpcObject params = new RpcObject.Builder().put("_tokenName", new RpcValue(tokenName)).build(); + + TransactionResult result = invokeAndWaitResult(wallet, "claim", params, null); + if (Constants.STATUS_SUCCESS.equals(result.getStatus())) { + throw new AssertionError("Expect to fail but success"); + } + + if (!result.getFailure().getMessage().equals("Reverted(" + errorCode + ")")) { + throw new AssertionError("Expect error code to be " + errorCode + " but receive " + result.getFailure().getMessage()); + } + + return result; + } + + public TransactionResult ensureClaimSuccess(Wallet wallet, String tokenName) throws Exception { + RpcObject params = new RpcObject.Builder().put("_tokenName", new RpcValue(tokenName)).build(); + + TransactionResult result = invokeAndWaitResult(wallet, "claim", params, null); + if (!result.getStatus().equals(BigInteger.ONE)) { + throw new IOException("ensureClaimSuccess failed"); + }; + return result; + } + + public BigInteger getDepositAmount(String tokenName, Address depositor) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .put("_depositor", new RpcValue(depositor)) + .build(); + return call("getDepositedAmount", params).asInteger(); + } + + public BigInteger getRefundableBalance(Address address) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_address", new RpcValue(address)) + .build(); + return call("getRefundableBalance", params).asInteger(); + } + + public BigInteger getTokenBalance(Address address, String tokenName) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_address", new RpcValue(address)) + .put("_tokenName", new RpcValue(tokenName)) + .build(); + return call("getTokenBalance", params).asInteger(); + } + + public BigInteger getTotalDeposited(String tokenName) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .build(); + + RpcItem syndication = call("getCurrentSyndication", params); + return syndication.asObject().getItem("_totalDeposited").asInteger(); + } + + public BigInteger getEndTime(String tokenName) throws Exception { + RpcObject params = new RpcObject.Builder() + .put("_tokenName", new RpcValue(tokenName)) + .build(); + + RpcObject syndication = call("getCurrentSyndication", params).asObject(); + return syndication.getItem("_endTime").asInteger(); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleMultiTokenScore.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleMultiTokenScore.java new file mode 100644 index 00000000..49499c2e --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleMultiTokenScore.java @@ -0,0 +1,87 @@ +package foundation.icon.btp.score; + +import foundation.icon.btp.ResultTimeoutException; +import foundation.icon.btp.TransactionFailureException; +import foundation.icon.btp.TransactionHandler; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; + +import java.io.IOException; +import java.math.BigInteger; + +import static foundation.icon.btp.Env.LOG; + +public class SampleMultiTokenScore extends Score { + public final static BigInteger ID = BigInteger.valueOf(100); + public final static String NAME = "MyIRC31SampleToken"; + private BigInteger decimals; + + public static SampleMultiTokenScore mustDeploy(TransactionHandler txHandler, Wallet wallet, BigInteger decimals) + throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "SampleMultiToken"); + Score score = txHandler.deploy(wallet, "SampleMultiToken.zip", null); + LOG.info("scoreAddr = " + score.getAddress()); + LOG.infoExiting(); + + return new SampleMultiTokenScore(score, decimals); + } + + public SampleMultiTokenScore(Score other, BigInteger decimals) { + super(other); + this.decimals = decimals; + } + + public BigInteger unit() { + return BigInteger.TEN.pow(this.decimals.intValue()); + } + + public BigInteger balanceOf(Address owner, BigInteger id) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("_owner", new RpcValue(owner)) + .put("_id", new RpcValue(id)) + .build(); + return call("balanceOf", params).asInteger(); + } + + public void mintToken(Wallet wallet) throws Exception { + // mint a Token + RpcObject params = new RpcObject.Builder() + .put("_id", new RpcValue(ID)) + .put("_supply", new RpcValue(BigInteger.TEN.pow(10).multiply(BigInteger.valueOf(10000)))) + .put("_uri", new RpcValue("https://example.com")) + .build(); + + invokeAndWaitResult(wallet, "mint", params); + } + + public TransactionResult transfer(Wallet wallet, Address from, Address to, BigInteger id, BigInteger value, byte[] data) + throws IOException, ResultTimeoutException { + RpcObject.Builder builder = new RpcObject.Builder() + .put("_from", new RpcValue(from)) + .put("_to", new RpcValue(to)) + .put("_id", new RpcValue(id)) + .put("_value", new RpcValue(value)); + if (data != null) { + builder.put("_data", new RpcValue(data)); + } + return this.invokeAndWaitResult(wallet, "transferFrom", builder.build()); + } + + public void ensureTransfer(TransactionResult result, Address from, Address to, BigInteger id, BigInteger value, byte[] data) + throws IOException { + TransactionResult.EventLog event = findEventLog(result, getAddress(), "TransferSingle(Address,Address,Address,int,int)"); + if (event != null) { + Address _from = event.getIndexed().get(2).asAddress(); + Address _to = event.getIndexed().get(3).asAddress(); + BigInteger _id = event.getData().get(0).asInteger(); + BigInteger _value = event.getData().get(1).asInteger(); + if (from.equals(_from) && to.equals(_to) && id.equals(_id) && value.equals(_value)) { + return; // ensured + } + } + throw new IOException("ensureTransfer failed."); + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleTokenScore.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleTokenScore.java new file mode 100644 index 00000000..b896efa7 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/SampleTokenScore.java @@ -0,0 +1,134 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.score; + +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.icx.transport.jsonrpc.RpcValue; +import foundation.icon.btp.Constants; +import foundation.icon.btp.ResultTimeoutException; +import foundation.icon.btp.TransactionFailureException; +import foundation.icon.btp.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; + +import static foundation.icon.btp.Env.LOG; + +public class SampleTokenScore extends Score { + private String name; + private BigInteger decimals; + + public static SampleTokenScore mustDeploy(TransactionHandler txHandler, Wallet wallet, String name, + BigInteger decimals, BigInteger initialSupply) + throws ResultTimeoutException, TransactionFailureException, IOException { + LOG.infoEntering("deploy", "SampleToken"); + RpcObject params = new RpcObject.Builder() + .put("_name", new RpcValue(name)) + .put("_symbol", new RpcValue("MST")) + .put("_decimals", new RpcValue(decimals)) + .put("_initialSupply", new RpcValue(initialSupply)) + .build(); + Score score = txHandler.deploy(wallet, "sample-token-0.2.0-optimized.jar", params); + LOG.info("scoreAddr = " + score.getAddress()); + LOG.infoExiting(); + return new SampleTokenScore(score, name, decimals); + } + + public SampleTokenScore(Score other, String name, BigInteger decimals) { + super(other); + this.name = name; + this.decimals = decimals; + } + + public String name() { + return this.name; + } + + public BigInteger unit() { + return BigInteger.TEN.pow(this.decimals.intValue()); + } + + public BigInteger balanceOf(Address owner) throws IOException { + RpcObject params = new RpcObject.Builder() + .put("_owner", new RpcValue(owner)) + .build(); + return call("balanceOf", params).asInteger(); + } + + public TransactionResult transfer(Wallet wallet, Address to, BigInteger value) + throws IOException, ResultTimeoutException { + return this.transfer(wallet, to, value, null); + } + + public TransactionResult transfer(Wallet wallet, Address to, BigInteger value, byte[] data) + throws IOException, ResultTimeoutException { + RpcObject.Builder builder = new RpcObject.Builder() + .put("_to", new RpcValue(to)) + .put("_value", new RpcValue(value)); + if (data != null) { + builder.put("_data", new RpcValue(data)); + } + return this.invokeAndWaitResult(wallet, "transfer", builder.build()); + } + + public void ensureTransfer(TransactionResult result, Address from, Address to, BigInteger value, byte[] data) + throws IOException { + TransactionResult.EventLog event = findEventLog(result, getAddress(), "Transfer(Address,Address,int,bytes)"); + if (event != null) { + if (data == null) { + data = new byte[0]; + } + Address _from = event.getIndexed().get(1).asAddress(); + Address _to = event.getIndexed().get(2).asAddress(); + BigInteger _value = event.getIndexed().get(3).asInteger(); + byte[] _data = event.getData().get(0).asByteArray(); + if (from.equals(_from) && to.equals(_to) && value.equals(_value) && Arrays.equals(data, _data)) { + return; // ensured + } + } + throw new IOException("ensureTransfer failed."); + } + + public void ensureTokenBalance(Address owner, long value) throws ResultTimeoutException, IOException { + long limitTime = System.currentTimeMillis() + Constants.DEFAULT_WAITING_TIME; + while (true) { + BigInteger balance = balanceOf(owner); + String msg = "Token balance of " + owner + ": " + balance; + if (balance.equals(BigInteger.valueOf(0))) { + try { + if (limitTime < System.currentTimeMillis()) { + throw new ResultTimeoutException(); + } + // wait until block confirmation + LOG.info(msg + "; Retry in 1 sec."); + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else if (balance.equals(BigInteger.valueOf(value).multiply(BigInteger.TEN.pow(18)))) { + LOG.info(msg); + break; + } else { + throw new IOException("Token balance mismatch!"); + } + } + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/Score.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/Score.java new file mode 100644 index 00000000..c0e44ab3 --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/score/Score.java @@ -0,0 +1,164 @@ +/* + * Copyright 2019 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.score; + +import foundation.icon.icx.Call; +import foundation.icon.icx.Transaction; +import foundation.icon.icx.TransactionBuilder; +import foundation.icon.icx.Wallet; +import foundation.icon.icx.data.Address; +import foundation.icon.icx.data.Bytes; +import foundation.icon.icx.data.TransactionResult; +import foundation.icon.icx.data.TransactionResult.EventLog; +import foundation.icon.icx.transport.jsonrpc.RpcItem; +import foundation.icon.icx.transport.jsonrpc.RpcObject; +import foundation.icon.btp.Constants; +import foundation.icon.btp.ResultTimeoutException; +import foundation.icon.btp.TransactionHandler; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.List; + +public class Score { + private final TransactionHandler txHandler; + private final Address address; + + public Score(TransactionHandler txHandler, Address scoreAddress) { + this.txHandler = txHandler; + this.address = scoreAddress; + } + + public Score(Score other) { + this(other.txHandler, other.address); + } + + public static String getFilePath(String pkgName) { + String key = "score.path." + pkgName; + String path = System.getProperty(key); + if (path == null) { + throw new IllegalArgumentException("No such property: " + key); + } + return path; + } + + protected static EventLog findEventLog(TransactionResult result, Address scoreAddress, String funcSig) { + List eventLogs = result.getEventLogs(); + for (EventLog event : eventLogs) { + if (event.getScoreAddress().equals(scoreAddress.toString())) { + String signature = event.getIndexed().get(0).asString(); + if (funcSig.equals(signature)) { + return event; + } + } + } + return null; + } + + public RpcItem call(String method, RpcObject params) + throws IOException { + if (params == null) { + params = new RpcObject.Builder().build(); + } + Call call = new Call.Builder() + .to(getAddress()) + .method(method) + .params(params) + .build(); + return this.txHandler.call(call); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params) throws IOException { + return invoke(wallet, method, params, BigInteger.ZERO, null); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params, + BigInteger value, BigInteger steps) throws IOException { + return invoke(wallet, method, params, value, steps, null, null); + } + + public Bytes invoke(Wallet wallet, String method, RpcObject params, BigInteger value, + BigInteger steps, BigInteger timestamp, BigInteger nonce) throws IOException { + Transaction tx = getTransaction(wallet, method, params, value, timestamp, nonce); + return this.txHandler.invoke(wallet, tx, steps); + } + + private Transaction getTransaction(Wallet wallet, String method, RpcObject params, BigInteger value, + BigInteger timestamp, BigInteger nonce) { + TransactionBuilder.Builder builder = TransactionBuilder.newBuilder() + .nid(getNetworkId()) + .from(wallet.getAddress()) + .to(getAddress()); + + if ((value != null) && value.bitLength() != 0) { + builder.value(value); + } + if ((timestamp != null) && timestamp.bitLength() != 0) { + builder.timestamp(timestamp); + } + if (nonce != null) { + builder.nonce(nonce); + } + + Transaction tx; + if (params != null) { + tx = builder.call(method).params(params).build(); + } else { + tx = builder.call(method).build(); + } + return tx; + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params) + throws ResultTimeoutException, IOException { + return invokeAndWaitResult(wallet, method, params, null, null); + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params, + BigInteger steps) + throws ResultTimeoutException, IOException { + return invokeAndWaitResult(wallet, method, params, null, steps); + } + + public TransactionResult invokeAndWaitResult(Wallet wallet, String method, RpcObject params, + BigInteger value, BigInteger steps) + throws ResultTimeoutException, IOException { + Bytes txHash = this.invoke(wallet, method, params, value, steps); + return getResult(txHash); + } + + public TransactionResult getResult(Bytes txHash) throws ResultTimeoutException, IOException { + return getResult(txHash, Constants.DEFAULT_WAITING_TIME); + } + + public TransactionResult getResult(Bytes txHash, long waiting) throws ResultTimeoutException, IOException { + return this.txHandler.getResult(txHash, waiting); + } + + public Address getAddress() { + return this.address; + } + + public BigInteger getNetworkId() { + return txHandler.getNetworkId(); + } + + @Override + public String toString() { + return "SCORE(" + getAddress().toString() + ")"; + } +} diff --git a/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/util/ZipFile.java b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/util/ZipFile.java new file mode 100644 index 00000000..ff20eb0b --- /dev/null +++ b/javascore/fee_aggregation/src/intTest/java/foundation/icon/btp/util/ZipFile.java @@ -0,0 +1,62 @@ +/* + * Copyright 2020 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.util; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class ZipFile { + public static byte[] zipContent(String path) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(outputStream); + recursiveZip(new File(path), null, zos); + zos.close(); + outputStream.close(); + return outputStream.toByteArray(); + } + + private static void recursiveZip(File source, String zipPath, ZipOutputStream zos) throws IOException { + if (source.isHidden()) { + return; + } + if (source.isDirectory()) { + String dir = source.getName(); + if (!dir.endsWith(File.separator)) { + dir = dir + File.separator; + } + zos.putNextEntry(new ZipEntry(dir)); + zos.closeEntry(); + File[] files = source.listFiles(); + if (files == null) { + return; + } + String path = zipPath == null ? dir : zipPath + dir; + for (File file : files) { + recursiveZip(file, path, zos); + } + } else { + ZipEntry ze = new ZipEntry(zipPath + source.getName()); + zos.putNextEntry(ze); + zos.write(Files.readAllBytes(source.toPath())); + zos.closeEntry(); + } + } +} diff --git a/javascore/fee_aggregation/src/main/java/foundation/icon/btp/ErrorCode.java b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/ErrorCode.java new file mode 100644 index 00000000..bef8626f --- /dev/null +++ b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/ErrorCode.java @@ -0,0 +1,11 @@ +package foundation.icon.btp; + +public class ErrorCode { + public static final int PERMISSION_DENIED = 56; + public static final int INVALID_TOKEN_NAME = 57; + public static final int INVALID_CONTRACT_ADDRESS = 58; + public static final int INVALID_VALUE = 59; + public static final int INVALID_TOKEN_BALANCE = 61; + public static final int NOT_FOUND_BALANCE = 62; +} + diff --git a/javascore/fee_aggregation/src/main/java/foundation/icon/btp/FeeAggregationSCORE.java b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/FeeAggregationSCORE.java new file mode 100644 index 00000000..7e10a141 --- /dev/null +++ b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/FeeAggregationSCORE.java @@ -0,0 +1,503 @@ +package foundation.icon.btp; + +import score.*; +import score.annotation.EventLog; +import score.annotation.External; +import score.annotation.Payable; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +public class FeeAggregationSCORE extends IRC31Receiver { + public static final VarDB syndicationCount = Context.newVarDB("syndicationCount", BigInteger.class); + public static final BranchDB> syndicationDepositorAmount = Context.newBranchDB("syndicationDepositorAmount", BigInteger.class); + public static final VarDB durationTime = Context.newVarDB("durationTime", Long.class); + public static final BranchDB> syndicationDepositorList = Context.newBranchDB("syndicationDepositorList", Address.class); + + /** + * Address of the Contribution Proposal System. + */ + private final VarDB

addressCPS = Context.newVarDB("addressCPS", Address.class); + + /** + * Address of the Band protocol + */ + private final VarDB
addressBandProtocol = Context.newVarDB("addressBandProtocol", Address.class); + + /** + * Discount percent of token price + */ + private final VarDB discount = Context.newVarDB("discount", BigInteger.class); + + /** + * List of token contract, which system accepted. + * + * Example: { + * "IRC2Token1": { + * "address": "0x213123123123123", + * "tokenId": 1 + * } + * } + */ + private final DictDB tokensScore; + private final ArrayDB supportedTokens; + + /** + * Auction Information by tokenName + */ + private final DictDB syndications; + + /** + * Lock balance not available for auction; + * - Key: tokenName + * - Value: amount of tokens + */ + public final DictDB lockedBalances; + + /** + * Balance token of winner + * - Key: tokenName + */ + private final BranchDB> tokenBalances; + + /** + * Amount to refund + */ + private final DictDB refundableBalances; + + public FeeAggregationSCORE(Address _cps_address, Address _band_protocol_address) { + this.addressCPS.set(_cps_address); + this.addressBandProtocol.set(_band_protocol_address); + this.tokensScore = Context.newDictDB("tokensScore", Token.class); + this.supportedTokens = Context.newArrayDB("supportedTokens", String.class); + this.syndications = Context.newDictDB("syndications", Syndication.class); + this.tokenBalances = Context.newBranchDB("tokenBalances", BigInteger.class); + this.refundableBalances = Context.newDictDB("refundableBalances", BigInteger.class); + this.lockedBalances = Context.newDictDB("lockedBalances", BigInteger.class); + this.discount.set(BigInteger.valueOf(10L)); + durationTime.set(43200000000L); + syndicationCount.set(BigInteger.ZERO); + }; + + private Token getSafeTokenScore(String _tokenName) { + return this.tokensScore.getOrDefault(_tokenName, null); + } + + private BigInteger getSafeLockBalance(String _tokenName) { + return this.lockedBalances.getOrDefault(_tokenName, BigInteger.ZERO); + } + + private Syndication getSafeSyndication(String _tokenName) { + return this.syndications.getOrDefault(_tokenName, null); + } + + private BigInteger getSafeRefundableBalance(Address _address) { + return this.refundableBalances.getOrDefault(_address, BigInteger.ZERO); + } + + private BigInteger getSafeTokenBalance(Address _address, String _tokenName) { + return this.tokenBalances.at(_address).getOrDefault(_tokenName, BigInteger.ZERO); + } + + private BigInteger getTokenBalance(Token _token, Address _owner) { + if (_token.isIRC31()) { + return (BigInteger) Context.call(_token.address(), "balanceOf", _owner, _token.tokenId()); + } else { + return (BigInteger) Context.call(_token.address(), "balanceOf", _owner); + } + } + + private BigInteger getTokenPrice(Token _token) { + Map referenceData = (Map) Context.call(this.addressBandProtocol.get(), "get_reference_data", _token.name(), "ICX"); + return (BigInteger) referenceData.get("rate"); + } + + /** + * (Operator) + * Operator register a IRC2 token + * + * @param _tokenName Name of a token + * @param _tokenAddress Address of token contract + */ + @External + public void registerIRC2(String _tokenName, Address _tokenAddress) { + if (!Context.getCaller().equals(Context.getOwner())) { + Context.revert(ErrorCode.PERMISSION_DENIED, "permission denied"); + } + if (getSafeTokenScore(_tokenName) != null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is existed"); + } + if (!_tokenAddress.isContract()) { + Context.revert(ErrorCode.INVALID_CONTRACT_ADDRESS, "_tokenAddress is invalid"); + } + + this.tokensScore.set(_tokenName, new Token(_tokenName, _tokenAddress)); + this.supportedTokens.add(_tokenName); + } + + /** + * (Operator) + * Operator register a IRC31 Token + * + * @param _tokenName Name of a token + * @param _tokenAddress Address of token contract + * @param _tokenId Id of token + */ + @External + public void registerIRC31(String _tokenName, Address _tokenAddress, BigInteger _tokenId, BigInteger _decimal) { + if (!Context.getCaller().equals(Context.getOwner())) { + Context.revert(ErrorCode.PERMISSION_DENIED, "permission denied"); + } + if (getSafeTokenScore(_tokenName) != null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is existed"); + } + if (!_tokenAddress.isContract()) { + Context.revert(ErrorCode.INVALID_CONTRACT_ADDRESS, "_tokenAddress is invalid"); + } + if (_tokenId.compareTo(BigInteger.ZERO) <= 0) { + Context.revert(ErrorCode.INVALID_VALUE, "_tokenId is invalid, must > 0"); + } + + this.tokensScore.set(_tokenName, new Token(_tokenName, _tokenAddress, _tokenId, _decimal)); + this.supportedTokens.add(_tokenName); + } + + /** + * (Operator) + * Operator set the duration time for auction + * + * @param _duration Duration time of a auction + */ + @External + public void setDurationTime(BigInteger _duration) { + if (!Context.getCaller().equals(Context.getOwner())) { + Context.revert(ErrorCode.PERMISSION_DENIED, "permission denied"); + } + if (_duration.equals(BigInteger.ZERO)) { + Context.revert(ErrorCode.INVALID_VALUE, "_duration is invalid, must > 0"); + } + + durationTime.set(_duration.longValueExact()); + } + + @External(readonly = true) + public BigInteger getDurationTime() { + return BigInteger.valueOf(durationTime.getOrDefault(0L)); + } + + @External(readonly = true) + public BigInteger getTokenBalance(String _tokenName, Address _address) { + return getSafeTokenBalance(_address, _tokenName); + } + + @External(readonly = true) + public BigInteger getRefundableBalance(Address _address) { + return this.getSafeRefundableBalance(_address); + } + + /** + * (User) + * List of tokens which defined in supportedTokens and tokensScore + * + * @return List of Token + */ + @External(readonly = true) + public List> tokens() { + int len = this.supportedTokens.size(); + Map[] tokens = new Map[len]; + + for (int i = 0; i < len; i++) { + tokens[i] = (this.tokensScore.get(this.supportedTokens.get(i)).toMap()); + } + + return List.of(tokens); + } + + + /** + * Get auction information. + * + * @param _tokenName - The name of token which defined in supportedTokens and tokensScore + * @return Auction + */ + @External(readonly = true) + public Map getCurrentSyndication(String _tokenName) { + if (getSafeTokenScore(_tokenName) == null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is not registered yet"); + } + + if (isSyndicationExpired(_tokenName)) return null; + return getSafeSyndication(_tokenName).toMap(); + } + + /** + * Get deposited amount of user of current syndication + * + * @param _tokenName - The name of token which defined in supportedTokens and tokensScore + * @return Auction + */ + @External(readonly = true) + public BigInteger getDepositedAmount(String _tokenName, Address _depositor) { + if (getSafeTokenScore(_tokenName) == null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is not registered yet"); + } + + Syndication syndication = this.getSafeSyndication(_tokenName); + if (syndication == null) { + Context.revert("no active syndication"); + } + + return FeeAggregationSCORE.syndicationDepositorAmount.at(syndication.id()).getOrDefault(_depositor, BigInteger.ZERO); + } + + /** + * Pure transfer ICX + */ + @Payable + public void fallback() { + Address _from = Context.getCaller(); + BigInteger _value = Context.getValue(); + Context.require(_value.compareTo(BigInteger.ZERO) > 0); + + Context.call(_value, this.addressCPS.get(), "add_fund"); + } + + @External + public void tokenFallback(Address _from, BigInteger _value, byte[] _data) { } + + /** + * Called when a user submit a bid on asset. This function will: + * - Hold ICX of user + * - Add bid of user + * + * @param _tokenName - The name of token which defined in supportedTokens and tokensScore + */ + @External + @Payable + public void deposit(String _tokenName) { + Token token = getSafeTokenScore(_tokenName); + if (token == null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is not registered yet"); + } + BigInteger balance = getTokenBalance(token, Context.getAddress()); + + if (balance.equals(BigInteger.ZERO)) { + Context.revert(ErrorCode.INVALID_TOKEN_BALANCE, "available token balance is 0"); + } + + BigInteger value = Context.getValue(); + + long now = Context.getBlockTimestamp(); + Syndication syndication = getSafeSyndication(_tokenName); + Address caller = Context.getCaller(); + + // Check if auction is not start yet + if (syndication == null) { + // Check available balance of token enough to start an auction + BigInteger availableBalance = balance.subtract(getSafeLockBalance(_tokenName)); + if (availableBalance.compareTo(BigInteger.ZERO) <= 0) { + Context.revert(ErrorCode.INVALID_TOKEN_BALANCE, "available token balance is 0"); + } + + syndication = new Syndication(availableBalance, caller, value, now); + this.syndications.set(_tokenName, syndication); + + // Event log + SyndicationStart(syndication.id(), _tokenName, availableBalance, caller, value, syndication.endTime()); + } else { + // Check auction is ended + if (isSyndicationExpired(_tokenName)) { + Syndication oldSyndication = this.syndications.get(_tokenName); + + // Bid for new auction + balance = getTokenBalance(token, Context.getAddress()); + BigInteger availableBalance = balance.subtract(getSafeLockBalance(_tokenName)).subtract(oldSyndication.tokenAmount()); + if (availableBalance.compareTo(BigInteger.ZERO) <= 0) { + Context.revert(ErrorCode.INVALID_TOKEN_BALANCE, "available token balance is 0"); + } + + syndication = new Syndication(availableBalance, caller, value, now); + this.syndications.set(_tokenName, syndication); + + // Transfer token to winner & ICX to CPS + endAuction(token, oldSyndication); + + // Event log + SyndicationStart(syndication.id(), _tokenName, availableBalance, caller, value, syndication.endTime()); + } else { + syndication = getSafeSyndication(_tokenName); + syndication.deposit(caller, value); + + this.syndications.set(_tokenName, syndication); + + // Event Log + DepositInfo(syndication.id(), _tokenName, caller, value); + } + } + } + + /** + * (User) + * User use to withdraw their ICX when another bid higher but the system can not transfer ICX to them + */ + @External + public void withdrawal() { + Address caller = Context.getCaller(); + BigInteger amount = getSafeRefundableBalance(caller); + if (amount.compareTo(BigInteger.ZERO) <= 0) { + Context.revert(ErrorCode.NOT_FOUND_BALANCE, "not found ICX balance"); + } + + // Remove loser in + this.refundableBalances.set(caller, BigInteger.ZERO); + + // refund to loser + try { + Context.transfer(caller, amount); + } catch (Exception e) { + Context.println("[Exception] Withdrawal " + e.getMessage()); + this.refundableBalances.set(caller, getSafeRefundableBalance(caller).add(amount)); + } + } + + /** + * (User) + * User use to claim their tokens when User deposit and get token + */ + @External + public void claim(String _tokenName) { + Token token = getSafeTokenScore(_tokenName); + if (token == null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is not registered yet"); + } + + Address caller = Context.getCaller(); + BigInteger amount = getSafeTokenBalance(caller, _tokenName); + if (amount.compareTo(BigInteger.ZERO) <= 0) { + Context.revert(ErrorCode.NOT_FOUND_BALANCE, "not found _tokenName balance"); + } + + // release Token locked + this.lockedBalances.set(_tokenName, this.getSafeLockBalance(_tokenName).subtract(amount)); + + // Remove loser in + this.tokenBalances.at(caller).set(_tokenName, BigInteger.ZERO); + + try { + // transfer token to winner + if (!token.isIRC31()) { + Context.call(token.address(), "transfer", caller, amount, "transfer to depositor".getBytes()); + } else { + Context.call(token.address(), "transferFrom", Context.getAddress(), caller, token.tokenId(), amount, "transfer to depositor".getBytes()); + } + } catch (Exception e) { + Context.println("[Exception] " + e.getMessage()); + + // Lock amount token to winner claims manually + this.lockedBalances.set(_tokenName, getSafeLockBalance(_tokenName).add(amount)); + this.tokenBalances.at(caller).set(_tokenName, amount); + } + + // End Auction + Syndication syndication = getSafeSyndication(_tokenName); + if (syndication != null && Context.getBlockTimestamp() >= syndication.endTime()) { + this.syndications.set(_tokenName, null); + endAuction(getSafeTokenScore(_tokenName), syndication); + } + } + + @External(readonly = true) + public BigInteger availableBalance(String _tokenName) { + Token token = getSafeTokenScore(_tokenName); + if (token == null) { + Context.revert(ErrorCode.INVALID_TOKEN_NAME, "_tokenName is not registered yet"); + } + BigInteger balance = getTokenBalance(token, Context.getAddress()); + Syndication syndication = getSafeSyndication(_tokenName); + BigInteger lockedBalance = getSafeLockBalance(_tokenName); + if (syndication == null) { + return balance.subtract(lockedBalance); + } else { + if (isSyndicationExpired(_tokenName)) { + if (balance.compareTo(syndication.tokenAmount().add(lockedBalance)) > -1) { + return balance.subtract(syndication.tokenAmount()).subtract(lockedBalance); + } else return BigInteger.ZERO; + } else { + return syndication.tokenAmount(); + } + } + } + + /** + * Set end syndication if current syndication start over TWELVE_HOURS milliseconds + * + * This function will: + * - set end current syndication + * - send token to depositor + * - send ICX to CPS + */ + private void endAuction(Token _token, Syndication _syndication) { + // Send token to winner + BigInteger balance = getTokenBalance(_token, Context.getAddress()); + if (balance.compareTo(_syndication.tokenAmount()) < 0) { + Context.revert(ErrorCode.INVALID_TOKEN_BALANCE, "not enough token"); + } + + // Send ICX to CPS + if (!_syndication.totalDeposited().equals(BigInteger.ZERO)) { + Context.call(_syndication.totalDeposited(), this.addressCPS.get(), "add_fund"); + } + + this.distributeTokenShare(_token, _syndication); + + // Event log + SyndicationEnded(_syndication.id(), _token.name(), _syndication.tokenAmount(), Context.getBlockTimestamp()); + } + + public void distributeTokenShare(Token _token, Syndication _syndication) { + BigInteger syndicationId = _syndication.id(); + BigInteger tokenAmount = _syndication.tokenAmount(); + BigInteger tokenUnit = _token.getUnit(); + BigInteger tokenPrice = this.getTokenPrice(_token); + BigInteger tokenPriceWithDiscount = tokenPrice.multiply(BigInteger.valueOf(100).subtract(this.discount.get())).divide(BigInteger.valueOf(100L)); + BigInteger tokenAmountInIcx = tokenAmount.multiply(tokenPriceWithDiscount).divide(tokenUnit); + int numberDepositor = FeeAggregationSCORE.syndicationDepositorList.at(syndicationId).size(); + BigInteger totalDeposited = _syndication.totalDeposited(); + BigInteger totalDistributedToken = BigInteger.ZERO; + for (int i = 0; i < numberDepositor; i++) { + Address currentDepositorAddress = FeeAggregationSCORE.syndicationDepositorList.at(syndicationId).get(i); + BigInteger currentDepositorAmount = FeeAggregationSCORE.syndicationDepositorAmount.at(syndicationId).get(currentDepositorAddress); + BigInteger tokenShareAmount; + BigInteger icxRemain = BigInteger.ZERO; + if (tokenAmountInIcx.compareTo(totalDeposited) > 0) { + tokenShareAmount = currentDepositorAmount.multiply(tokenUnit).divide(tokenPriceWithDiscount); + } else { + tokenShareAmount = currentDepositorAmount.multiply(tokenAmount).divide(totalDeposited); + icxRemain = currentDepositorAmount.subtract(tokenShareAmount.multiply(tokenPriceWithDiscount).divide(tokenUnit)); + } + totalDistributedToken = totalDistributedToken.add(tokenShareAmount); + this.tokenBalances.at(currentDepositorAddress).set(_token.name(), getSafeTokenBalance(currentDepositorAddress, _token.name()).add(tokenShareAmount)); + + if (icxRemain.compareTo(BigInteger.ZERO) > 0) { + this.refundableBalances.set(currentDepositorAddress, getSafeRefundableBalance(currentDepositorAddress).add(icxRemain)); + } + } + this.lockedBalances.set(_token.name(), getSafeLockBalance(_token.name()).add(totalDistributedToken)); + } + + boolean isSyndicationExpired(String _tokenName) { + Syndication syndication = getSafeSyndication(_tokenName); + // checks if auction is not started yet or ended + return syndication == null || Context.getBlockTimestamp() >= syndication.endTime(); + } + + @EventLog(indexed = 3) + protected void DepositInfo(BigInteger auctionID, String tokenName, Address depositor, BigInteger amount) {} + + @EventLog(indexed = 3) + protected void SyndicationStart(BigInteger auctionID, String tokenName, BigInteger tokenAmount, Address firstDepositor, BigInteger amount, long deadline) {} + + @EventLog(indexed = 3) + protected void SyndicationEnded(BigInteger auctionID, String tokenName, BigInteger tokenAmount, long deadline) {} +} \ No newline at end of file diff --git a/javascore/fee_aggregation/src/main/java/foundation/icon/btp/IRC31Receiver.java b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/IRC31Receiver.java new file mode 100644 index 00000000..787a4159 --- /dev/null +++ b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/IRC31Receiver.java @@ -0,0 +1,51 @@ +package foundation.icon.btp; + +import score.Address; +import score.Context; +import score.DictDB; +import score.annotation.EventLog; +import score.annotation.External; + +import java.math.BigInteger; + +public class IRC31Receiver { + + public IRC31Receiver() {} + + /** + * A method for handling a single token type transfer, which is called from the multi token contract. + * It works by analogy with the fallback method of the normal transactions and returns nothing. + * @throws if it rejects the transfer. + * + * @param _operator: The address which initiated the transfer + * @param _from: the address which previously owned the token + * @param _id: the ID of the token being transferred + * @param _value: the amount of tokens being transferred + * @param _data: additional data with no specified format + */ + @External + public void onIRC31Received(Address _operator, Address _from, BigInteger _id, BigInteger _value, byte[] _data) { + IRC31Received(_operator, _from, _id, _value, _data); + } + + /** + * A method for handling multiple token type transfers, which is called from the multi token contract. + * It works by analogy with the fallback method of the normal transactions and returns nothing. + * @throws if it rejects the transfer. + * + * @param _operator: The address which initiated the transfer + * @param _from: the address which previously owned the token + * @param _ids: the list of IDs of each token being transferred (order and length must match `_values` list) + * @param _values: the list of amounts of each token being transferred (order and length must match `_ids` list) + * @param _data: additional data with no specified format + */ + @External + public void onIRC31BatchReceived(Address _operator, Address _from, BigInteger[] _ids, BigInteger[] _values, byte[] _data) { + for (int i = 0; i < _ids.length; i++) { + IRC31Received(_operator, _from, _ids[i], _values[i], _data); + } + } + + @EventLog(indexed=3) + protected void IRC31Received(Address _operator, Address _from, BigInteger _id, BigInteger _value, byte[] _data) {}; +} diff --git a/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Syndication.java b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Syndication.java new file mode 100644 index 00000000..c5616d8f --- /dev/null +++ b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Syndication.java @@ -0,0 +1,104 @@ +package foundation.icon.btp; + +import score.Address; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; +import java.util.Map; +import score.*; + +public class Syndication { + private BigInteger id; + + /** + * Amount of token to bid + */ + private BigInteger tokenAmount; + + /** + * Total amount has been deposited to syndication + */ + private BigInteger totalDeposited; + + /** + * Start time of a syndication by token contract address + */ + private long startTime; + + private long endTime; + + public Syndication(BigInteger _tokenAmount, Address _depositor, BigInteger _amount, long _startTime) { + this.id = FeeAggregationSCORE.syndicationCount.getOrDefault(BigInteger.ZERO).add(BigInteger.ONE); + FeeAggregationSCORE.syndicationCount.set(this.id); + this.tokenAmount = _tokenAmount; + this.startTime = _startTime; + this.endTime = _startTime + FeeAggregationSCORE.durationTime.get(); + this.totalDeposited = BigInteger.ZERO; + this.deposit(_depositor, _amount); + } + + private Syndication(BigInteger _id, BigInteger _tokenAmount, BigInteger _totalDeposited, long _startTime, long _endTime) { + this.id = _id; + this.tokenAmount = _tokenAmount; + this.startTime = _startTime; + this.endTime = _endTime; + this.totalDeposited = _totalDeposited; + } + + public static void writeObject(ObjectWriter w, Syndication t) { + w.beginList(6); + w.write(t.id); + w.write(t.tokenAmount); + w.write(t.totalDeposited); + w.write(t.startTime); + w.write(t.endTime); + w.end(); + } + + public static Syndication readObject(ObjectReader r) { + r.beginList(); + Syndication t = new Syndication( + r.readBigInteger(), + r.readBigInteger(), + r.readBigInteger(), + r.readLong(), + r.readLong()); + r.end(); + return t; + } + + public BigInteger id() { return this.id; } + + public BigInteger tokenAmount() { + return this.tokenAmount; + } + + public BigInteger totalDeposited() { + return this.totalDeposited; + } + + public void deposit(Address depositor, BigInteger amount) { + BigInteger totalUserDeposit = FeeAggregationSCORE.syndicationDepositorAmount.at(this.id).getOrDefault(depositor, BigInteger.ZERO); + if (totalUserDeposit.compareTo(BigInteger.ZERO) == 0) { + FeeAggregationSCORE.syndicationDepositorList.at(this.id).add(depositor); + } + totalUserDeposit = totalUserDeposit.add(amount); + this.totalDeposited = this.totalDeposited.add(amount); + FeeAggregationSCORE.syndicationDepositorAmount.at(this.id).set(depositor, totalUserDeposit); + } + + public long startTime() { return this.startTime; } + + public long endTime() { return this.endTime; } + + public Map toMap() { + return Map.of( + "_id", "0x" + this.id.toString(16), + "_tokenAmount", "0x" + this.tokenAmount.toString(16), + "_totalDeposited", "0x" + this.totalDeposited.toString(16), + "_startTime", "0x" + BigInteger.valueOf(this.startTime).toString(16), + "_endTime", "0x" + BigInteger.valueOf(this.endTime).toString(16) + ); + } +} diff --git a/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Token.java b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Token.java new file mode 100644 index 00000000..6ad99ca4 --- /dev/null +++ b/javascore/fee_aggregation/src/main/java/foundation/icon/btp/Token.java @@ -0,0 +1,91 @@ +package foundation.icon.btp; + +import score.Address; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; +import java.util.Map; +import score.Context; + +public class Token { + private static final BigInteger BIGINT_NO_EXISTED_IN_TOKEN_ID = BigInteger.ZERO; + private String name; + private Address address; + private BigInteger decimals; + /** + * Default value is BIGINT_NO_EXISTED_IN_TOKEN_ID, mean this token not exist tokenId + */ + private BigInteger tokenId; + + public Token(String _name, Address _address) { + this.name = _name; + this.address = _address; + this.tokenId = BIGINT_NO_EXISTED_IN_TOKEN_ID; + } + public Token(String _name, Address _address, BigInteger _tokenId, BigInteger _decimals) { + if (_tokenId != null) { + this.tokenId = _tokenId; + } + this.decimals = _decimals; + this.name = _name; + this.address = _address; + } + + public BigInteger getDecimals() { + if (this.decimals != null) { + return this.decimals; + } + return (BigInteger) Context.call(this.address, "decimals"); + } + + public BigInteger getUnit() { + int tokenDecimals = this.getDecimals().intValue(); + String unitString = "1"; + for (int i = 0 ; i < tokenDecimals; i++) { + unitString = unitString + "0"; + } + return new BigInteger(unitString); + } + + public String name() { + return this.name; + } + + public Address address() { + return this.address; + } + + public BigInteger tokenId() { + return this.tokenId; + } + + public Boolean isIRC31() { + return !this.tokenId.equals(BIGINT_NO_EXISTED_IN_TOKEN_ID); + } + + public static void writeObject(ObjectWriter w, Token t) { + w.beginList(3); + w.write(t.name); + w.write(t.address); + w.writeNullable(t.tokenId); + w.writeNullable(t.decimals); + w.end(); + } + + public static Token readObject(ObjectReader r) { + r.beginList(); + Token t = new Token( + r.readString(), + r.readAddress(), + r.readNullable(BigInteger.class), + r.readNullable(BigInteger.class) + ); + r.end(); + return t; + } + + public Map toMap() { + return Map.of("name", this.name, "address", this.address.toString(), "tokenId", this.tokenId.toString()); + } +} \ No newline at end of file diff --git a/javascore/gradle.properties b/javascore/gradle.properties new file mode 100644 index 00000000..9f4b0574 --- /dev/null +++ b/javascore/gradle.properties @@ -0,0 +1,15 @@ +group=foundation.icon +score-util.version=0.1.0 +#dependencies +javaee.version=0.9.0 +scorex.version=0.5.2 +javaee-unittest.version=0.9.1 +score-client.version=0.9.0 +iconsdk.version=2.0.0 +jackson.version=2.9.6 +#optimizedJar +debugJar=false +#test +integrationTest=false +score-test.url=http://localhost:9082/api/v3 +score-test.nid=0x3 diff --git a/javascore/gradle/wrapper/gradle-wrapper.jar b/javascore/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..e708b1c0 Binary files /dev/null and b/javascore/gradle/wrapper/gradle-wrapper.jar differ diff --git a/javascore/gradle/wrapper/gradle-wrapper.properties b/javascore/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..e5a62445 --- /dev/null +++ b/javascore/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jul 15 15:28:05 KST 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/javascore/gradlew b/javascore/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/javascore/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/javascore/gradlew.bat b/javascore/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/javascore/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/javascore/lib/build.gradle b/javascore/lib/build.gradle new file mode 100644 index 00000000..3e855a1e --- /dev/null +++ b/javascore/lib/build.gradle @@ -0,0 +1,18 @@ +version = '0.1.0' + +apply plugin: 'java-library' + +dependencies { + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + implementation project(':score-util') + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion") +} + +test { + useJUnitPlatform() +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMC.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMC.java new file mode 100644 index 00000000..25dfad52 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMC.java @@ -0,0 +1,195 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.annotation.External; + +import java.math.BigInteger; +import java.util.Map; + +public interface BMC { + /** + * Registers BMV for the network. + * Called by the operator to manage the BTP network. + * + * @param _net String (Network Address of the blockchain ) + * @param _addr Address (the address of BMV) + */ + @External + void addVerifier(String _net, Address _addr); + + /** + * Unregisters BMV for the network. + * May fail if it's referred by the link. + * Called by the operator to manage the BTP network. + * + * @param _net String (Network Address of the blockchain ) + */ + @External + void removeVerifier(String _net); + + /** + * Get registered verifiers. + * + * @return A dictionary with the Network Address as a key and smart contract address of the BMV as a value. + *
+ * For Example:: + *
+ * { + * "0x1.iconee": "cx72eaed466599ca5ea377637c6fa2c5c0978537da" + * } + */ + @External(readonly = true) + Map getVerifiers(); + + /** + * Registers the smart contract for the service. + * Called by the operator to manage the BTP network. + * + * @param _svc String (the name of the service) + * @param _addr Address (the address of the smart contract handling the service) + */ + @External + void addService(String _svc, Address _addr); + + /** + * Unregisters the smart contract for the service. + * Called by the operator to manage the BTP network. + * + * @param _svc String (the name of the service) + */ + @External + void removeService(String _svc); + /** + * Get registered services. + * + * @return A dictionary with the name of the service as key and address of the BSH related to the service as value. + *
For example::
+ * { + * "token": "cx72eaed466599ca5ea377637c6fa2c5c0978537da" + * } + */ + @External(readonly = true) + Map getServices(); + + /** + * If it generates the event related to the link, the relay shall handle the event to deliver BTP Message to the BMC. + * If the link is already registered, or its network is already registered then it fails. + * If there is no verifier related with the network of the link, then it fails. + * Initializes status information for the link. + * Called by the operator to manage the BTP network. + * + * @param _link String (BTP Address of connected BMC) + */ + @External + void addLink(String _link); + + /** + * Removes the link and status information. + * Called by the operator to manage the BTP network. + * + * @param _link String (BTP Address of connected BMC) + */ + @External + void removeLink(String _link); + + /** + * Get status of BMC. + * Used by the relay to resolve next BTP Message to send. + * If target is not registered, it will fail. + * + * @param _link String ( BTP Address of the connected BMC ) + * @return The object contains followings fields. + */ + @External(readonly = true) + BMCStatus getStatus(String _link); + + /** + * Get registered links. + * + * @return A list of links ( BTP Addresses of the BMCs ) + *
For Example::
+ * [ + * "btp://0x1.iconee/cx9f8a75111fd611710702e76440ba9adaffef8656" + * ] + */ + @External(readonly = true) + String[] getLinks(); + + /** + * Add route to the BMC. + * May fail if there more than one BMC for the network. + * Called by the operator to manage the BTP network. + * + * @param _dst String ( BTP Address of the destination BMC ) + * @param _link String ( BTP Address of the next BMC for the destination ) + */ + @External + void addRoute(String _dst, String _link); + /** + * Remove route to the BMC. + * Called by the operator to manage the BTP network. + * + * @param _dst String ( BTP Address of the destination BMC ) + */ + @External + void removeRoute(String _dst); + /** + * Get routing information. + * + * @return A dictionary with the BTP Address of the destination BMC as key and the BTP Address of the next as value. + * + *
For Example::
+ * { + * "btp://0x2.iconee/cx1d6e4decae8160386f4ecbfc7e97a1bc5f74d35b": "btp://0x1.iconee/cx9f8a75111fd611710702e76440ba9adaffef8656" + * } + */ + @External(readonly = true) + Map getRoutes(); + + /** + * Sends the message to a specific network. + * Only allowed to be called by registered BSHs. + * + * @param _to String ( Network Address of destination network ) + * @param _svc String ( name of the service ) + * @param _sn Integer ( serial number of the message, must be positive ) + * @param _msg Bytes ( serialized bytes of Service Message ) + */ + @External + void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg); + + /** + * It verifies and decodes the Relay Message with BMV and dispatches BTP Messages to registered BSHs. + * It's allowed to be called by registered Relay. + * + * @param _prev String ( BTP Address of the previous BMC ) + * @param _msg String ( base64 encoded string of serialized bytes of Relay Message ) + */ + @External + void handleRelayMessage(String _prev, String _msg); + + /** + * TODO [TBD] add 'getBtpAddress' to IIP-25.BMC.Read-only methods + * Returns BTP Address of BMC + * + * @return String (BTP Address of BMC) + */ + @External(readonly = true) + String getBtpAddress(); +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCEvent.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCEvent.java new file mode 100644 index 00000000..a5fe1cdf --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.annotation.EventLog; + +import java.math.BigInteger; + +public interface BMCEvent { + /** + * (EventLog) Sends the message to the next BMC. + * The relay monitors this event. + *

+ * indexed: 2 + * + * @param _next String ( BTP Address of the BMC to handle the message ) + * @param _seq Integer ( sequence number of the message from current BMC to the next ) + * @param _msg Bytes ( serialized bytes of BTP Message ) + */ + @EventLog(indexed = 2) + void Message(String _next, BigInteger _seq, byte[] _msg); + + /** + * TODO [TBD] add 'ErrorOnBTPError' to IIP-25.BMC.Events + * (EventLog) raised BTPException while BSH.handleBTPError + *

+ * indexed: 2 + * + * @param _svc String ( name of the service ) + * @param _seq Integer ( serial number of the message, must be positive ) + * @param _code Integer ( error code ) + * @param _msg String ( error message ) + * @param _ecode ( BTPException code ) + * @param _emsg ( BTPException message ) + */ + @EventLog(indexed = 2) + void ErrorOnBTPError(String _svc, BigInteger _seq, long _code, String _msg, long _ecode, String _emsg); +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCScoreInterface.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCScoreInterface.java new file mode 100644 index 00000000..41529953 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCScoreInterface.java @@ -0,0 +1,135 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.Context; + +import java.math.BigInteger; +import java.util.Map; + +public final class BMCScoreInterface implements BMC { + protected final Address address; + + protected final BigInteger valueForPayable; + + public BMCScoreInterface(Address address) { + this.address = address; + this.valueForPayable = null; + } + + public BMCScoreInterface(Address address, BigInteger valueForPayable) { + this.address = address; + this.valueForPayable = valueForPayable; + } + + public Address _getAddress() { + return this.address; + } + + public BMCScoreInterface _payable(BigInteger valueForPayable) { + return new BMCScoreInterface(address,valueForPayable); + } + + public BMCScoreInterface _payable(long valueForPayable) { + return this._payable(BigInteger.valueOf(valueForPayable)); + } + + public BigInteger _getICX() { + return this.valueForPayable; + } + + @Override + public void addVerifier(String _net, Address _addr) { + Context.call(this.address, "addVerifier", _net, _addr); + } + + @Override + public void removeVerifier(String _net) { + Context.call(this.address, "removeVerifier", _net); + } + + @Override + public Map getVerifiers() { + return Context.call(Map.class, this.address, "getVerifiers"); + } + + @Override + public void addService(String _svc, Address _addr) { + Context.call(this.address, "addService", _svc, _addr); + } + + @Override + public void removeService(String _svc) { + Context.call(this.address, "removeService", _svc); + } + + @Override + public Map getServices() { + return Context.call(Map.class, this.address, "getServices"); + } + + @Override + public void addLink(String _link) { + Context.call(this.address, "addLink", _link); + } + + @Override + public void removeLink(String _link) { + Context.call(this.address, "removeLink", _link); + } + + @Override + public BMCStatus getStatus(String _link) { + return Context.call(BMCStatus.class, this.address, "getStatus", _link); + } + + @Override + public String[] getLinks() { + return Context.call(String[].class, this.address, "getLinks"); + } + + @Override + public void addRoute(String _dst, String _link) { + Context.call(this.address, "addRoute", _dst, _link); + } + + @Override + public void removeRoute(String _dst) { + Context.call(this.address, "removeRoute", _dst); + } + + @Override + public Map getRoutes() { + return Context.call(Map.class, this.address, "getRoutes"); + } + + @Override + public void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { + Context.call(this.address, "sendMessage", _to, _svc, _sn, _msg); + } + + @Override + public void handleRelayMessage(String _prev, String _msg) { + Context.call(this.address, "handleRelayMessage", _prev, _msg); + } + + @Override + public String getBtpAddress() { + return Context.call(String.class, this.address, "getBtpAddress"); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCStatus.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCStatus.java new file mode 100644 index 00000000..29bc3ec4 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMCStatus.java @@ -0,0 +1,252 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import foundation.icon.score.util.StringUtil; +import score.annotation.Keep; + +import java.math.BigInteger; + +public class BMCStatus { + private BigInteger rx_seq; + private BigInteger tx_seq; + private BMVStatus verifier; + private BMRStatus[] relays; + + private int block_interval_src; + private int block_interval_dst; + private int max_agg; + private int delay_limit; + + private int relay_idx; + private long rotate_height; + private int rotate_term; + private long rx_height; //initialize with BMC.block_height + private long rx_height_src; //initialize with BMV._offset + + private int sack_term; //0: disable sack + private long sack_next; + private long sack_height; + private BigInteger sack_seq; + + private long cur_height; + + @Keep + public BigInteger getRx_seq() { + return rx_seq; + } + + @Keep + public void setRx_seq(BigInteger rx_seq) { + this.rx_seq = rx_seq; + } + + @Keep + public BigInteger getTx_seq() { + return tx_seq; + } + + @Keep + public void setTx_seq(BigInteger tx_seq) { + this.tx_seq = tx_seq; + } + + @Keep + public BMVStatus getVerifier() { + return verifier; + } + + @Keep + public void setVerifier(BMVStatus verifier) { + this.verifier = verifier; + } + + @Keep + public BMRStatus[] getRelays() { + return relays; + } + + @Keep + public void setRelays(BMRStatus[] relays) { + this.relays = relays; + } + + @Keep + public int getBlock_interval_src() { + return block_interval_src; + } + + @Keep + public void setBlock_interval_src(int block_interval_src) { + this.block_interval_src = block_interval_src; + } + + @Keep + public int getBlock_interval_dst() { + return block_interval_dst; + } + + @Keep + public void setBlock_interval_dst(int block_interval_dst) { + this.block_interval_dst = block_interval_dst; + } + + @Keep + public int getMax_agg() { + return max_agg; + } + + @Keep + public void setMax_agg(int max_agg) { + this.max_agg = max_agg; + } + + @Keep + public int getDelay_limit() { + return delay_limit; + } + + @Keep + public void setDelay_limit(int delay_limit) { + this.delay_limit = delay_limit; + } + + @Keep + public int getRelay_idx() { + return relay_idx; + } + + @Keep + public void setRelay_idx(int relay_idx) { + this.relay_idx = relay_idx; + } + + @Keep + public long getRotate_height() { + return rotate_height; + } + + @Keep + public void setRotate_height(long rotate_height) { + this.rotate_height = rotate_height; + } + + @Keep + public int getRotate_term() { + return rotate_term; + } + + @Keep + public void setRotate_term(int rotate_term) { + this.rotate_term = rotate_term; + } + + @Keep + public long getRx_height() { + return rx_height; + } + + @Keep + public void setRx_height(long rx_height) { + this.rx_height = rx_height; + } + + @Keep + public long getRx_height_src() { + return rx_height_src; + } + + @Keep + public void setRx_height_src(long rx_height_src) { + this.rx_height_src = rx_height_src; + } + + @Keep + public int getSack_term() { + return sack_term; + } + + @Keep + public void setSack_term(int sack_term) { + this.sack_term = sack_term; + } + + @Keep + public long getSack_next() { + return sack_next; + } + + @Keep + public void setSack_next(long sack_next) { + this.sack_next = sack_next; + } + + @Keep + public long getSack_height() { + return sack_height; + } + + @Keep + public void setSack_height(long sack_height) { + this.sack_height = sack_height; + } + + @Keep + public BigInteger getSack_seq() { + return sack_seq; + } + + @Keep + public void setSack_seq(BigInteger sack_seq) { + this.sack_seq = sack_seq; + } + + @Keep + public long getCur_height() { + return cur_height; + } + + @Keep + public void setCur_height(long cur_height) { + this.cur_height = cur_height; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMCStatus{"); + sb.append("rx_seq=").append(rx_seq); + sb.append(", tx_seq=").append(tx_seq); + sb.append(", verifier=").append(verifier); + sb.append(", relays=").append(StringUtil.toString(relays)); + sb.append(", block_interval_src=").append(block_interval_src); + sb.append(", block_interval_dst=").append(block_interval_dst); + sb.append(", max_agg=").append(max_agg); + sb.append(", delay_limit=").append(delay_limit); + sb.append(", relay_idx=").append(relay_idx); + sb.append(", rotate_height=").append(rotate_height); + sb.append(", rotate_term=").append(rotate_term); + sb.append(", rx_height=").append(rx_height); + sb.append(", rx_height_src=").append(rx_height_src); + sb.append(", sack_term=").append(sack_term); + sb.append(", sack_next=").append(sack_next); + sb.append(", sack_height=").append(sack_height); + sb.append(", sack_seq=").append(sack_seq); + sb.append(", cur_height=").append(cur_height); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMRStatus.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMRStatus.java new file mode 100644 index 00000000..2a91925f --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMRStatus.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.annotation.Keep; + +import java.math.BigInteger; + +public class BMRStatus { + private Address address; + private long block_count; + private BigInteger msg_count; + + @Keep + public Address getAddress() { + return address; + } + + @Keep + public void setAddress(Address address) { + this.address = address; + } + + @Keep + public long getBlock_count() { + return block_count; + } + + @Keep + public void setBlock_count(long block_count) { + this.block_count = block_count; + } + + @Keep + public BigInteger getMsg_count() { + return msg_count; + } + + @Keep + public void setMsg_count(BigInteger msg_count) { + this.msg_count = msg_count; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMRStatus{"); + sb.append("address=").append(address); + sb.append(", block_count=").append(block_count); + sb.append(", msg_count=").append(msg_count); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMV.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMV.java new file mode 100644 index 00000000..3e5d4760 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMV.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.annotation.External; + +import java.math.BigInteger; + +public interface BMV { + /** + * Decodes Relay Messages and process BTP Messages + * If there is an error, then it sends a BTP Message containing the Error Message + * BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + * + * @param _bmc String ( BTP Address of the BMC handling the message ) + * @param _prev String ( BTP Address of the previous BMC ) + * @param _seq Integer ( next sequence number to get a message ) + * @param _msg String ( base64 encoded string of serialized bytes of Relay Message ) + * @return List of serialized bytes of a BTP Message + */ + @External + byte[][] handleRelayMessage(String _bmc, String _prev, BigInteger _seq, String _msg); + + /** + * Get status of BMV. + * Used by the relay to resolve next BTP Message to send. + * Called by BMC. + * + * @return The object contains followings fields. + */ + @External(readonly = true) + BMVStatus getStatus(); +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVScoreInterface.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVScoreInterface.java new file mode 100644 index 00000000..cd2c328b --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVScoreInterface.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.Context; + +import java.math.BigInteger; + +public final class BMVScoreInterface implements BMV { + protected final Address address; + + protected final BigInteger valueForPayable; + + public BMVScoreInterface(Address address) { + this.address = address; + this.valueForPayable = null; + } + + public BMVScoreInterface(Address address, BigInteger valueForPayable) { + this.address = address; + this.valueForPayable = valueForPayable; + } + + public Address _getAddress() { + return this.address; + } + + public BMVScoreInterface _payable(BigInteger valueForPayable) { + return new BMVScoreInterface(address,valueForPayable); + } + + public BMVScoreInterface _payable(long valueForPayable) { + return this._payable(BigInteger.valueOf(valueForPayable)); + } + + public BigInteger _getICX() { + return this.valueForPayable; + } + + @Override + public byte[][] handleRelayMessage(String _bmc, String _prev, BigInteger _seq, String _msg) { + return Context.call(byte[][].class, this.address, "handleRelayMessage", _bmc, _prev, _seq, _msg); + } + + @Override + public BMVStatus getStatus() { + return Context.call(BMVStatus.class, this.address, "getStatus"); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVStatus.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVStatus.java new file mode 100644 index 00000000..dd1d5748 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BMVStatus.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.annotation.Keep; + +public class BMVStatus { + private long height; + private long offset; + private long last_height; + + @Keep + public BMVStatus() { + } + + @Keep + public long getHeight() { + return height; + } + + @Keep + public void setHeight(long height) { + this.height = height; + } + + @Keep + public long getOffset() { + return offset; + } + + @Keep + public void setOffset(long offset) { + this.offset = offset; + } + + @Keep + public long getLast_height() { + return last_height; + } + + @Keep + public void setLast_height(long last_height) { + this.last_height = last_height; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMVStatus{"); + sb.append("height=").append(height); + sb.append(", offset=").append(offset); + sb.append(", last_height=").append(last_height); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BSH.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BSH.java new file mode 100644 index 00000000..9bef0a4d --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BSH.java @@ -0,0 +1,59 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.annotation.External; + +import java.math.BigInteger; + +public interface BSH { + /** + * Handles BTP Messages from other blockchains. + * Accepts messages only from BMC. + * If it fails, then BMC will generate a BTP Message that includes error information, then delivered to the source. + * + * @param _from String ( Network Address of source network ) + * @param _svc String ( name of the service ) + * @param _sn Integer ( serial number of the message ) + * @param _msg Bytes ( serialized bytes of ServiceMessage ) + */ + @External + void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg); + + /** + * Handle the error on delivering the message. + * Accept the error only from the BMC. + * + * @param _src String ( BTP Address of BMC that generated the error ) + * @param _svc String ( name of the service ) + * @param _sn Integer ( serial number of the original message ) + * @param _code Integer ( code of the error ) + * @param _msg String ( message of the error ) + */ + @External + void handleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg); + + /** + * Handle the gatherFee + * Accept call only from the BMC. + * + * @param _fa String ( BTP Address of Fee-aggregator contract ) + * @param _svc String ( name of the service ) + */ + @External + void handleFeeGathering(String _fa, String _svc); +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BSHScoreInterface.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BSHScoreInterface.java new file mode 100644 index 00000000..57aa779f --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BSHScoreInterface.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.Context; + +import java.math.BigInteger; + +public final class BSHScoreInterface implements BSH { + protected final Address address; + + protected final BigInteger valueForPayable; + + public BSHScoreInterface(Address address) { + this.address = address; + this.valueForPayable = null; + } + + public BSHScoreInterface(Address address, BigInteger valueForPayable) { + this.address = address; + this.valueForPayable = valueForPayable; + } + + public Address _getAddress() { + return this.address; + } + + public BSHScoreInterface _payable(BigInteger valueForPayable) { + return new BSHScoreInterface(address,valueForPayable); + } + + public BSHScoreInterface _payable(long valueForPayable) { + return this._payable(BigInteger.valueOf(valueForPayable)); + } + + public BigInteger _getICX() { + return this.valueForPayable; + } + + @Override + public void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg) { + Context.call(this.address, "handleBTPMessage", _from, _svc, _sn, _msg); + } + + @Override + public void handleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg) { + Context.call(this.address, "handleBTPError", _src, _svc, _sn, _code, _msg); + } + + @Override + public void handleFeeGathering(String _fa, String _svc) { + Context.call(this.address, "handleFeeGathering", _fa, _svc); + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPAddress.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPAddress.java new file mode 100644 index 00000000..63887fa9 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPAddress.java @@ -0,0 +1,127 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class BTPAddress { + public static final String PROTOCOL_BTP="btp"; + private static final String DELIM_PROTOCOL="://"; + private static final String DELIM_NET="/"; + String protocol; + String net; + String account; + + public BTPAddress(String net, String account) { + this(PROTOCOL_BTP, net, account); + } + + public BTPAddress(String protocol, String net, String account) { + this.protocol = protocol; + this.net = net; + this.account = account; + } + + public String protocol() { + return protocol; + } + + public String net() { + return net; + } + + public String account() { + return account; + } + + @Override + public String toString() { + return protocol + DELIM_PROTOCOL + net + DELIM_NET + account; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + BTPAddress that = (BTPAddress) o; + return toString().equals(that.toString()); + } + + public boolean isValid() { + return (!(protocol == null || protocol.isEmpty())) && + (!(net == null || net.isEmpty())) && + (!(account == null || account.isEmpty())); + } + + public static BTPAddress parse(String str) { + if (str == null) { + return null; + } + String protocol = ""; + String net = ""; + String contract = ""; + int protocolIdx = str.indexOf(DELIM_PROTOCOL); + if(protocolIdx >= 0) { + protocol = str.substring(0, protocolIdx); + str = str.substring(protocolIdx + DELIM_PROTOCOL.length()); + } + int netIdx = str.indexOf(DELIM_NET); + if (netIdx >= 0) { + net = str.substring(0, netIdx); + contract = str.substring(netIdx+DELIM_NET.length()); + } else { + contract = str; + } + return new BTPAddress(protocol, net, contract); + } + + public static BTPAddress valueOf(String str) throws BTPException { + BTPAddress btpAddress = parse(str); + if (btpAddress == null || !btpAddress.isValid()) { + throw BTPException.unknown("invalid BTPAddress"); + } + return btpAddress; + } + + public static void writeObject(ObjectWriter writer, BTPAddress obj) { + obj.writeObject(writer); + } + + public void writeObject(ObjectWriter writer) { + writer.write(this.toString()); + } + + public static BTPAddress readObject(ObjectReader reader) { + return BTPAddress.parse(reader.readString()); + } + + public static BTPAddress fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return BTPAddress.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + BTPAddress.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPException.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPException.java new file mode 100644 index 00000000..6d9c8742 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/BTPException.java @@ -0,0 +1,167 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.UserRevertException; +import score.UserRevertedException; + +public class BTPException extends UserRevertException { + /** + * BTPException.BTP => 0 ~ 9 + * BTPException.BMC => 10 ~ 24 + * BTPException.BMV => 25 ~ 39 + * BTPException.BSH => 40 ~ 54 + * BTPException.RESERVED => 55 ~ 68 + */ + enum Type { + BTP(0), + BMC(10), + BMV(25), + BSH(40), + RESERVED(55); + + int offset; + Type(int offset) { + this.offset = offset; + } + int apply(int code) { + code = offset + code; + if (this.equals(RESERVED) || code >= values()[ordinal() + 1].offset) { + throw new IllegalArgumentException(); + } + return code; + } + int recover(int code) { + code = code - offset; + if (this.equals(RESERVED) || code < 0) { + throw new IllegalArgumentException(); + } + return code; + } + static Type valueOf(int code) throws IllegalArgumentException { + for(Type t : values()) { + if (code < t.offset) { + if (t.ordinal() == 0) { + throw new IllegalArgumentException(); + } else { + return t; + } + } + } + throw new IllegalArgumentException(); + } + }; + + private final Type type; + private final int code; + + BTPException(Code c) { + this(Type.BTP, c, c.name()); + } + + BTPException(Code code, String message) { + this(Type.BTP, code, message); + } + + BTPException(Type type, Coded code, String message) { + this(type, code.code(), message); + } + + BTPException(Type type, int code, String message) { + super(message); + this.type = type; + this.code = type.apply(code); + } + + BTPException(UserRevertedException e) { + super(e.getMessage(), e); + this.code = e.getCode(); + this.type = Type.valueOf(code); + } + + public static BTPException of(UserRevertedException e) { + return new BTPException(e); + } + + @Override + public int getCode() { + return code; + } + + public int getCodeOfType() { + return type.recover(code); + } + + public interface Coded { + int code(); + default boolean equals(BTPException e) { + return code() == e.getCodeOfType(); + } + } + + //BTPException.BTP => 0 ~ 9 + public enum Code implements Coded { + Unknown(0); + + final int code; + Code(int code){ this.code = code; } + + public int code() { return code; } + + } + + public static BTPException unknown(String message) { + return new BTPException(Code.Unknown, message); + } + + public static class BMC extends BTPException { + + public BMC(int code, String message) { + super(Type.BMC, code, message); + } + + public BMC(Coded code, String message) { + this(code.code(), message); + } + + } + + public static class BMV extends BTPException { + + public BMV(int code, String message) { + super(Type.BMV, code, message); + } + + public BMV(Coded code, String message) { + this(code.code(), message); + } + + } + + public static class BSH extends BTPException { + + public BSH(int code, String message) { + super(Type.BSH, code, message); + } + + public BSH(Coded code, String message) { + this(code.code(), message); + } + + } + +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManager.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManager.java new file mode 100644 index 00000000..05ccc81c --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManager.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.annotation.External; + +public interface OwnerManager { + /** + * Registers the Owner. + * Called by contract owner or registered Owner + * + * @param _addr Address of the Owner + * @throws IllegalStateException if caller is not contract owner or registered Owner + * @throws IllegalArgumentException if given address is already registered or contract owner + */ + @External + void addOwner(Address _addr) throws IllegalStateException, IllegalArgumentException; + + /** + * Unregisters the Owner. + * Called by the operator to manage the BTP network. + * + * @param _addr Address + * @throws IllegalStateException if caller is not contract owner or registered Owner + * @throws IllegalArgumentException if given address is not registered or contract owner + */ + @External + void removeOwner(Address _addr); + + /** + * Get registered the Owners. + * + * @return A list of Owners. ( Address of Owners ) + */ + @External(readonly = true) + Address[] getOwners(); + + /** + * Return given address is registered as owner + * + * @param _addr Address + * @return boolean true if registered, otherwise false + */ + @External(readonly = true) + boolean isOwner(Address _addr); +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerImpl.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerImpl.java new file mode 100644 index 00000000..6d632fe7 --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.ArrayDB; +import score.Context; + +public class OwnerManagerImpl implements OwnerManager { + private final ArrayDB

owners; + + public OwnerManagerImpl(String id) { + owners = Context.newArrayDB(id, Address.class); + } + + protected int indexOf(Address address) { + for (int i = 0; i < owners.size(); i++) { + if (owners.get(i).equals(address)) { + return i; + } + } + return -1; + } + + protected void add(Address address) throws IllegalArgumentException { + int idx = indexOf(address); + if (idx >= 0) { + throw new IllegalArgumentException("already exists owner"); + } + owners.add(address); + } + + protected void remove(Address address) throws IllegalArgumentException { + int idx = indexOf(address); + if (idx < 0) { + throw new IllegalArgumentException("not exists owner"); + } + Address last = owners.pop(); + if (idx != owners.size()) { + owners.set(idx, last); + } + } + + protected void requireOwnerAccess() { + if (!isOwner(Context.getCaller())) { + throw new IllegalStateException("caller is not owner"); + } + } + + protected void requireNotScoreOwner(Address address) { + if (Context.getOwner().equals(address)) { + throw new IllegalArgumentException("given address is score owner"); + } + } + + @Override + public void addOwner(Address _addr) throws IllegalStateException, IllegalArgumentException{ + requireOwnerAccess(); + requireNotScoreOwner(_addr); + add(_addr); + } + + @Override + public void removeOwner(Address _addr) throws IllegalStateException, IllegalArgumentException { + requireOwnerAccess(); + requireNotScoreOwner(_addr); + remove(_addr); + } + + @Override + public Address[] getOwners() { + Address[] owners = new Address[this.owners.size() + 1]; + owners[0] = Context.getOwner(); + int size = this.owners.size(); + for (int i = 0; i < size; i++) { + owners[i+1] = this.owners.get(i); + } + return owners; + } + + @Override + public boolean isOwner(Address _addr) { + if (Context.getOwner().equals(_addr) || indexOf(_addr) >= 0) { + return true; + } + return false; + } +} diff --git a/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerScoreInterface.java b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerScoreInterface.java new file mode 100644 index 00000000..e05a17dd --- /dev/null +++ b/javascore/lib/src/main/java/foundation/icon/btp/lib/OwnerManagerScoreInterface.java @@ -0,0 +1,74 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import score.Address; +import score.Context; + +import java.math.BigInteger; + +public final class OwnerManagerScoreInterface implements OwnerManager { + protected final Address address; + + protected final BigInteger valueForPayable; + + public OwnerManagerScoreInterface(Address address) { + this.address = address; + this.valueForPayable = null; + } + + public OwnerManagerScoreInterface(Address address, BigInteger valueForPayable) { + this.address = address; + this.valueForPayable = valueForPayable; + } + + public Address _getAddress() { + return this.address; + } + + public OwnerManagerScoreInterface _payable(BigInteger valueForPayable) { + return new OwnerManagerScoreInterface(address,valueForPayable); + } + + public OwnerManagerScoreInterface _payable(long valueForPayable) { + return this._payable(BigInteger.valueOf(valueForPayable)); + } + + public BigInteger _getICX() { + return this.valueForPayable; + } + + @Override + public void addOwner(Address _addr) { + Context.call(this.address, "addOwner", _addr); + } + + @Override + public void removeOwner(Address _addr) { + Context.call(this.address, "removeOwner", _addr); + } + + @Override + public Address[] getOwners() { + return Context.call(Address[].class, this.address, "getOwners"); + } + + @Override + public boolean isOwner(Address _addr) { + return Context.call(Boolean.class, this.address, "isOwner", _addr); + } +} diff --git a/javascore/lib/src/test/java/foundation/icon/btp/lib/BTPAddressTest.java b/javascore/lib/src/test/java/foundation/icon/btp/lib/BTPAddressTest.java new file mode 100644 index 00000000..763e015b --- /dev/null +++ b/javascore/lib/src/test/java/foundation/icon/btp/lib/BTPAddressTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.lib; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class BTPAddressTest { + + @ParameterizedTest + @MethodSource("parseParameters") + void parse(String str, String repr, boolean valid) { + BTPAddress address = BTPAddress.parse(str); + System.out.println( + "actual:" + String.format("[repr:%s, valid:%s]", address.toString(), address.isValid()) + + ", expected:" + String.format("[repr:%s, valid:%s]", repr, valid)); + assertEquals(repr, address.toString()); + assertEquals(valid, address.isValid()); + } + + private static Stream parseParameters() { + return Stream.of( + Arguments.of( + "btp://0xb34eca.icon/cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + "btp://0xb34eca.icon/cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + true), + Arguments.of( + "0xb34eca.icon/cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + "://0xb34eca.icon/cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + false), + Arguments.of( + "btp:///cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + "btp:///cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + false), + Arguments.of( + "cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + ":///cx69d93f6fe1fe6e4b150fe80004d2d2ce7fc36173", + false) + ); + } +} \ No newline at end of file diff --git a/javascore/nativecoin/build.gradle b/javascore/nativecoin/build.gradle new file mode 100644 index 00000000..7522f044 --- /dev/null +++ b/javascore/nativecoin/build.gradle @@ -0,0 +1,107 @@ +version = '0.1.0' + +dependencies { + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + implementation project(':score-util') + implementation project(':lib') + implementation("com.github.sink772:javaee-tokens:0.6.0") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + + testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion") + testImplementation project(':test-lib') + testAnnotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion") + testImplementation("foundation.icon:javaee-score-client:$scoreClientVersion") + testImplementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + testImplementation("foundation.icon:icon-sdk:$iconsdkVersion") + testImplementation("com.github.javafaker:javafaker:1.0.2") +} + +optimizedJar { + mainClassName = 'foundation.icon.btp.nativecoin.NativeCoinService' +// archivesBaseName = 'nativecoin' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +import foundation.icon.gradle.plugins.javaee.task.OptimizedJar + +task optimizedJarIRC31(type: OptimizedJar) { + mainClassName = 'foundation.icon.btp.nativecoin.irc31.OwnerBasedIRC31Supplier' + archiveName("irc31-" + archiveVersion.get() + "." + archiveExtension.get()); + from { sourceSets.main.output } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = scoreTest.url + nid = scoreTest.parseNid(scoreTest.nid) + } + } + keystore = scoreTest.default.keyStore + password = scoreTest.default.resolvedKeyPassword + parameters {[ + arg('_bmc', 'cx...'), + arg('_irc31', 'cx...'), + arg('_name', 'src') + ] + } +} + +test { + useJUnitPlatform { + if (!integrationTest) { + excludeTags("integration") + } + } + options { + testLogging.showStandardStreams = true + systemProperty 'url', scoreTest.url + systemProperty 'nid', scoreTest.nid + systemProperty 'keyStore', scoreTest.default.keyStore + systemProperty 'keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'address', "cxd81b47925568321bdf8bb123cb1738a0dc4c5458" +// systemProperty 'isUpdate', "true" + dependsOn optimizedJar + systemProperty 'scoreFilePath', project.tasks.optimizedJar.outputJarName + project.extensions.deployJar.arguments.each { + arg -> systemProperty 'params.'+arg.name, arg.value + } + + systemProperty 'tester.keyStore', scoreTest.tester.keyStore + systemProperty 'tester.keySecret', scoreTest.tester.keySecret + + systemProperty 'irc31.url', scoreTest.url + systemProperty 'irc31.nid', scoreTest.nid + systemProperty 'irc31.keyStore', scoreTest.default.keyStore + systemProperty 'irc31.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'irc31.address', "cxb25cd0f4535726517800983c262c081df0a48731" +// systemProperty 'irc31.isUpdate', "true" + dependsOn optimizedJarIRC31 + systemProperty 'irc31.scoreFilePath', project.tasks.optimizedJarIRC31.outputJarName + + //for bmc-mock client + systemProperty 'bmc-mock.url', scoreTest.url + systemProperty 'bmc-mock.nid', scoreTest.nid + systemProperty 'bmc-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bmc-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bmc-mock.address', "cxb0b4e42d25e8f7a0e4f9d4c3853c3515c6cf2f2b" +// systemProperty 'bmc-mock.isUpdate', "true" + dependsOn ":test-lib:optimizedJarMockBMC" + systemProperty 'bmc-mock.scoreFilePath', tasks.getByPath(":test-lib:optimizedJarMockBMC").outputJarName + systemProperty 'bmc-mock.params._net', scoreTest.nid+'.icon' + } +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Asset.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Asset.java new file mode 100644 index 00000000..0c331b29 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Asset.java @@ -0,0 +1,97 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Asset { + private String coinName; + private BigInteger amount; + + public Asset() {} + + public Asset(Asset asset) { + setCoinName(asset.getCoinName()); + setAmount(asset.getAmount()); + } + + public Asset(String coinName, BigInteger amount) { + this.coinName = coinName; + this.amount = amount; + } + + public String getCoinName() { + return coinName; + } + + public void setCoinName(String coinName) { + this.coinName = coinName; + } + + public BigInteger getAmount() { + return amount; + } + + public void setAmount(BigInteger amount) { + this.amount = amount; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Asset{"); + sb.append("coinName='").append(coinName).append('\''); + sb.append(", amount=").append(amount); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Asset obj) { + obj.writeObject(writer); + } + + public static Asset readObject(ObjectReader reader) { + Asset obj = new Asset(); + reader.beginList(); + obj.setCoinName(reader.readNullable(String.class)); + obj.setAmount(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getCoinName()); + writer.writeNullable(this.getAmount()); + writer.end(); + } + + public static Asset fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return Asset.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + Asset.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/AssetTransferDetail.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/AssetTransferDetail.java new file mode 100644 index 00000000..7b029b55 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/AssetTransferDetail.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class AssetTransferDetail extends Asset { + private BigInteger fee; + + public BigInteger getFee() { + return fee; + } + + public void setFee(BigInteger fee) { + this.fee = fee; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("AssetTransferDetail{"); + sb.append("fee=").append(fee); + sb.append('}').append(super.toString()); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, AssetTransferDetail obj) { + obj.writeObject(writer); + } + + public static AssetTransferDetail readObject(ObjectReader reader) { + AssetTransferDetail obj = new AssetTransferDetail(); + reader.beginList(); + obj.setCoinName(reader.readNullable(String.class)); + obj.setAmount(reader.readNullable(BigInteger.class)); + obj.setFee(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.writeNullable(this.getCoinName()); + writer.writeNullable(this.getAmount()); + writer.writeNullable(this.getFee()); + writer.end(); + } + + public static AssetTransferDetail fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return AssetTransferDetail.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + AssetTransferDetail.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Balance.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Balance.java new file mode 100644 index 00000000..98e2f977 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/Balance.java @@ -0,0 +1,86 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Balance { + private BigInteger locked; + private BigInteger refundable; + + public BigInteger getLocked() { + return locked; + } + + public void setLocked(BigInteger locked) { + this.locked = locked; + } + + public BigInteger getRefundable() { + return refundable; + } + + public void setRefundable(BigInteger refundable) { + this.refundable = refundable; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Balance{"); + sb.append("locked=").append(locked); + sb.append(", refundable=").append(refundable); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Balance obj) { + obj.writeObject(writer); + } + + public static Balance readObject(ObjectReader reader) { + Balance obj = new Balance(); + reader.beginList(); + obj.setLocked(reader.readNullable(BigInteger.class)); + obj.setRefundable(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getLocked()); + writer.writeNullable(this.getRefundable()); + writer.end(); + } + + public static Balance fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return Balance.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + Balance.writeObject(writer, this); + return writer.toByteArray(); + } + +} \ No newline at end of file diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCS.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCS.java new file mode 100644 index 00000000..2f175bac --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCS.java @@ -0,0 +1,167 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.Address; +import score.annotation.External; +import score.annotation.Payable; + +import java.math.BigInteger; + +public interface NCS { + + /** + * Registers a wrapped token smart contract and id number of a supporting coin. + * Caller must be the owner + * + * {@code _name} It must be listed in the code name registry.. + * The native coin (ICX) must be registered through the constructor. + * + * @param _name A coin name. + */ + @External + void register(String _name); + + /** + * Return all supported coins names in other networks by the BSH contract + * + * @return An array of strings. + */ + @External(readonly = true) + String[] coinNames(); + + /** + * Return an _id number and a symbol of Coin whose name is the same with given _coinName. + * + * @implSpec Return nullempty if not found. + * + * @param _coinName Coin name. + * @return id of Native Coin Token. + */ + @External(readonly = true) + BigInteger coinId(String _coinName); + + /** + * Return a usable/locked balance of an account based on coinName. + * + * @implSpec Locked Balance means an amount of Coins/Wrapped Coins is currently + * at a pending state when a transfer tx is requested from this chain to another + * @implSpec Return 0 if not found + * + * @param _owner + * @param _coinName Coin name. + * @return Balance + * { + * "locked" : an amount of locked Coins/WrappedCoins, + * "refundable" : an amount of refundable Coins/WrappedCoins + * } + */ + @External(readonly = true) + Balance balanceOf(Address _owner, String _coinName); + + /** + * Return a list locked/usable balance of an account. + * + * @implSpec The order of request's coinNames must be the same with the order of return balance + * @implSpec Return 0 if not found. + * + * @param _owner + * @param _coinNames + * @return Balance[] + * [ + * { + * "locked" : an amount of locked Coins/WrappedCoins, + * "refundable" : an amount of refundable Coins/WrappedCoins + * } + * ] + */ + @External(readonly = true) + Balance[] balanceOfBatch(Address _owner, String[] _coinNames); + + /** + * Reclaim the coin's refundable balance by an owner. + * + * @apiNote Caller must be an owner of coin + * @implSpec This function only applies on native coin (not wrapped coins) + * The amount to claim must be smaller than refundable balance + * + * @param _coinName A given name of coin to be re-claim + * @param _value An amount of re-claiming + */ + @External + void reclaim(String _coinName, BigInteger _value); + + /** + * Allow users to deposit `msg.value` native coin into a BSH contract. + * + * @implSpec MUST specify msg.value + * + * @param _to An address that a user expects to receive an equivalent amount of tokens. + */ + @Payable + @External + void transferNativeCoin(String _to); + + /** + * Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSH contract. + * + * @apiNote Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by the operator. + * @implSpec It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + * + * @param _coinName A given name of coin that is equivalent to retrieve a wrapped Token Contract's address, i.e. list["Token A"] = 0x12345678 + * @param _value Transferring amount. + * @param _to Target address. + */ + @External + void transfer(String _coinName, BigInteger _value, String _to); + + /** + * Allows users to deposit `_values` amounts of coins `_coinName` from the `msg.sender` address into the BSH contract. + * + * The caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by the contract. It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + * The order of coinName and value should be matched + * + * @param _coinNames Given names of each coin that is equivalent to retrieve a wrapped Token Contract's address, i.e. list["Token A"] = 0x12345678 + * @param _values Transferring amounts per coin + * @param _to Target BTP Address. + */ + @Payable + @External + void transferBatch(String[] _coinNames, BigInteger[] _values, String _to); + + /** + * Sets a new transfer fee ratio. + * + * The caller must be the owner. + * The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. The feeNumetator should be less than FEE_DEMONINATOR and greater than 1 + * feeNumetator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + * + * @param _feeNumerator the fee numerator + */ + @External + void setFeeRatio(BigInteger _feeNumerator); + + /** + * Get transfer fee ratio. + * + * @return BigInteger the fee ratio + * + */ + @External(readonly = true) + BigInteger feeRatio(); + +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSEvents.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSEvents.java new file mode 100644 index 00000000..1580926d --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSEvents.java @@ -0,0 +1,56 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.Address; +import score.annotation.EventLog; + +import java.math.BigInteger; + +public interface NCSEvents { + + /** + * (EventLog) Sends a receipt to sender + * + * @param _from The {@code _from} sender. (Indexed) + * @param _to The {@code _to} receiver. + * @param _sn The {@code _sn} sequence number of the service message. + * @param _assets The {@code _assets} asset details that is the serialized data of AssetTransferDetail + */ + @EventLog(indexed = 1) + void TransferStart(Address _from, String _to, BigInteger _sn, byte[] _assets); + + /** + * (EventLog) Sends a receipt to sender to notify the transfer's result + * + * @param _sender The {@code _sender} account sends the service message. (Indexed) + * @param _sn The {@code _sn} sequence number of the service message. + * @param _code The {@code _code} response code. + * @param _msg The {@code _msg} response message. + */ + @EventLog(indexed = 1) + void TransferEnd(Address _sender, BigInteger _sn, BigInteger _code, byte[] _msg); + + /** + * Notify to the BSH owner that it has received unknown response + * + * @param _from The {@code _from} Network Address of source network. + * @param _sn The {@code _sn} sequence number of the service message. + */ + @EventLog(indexed = 1) + void UnknownResponse(String _from, BigInteger _sn); +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSException.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSException.java new file mode 100644 index 00000000..6a6f7ac4 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSException.java @@ -0,0 +1,71 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.btp.lib.BTPException; + +public class NCSException extends BTPException.BSH { + + public NCSException(Code c) { + super(c, c.name()); + } + + public NCSException(Code c, String message) { + super(c, message); + } + + public static NCSException unknown(String message) { + return new NCSException(Code.Unknown, message); + } + + public static NCSException unauthorized() { + return new NCSException(Code.Unauthorized); + } + public static NCSException unauthorized(String message) { + return new NCSException(Code.Unauthorized, message); + } + + public static NCSException irc31Failure() { + return new NCSException(Code.IRC31Failure); + } + public static NCSException irc31Failure(String message) { + return new NCSException(Code.IRC31Failure, message); + } + + public static NCSException irc31Reverted() { + return new NCSException(Code.IRC31Reverted); + } + public static NCSException irc31Reverted(String message) { + return new NCSException(Code.IRC31Reverted, message); + } + + + //BTPException.BSH => 40 ~ 54 + public enum Code implements BTPException.Coded{ + Unknown(0), + Unauthorized(1), + IRC31Failure(2), + IRC31Reverted(3); + + final int code; + Code(int code){ this.code = code; } + + @Override + public int code() { return code; } + + } +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSMessage.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSMessage.java new file mode 100644 index 00000000..0945aec6 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSMessage.java @@ -0,0 +1,89 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class NCSMessage { + public static int REQUEST_COIN_TRANSFER = 0; + public static int REQUEST_COIN_REGISTER = 1; + public static int REPONSE_HANDLE_SERVICE = 2; + public static int UNKNOWN_TYPE = 3; + + private int serviceType; + private byte[] data; + + public int getServiceType() { + return serviceType; + } + + public void setServiceType(int serviceType) { + this.serviceType = serviceType; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ServiceMessage{"); + sb.append("serviceType='").append(serviceType).append('\''); + sb.append(", data=").append(StringUtil.toString(data)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, NCSMessage obj) { + obj.writeObject(writer); + } + + public static NCSMessage readObject(ObjectReader reader) { + NCSMessage obj = new NCSMessage(); + reader.beginList(); + obj.setServiceType(reader.readInt()); + obj.setData(reader.readNullable(byte[].class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.write(this.getServiceType()); + writer.writeNullable(this.getData()); + writer.end(); + } + + public static NCSMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return NCSMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + NCSMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSProperties.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSProperties.java new file mode 100644 index 00000000..163548eb --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NCSProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class NCSProperties { + public static final NCSProperties DEFAULT; + + static { + DEFAULT = new NCSProperties(); + DEFAULT.setSn(BigInteger.ZERO); + DEFAULT.setFeeRatio(BigInteger.valueOf(10)); + } + + private BigInteger sn; + private BigInteger feeRatio; + + public BigInteger getSn() { + return sn; + } + + public void setSn(BigInteger sn) { + this.sn = sn; + } + + public BigInteger getFeeRatio() { + return feeRatio; + } + + public void setFeeRatio(BigInteger feeRatio) { + this.feeRatio = feeRatio; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("NCSProperties{"); + sb.append("sn=").append(sn); + sb.append(", feeRatio=").append(feeRatio); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, NCSProperties obj) { + obj.writeObject(writer); + } + + public static NCSProperties readObject(ObjectReader reader) { + NCSProperties obj = new NCSProperties(); + reader.beginList(); + obj.setSn(reader.readNullable(BigInteger.class)); + obj.setFeeRatio(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getSn()); + writer.writeNullable(this.getFeeRatio()); + writer.end(); + } + + public static NCSProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return NCSProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + NCSProperties.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NativeCoinService.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NativeCoinService.java new file mode 100644 index 00000000..70302ef1 --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/NativeCoinService.java @@ -0,0 +1,710 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import com.iconloop.score.token.irc31.IRC31Receiver; +import foundation.icon.btp.lib.*; +import foundation.icon.btp.nativecoin.irc31.IRC31SupplierScoreInterface; +import foundation.icon.score.util.ArrayUtil; +import foundation.icon.score.util.BigIntegerUtil; +import foundation.icon.score.util.Logger; +import foundation.icon.score.util.StringUtil; +import score.*; +import score.annotation.EventLog; +import score.annotation.External; +import score.annotation.Payable; +import scorex.util.ArrayList; + +import java.math.BigInteger; +import java.util.List; + +public class NativeCoinService implements NCS, NCSEvents, IRC31Receiver, BSH, OwnerManager { + private static final Logger logger = Logger.getLogger(NativeCoinService.class); + + public static final String SERVICE = "nativecoin"; + public static final BigInteger NATIVE_COIN_ID = BigInteger.ZERO; + public static final BigInteger FEE_DENOMINATOR = BigInteger.valueOf(10000); + + // + private final Address bmc; + private final String net; + private final Address irc31; + private final String name; + private final VarDB properties = Context.newVarDB("properties", NCSProperties.class); + + // + private final OwnerManager ownerManager = new OwnerManagerImpl("owners"); + + // + private final ArrayDB coinNames = Context.newArrayDB("coinNames", String.class); + private final BranchDB> balances = Context.newBranchDB("balances", Balance.class); + private final DictDB feeBalances = Context.newDictDB("feeBalances", BigInteger.class); + private final DictDB transactions = Context.newDictDB("transactions", TransferTransaction.class); + + public NativeCoinService(Address _bmc, Address _irc31, String _name) { + bmc = _bmc; + BMCScoreInterface bmcInterface = new BMCScoreInterface(bmc); + BTPAddress btpAddress = BTPAddress.valueOf(bmcInterface.getBtpAddress()); + net = btpAddress.net(); + irc31 = _irc31; + name = _name; + } + + public NCSProperties getProperties() { + return properties.getOrDefault(NCSProperties.DEFAULT); + } + + public void setProperties(NCSProperties properties) { + this.properties.set(properties); + } + + private boolean isRegistered(String name) { + int len = coinNames.size(); + for (int i = 0; i < len; i++) { + if(coinNames.get(i).equals(name)) { + return true; + } + } + return false; + } + + private List getCoinNamesAsList() { + List coinNames = new ArrayList<>(); + int len = this.coinNames.size(); + for (int i = 0; i < len; i++) { + coinNames.add(this.coinNames.get(i)); + } + return coinNames; + } + + static BigInteger generateTokenId(String name) { + return new BigInteger(Context.hash("sha3-256", name.getBytes())); + } + + static void require(boolean condition, String message) { + if (!condition) { + throw NCSException.unknown(message); + } + } + + @External + public void register(String _name) { + requireOwnerAccess(); + + require(!name.equals(_name) && !isRegistered(_name), "already existed"); + coinNames.add(_name); + } + + @External(readonly = true) + public String[] coinNames() { + int len = coinNames.size(); + String[] names = new String[len + 1]; + names[0] = name; + for (int i = 0; i < len; i++) { + names[i+1] = coinNames.get(i); + } + return names; + } + + @External(readonly = true) + public BigInteger coinId(String _coinName) { + if (name.equals(_coinName)) { + return NATIVE_COIN_ID; + } else if (isRegistered(_coinName)) { + return generateTokenId(_coinName); + } + return null; + } + + @External(readonly = true) + public Balance balanceOf(Address _owner, String _coinName) { + if (_owner.equals(Context.getAddress())) { + Balance balance = new Balance(); + balance.setLocked(BigInteger.ZERO); + balance.setRefundable(feeBalances.getOrDefault(_coinName, BigInteger.ZERO)); + return balance; + } else { + return getBalance(_coinName, _owner); + } + } + + @External(readonly = true) + public Balance[] balanceOfBatch(Address _owner, String[] _coinNames) { + int len = _coinNames.length; + Balance[] balances = new Balance[len]; + for (int i = 0; i < len; i++) { + balances[i] = balanceOf(_owner, _coinNames[i]); + } + return balances; + } + + @External + public void reclaim(String _coinName, BigInteger _value) { + require(_value.compareTo(BigInteger.ZERO) > 0, "_value must be positive"); + + Address owner = Context.getCaller(); + Balance balance = getBalance(_coinName, owner); + require(balance.getRefundable().compareTo(_value) > -1, "invalid value"); + balance.setRefundable(balance.getRefundable().subtract(_value)); + setBalance(_coinName, owner, balance); + + if (name.equals(_coinName)) { + Context.transfer(owner, _value); + } else { + transferFrom(Context.getAddress(), owner, generateTokenId(_coinName), _value); + } + } + + @Payable + @External + public void transferNativeCoin(String _to) { + BigInteger value = Context.getValue(); + require(value != null && value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); + sendRequest(Context.getCaller(), BTPAddress.valueOf(_to), List.of(name), List.of(value)); + } + + @External + public void transfer(String _coinName, BigInteger _value, String _to) { + require(_value != null && _value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); + require(!name.equals(_coinName) && isRegistered(_coinName), "Not supported Token"); + + Address owner = Context.getCaller(); + transferFrom(owner, Context.getAddress(), generateTokenId(_coinName), _value); + sendRequest(owner, BTPAddress.valueOf(_to), List.of(_coinName), List.of(_value)); + } + + @Payable + @External + public void transferBatch(String[] _coinNames, BigInteger[] _values, String _to) { + require(_coinNames.length == _values.length, "Invalid arguments"); + + List registeredCoinNames = getCoinNamesAsList(); + List coinNames = new ArrayList<>(); + List values = new ArrayList<>(); + int len = _coinNames.length; + BigInteger[] ids = new BigInteger[len]; + for (int i = 0; i < len; i++) { + String coinName = _coinNames[i]; + require(!name.equals(coinName) && registeredCoinNames.contains(coinName), "Not supported Token"); + coinNames.add(coinName); + values.add(_values[i]); + ids[i] = generateTokenId(coinName); + } + + Address owner = Context.getCaller(); + transferFromBatch(owner, Context.getAddress(), ids, _values); + + BigInteger value = Context.getValue(); + if (value != null && value.compareTo(BigInteger.ZERO) > 0) { + coinNames.add(name); + values.add(value); + } + sendRequest(owner, BTPAddress.valueOf(_to), coinNames, values); + } + + @EventLog(indexed = 1) + public void TransferStart(Address _from, String _to, BigInteger _sn, byte[] _assetDetails) {} + + @EventLog(indexed = 1) + public void TransferEnd(Address _from, BigInteger _sn, BigInteger _code, byte[] _msg) {} + + @EventLog(indexed = 1) + public void UnknownResponse(String _from, BigInteger _sn) { } + + @External(readonly = true) + public TransferTransaction getTransaction(BigInteger _sn) { + return transactions.get(_sn); + } + + private void sendRequest(Address owner, BTPAddress to, List coinNames, List amounts) { + logger.println("sendRequest","begin"); + NCSProperties properties = getProperties(); + + BigInteger feeRatio = properties.getFeeRatio(); + if (owner.equals(Context.getAddress())) { + feeRatio = BigInteger.ZERO; + } + int len = coinNames.size(); + AssetTransferDetail[] assetTransferDetails = new AssetTransferDetail[len]; + Asset[] assets = new Asset[len]; + for (int i = 0; i < len; i++) { + String coinName = coinNames.get(i); + BigInteger amount = amounts.get(i); + AssetTransferDetail assetTransferDetail = newAssetTransferDetail(coinName, amount, feeRatio); + lock(coinName, owner, amount); + assetTransferDetails[i] = assetTransferDetail; + assets[i] = new Asset(assetTransferDetail); + } + + TransferRequest request = new TransferRequest(); + request.setFrom(owner.toString()); + request.setTo(to.account()); + request.setAssets(assets); + + TransferTransaction transaction = new TransferTransaction(); + transaction.setFrom(owner.toString()); + transaction.setTo(to.toString()); + transaction.setAssets(assetTransferDetails); + + BigInteger sn = properties.getSn().add(BigInteger.ONE); + properties.setSn(sn); + setProperties(properties); + transactions.set(sn, transaction); + + sendMessage(to.net(), NCSMessage.REQUEST_COIN_TRANSFER, sn, request.toBytes()); + TransferStart(owner, to.toString(), sn, encode(assetTransferDetails)); + logger.println("sendRequest","end"); + } + + static byte[] encode(AssetTransferDetail[] assetTransferDetails) { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + writer.beginList(assetTransferDetails.length); + for(AssetTransferDetail v : assetTransferDetails) { + writer.write(v); + } + writer.end(); + return writer.toByteArray(); + } + + private void sendMessage(String to, int serviceType, BigInteger sn, byte[] data) { + logger.println("sendMessage","begin"); + NCSMessage message = new NCSMessage(); + message.setServiceType(serviceType); + message.setData(data); + + BMCScoreInterface bmc = new BMCScoreInterface(this.bmc); + bmc.sendMessage(to, SERVICE, sn, message.toBytes()); + logger.println("sendMessage","end"); + } + + private void responseSuccess(String to, BigInteger sn) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_OK); + response.setMessage(TransferResponse.OK_MSG); + sendMessage(to, NCSMessage.REPONSE_HANDLE_SERVICE, sn, response.toBytes()); + } + + private void responseError(String to, BigInteger sn, String message) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(message); + sendMessage(to, NCSMessage.REPONSE_HANDLE_SERVICE, sn, response.toBytes()); + } + + @External + public void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg) { + require(Context.getCaller().equals(bmc), "Only BMC"); + + NCSMessage message = NCSMessage.fromBytes(_msg); + int serviceType = message.getServiceType(); + if (serviceType == NCSMessage.REQUEST_COIN_TRANSFER) { + TransferRequest request = TransferRequest.fromBytes(message.getData()); + handleRequest(request, _from, _sn); + } else if (serviceType == NCSMessage.REPONSE_HANDLE_SERVICE) { + TransferResponse response = TransferResponse.fromBytes(message.getData()); + handleResponse(_sn, response); + } else if (serviceType == NCSMessage.UNKNOWN_TYPE) { + // If receiving a RES_UNKNOWN_TYPE, ignore this message + // or re-send another correct message + UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(TransferResponse.ERR_MSG_UNKNOWN_TYPE); + sendMessage(_from, NCSMessage.UNKNOWN_TYPE, _sn, response.toBytes()); + } + } + + @External + public void handleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg) { + require(Context.getCaller().equals(bmc), "Only BMC"); + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage("BTPError [code:" + _code + ",msg:" + _msg); + handleResponse(_sn, response); + } + + @External + public void handleFeeGathering(String _fa, String _svc) { + require(Context.getCaller().equals(bmc), "Only BMC"); + BTPAddress from = BTPAddress.valueOf(_fa); + Address owner = Context.getAddress(); + + List coinNames = new ArrayList<>(); + List feeAmounts = new ArrayList<>(); + for(String coinName : coinNames()) { + BigInteger feeAmount = clearFee(coinName); + if (feeAmount.compareTo(BigInteger.ZERO) > 0) { + coinNames.add(coinName); + feeAmounts.add(feeAmount); + } + } + + if (coinNames.size() > 0) { + if (from.net().equals(net)) { + Address fa = Address.fromString(from.account()); + int idx = coinNames.indexOf(name); + if (idx >= 0) { + coinNames.remove(idx); + BigInteger feeAmount = feeAmounts.remove(idx); + Context.transfer(fa, feeAmount); + } + transferFromBatch(owner, fa, coinNamesToIds(coinNames), ArrayUtil.toBigIntegerArray(feeAmounts)); + } else { + sendRequest(owner, from, coinNames, feeAmounts); + } + } + } + + private BigInteger[] coinNamesToIds(List coinNames) { + BigInteger[] ids = new BigInteger[coinNames.size()]; + for (int i = 0; i < coinNames.size(); i++) { + ids[i] = generateTokenId(coinNames.get(i)); + } + return ids; + } + + private Balance getBalance(String coinName, Address owner) { + Balance balance = balances.at(coinName).get(owner); + if (balance == null) { + balance = new Balance(); + balance.setLocked(BigInteger.ZERO); + balance.setRefundable(BigInteger.ZERO); + } + return balance; + } + + private void setBalance(String coinName, Address owner, Balance balance) { + balances.at(coinName).set(owner, balance); + } + + private void lock(String coinName, Address owner, BigInteger value) { + logger.println("lock","coinName:",coinName,"owner:",owner,"value:",value); + Balance balance = getBalance(coinName, owner); + balance.setLocked(balance.getLocked().add(value)); + setBalance(coinName, owner, balance); + } + + private void unlock(String coinName, Address owner, BigInteger value) { + logger.println("unlock","coinName:",coinName,"owner:",owner,"value:",value); + Balance balance = getBalance(coinName, owner); + balance.setLocked(balance.getLocked().subtract(value)); + setBalance(coinName, owner, balance); + } + + private void refund(String coinName, Address owner, BigInteger value) { + logger.println("refund","coinName:",coinName,"owner:",owner,"value:",value); + //unlock and add refundable + Balance balance = getBalance(coinName, owner); + balance.setLocked(balance.getLocked().subtract(value)); + if (!owner.equals(Context.getAddress())) { + balance.setRefundable(balance.getRefundable().add(value)); + } + setBalance(coinName, owner, balance); + } + + private void addFee(String coinName, BigInteger amount) { + BigInteger fee = feeBalances.getOrDefault(coinName, BigInteger.ZERO); + feeBalances.set(coinName, fee.add(amount)); + } + + private BigInteger clearFee(String coinName) { + BigInteger fee = feeBalances.getOrDefault(coinName, BigInteger.ZERO); + if (fee.compareTo(BigInteger.ZERO) > 0) { + feeBalances.set(coinName, BigInteger.ZERO); + } + return fee; + } + + private void handleRequest(TransferRequest request, String from, BigInteger sn) { + logger.println("handleRequest","begin","sn:",sn); + Address to; + try { + to = Address.fromString(request.getTo()); + } catch (IllegalArgumentException | NullPointerException e) { + throw NCSException.unknown(e.getMessage()); + } + + BigInteger nativeCoinTransferAmount = null; + Asset[] assets = request.getAssets(); + List coinNames = new ArrayList<>(); + List amounts = new ArrayList<>(); + List registeredCoinNames = getCoinNamesAsList(); + for (Asset asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + if (amount == null || amount.compareTo(BigInteger.ZERO) < 1) { + throw NCSException.unknown("Amount must be positive value"); + } + + if (registeredCoinNames.contains(coinName)) { + coinNames.add(coinName); + amounts.add(amount); + } else if (name.equals(coinName)) { + nativeCoinTransferAmount = amount; + } else { + throw NCSException.unknown("Invalid Token"); + } + } + + if (nativeCoinTransferAmount != null) { + try { + Context.transfer(to, nativeCoinTransferAmount); + } catch (Exception e) { + throw NCSException.unknown("fail to transfer err:"+e.getMessage()); + } + } + + if (coinNames.size() > 0) { + mintBatch(to, coinNamesToIds(coinNames), ArrayUtil.toBigIntegerArray(amounts)); + } + + logger.println("handleRequest","responseSuccess"); + responseSuccess(from, sn); + logger.println("handleRequest","end"); + } + + private void handleResponse(BigInteger sn, TransferResponse response) { + logger.println("handleResponse","begin","sn:",sn); + TransferTransaction transaction = transactions.get(sn); + List registeredCoinNames = getCoinNamesAsList(); + // ignore when not exists pending request + if (transaction != null) { + BigInteger code = response.getCode(); + Address owner = Address.fromString(transaction.getFrom()); + AssetTransferDetail[] assets = transaction.getAssets(); + + logger.println("handleResponse","code:",code); + if (TransferResponse.RC_OK.equals(code)) { + List coinNames = new ArrayList<>(); + List amounts = new ArrayList<>(); + for (AssetTransferDetail asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + BigInteger fee = asset.getFee(); + BigInteger locked = amount.add(fee); + boolean isNativeCoin = name.equals(coinName); + if(isNativeCoin || registeredCoinNames.contains(coinName)) { + unlock(coinName, owner, locked); + addFee(coinName, fee); + if (!isNativeCoin) { + coinNames.add(coinName); + amounts.add(amount); + } + } else { + //This should not happen + throw NCSException.unknown("invalid transaction, invalid coinName"); + } + } + + if (coinNames.size() > 0) { + burnBatch(coinNamesToIds(coinNames), ArrayUtil.toBigIntegerArray(amounts)); + } + } else { + for (AssetTransferDetail asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + BigInteger fee = asset.getFee(); + BigInteger locked = amount.add(fee); + boolean isNativeCoin = name.equals(coinName); + if(isNativeCoin || registeredCoinNames.contains(coinName)) { + refund(coinName, owner, locked); + } else { + //This should not happen + throw NCSException.unknown("invalid transaction, invalid coinName"); + } + } + } + + transactions.set(sn, null); + TransferEnd(owner, sn, code, response.getMessage() != null ? response.getMessage().getBytes() : null); + } + logger.println("handleResponse","end"); + } + + @External + public void onIRC31Received(Address _operator, Address _from, BigInteger _id, BigInteger _value, byte[] _data) { + //nothing to do + } + + @External + public void onIRC31BatchReceived(Address _operator, Address _from, BigInteger[] _ids, BigInteger[] _values, byte[] _data) { + //nothing to do + } + + @External + public void setFeeRatio(BigInteger _feeNumerator) { + requireOwnerAccess(); + require(_feeNumerator.compareTo(BigInteger.ONE) >= 0 && + _feeNumerator.compareTo(FEE_DENOMINATOR) < 0, + "The feeNumetator should be less than FEE_DEMONINATOR and greater than 1"); + NCSProperties properties = getProperties(); + properties.setFeeRatio(_feeNumerator); + setProperties(properties); + } + + @External(readonly = true) + public BigInteger feeRatio() { + NCSProperties properties = getProperties(); + return properties.getFeeRatio(); + } + + private AssetTransferDetail newAssetTransferDetail(String coinName, BigInteger amount, BigInteger feeRatio) { + logger.println("newAssetTransferDetail","begin"); + BigInteger fee = amount.multiply(feeRatio).divide(FEE_DENOMINATOR); + if (feeRatio.compareTo(BigInteger.ZERO) > 0 && fee.compareTo(BigInteger.ZERO) == 0) { + fee = BigInteger.ONE; + } + BigInteger transferAmount = amount.subtract(fee); + logger.println("newAssetTransferDetail","amount:",amount,"fee:",fee); + if (transferAmount.compareTo(BigInteger.ZERO) < 1) { + throw NCSException.unknown("not enough value"); + } + AssetTransferDetail asset = new AssetTransferDetail(); + asset.setCoinName(coinName); + asset.setAmount(transferAmount); + asset.setFee(fee); + logger.println("newAssetTransferDetail","end"); + return asset; + } + + /* Intercall with IRC31Supplier */ + private void transferFrom(Address from, Address to, BigInteger id, BigInteger amount) { + logger.println("transferFrom", from, to, id, amount); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.transferFrom(from, to, id, amount, null); + } catch (UserRevertedException e) { + logger.println("transferFrom", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("transferFrom", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void transferFromBatch(Address from, Address to, BigInteger[] ids, BigInteger[] amounts) { + logger.println("transferFromBatch", from, to, StringUtil.toString(ids), StringUtil.toString(amounts)); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.transferFromBatch(from, to, ids, amounts, null); + } catch (UserRevertedException e) { + logger.println("transferFromBatch", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("transferFromBatch", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void mint(Address to, BigInteger id, BigInteger amount) { + logger.println("mint", to, id, amount); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.mint(to, id, amount); + } catch (UserRevertedException e) { + logger.println("mint", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("mint", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void mintBatch(Address to, BigInteger[] ids, BigInteger[] amounts) { + logger.println("mintBatch", to, StringUtil.toString(ids), StringUtil.toString(amounts)); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.mintBatch(to, ids, amounts); + } catch (UserRevertedException e) { + logger.println("mintBatch", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("mintBatch", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void burn(BigInteger id, BigInteger amount) { + logger.println("burn", id, amount); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.burn(Context.getAddress(), id, amount); + } catch (UserRevertedException e) { + logger.println("burn", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("burn", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void burnBatch(BigInteger[] ids, BigInteger[] amounts) { + logger.println("burnBatch", StringUtil.toString(ids), StringUtil.toString(amounts)); + IRC31SupplierScoreInterface irc31 = new IRC31SupplierScoreInterface(this.irc31); + try { + irc31.burnBatch(Context.getAddress(), ids, amounts); + } catch (UserRevertedException e) { + logger.println("mintBatch", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("mintBatch", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + /* Delegate OwnerManager */ + private void requireOwnerAccess() { + if (!ownerManager.isOwner(Context.getCaller())) { + throw NCSException.unauthorized("require owner access"); + } + } + + @External + public void addOwner(Address _addr) { + try { + ownerManager.addOwner(_addr); + } catch (IllegalStateException e) { + throw NCSException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw NCSException.unknown(e.getMessage()); + } + } + + @External + public void removeOwner(Address _addr) { + try { + ownerManager.removeOwner(_addr); + } catch (IllegalStateException e) { + throw NCSException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw NCSException.unknown(e.getMessage()); + } + } + + @External(readonly = true) + public Address[] getOwners() { + return ownerManager.getOwners(); + } + + @External(readonly = true) + public boolean isOwner(Address _addr) { + return ownerManager.isOwner(_addr); + } + +} \ No newline at end of file diff --git a/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/TransferRequest.java b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/TransferRequest.java new file mode 100644 index 00000000..ddfeadfa --- /dev/null +++ b/javascore/nativecoin/src/main/java/foundation/icon/btp/nativecoin/TransferRequest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class TransferRequest { + private String from; + private String to; + private Asset[] assets; + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public Asset[] getAssets() { + return assets; + } + + public void setAssets(Asset[] assets) { + this.assets = assets; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferRequest{"); + sb.append("from='").append(from).append('\''); + sb.append(", to='").append(to).append('\''); + sb.append(", assets=").append(StringUtil.toString(assets)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, TransferRequest obj) { + obj.writeObject(writer); + } + + public static TransferRequest readObject(ObjectReader reader) { + TransferRequest obj = new TransferRequest(); + reader.beginList(); + obj.setFrom(reader.readNullable(String.class)); + obj.setTo(reader.readNullable(String.class)); + if (reader.beginNullableList()) { + Asset[] assets = null; + List assetsList = new ArrayList<>(); + while(reader.hasNext()) { + assetsList.add(reader.readNullable(Asset.class)); + } + assets = new Asset[assetsList.size()]; + for(int i=0; i supplies = Context.newDictDB("supplies", BigInteger.class); + private final OwnerManager ownerManager = new OwnerManagerImpl("owners"); + + @External(readonly = true) + public BigInteger totalSupply(BigInteger _id) { + BigInteger supply = supplies.get(_id); + //"Invalid token id" + Context.require(supply != null); + return supply; + } + + @External + public void mint(Address _owner, BigInteger _id, BigInteger _amount) { + requireOwnerAccess(); + //"Amount should be positive" + Context.require(_amount.compareTo(BigInteger.ZERO) > 0); + + supplies.set(_id, supplies.getOrDefault(_id, BigInteger.ZERO).add(_amount)); + + //FIXME IRC31Basic._mint should emit + // TransferSingle(Context.caller(), ZERO_ADDRESS, owner, id, amount); + super._mint(_owner, _id, _amount); + } + + @External + public void mintBatch(Address _owner, BigInteger[] _ids, BigInteger[] _amounts) { + requireOwnerAccess(); + //"id/amount pairs mismatch" + Context.require(_ids.length == _amounts.length); + + for (int i = 0; i < _ids.length; i++) { + BigInteger id = _ids[i]; + BigInteger amount = _amounts[i]; + //"Amount should be positive" + Context.require(amount.compareTo(BigInteger.ZERO) > 0); + + supplies.set(id, supplies.getOrDefault(id, BigInteger.ZERO).add(amount)); + } + + //FIXME IRC31Basic._mintBatch should emit + // TransferBatch(Context.caller(), ZERO_ADDRESS, owner, ids, amounts); + super._mintBatch(_owner, _ids, _amounts); + } + + @External + public void burn(Address _owner, BigInteger _id, BigInteger _amount) { + requireOwnerAccess(); + //"Amount should be positive" + Context.require(_amount.compareTo(BigInteger.ZERO) > 0); + BigInteger supply = supplies.get(_id); + //"Invalid token id" + Context.require(supply != null); + supplies.set(_id, supply.subtract(_amount)); + + super._burn(_owner, _id, _amount); + } + + @External + public void burnBatch(Address _owner, BigInteger[] _ids, BigInteger[] _amounts) { + requireOwnerAccess(); + //"id/amount pairs mismatch" + Context.require(_ids.length == _amounts.length); + + for (int i = 0; i < _ids.length; i++) { + BigInteger id = _ids[i]; + BigInteger amount = _amounts[i]; + //"Amount should be positive" + Context.require(amount.compareTo(BigInteger.ZERO) > 0); + + BigInteger supply = supplies.get(id); + //"Invalid token id" + Context.require(supply != null); + supplies.set(id, supply.subtract(amount)); + } + + super._burnBatch(_owner, _ids, _amounts); + } + + @External + public void setTokenURI(BigInteger _id, String _uri) { + requireOwnerAccess(); + //"Uri should be set" + Context.require(!_uri.isEmpty()); + + super._setTokenURI(_id, _uri); + } + + /* Delegate OwnerManager */ + private void requireOwnerAccess() { + Context.require(ownerManager.isOwner(Context.getCaller())); + } + + @External + public void addOwner(Address _addr) { + try { + ownerManager.addOwner(_addr); + } catch (IllegalStateException | IllegalArgumentException e) { + Context.revert(0, e.getMessage()); + } + } + + @External + public void removeOwner(Address _addr) { + try { + ownerManager.removeOwner(_addr); + } catch (IllegalStateException | IllegalArgumentException e) { + Context.revert(0, e.getMessage()); + } + } + + @External(readonly = true) + public Address[] getOwners() { + return ownerManager.getOwners(); + } + + @External(readonly = true) + public boolean isOwner(Address _addr) { + return ownerManager.isOwner(_addr); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCS.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCS.java new file mode 100644 index 00000000..3ba03c1e --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCS.java @@ -0,0 +1,75 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.btp.lib.BTPAddress; + +import java.util.Arrays; +import java.util.Comparator; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class AssertNCS { + static Comparator assetComparator = (o1, o2) -> { + int ret = o1.getCoinName().compareTo(o2.getCoinName()); + if (ret == 0) { + return o1.getAmount().compareTo(o2.getAmount()); + } + return ret; + }; + static Comparator assetTransferDetailComparator = (o1, o2) -> { + int ret = assetComparator.compare(o1, o2); + if (ret == 0) { + return o1.getFee().compareTo(o2.getFee()); + } + return ret; + }; + + static void assertEqualsAsset(Asset o1, Asset o2) { + assertEquals(o1.getCoinName(), o2.getCoinName()); + assertEquals(o1.getAmount(), o2.getAmount()); + } + + static void assertEqualsAssets(Asset[] o1, Asset[] o2) { + assertEquals(o1.length, o2.length); + Arrays.sort(o1, assetComparator); + Arrays.sort(o2, assetComparator); + for (int i = 0; i < o1.length; i++) { + assertEqualsAsset(o1[i], o2[i]); + } + } + + static void assertEqualsTransferRequest(TransferTransaction o1, TransferRequest o2) { + assertEquals(o1.getFrom(), o2.getFrom()); + assertEquals(BTPAddress.valueOf(o1.getTo()).account(), o2.getTo()); + assertEqualsAssets(o1.getAssets(), o2.getAssets()); + } + + static void assertEqualsAssetTransferDetail(AssetTransferDetail o1, AssetTransferDetail o2) { + assertEqualsAsset(o1, o2); + assertEquals(o1.getFee(), o2.getFee()); + } + + static void assertEqualsAssetTransferDetails(AssetTransferDetail[] o1, AssetTransferDetail[] o2) { + assertEquals(o1.length, o2.length); + Arrays.sort(o1, assetTransferDetailComparator); + Arrays.sort(o2, assetTransferDetailComparator); + for (int i = 0; i < o1.length; i++) { + assertEqualsAssetTransferDetail(o1[i], o2[i]); + } + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCSException.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCSException.java new file mode 100644 index 00000000..7ec594d8 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/AssertNCSException.java @@ -0,0 +1,41 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.btp.test.AssertBTPException; +import org.junit.jupiter.api.function.Executable; + +@SuppressWarnings("ThrowableNotThrown") +public class AssertNCSException { + + public static void assertUnknown(Executable executable) { + AssertBTPException.assertBTPException(NCSException.unknown(""), executable); + } + + public static void assertUnauthorized(Executable executable) { + AssertBTPException.assertBTPException(NCSException.unauthorized(), executable); + } + + public static void assertIRC31Failure(Executable executable) { + AssertBTPException.assertBTPException(NCSException.irc31Failure(), executable); + } + + public static void assertIRC31Reverted(Executable executable) { + AssertBTPException.assertBTPException(NCSException.irc31Reverted(), executable); + } + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NCSIntegrationTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NCSIntegrationTest.java new file mode 100644 index 00000000..cce4fd57 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NCSIntegrationTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.btp.lib.BSH; +import foundation.icon.btp.lib.BSHScoreClient; +import foundation.icon.btp.lib.OwnerManager; +import foundation.icon.btp.lib.OwnerManagerScoreClient; +import foundation.icon.btp.nativecoin.irc31.IRC31IntegrationTest; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.btp.test.MockBMCIntegrationTest; +import foundation.icon.btp.test.SendMessageEventLog; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.client.ScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.icx.Wallet; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.TestMethodOrder; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) +public interface NCSIntegrationTest extends BTPIntegrationTest { + + DefaultScoreClient ncsClient = DefaultScoreClient.of( + System.getProperties(), Map.of( + "_bmc", MockBMCIntegrationTest.mockBMCClient._address(), + "_irc31", IRC31IntegrationTest.irc31Client._address())); + + @ScoreClient + NCS ncs = new NCSScoreClient(ncsClient); + + @ScoreClient + BSH ncsBSH = new BSHScoreClient(ncsClient); + + @ScoreClient + OwnerManager ncsOwnerManager = new OwnerManagerScoreClient(ncsClient); + + Wallet tester = ScoreIntegrationTest.getOrGenerateWallet("tester.", System.getProperties()); + DefaultScoreClient ncsClientWithTester = new DefaultScoreClient( + ncsClient.endpoint(), ncsClient._nid(), tester, ncsClient._address()); + NCS ncsWithTester = new NCSScoreClient(ncsClientWithTester); + OwnerManager ncsOwnerManagerWithTester = new OwnerManagerScoreClient(ncsClientWithTester); + + static Consumer eventLogChecker( + EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + ncsClient._address(), supplier, consumer); + } + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NativeCoinServiceTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NativeCoinServiceTest.java new file mode 100644 index 00000000..defe15f4 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/NativeCoinServiceTest.java @@ -0,0 +1,570 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.mock.MockBMCScoreClient; +import foundation.icon.btp.nativecoin.irc31.IRC31IntegrationTest; +import foundation.icon.btp.nativecoin.irc31.IRC31SupplierTest; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.btp.test.MockBMCIntegrationTest; +import foundation.icon.btp.test.SendMessageEventLog; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.RevertedException; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; +import score.UserRevertedException; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class NativeCoinServiceTest implements NCSIntegrationTest { + + static Address ncsAddress = ncsClient._address(); + static Address owner = Address.of(ncsClient._wallet()); + static BTPAddress link = BTPIntegrationTest.Faker.btpLink(); + static String linkNet = BTPIntegrationTest.Faker.btpNetwork(); + static Address testerAddress = Address.of(tester); + static BTPAddress to = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); + static String nativeCoinName = ncs.coinNames()[0]; + static BigInteger nativeValue = BigInteger.valueOf(10); + static String coinName = "coin"; + static BigInteger coinValue = BigInteger.valueOf(10); + static BigInteger coinId; + static long code = 1; + static String msg = "err"; + + static String net = MockBMCIntegrationTest.mockBMC.getNet(); + static BTPAddress fa = new BTPAddress(BTPAddress.PROTOCOL_BTP, net, testerAddress.toString()); + static BTPAddress linkFa = new BTPAddress(BTPAddress.PROTOCOL_BTP, linkNet, testerAddress.toString()); + static BigInteger feeRatio = BigInteger.TEN; + + static boolean isExistsCoin(String name) { + return ScoreIntegrationTest.indexOf(ncs.coinNames(), name) >= 0; + } + + static void register(String name) { + ncs.register(name); + assertTrue(isExistsCoin(name)); + } + + static BigInteger transferBatch(TransferTransaction transaction) { + BigInteger nativeCoinValue = nativeCoinValue(transaction.getAssets()); + List assets = coinAssets(transaction.getAssets()); + BigInteger[] coinIds = coinIds(assets); + String[] coinNames = coinNames(assets); + BigInteger[] coinValues = coinValues(assets); + Address from = new Address(transaction.getFrom()); + BigInteger[] snContainer = new BigInteger[1]; + Consumer checker = transferStartEventLogChecker(transaction) + .andThen(sendMessageEventLogChecker(transaction, snContainer)) + .andThen(IRC31SupplierTest.transferFromBatchChecker( + ncsAddress, from, ncsAddress, coinIds, coinValues)); + Executable executable = () -> ((NCSScoreClient) ncs).transferBatch( + checker, + nativeCoinValue, + coinNames, coinValues, to.toString()); + ScoreIntegrationTest.balanceCheck(ncsAddress, nativeCoinValue, () -> + IRC31SupplierTest.balanceBatchCheck(ncsAddress, coinIds, coinValues, () -> + balanceBatchCheck(from, transaction.getAssets(), executable, + BalanceCheckType.lock))); + return snContainer[0]; + } + + + static void handleTransferResponse(TransferTransaction transaction, BigInteger sn) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_OK); + response.setMessage(TransferResponse.OK_MSG); + NCSMessage ncsMessage = new NCSMessage(); + ncsMessage.setServiceType(NCSMessage.REPONSE_HANDLE_SERVICE); + ncsMessage.setData(response.toBytes()); + + List assets = coinAssets(transferRequest(transaction).getAssets()); + BigInteger[] coinIds = coinIds(assets); + BigInteger[] coinValues = coinValues(assets); + Address from = new Address(transaction.getFrom()); + Consumer checker = IRC31SupplierTest.burnBatchChecker( + ncsAddress, ncsAddress, coinIds, coinValues) + .andThen(transferEndEventLogChecker(from, sn, response)); + balanceBatchCheck(from, transaction.getAssets(), () -> + ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( + checker, + ncsAddress, linkNet, NativeCoinService.SERVICE, sn, ncsMessage.toBytes()), + BalanceCheckType.unlock); + } + + static void handleBTPError(TransferTransaction transaction, BigInteger sn, long code, String msg) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(("BTPError [code:" + code + ",msg:" + msg)); + NCSMessage ncsMessage = new NCSMessage(); + ncsMessage.setServiceType(NCSMessage.REPONSE_HANDLE_SERVICE); + ncsMessage.setData(response.toBytes()); + + Address from = new Address(transaction.getFrom()); + balanceBatchCheck( + from, + transaction.getAssets(), + () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPError( + transferEndEventLogChecker(from, sn, response), + ncsAddress, link.toString(), NativeCoinService.SERVICE, sn, code, msg), + BalanceCheckType.refund); + } + + static Consumer transferStartEventLogChecker(TransferTransaction transaction) { + return NCSIntegrationTest.eventLogChecker(TransferStartEventLog::eventLogs, (el) -> { + System.out.println(el); + assertEquals(transaction.getFrom(), el.getFrom().toString()); + assertEquals(transaction.getTo(), el.getTo()); + AssertNCS.assertEqualsAssetTransferDetails(transaction.getAssets(), el.getAssets()); + }); + } + + static Consumer transferEndEventLogChecker(Address from, BigInteger sn, TransferResponse response) { + return NCSIntegrationTest.eventLogChecker(TransferEndEventLog::eventLogs, (el) -> { + assertEquals(from, el.getFrom()); + assertEquals(sn, el.getSn()); + assertEquals(response.getCode(), el.getCode()); + assertEquals(response.getMessage(), new String(el.getMsg())); + }); + } + + static Consumer sendMessageEventLogChecker(TransferTransaction transaction) { + return sendMessageEventLogChecker(transaction, null); + } + + static Consumer sendMessageEventLogChecker(TransferTransaction transaction, BigInteger[] snContainer) { + return MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { + assertEquals(BTPAddress.valueOf(transaction.getTo()).net(), el.getTo()); + assertEquals(NativeCoinService.SERVICE, el.getSvc()); + if (snContainer != null) { + snContainer[0] = el.getSn(); + } +// assertEquals(sn, el.getSn()); + NCSMessage ncsMessage = NCSMessage.fromBytes(el.getMsg()); + assertEquals(NCSMessage.REQUEST_COIN_TRANSFER, ncsMessage.getServiceType()); + AssertNCS.assertEqualsTransferRequest(transaction, TransferRequest.fromBytes(ncsMessage.getData())); + }); + } + + static void lockedBalanceCheck(Address address, Asset asset, Executable executable) { + Balance balance = ncs.balanceOf(address, asset.getCoinName()); + try { + executable.execute(); + } catch (UserRevertedException | RevertedException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + assertEquals(balance.getLocked().add(asset.getAmount()), + ncs.balanceOf(address, asset.getCoinName()).getLocked()); + } + + enum BalanceCheckType { + lock, unlock, refund + } + + static void balanceBatchCheck(Address address, AssetTransferDetail[] assetDetails, Executable executable, BalanceCheckType type) { + List list = Arrays.asList(assetDetails); + String[] coinNames = list.stream() + .map(AssetTransferDetail::getCoinName).toArray(String[]::new); + BigInteger[] values = list.stream() + .map((a) -> a.getAmount().add(a.getFee())).toArray(BigInteger[]::new); + + Balance[] balances = ncs.balanceOfBatch(address, coinNames); + assertEquals(coinNames.length, balances.length); + try { + executable.execute(); + } catch (UserRevertedException | RevertedException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + Balance[] actual = ncs.balanceOfBatch(address, coinNames); + for (int i = 0; i < coinNames.length; i++) { + BigInteger locked = balances[i].getLocked(); + if (BalanceCheckType.lock.equals(type)) { + locked = locked.add(values[i]); + } else if (BalanceCheckType.unlock.equals(type)) { + locked = locked.subtract(values[i]); + } else if (BalanceCheckType.refund.equals(type)) { + locked = locked.subtract(values[i]); + assertEquals(balances[i].getRefundable().add(values[i]), actual[i].getRefundable()); + } + assertEquals(locked, actual[i].getLocked()); + } + } + + static TransferRequest transferRequest(TransferTransaction transaction) { + TransferRequest request = new TransferRequest(); + request.setFrom(transaction.getFrom()); + request.setTo(BTPAddress.valueOf(transaction.getTo()).account()); + + AssetTransferDetail[] assetDetails = transaction.getAssets(); + Asset[] assets = new Asset[assetDetails.length]; + for (int i = 0; i < assetDetails.length; i++) { + assets[i] = new Asset(assetDetails[i]); + } + request.setAssets(assets); + return request; + } + + static TransferTransaction transferTransaction(Address from, BTPAddress to, BigInteger feeRatio, Asset... assets) { + TransferTransaction transaction = new TransferTransaction(); + transaction.setFrom(from.toString()); + transaction.setTo(to.toString()); + AssetTransferDetail[] assetDetails = new AssetTransferDetail[assets.length]; + for (int i = 0; i < assets.length; i++) { + Asset asset = assets[i]; + AssetTransferDetail assetDetail = new AssetTransferDetail(); + assetDetail.setCoinName(asset.getCoinName()); + BigInteger fee = asset.getAmount().multiply(feeRatio).divide(NativeCoinService.FEE_DENOMINATOR); + if (feeRatio.compareTo(BigInteger.ZERO) > 0 && fee.compareTo(BigInteger.ZERO) == 0) { + fee = BigInteger.ONE; + } + assetDetail.setFee(fee); + assetDetail.setAmount(asset.getAmount().subtract(assetDetail.getFee())); + assetDetails[i] = assetDetail; + } + transaction.setAssets(assetDetails); + return transaction; + } + + static BigInteger nativeCoinValue(AssetTransferDetail[] assetDetails) { + return Arrays.stream(assetDetails) + .filter((a) -> a.getCoinName().equals(nativeCoinName)) + .map((a) -> a.getAmount().add(a.getFee())) + .findAny().orElse(BigInteger.ZERO); + } + + static BigInteger nativeCoinValue(Asset[] assets) { + return Arrays.stream(assets) + .filter((a) -> a.getCoinName().equals(nativeCoinName)) + .map(Asset::getAmount) + .findAny().orElse(BigInteger.ZERO); + } + + static List coinAssets(AssetTransferDetail[] assetDetails) { + return Arrays.stream(assetDetails) + .filter((a) -> !a.getCoinName().equals(nativeCoinName)) + .map((a) -> { + Asset asset = new Asset(a); + asset.setAmount(a.getAmount().add(a.getFee())); + return asset; + }).collect(Collectors.toList()); + } + + static List coinAssets(Asset[] assets) { + return Arrays.stream(assets) + .filter((a) -> !a.getCoinName().equals(nativeCoinName)) + .collect(Collectors.toList()); + } + + static BigInteger[] coinIds(List assets) { + return assets.stream() + .map((a) -> ncs.coinId(a.getCoinName())).toArray(BigInteger[]::new); + } + + static String[] coinNames(List assets) { + return assets.stream() + .map(Asset::getCoinName).toArray(String[]::new); + } + + static BigInteger[] coinValues(List assets) { + return assets.stream() + .map(Asset::getAmount).toArray(BigInteger[]::new); + } + + static Asset[] feeAssets() { + String[] coinNames = ncs.coinNames(); + Balance[] balances = ncs.balanceOfBatch(ncsAddress, coinNames); + List feeAssets = new ArrayList<>(); + for (int i = 0; i < coinNames.length; i++) { + BigInteger coinFee = balances[i].getRefundable(); + if (coinFee.compareTo(BigInteger.ZERO) > 0) { + String coinName = coinNames[i]; + feeAssets.add(new Asset(coinName, coinFee)); + } + } + return feeAssets.toArray(Asset[]::new); + } + + @BeforeAll + static void beforeAll() { + if (!IRC31IntegrationTest.irc31OwnerManager.isOwner(ncsAddress)) { + IRC31IntegrationTest.irc31OwnerManager.addOwner(ncsAddress); + } + IRC31SupplierTest.setApprovalForAll(ncsAddress, true); + if (!isExistsCoin(coinName)) { + register(coinName); + coinId = ncs.coinId(coinName); + } + if (!ncs.feeRatio().equals(feeRatio)) { + ncs.setFeeRatio(feeRatio); + } + } + + @Test + void registerShouldSuccess() { + register(ScoreIntegrationTest.Faker.faker.name().name()); + } + + @Test + void transferNativeCoinShouldMakeEventLogAndLockBalance() { + Asset asset = new Asset(nativeCoinName, nativeValue); + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); + Consumer checker = transferStartEventLogChecker(transaction) + .andThen(sendMessageEventLogChecker(transaction)); + ScoreIntegrationTest.balanceCheck(ncsAddress, asset.getAmount(), () -> + lockedBalanceCheck(new Address(transaction.getFrom()), asset, () -> + ((NCSScoreClient) ncs).transferNativeCoin( + checker, + asset.getAmount(), + transaction.getTo()))); + } + + @Test + void transferShouldMakeEventLogAndLockBalance() { + IRC31SupplierTest.mint(owner, coinId, coinValue); + + Asset asset = new Asset(coinName, coinValue); + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, asset); + Address from = new Address(transaction.getFrom()); + Consumer checker = transferStartEventLogChecker(transaction) + .andThen(sendMessageEventLogChecker(transaction)) + .andThen(IRC31SupplierTest.transferFromChecker( + ncsAddress, from, ncsAddress, coinId, asset.getAmount())); + IRC31SupplierTest.balanceCheck(ncsAddress, coinId, coinValue, () -> + lockedBalanceCheck(from, asset, () -> + ((NCSScoreClient) ncs).transfer( + checker, + asset.getCoinName(), asset.getAmount(), transaction.getTo()) + ) + ); + } + + @Test + void transferBatchShouldShouldMakeEventLogAndLockBalance() { + IRC31SupplierTest.mint(owner, coinId, coinValue); + + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + + transferBatch(transaction); + } + + @Test + void handleTransferRequestShouldIRC31MintBatchAndResponse() { + //mint to tester + IRC31SupplierTest.mint(testerAddress, coinId, coinValue); + + //transfer owner to tester + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + TransferRequest request = transferRequest(transaction); + List assets = coinAssets(request.getAssets()); + + BigInteger[] coinIds = coinIds(assets); + BigInteger[] coinValues = coinValues(assets); + + NCSMessage ncsMessage = new NCSMessage(); + ncsMessage.setServiceType(NCSMessage.REQUEST_COIN_TRANSFER); + ncsMessage.setData(request.toBytes()); + + Address to = new Address(BTPAddress.valueOf(transaction.getTo()).account()); + + BigInteger sn = BigInteger.ONE; + Consumer checker = IRC31SupplierTest.mintBatchChecker( + ncsAddress, to, coinIds, coinValues).andThen( + MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { + assertEquals(linkNet, el.getTo()); + assertEquals(NativeCoinService.SERVICE, el.getSvc()); + assertEquals(sn, el.getSn()); + NCSMessage ncsMsg = NCSMessage.fromBytes(el.getMsg()); + assertEquals(NCSMessage.REPONSE_HANDLE_SERVICE, ncsMsg.getServiceType()); + TransferResponse response = TransferResponse.fromBytes(ncsMsg.getData()); + assertEquals(TransferResponse.RC_OK, response.getCode()); + assertEquals(TransferResponse.OK_MSG, response.getMessage()); + })); + Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) + .intercallHandleBTPMessage( + checker, + ncsAddress, linkNet, NativeCoinService.SERVICE, sn, ncsMessage.toBytes()); + ScoreIntegrationTest.balanceCheck(to, nativeCoinValue(request.getAssets()), () -> + IRC31SupplierTest.balanceBatchCheck(to, coinIds, coinValues, executable)); + } + + @Test + void handleTransferResponseShouldIRC31BurnBatchAndMakeEventLog() { + //mint + IRC31SupplierTest.mint(owner, coinId, coinValue); + + //transferBatch + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + BigInteger sn = transferBatch(transaction); + + // + handleTransferResponse(transaction, sn); + } + + @Test + void handleUnknownResponseShouldMakeEventLog() { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(TransferResponse.ERR_MSG_UNKNOWN_TYPE); + NCSMessage ncsMessage = new NCSMessage(); + ncsMessage.setServiceType(NCSMessage.UNKNOWN_TYPE); + ncsMessage.setData(response.toBytes()); + + BigInteger sn = BigInteger.ONE; + ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC).intercallHandleBTPMessage( + NCSIntegrationTest.eventLogChecker(UnknownResponseEventLog::eventLogs, (el) -> { + assertEquals(linkNet, el.getFrom()); + assertEquals(sn, el.getSn()); + }), + ncsAddress, linkNet, NativeCoinService.SERVICE, sn, ncsMessage.toBytes()); + } + + @Test + void handleBTPMessageShouldRevert() { + AssertNCSException.assertUnknown(() -> + ncsBSH.handleBTPMessage(linkNet, NativeCoinService.SERVICE, BigInteger.ONE, new byte[]{})); + } + + @Test + void handleBTPErrorShouldMakeEventLogAndAddRefundableBalance() { + //mint + IRC31SupplierTest.mint(owner, coinId, coinValue); + + //transferBatch + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + BigInteger sn = transferBatch(transaction); + + //handleBTPError + handleBTPError(transaction, sn, code, msg); + } + + @Test + void reclaim() { + //mint + IRC31SupplierTest.mint(owner, coinId, coinValue); + + //transferBatch + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + BigInteger sn = transferBatch(transaction); + + //handleBTPError for make refundable balance + handleBTPError(transaction, sn, code, msg); + + //reclaim + ScoreIntegrationTest.balanceCheck(owner, nativeValue, () -> + ncs.reclaim(nativeCoinName, nativeValue)); + IRC31SupplierTest.balanceCheck(owner, coinId, coinValue, () -> + ncs.reclaim(coinName, coinValue)); + } + + @Test + void handleBTPErrorShouldRevert() { + AssertNCSException.assertUnknown(() -> + ncsBSH.handleBTPError(linkNet, + NativeCoinService.SERVICE, BigInteger.ONE, 0, "")); + } + + @Test + void handleFeeGatheringShouldIRC31Transfer() {//how to clear feeBalances as zero? + //mint + IRC31SupplierTest.mint(owner, coinId, coinValue); + + //transferBatch + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + BigInteger sn = transferBatch(transaction); + + //handleTransferResponse + handleTransferResponse(transaction, sn); + + // + Asset[] feeAssets = feeAssets(); + System.out.println(Arrays.toString(feeAssets)); + BigInteger nativeFee = nativeCoinValue(feeAssets); + List coinAssets = coinAssets(feeAssets); + BigInteger[] coinIds = coinIds(coinAssets); + BigInteger[] coinValues = coinValues(coinAssets); + + Address faAddr = new Address(fa.account()); + Executable executable = () -> ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) + .intercallHandleFeeGathering( + IRC31SupplierTest.transferFromBatchChecker( + ncsAddress, ncsAddress, faAddr, coinIds, coinValues), + ncsAddress, fa.toString(), NativeCoinService.SERVICE); + ScoreIntegrationTest.balanceCheck(faAddr, nativeFee, () -> + IRC31SupplierTest.balanceBatchCheck(faAddr, coinIds, coinValues, executable)); + } + + @Test + void handleFeeGatheringShouldTransferStart() { + //mint + IRC31SupplierTest.mint(owner, coinId, coinValue); + + //transferBatch + TransferTransaction transaction = transferTransaction(owner, to, feeRatio, + new Asset(nativeCoinName, nativeValue), + new Asset(coinName, coinValue)); + BigInteger sn = transferBatch(transaction); + + //handleTransferResponse + handleTransferResponse(transaction, sn); + + // + Asset[] feeAssets = feeAssets(); + System.out.println(Arrays.toString(feeAssets)); + + TransferTransaction feeTransaction = transferTransaction( + ncsAddress, linkFa, BigInteger.ZERO, feeAssets); + ((MockBMCScoreClient) MockBMCIntegrationTest.mockBMC) + .intercallHandleFeeGathering( + transferStartEventLogChecker(feeTransaction) + .andThen(sendMessageEventLogChecker(feeTransaction)), + ncsAddress, linkFa.toString(), NativeCoinService.SERVICE); + } + + @Test + void handleFeeGatheringShouldRevert() { + AssertNCSException.assertUnknown(() -> + ncsBSH.handleFeeGathering(link.toString(), NativeCoinService.SERVICE)); + } +} \ No newline at end of file diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/OwnershipTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/OwnershipTest.java new file mode 100644 index 00000000..fc1989bc --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/OwnershipTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.function.Executable; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OwnershipTest implements NCSIntegrationTest { + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); + static String string = ""; + static BigInteger bigInteger = BigInteger.ONE; + + static boolean isExistsOwner(Address address) { + return ncsOwnerManager.isOwner(address); + } + + static void addOwner(Address address) { + ncsOwnerManager.addOwner(address); + assertTrue(isExistsOwner(address)); + } + + static void removeOwner(Address address) { + ncsOwnerManager.removeOwner(address); + assertFalse(isExistsOwner(address)); + } + + static void clearOwner(Address address) { + if (isExistsOwner(address)) { + System.out.println("clear owner address:"+address); + removeOwner(address); + } + } + + @Override + public void clearIfExists(TestInfo testInfo) { + String testMethod = testInfo.getTestMethod().orElseThrow().getName(); + if (!testMethod.endsWith("RevertUnauthorized")) { + clearOwner(address); + } + } + + @Test + void addOwnerShouldSuccess() { + addOwner(address); + } + + static void assertAlreadyExists(Executable executable) { + AssertNCSException.assertUnknown(executable); + } + + @Test + void addOwnerShouldRevertAlreadyExists() { + addOwner(address); + + assertAlreadyExists(() -> addOwner(address)); + } + + @Test + void removeOwnerShouldSuccess() { + addOwner(address); + + removeOwner(address); + } + + static void assertNotExists(Executable executable) { + AssertNCSException.assertUnknown(executable); + } + + @Test + void removeOwnerShouldRevertNotExists() { + assertNotExists(() -> removeOwner(address)); + } + + static void assertUnauthorized(Executable executable) { + AssertNCSException.assertUnauthorized(executable); + } + + @Test + void addOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> ncsOwnerManagerWithTester.addOwner(address)); + } + + @Test + void removeOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> ncsOwnerManagerWithTester.removeOwner(address)); + } + + @Test + void registerShouldRevertUnauthorized() { + assertUnauthorized(() -> ncsWithTester.register(string)); + } + + @Test + void setFeeRateShouldRevertUnauthorized() { + assertUnauthorized(() -> ncsWithTester.setFeeRatio(bigInteger)); + } + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferEndEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferEndEventLog.java new file mode 100644 index 00000000..87dfb707 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferEndEventLog.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class TransferEndEventLog { + static final String SIGNATURE = "TransferEnd(Address,int,int,bytes)"; + private Address from; + private BigInteger sn; + private BigInteger code; + private byte[] msg; + + public TransferEndEventLog(TransactionResult.EventLog el) { + from = new Address(el.getIndexed().get(1)); + sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); + code = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); + msg = IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2)); + } + + public Address getFrom() { + return from; + } + + public BigInteger getSn() { + return sn; + } + + public BigInteger getCode() { + return code; + } + + public byte[] getMsg() { + return msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferEndEventLog{"); + sb.append("from=").append(from); + sb.append(", sn=").append(sn); + sb.append(", code=").append(code); + sb.append(", msg=").append(StringUtil.toString(msg)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + TransferEndEventLog.SIGNATURE, + address, + TransferEndEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferStartEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferStartEventLog.java new file mode 100644 index 00000000..42f5d28c --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/TransferStartEventLog.java @@ -0,0 +1,92 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; +import score.Context; +import score.ObjectReader; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class TransferStartEventLog { + static final String SIGNATURE = "TransferStart(Address,str,int,bytes)"; + private Address from; + private String to; + private BigInteger sn; + private AssetTransferDetail[] assets; + + public TransferStartEventLog(TransactionResult.EventLog el) { + from = new Address(el.getIndexed().get(1)); + to = el.getData().get(0); + sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); + assets = toAssetTransferDetailArray(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(2))); + } + + public static AssetTransferDetail[] toAssetTransferDetailArray(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + reader.beginList(); + List list = new ArrayList<>(); + while (reader.hasNext()) { + list.add(reader.read(AssetTransferDetail.class)); + } + reader.end(); + return list.toArray(new AssetTransferDetail[]{}); + } + + public Address getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public BigInteger getSn() { + return sn; + } + + public AssetTransferDetail[] getAssets() { + return assets; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferStartEventLog{"); + sb.append("from=").append(from); + sb.append(", to='").append(to).append('\''); + sb.append(", sn=").append(sn); + sb.append(", assets=").append(StringUtil.toString(assets)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + TransferStartEventLog.SIGNATURE, + address, + TransferStartEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/UnknownResponseEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/UnknownResponseEventLog.java new file mode 100644 index 00000000..ae391c28 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/UnknownResponseEventLog.java @@ -0,0 +1,67 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; +import score.Context; +import score.ObjectReader; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class UnknownResponseEventLog { + static final String SIGNATURE = "UnknownResponse(str,int)"; + private String from; + private BigInteger sn; + + public UnknownResponseEventLog(TransactionResult.EventLog el) { + from = el.getIndexed().get(1); + sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); + } + + public String getFrom() { + return from; + } + + public BigInteger getSn() { + return sn; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("UnknownResponseEventLog{"); + sb.append("from='").append(from).append('\''); + sb.append(", sn=").append(sn); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + UnknownResponseEventLog.SIGNATURE, + address, + UnknownResponseEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/ApprovalForAllEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/ApprovalForAllEventLog.java new file mode 100644 index 00000000..5c3c3806 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/ApprovalForAllEventLog.java @@ -0,0 +1,70 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.util.List; +import java.util.function.Predicate; + +public class ApprovalForAllEventLog { + static final String SIGNATURE = "ApprovalForAll(Address,Address,bool)"; + private Address owner; + private Address operator; + private boolean approved; + + public ApprovalForAllEventLog(TransactionResult.EventLog el) { + this.owner = new Address(el.getIndexed().get(1)); + this.operator = new Address(el.getIndexed().get(2)); + this.approved = IconJsonModule.BooleanDeserializer.BOOLEAN.convert(el.getData().get(0)); + } + + public Address getOwner() { + return owner; + } + + public Address getOperator() { + return operator; + } + + public boolean isApproved() { + return approved; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ApprovalForAllEventLog{"); + sb.append("operator=").append(owner); + sb.append(", from=").append(operator); + sb.append(", approved=").append(approved); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + ApprovalForAllEventLog.SIGNATURE, + address, + ApprovalForAllEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31IntegrationTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31IntegrationTest.java new file mode 100644 index 00000000..2d0663f7 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31IntegrationTest.java @@ -0,0 +1,60 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + +import foundation.icon.btp.lib.OwnerManager; +import foundation.icon.btp.lib.OwnerManagerScoreClient; +import foundation.icon.btp.nativecoin.TransferStartEventLog; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.client.ScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.TestInfo; + +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public interface IRC31IntegrationTest extends ScoreIntegrationTest { + @Override + default void internalBeforeEach(TestInfo testInfo) {} + + @Override + default void internalAfterEach(TestInfo testInfo) {} + + @Override + default void clearIfExists(TestInfo testInfo) {} + + DefaultScoreClient irc31Client = DefaultScoreClient.of("irc31.", System.getProperties()); + @ScoreClient + IRC31Supplier irc31Supplier = new IRC31SupplierScoreClient(irc31Client); + @ScoreClient + OwnerManager irc31OwnerManager = new OwnerManagerScoreClient(irc31Client); + + DefaultScoreClient irc31ClientWithTester = new DefaultScoreClient( + irc31Client.endpoint(), irc31Client._nid(), tester, irc31Client._address()); + IRC31Supplier irc31SupplierWithTester = new IRC31SupplierScoreClient(irc31ClientWithTester); + OwnerManager irc31OwnerManagerWithTester = new OwnerManagerScoreClient(irc31ClientWithTester); + + static Consumer eventLogChecker( + EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + irc31Client._address(), supplier, consumer); + } + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31SupplierTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31SupplierTest.java new file mode 100644 index 00000000..2c3bb6a9 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31SupplierTest.java @@ -0,0 +1,282 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + +import com.iconloop.score.token.irc31.IRC31Basic; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.RevertedException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.function.Executable; +import score.UserRevertedException; + +import java.math.BigInteger; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.*; + +public class IRC31SupplierTest implements IRC31IntegrationTest { + + static Address caller = Address.of(irc31Client._wallet()); + static Address owner = caller; + static BigInteger id = BigInteger.ONE; + static BigInteger value = BigInteger.ONE; + static BigInteger[] ids = new BigInteger[]{id}; + static BigInteger[] values = new BigInteger[]{value}; + static String uri = Faker.faker.internet().url(); + static Address operator = Faker.address(Address.Type.EOA); + + public static Consumer mintChecker( + Address caller, Address to, BigInteger id, BigInteger value) { + return IRC31IntegrationTest.eventLogChecker(TransferSingleEventLog::eventLogs, (el) -> { + //caller must be registered owner of OwnerBasedIRC31Supplier +// assertEquals(caller, el.getOperator()); + assertEquals(to, el.getOperator());//IRC31 must be fixed + assertEquals(IRC31Basic.ZERO_ADDRESS, el.getFrom()); + assertEquals(to, el.getTo()); + assertEquals(id, el.getId()); + assertEquals(value, el.getValue()); + }); + } + + public static Consumer transferFromChecker( + Address caller, Address from, Address to, BigInteger id, BigInteger value) { + return IRC31IntegrationTest.eventLogChecker(TransferSingleEventLog::eventLogs, (el) -> { + assertEquals(caller, el.getOperator()); + assertEquals(from, el.getFrom()); + assertEquals(to, el.getTo()); + assertEquals(id, el.getId()); + assertEquals(value, el.getValue()); + }); + } + + public static Consumer burnChecker( + Address caller, Address from, BigInteger id, BigInteger value) { + return IRC31IntegrationTest.eventLogChecker(TransferSingleEventLog::eventLogs, (el) -> { + //caller must be registered owner of OwnerBasedIRC31Supplier +// assertEquals(caller, el.getOperator()); + assertEquals(from, el.getOperator());//IRC31 must be fixed + assertEquals(from, el.getFrom()); + assertEquals(IRC31Basic.ZERO_ADDRESS, el.getTo()); + assertEquals(id, el.getId()); + assertEquals(value, el.getValue()); + }); + } + + public static Consumer mintBatchChecker( + Address caller, Address to, BigInteger[] ids, BigInteger[] values) { + return IRC31IntegrationTest.eventLogChecker(TransferBatchEventLog::eventLogs, (el) -> { + //caller must be registered owner of OwnerBasedIRC31Supplier +// assertEquals(caller, el.getOperator()); + assertEquals(to, el.getOperator());//IRC31 must be fixed + assertEquals(IRC31Basic.ZERO_ADDRESS, el.getFrom()); + assertEquals(to, el.getTo()); + assertArrayEquals(ids, el.getIds()); + assertArrayEquals(values, el.getValues()); + }); + } + + public static Consumer transferFromBatchChecker( + Address caller, Address from, Address to, BigInteger[] ids, BigInteger[] values) { + return IRC31IntegrationTest.eventLogChecker(TransferBatchEventLog::eventLogs, (el) -> { + assertEquals(caller, el.getOperator()); + assertEquals(from, el.getFrom()); + assertEquals(to, el.getTo()); + assertArrayEquals(ids, el.getIds()); + assertArrayEquals(values, el.getValues()); + }); + } + + public static Consumer burnBatchChecker( + Address caller, Address from, BigInteger[] ids, BigInteger[] values) { + return IRC31IntegrationTest.eventLogChecker(TransferBatchEventLog::eventLogs, (el) -> { + //caller must be registered owner of OwnerBasedIRC31Supplier +// assertEquals(caller, el.getOperator()); + assertEquals(from, el.getOperator());//IRC31 must be fixed + assertEquals(from, el.getFrom()); + assertEquals(IRC31Basic.ZERO_ADDRESS, el.getTo()); + assertArrayEquals(ids, el.getIds()); + assertArrayEquals(values, el.getValues()); + }); + } + + public static void balanceCheck(Address address, BigInteger id, BigInteger value, Executable executable) { + BigInteger balance = irc31Supplier.balanceOf(address, id); + try { + executable.execute(); + } catch (UserRevertedException | RevertedException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + assertEquals(balance.add(value), irc31Supplier.balanceOf(address, id)); + } + + public static void balanceBatchCheck( + Address address, BigInteger[] ids, BigInteger[] values, Executable executable) { + balanceBatchCheck(address, ids, values, executable, true); + } + + public static void balanceBatchCheck( + Address address, BigInteger[] ids, BigInteger[] values, Executable executable, boolean increase) { + Address[] owners = new Address[ids.length]; + for (int i = 0; i < ids.length; i++) { + owners[i] = address; + } + BigInteger[] balances = irc31Supplier.balanceOfBatch(owners, ids); + assertEquals(ids.length, balances.length); + try { + executable.execute(); + } catch (UserRevertedException | RevertedException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + BigInteger[] expected = new BigInteger[ids.length]; + for (int i = 0; i < ids.length; i++) { + if (increase) { + expected[i] = balances[i].add(values[i]); + } else { + expected[i] = balances[i].subtract(values[i]); + } + } + assertArrayEquals(expected, irc31Supplier.balanceOfBatch(owners, ids)); + } + + public static void mint(Address to, BigInteger id, BigInteger value) { + balanceCheck(to, id, value, () -> + ((IRC31SupplierScoreClient) irc31Supplier).mint( + mintChecker(caller, to, id, value), + to, id, value)); + } + + public static void burn(Address from, BigInteger id, BigInteger value) { + balanceCheck(from, id, value.negate(), () -> + ((IRC31SupplierScoreClient) irc31Supplier).burn( + burnChecker(caller, from, id, value), + from, id, value)); + } + + public static void mintBatch(Address to, BigInteger[] ids, BigInteger[] values) { + balanceBatchCheck(to, ids, values, () -> + ((IRC31SupplierScoreClient) irc31Supplier).mintBatch( + mintBatchChecker(caller, to, ids, values), + to, ids, values)); + } + + public static void burnBatch(Address from, BigInteger[] ids, BigInteger[] values) { + balanceBatchCheck(from, ids, values, () -> + ((IRC31SupplierScoreClient) irc31Supplier).burnBatch( + burnBatchChecker(caller, from, ids, values), + from, ids, values), false); + } + + @Test + void mintShuldSuccess() { + mint(owner, id, value); + } + + @Test + void burnShuldSuccess() { + mint(owner, id, value); + + burn(owner, id, value); + } + + @Test + void mintBatchShuldSuccess() { + mintBatch(owner, ids, values); + } + + @Test + void burnBatchShuldSuccess() { + mintBatch(owner, ids, values); + + burnBatch(owner, ids, values); + } + + @Test + void setTokenURI() { + ((IRC31SupplierScoreClient) irc31Supplier).setTokenURI( + IRC31IntegrationTest.eventLogChecker(URIEventLog::eventLogs, (el) -> { + assertEquals(id, el.getId()); + assertEquals(uri, el.getValue()); + }), + id, uri); + assertEquals(uri, irc31Supplier.tokenURI(id)); + } + + //test for IRC31 + static void transferFrom(Address from, Address to, BigInteger id, BigInteger value) { + balanceCheck(to, id, value, () -> + ((IRC31SupplierScoreClient) irc31Supplier).transferFrom( + transferFromChecker(caller, from, to, id, value), + from, to, id, value, null)); + } + + static void transferFromBatch(Address from, Address to, BigInteger[] ids, BigInteger[] values) { + balanceBatchCheck(to, ids, values, () -> + ((IRC31SupplierScoreClient) irc31Supplier).transferFromBatch( + transferFromBatchChecker(caller, from, to, ids, values), + from, to, ids, values, null)); + } + + public static void setApprovalForAll(Address operator, boolean approved) { + ((IRC31SupplierScoreClient) irc31Supplier).setApprovalForAll( + IRC31IntegrationTest.eventLogChecker(ApprovalForAllEventLog::eventLogs, (el) -> { + //caller must be registered owner of OwnerBasedIRC31Supplier + assertEquals(caller, el.getOwner()); + assertEquals(operator, el.getOperator()); + assertEquals(approved, el.isApproved()); + }), + operator, approved); + assertEquals(approved, isApprovalForAll(operator)); + } + + public static boolean isApprovalForAll(Address operator) { + return irc31Supplier.isApprovedForAll(caller, operator); + } + + @Override + public void clearIfExists(TestInfo testInfo) { + if(isApprovalForAll(operator)) { + System.out.println("clear approvedForAll operator:"+operator); + setApprovalForAll(operator, false); + } + } + + @Test + void transferFrom() { + mint(owner, id, value); + + transferFrom(owner, Address.of(tester), id, value); + } + + @Test + void transferFromBatch() { + mintBatch(owner, ids, values); + + transferFromBatch(owner, Address.of(tester), ids, values); + } + + @Test + void setApprovalForAll() { + setApprovalForAll(operator, true); + } + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31Test.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31Test.java new file mode 100644 index 00000000..b17f270a --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/IRC31Test.java @@ -0,0 +1,34 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + +import com.iconloop.score.token.irc31.IRC31; +import foundation.icon.btp.nativecoin.NCSMessage; +import foundation.icon.btp.nativecoin.TransferRequest; +import foundation.icon.jsonrpc.Address; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class IRC31Test implements IRC31IntegrationTest { + + static IRC31 irc31 = IRC31IntegrationTest.irc31Supplier; + + +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/OwnershipTest.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/OwnershipTest.java new file mode 100644 index 00000000..629982c5 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/OwnershipTest.java @@ -0,0 +1,139 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.AssertRevertedException; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.function.Executable; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OwnershipTest implements IRC31IntegrationTest { + static Address address = ScoreIntegrationTest.Faker.address(Address.Type.EOA); + static String string = ""; + static BigInteger bigInteger = BigInteger.ZERO; + static BigInteger[] bigIntegerArray = new BigInteger[]{BigInteger.ZERO}; + + static boolean isExistsOwner(Address address) { + return irc31OwnerManager.isOwner(address); + } + + static void addOwner(Address address) { + irc31OwnerManager.addOwner(address); + assertTrue(isExistsOwner(address)); + } + + static void removeOwner(Address address) { + irc31OwnerManager.removeOwner(address); + assertFalse(isExistsOwner(address)); + } + + static void clearOwner(Address address) { + if (isExistsOwner(address)) { + System.out.println("clear owner address:"+address); + removeOwner(address); + } + } + + @Override + public void clearIfExists(TestInfo testInfo) { + String testMethod = testInfo.getTestMethod().orElseThrow().getName(); + if (!testMethod.endsWith("RevertUnauthorized")) { + clearOwner(address); + } + } + + @Test + void addOwnerShouldSuccess() { + addOwner(address); + } + + @SuppressWarnings("ThrowableNotThrown") + static void assertAlreadyExists(Executable executable) { + AssertRevertedException.assertUserReverted(0, executable); + } + + @Test + void addOwnerShouldRevertAlreadyExists() { + addOwner(address); + + assertAlreadyExists(() -> addOwner(address)); + } + + @Test + void removeOwnerShouldSuccess() { + addOwner(address); + + removeOwner(address); + } + + @SuppressWarnings("ThrowableNotThrown") + static void assertNotExists(Executable executable) { + AssertRevertedException.assertUserReverted(0, executable); + } + + @Test + void removeOwnerShouldRevertNotExists() { + assertNotExists(() -> removeOwner(address)); + } + + @SuppressWarnings("ThrowableNotThrown") + static void assertUnauthorized(Executable executable) { + AssertRevertedException.assertUserReverted(0, executable); + } + + @Test + void addOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31OwnerManagerWithTester.addOwner(address)); + } + + @Test + void removeOwnerShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31OwnerManagerWithTester.removeOwner(address)); + } + + @Test + void mintShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31SupplierWithTester.mint(address, bigInteger, bigInteger)); + } + + @Test + void mintBatchShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31SupplierWithTester.mintBatch(address, bigIntegerArray, bigIntegerArray)); + } + + @Test + void burnShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31SupplierWithTester.burn(address, bigInteger, bigInteger)); + } + + @Test + void burnBatchShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31SupplierWithTester.burnBatch(address, bigIntegerArray, bigIntegerArray)); + } + + @Test + void setTokenURIShouldRevertUnauthorized() { + assertUnauthorized(() -> irc31SupplierWithTester.setTokenURI(bigInteger, string)); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferBatchEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferBatchEventLog.java new file mode 100644 index 00000000..e6206989 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferBatchEventLog.java @@ -0,0 +1,101 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.ArrayUtil; +import foundation.icon.score.util.StringUtil; +import score.Context; +import score.ObjectReader; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class TransferBatchEventLog { + static final String SIGNATURE = "TransferBatch(Address,Address,Address,bytes,bytes)"; + private Address operator; + private Address from; + private Address to; + private BigInteger[] ids; + private BigInteger[] values; + + public TransferBatchEventLog(TransactionResult.EventLog el) { + this.operator = new Address(el.getIndexed().get(1)); + this.from = new Address(el.getIndexed().get(2)); + this.to = new Address(el.getIndexed().get(3)); + this.ids = toBigIntegerArray(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(0))); + this.values = toBigIntegerArray(IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(1))); + } + + private static BigInteger[] toBigIntegerArray(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + reader.beginList(); + List list = new ArrayList<>(); + while(reader.hasNext()) { + list.add(reader.readBigInteger()); + } + reader.end(); + return ArrayUtil.toBigIntegerArray(list); + } + + public Address getOperator() { + return operator; + } + + public Address getFrom() { + return from; + } + + public Address getTo() { + return to; + } + + public BigInteger[] getIds() { + return ids; + } + + public BigInteger[] getValues() { + return values; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferBatchEventLog{"); + sb.append("operator=").append(operator); + sb.append(", from=").append(from); + sb.append(", to=").append(to); + sb.append(", ids=").append(StringUtil.toString(ids)); + sb.append(", values=").append(StringUtil.toString(values)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + TransferBatchEventLog.SIGNATURE, + address, + TransferBatchEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferSingleEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferSingleEventLog.java new file mode 100644 index 00000000..4687a089 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/TransferSingleEventLog.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class TransferSingleEventLog { + static final String SIGNATURE = "TransferSingle(Address,Address,Address,int,int)"; + private Address operator; + private Address from; + private Address to; + private BigInteger id; + private BigInteger value; + + public TransferSingleEventLog(TransactionResult.EventLog el) { + this.operator = new Address(el.getIndexed().get(1)); + this.from = new Address(el.getIndexed().get(2)); + this.to = new Address(el.getIndexed().get(3)); + this.id = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(0)); + this.value = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(1)); + } + + public Address getOperator() { + return operator; + } + + public Address getFrom() { + return from; + } + + public Address getTo() { + return to; + } + + public BigInteger getId() { + return id; + } + + public BigInteger getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferSingleEventLog{"); + sb.append("operator=").append(operator); + sb.append(", from=").append(from); + sb.append(", to=").append(to); + sb.append(", id=").append(id); + sb.append(", value=").append(value); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + TransferSingleEventLog.SIGNATURE, + address, + TransferSingleEventLog::new, + filter); + } +} diff --git a/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/URIEventLog.java b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/URIEventLog.java new file mode 100644 index 00000000..52252782 --- /dev/null +++ b/javascore/nativecoin/src/test/java/foundation/icon/btp/nativecoin/irc31/URIEventLog.java @@ -0,0 +1,64 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoin.irc31; + + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class URIEventLog { + static final String SIGNATURE = "URI(int,str)"; + private BigInteger id; + private String value; + + public URIEventLog(TransactionResult.EventLog el) { + this.id = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getIndexed().get(1)); + this.value = el.getData().get(0); + } + + public BigInteger getId() { + return id; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("URIEventLog{"); + sb.append("id=").append(id); + sb.append(", value='").append(value).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + URIEventLog.SIGNATURE, + address, + URIEventLog::new, + filter); + } +} diff --git a/javascore/nativecoinIRC2/build.gradle b/javascore/nativecoinIRC2/build.gradle new file mode 100644 index 00000000..609e73eb --- /dev/null +++ b/javascore/nativecoinIRC2/build.gradle @@ -0,0 +1,83 @@ +version = '0.1.0' + +dependencies { + compileOnly 'foundation.icon:javaee-api:0.9.0' + compileOnly 'foundation.icon:javaee-rt:0.9.0' + compileOnly 'foundation.icon:icon-sdk:2.0.0' + compileOnly 'foundation.icon:javaee-tooling:0.8.7' + + implementation project(':score-util') + implementation project(':lib') + implementation 'com.github.sink772:javaee-scorex:0.5.2' + implementation 'com.github.sink772:javaee-tokens:0.5.5' + implementation 'org.msgpack:msgpack-core:0.8.17' + + testImplementation 'foundation.icon:javaee-api:0.9.0' + testImplementation 'foundation.icon:javaee-rt:0.9.0' + testImplementation 'foundation.icon:icon-sdk:2.0.0' + testImplementation 'foundation.icon:javaee-tooling:0.9.0' + testImplementation 'org.mockito:mockito-core:3.3.3' + testImplementation 'com.squareup.okhttp3:okhttp:3.11.0' + testImplementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.0' + testImplementation 'org.bouncycastle:bcprov-jdk15on:1.60' + + testImplementation fileTree(dir: './lib', include: 'goloop-testsuite.jar') + testImplementation fileTree(dir: './lib', include: 'testsvc.jar') + + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.6.0' +} + + +optimizedJar { + mainClassName = 'foundation.icon.btp.nativecoinIRC2.NativeCoinService' + archivesBaseName = 'nativecoinIRC2' + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + + +import foundation.icon.gradle.plugins.javaee.task.OptimizedJar + +task optimizedJarIRC2(type: OptimizedJar) { + mainClassName = 'foundation.icon.btp.nativecoinIRC2.irc2.IRC2Basic' + archiveName("irc2-" + archiveVersion.get() + "." + archiveExtension.get()); + from { sourceSets.main.output } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +logging.println("BMC Address :" + System.getProperty("BMC_ADDRESS")) +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + Sejong { + uri = 'https://sejong.net.solidwallet.io/api/v3' + nid = 0x53 + } + local { + uri = 'http://localhost:9082/api/v3' + nid = 3 + } + BTPTestnet { + uri = 'https://btp.net.solidwallet.io/api/v3' + nid = 0x42 + } + } + keystore = rootProject.hasProperty('keystoreName') ? "$keystoreName" : '' + password = rootProject.hasProperty('keystorePass') ? "$keystorePass" : '' + + parameters { + arg('_bmc', System.getProperty("BMC_ADDRESS")) + } +} + +test { + useJUnitPlatform() +} diff --git a/javascore/nativecoinIRC2/lib/goloop-testsuite.jar b/javascore/nativecoinIRC2/lib/goloop-testsuite.jar new file mode 100644 index 00000000..931df1cb Binary files /dev/null and b/javascore/nativecoinIRC2/lib/goloop-testsuite.jar differ diff --git a/javascore/nativecoinIRC2/lib/testsvc.jar b/javascore/nativecoinIRC2/lib/testsvc.jar new file mode 100644 index 00000000..885a76cf Binary files /dev/null and b/javascore/nativecoinIRC2/lib/testsvc.jar differ diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Asset.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Asset.java new file mode 100644 index 00000000..4694f54f --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Asset.java @@ -0,0 +1,98 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Asset { + private String coinName; + private BigInteger amount; + + public Asset() {} + + public Asset(Asset asset) { + setCoinName(asset.getCoinName()); + setAmount(asset.getAmount()); + } + + public Asset(String coinName, BigInteger amount) { + this.coinName = coinName; + this.amount = amount; + } + + public String getCoinName() { + return coinName; + } + + public void setCoinName(String coinName) { + this.coinName = coinName; + } + + public BigInteger getAmount() { + return amount; + } + + public void setAmount(BigInteger amount) { + this.amount = amount; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Asset{"); + sb.append("coinName='").append(coinName).append('\''); + sb.append(", amount=").append(amount); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Asset obj) { + obj.writeObject(writer); + } + + public static Asset readObject(ObjectReader reader) { + Asset obj = new Asset(); + reader.beginList(); + obj.setCoinName(reader.readNullable(String.class)); + obj.setAmount(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getCoinName()); + writer.writeNullable(this.getAmount()); + writer.end(); + } + + public static Asset fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return Asset.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + Asset.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/AssetTransferDetail.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/AssetTransferDetail.java new file mode 100644 index 00000000..b2373081 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/AssetTransferDetail.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class AssetTransferDetail extends Asset { + private BigInteger fee; + + public BigInteger getFee() { + return fee; + } + + public void setFee(BigInteger fee) { + this.fee = fee; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("AssetTransferDetail{"); + sb.append("fee=").append(fee); + sb.append('}').append(super.toString()); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, AssetTransferDetail obj) { + obj.writeObject(writer); + } + + public static AssetTransferDetail readObject(ObjectReader reader) { + AssetTransferDetail obj = new AssetTransferDetail(); + reader.beginList(); + obj.setCoinName(reader.readNullable(String.class)); + obj.setAmount(reader.readNullable(BigInteger.class)); + obj.setFee(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.writeNullable(this.getCoinName()); + writer.writeNullable(this.getAmount()); + writer.writeNullable(this.getFee()); + writer.end(); + } + + public static AssetTransferDetail fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return AssetTransferDetail.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + AssetTransferDetail.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMC.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMC.java new file mode 100644 index 00000000..1b094d27 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMC.java @@ -0,0 +1,195 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.Address; +import score.annotation.External; + +import java.math.BigInteger; +import java.util.Map; + +public interface BMC { + /** + * Registers BMV for the network. + * Called by the operator to manage the BTP network. + * + * @param _net String (Network Address of the blockchain ) + * @param _addr Address (the address of BMV) + */ + @External + void addVerifier(String _net, Address _addr); + + /** + * Unregisters BMV for the network. + * May fail if it's referred by the link. + * Called by the operator to manage the BTP network. + * + * @param _net String (Network Address of the blockchain ) + */ + @External + void removeVerifier(String _net); + + /** + * Get registered verifiers. + * + * @return A dictionary with the Network Address as a key and smart contract address of the BMV as a value. + *
+ * For Example:: + *
+ * { + * "0x1.iconee": "cx72eaed466599ca5ea377637c6fa2c5c0978537da" + * } + */ + @External(readonly = true) + Map getVerifiers(); + + /** + * Registers the smart contract for the service. + * Called by the operator to manage the BTP network. + * + * @param _svc String (the name of the service) + * @param _addr Address (the address of the smart contract handling the service) + */ + @External + void addService(String _svc, Address _addr); + + /** + * Unregisters the smart contract for the service. + * Called by the operator to manage the BTP network. + * + * @param _svc String (the name of the service) + */ + @External + void removeService(String _svc); + /** + * Get registered services. + * + * @return A dictionary with the name of the service as key and address of the BSH related to the service as value. + *
For example::
+ * { + * "token": "cx72eaed466599ca5ea377637c6fa2c5c0978537da" + * } + */ + @External(readonly = true) + Map getServices(); + + /** + * If it generates the event related to the link, the relay shall handle the event to deliver BTP Message to the BMC. + * If the link is already registered, or its network is already registered then it fails. + * If there is no verifier related with the network of the link, then it fails. + * Initializes status information for the link. + * Called by the operator to manage the BTP network. + * + * @param _link String (BTP Address of connected BMC) + */ + @External + void addLink(String _link); + + /** + * Removes the link and status information. + * Called by the operator to manage the BTP network. + * + * @param _link String (BTP Address of connected BMC) + */ + @External + void removeLink(String _link); + + /** + * Get status of BMC. + * Used by the relay to resolve next BTP Message to send. + * If target is not registered, it will fail. + * + * @param _link String ( BTP Address of the connected BMC ) + * @return The object contains followings fields. + */ + @External(readonly = true) + BMCStatus getStatus(String _link); + + /** + * Get registered links. + * + * @return A list of links ( BTP Addresses of the BMCs ) + *
For Example::
+ * [ + * "btp://0x1.iconee/cx9f8a75111fd611710702e76440ba9adaffef8656" + * ] + */ + @External(readonly = true) + String[] getLinks(); + + /** + * Add route to the BMC. + * May fail if there more than one BMC for the network. + * Called by the operator to manage the BTP network. + * + * @param _dst String ( BTP Address of the destination BMC ) + * @param _link String ( BTP Address of the next BMC for the destination ) + */ + @External + void addRoute(String _dst, String _link); + /** + * Remove route to the BMC. + * Called by the operator to manage the BTP network. + * + * @param _dst String ( BTP Address of the destination BMC ) + */ + @External + void removeRoute(String _dst); + /** + * Get routing information. + * + * @return A dictionary with the BTP Address of the destination BMC as key and the BTP Address of the next as value. + * + *
For Example::
+ * { + * "btp://0x2.iconee/cx1d6e4decae8160386f4ecbfc7e97a1bc5f74d35b": "btp://0x1.iconee/cx9f8a75111fd611710702e76440ba9adaffef8656" + * } + */ + @External(readonly = true) + Map getRoutes(); + + /** + * Sends the message to a specific network. + * Only allowed to be called by registered BSHs. + * + * @param _to String ( Network Address of destination network ) + * @param _svc String ( name of the service ) + * @param _sn Integer ( serial number of the message, must be positive ) + * @param _msg Bytes ( serialized bytes of Service Message ) + */ + @External + void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg); + + /** + * It verifies and decodes the Relay Message with BMV and dispatches BTP Messages to registered BSHs. + * It's allowed to be called by registered Relay. + * + * @param _prev String ( BTP Address of the previous BMC ) + * @param _msg String ( base64 encoded string of serialized bytes of Relay Message ) + */ + @External + void handleRelayMessage(String _prev, String _msg); + + /** + * TODO [TBD] add 'getBtpAddress' to IIP-25.BMC.Read-only methods + * Returns BTP Address of BMC + * + * @return String (BTP Address of BMC) + */ + @External(readonly = true) + String getBtpAddress(); +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCMock.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCMock.java new file mode 100644 index 00000000..dec4ec00 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCMock.java @@ -0,0 +1,56 @@ +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.btp.lib.BTPAddress; +import score.Address; +import score.Context; +import score.DictDB; +import score.annotation.External; + +import java.math.BigInteger; + +public class BMCMock { + private final DictDB bshServices = Context.newDictDB("bshService", Address.class); + private final BTPAddress btpAddr; + + public BMCMock(String _net) { + this.btpAddr = new BTPAddress(BTPAddress.PROTOCOL_BTP, _net, Context.getAddress().toString()); + } + + + @External + public void addService(String _svc, Address _addr) { + bshServices.set(_svc, _addr); + } + + @External + public void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { + Address addr = bshServices.get(_svc); + if (addr == null) { + Context.revert("BSH doesnt exist"); + } + if (!Context.getCaller().equals(addr)) { + Context.revert("unauthorized"); + } + if (_sn.compareTo(BigInteger.ZERO) < 1) { + //Context.revert("invalid sn"); + } + } + + @External + public void handleBTPMessage(String from, String svc, BigInteger sn, byte[] msg) { + Address _addr = bshServices.get(svc); + Context.call(_addr, "handleBTPMessage", from, svc, sn, msg); + } + + @External + public void handleFeeGathering(String _fa, String _svc) { + Address _addr = bshServices.get(_svc); + Context.call(_addr, "handleFeeGathering", _fa, _svc); + } + + + @External(readonly = true) + public String getBtpAddress() { + return btpAddr.toString(); + } +} \ No newline at end of file diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCScoreInterface.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCScoreInterface.java new file mode 100644 index 00000000..b6709259 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCScoreInterface.java @@ -0,0 +1,135 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.Address; +import score.Context; + +import java.math.BigInteger; +import java.util.Map; + +public final class BMCScoreInterface implements BMC { + protected final Address address; + + protected final BigInteger valueForPayable; + + public BMCScoreInterface(Address address) { + this.address = address; + this.valueForPayable = null; + } + + public BMCScoreInterface(Address address, BigInteger valueForPayable) { + this.address = address; + this.valueForPayable = valueForPayable; + } + + public Address _getAddress() { + return this.address; + } + + public BMCScoreInterface _payable(BigInteger valueForPayable) { + return new BMCScoreInterface(address,valueForPayable); + } + + public BMCScoreInterface _payable(long valueForPayable) { + return this._payable(BigInteger.valueOf(valueForPayable)); + } + + public BigInteger _getICX() { + return this.valueForPayable; + } + + @Override + public void addVerifier(String _net, Address _addr) { + Context.call(this.address, "addVerifier", _net, _addr); + } + + @Override + public void removeVerifier(String _net) { + Context.call(this.address, "removeVerifier", _net); + } + + @Override + public Map getVerifiers() { + return Context.call(Map.class, this.address, "getVerifiers"); + } + + @Override + public void addService(String _svc, Address _addr) { + Context.call(this.address, "addService", _svc, _addr); + } + + @Override + public void removeService(String _svc) { + Context.call(this.address, "removeService", _svc); + } + + @Override + public Map getServices() { + return Context.call(Map.class, this.address, "getServices"); + } + + @Override + public void addLink(String _link) { + Context.call(this.address, "addLink", _link); + } + + @Override + public void removeLink(String _link) { + Context.call(this.address, "removeLink", _link); + } + + @Override + public BMCStatus getStatus(String _link) { + return Context.call(BMCStatus.class, this.address, "getStatus", _link); + } + + @Override + public String[] getLinks() { + return Context.call(String[].class, this.address, "getLinks"); + } + + @Override + public void addRoute(String _dst, String _link) { + Context.call(this.address, "addRoute", _dst, _link); + } + + @Override + public void removeRoute(String _dst) { + Context.call(this.address, "removeRoute", _dst); + } + + @Override + public Map getRoutes() { + return Context.call(Map.class, this.address, "getRoutes"); + } + + @Override + public void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { + Context.call(this.address, "sendMessage", _to, _svc, _sn, _msg); + } + + @Override + public void handleRelayMessage(String _prev, String _msg) { + Context.call(this.address, "handleRelayMessage", _prev, _msg); + } + + @Override + public String getBtpAddress() { + return Context.call(String.class, this.address, "getBtpAddress"); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCStatus.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCStatus.java new file mode 100644 index 00000000..a4fb6410 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMCStatus.java @@ -0,0 +1,252 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.score.util.StringUtil; +import score.annotation.Keep; + +import java.math.BigInteger; + +public class BMCStatus { + private BigInteger rx_seq; + private BigInteger tx_seq; + private BMVStatus verifier; + private BMRStatus[] relays; + + private int block_interval_src; + private int block_interval_dst; + private int max_agg; + private int delay_limit; + + private int relay_idx; + private long rotate_height; + private int rotate_term; + private long rx_height; //initialize with BMC.block_height + private long rx_height_src; //initialize with BMV._offset + + private int sack_term; //0: disable sack + private long sack_next; + private long sack_height; + private BigInteger sack_seq; + + private long cur_height; + + @Keep + public BigInteger getRx_seq() { + return rx_seq; + } + + @Keep + public void setRx_seq(BigInteger rx_seq) { + this.rx_seq = rx_seq; + } + + @Keep + public BigInteger getTx_seq() { + return tx_seq; + } + + @Keep + public void setTx_seq(BigInteger tx_seq) { + this.tx_seq = tx_seq; + } + + @Keep + public BMVStatus getVerifier() { + return verifier; + } + + @Keep + public void setVerifier(BMVStatus verifier) { + this.verifier = verifier; + } + + @Keep + public BMRStatus[] getRelays() { + return relays; + } + + @Keep + public void setRelays(BMRStatus[] relays) { + this.relays = relays; + } + + @Keep + public int getBlock_interval_src() { + return block_interval_src; + } + + @Keep + public void setBlock_interval_src(int block_interval_src) { + this.block_interval_src = block_interval_src; + } + + @Keep + public int getBlock_interval_dst() { + return block_interval_dst; + } + + @Keep + public void setBlock_interval_dst(int block_interval_dst) { + this.block_interval_dst = block_interval_dst; + } + + @Keep + public int getMax_agg() { + return max_agg; + } + + @Keep + public void setMax_agg(int max_agg) { + this.max_agg = max_agg; + } + + @Keep + public int getDelay_limit() { + return delay_limit; + } + + @Keep + public void setDelay_limit(int delay_limit) { + this.delay_limit = delay_limit; + } + + @Keep + public int getRelay_idx() { + return relay_idx; + } + + @Keep + public void setRelay_idx(int relay_idx) { + this.relay_idx = relay_idx; + } + + @Keep + public long getRotate_height() { + return rotate_height; + } + + @Keep + public void setRotate_height(long rotate_height) { + this.rotate_height = rotate_height; + } + + @Keep + public int getRotate_term() { + return rotate_term; + } + + @Keep + public void setRotate_term(int rotate_term) { + this.rotate_term = rotate_term; + } + + @Keep + public long getRx_height() { + return rx_height; + } + + @Keep + public void setRx_height(long rx_height) { + this.rx_height = rx_height; + } + + @Keep + public long getRx_height_src() { + return rx_height_src; + } + + @Keep + public void setRx_height_src(long rx_height_src) { + this.rx_height_src = rx_height_src; + } + + @Keep + public int getSack_term() { + return sack_term; + } + + @Keep + public void setSack_term(int sack_term) { + this.sack_term = sack_term; + } + + @Keep + public long getSack_next() { + return sack_next; + } + + @Keep + public void setSack_next(long sack_next) { + this.sack_next = sack_next; + } + + @Keep + public long getSack_height() { + return sack_height; + } + + @Keep + public void setSack_height(long sack_height) { + this.sack_height = sack_height; + } + + @Keep + public BigInteger getSack_seq() { + return sack_seq; + } + + @Keep + public void setSack_seq(BigInteger sack_seq) { + this.sack_seq = sack_seq; + } + + @Keep + public long getCur_height() { + return cur_height; + } + + @Keep + public void setCur_height(long cur_height) { + this.cur_height = cur_height; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMCStatus{"); + sb.append("rx_seq=").append(rx_seq); + sb.append(", tx_seq=").append(tx_seq); + sb.append(", verifier=").append(verifier); + sb.append(", relays=").append(StringUtil.toString(relays)); + sb.append(", block_interval_src=").append(block_interval_src); + sb.append(", block_interval_dst=").append(block_interval_dst); + sb.append(", max_agg=").append(max_agg); + sb.append(", delay_limit=").append(delay_limit); + sb.append(", relay_idx=").append(relay_idx); + sb.append(", rotate_height=").append(rotate_height); + sb.append(", rotate_term=").append(rotate_term); + sb.append(", rx_height=").append(rx_height); + sb.append(", rx_height_src=").append(rx_height_src); + sb.append(", sack_term=").append(sack_term); + sb.append(", sack_next=").append(sack_next); + sb.append(", sack_height=").append(sack_height); + sb.append(", sack_seq=").append(sack_seq); + sb.append(", cur_height=").append(cur_height); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMRStatus.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMRStatus.java new file mode 100644 index 00000000..eb02fc66 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMRStatus.java @@ -0,0 +1,68 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.Address; +import score.annotation.Keep; + +import java.math.BigInteger; + +public class BMRStatus { + private Address address; + private long block_count; + private BigInteger msg_count; + + @Keep + public Address getAddress() { + return address; + } + + @Keep + public void setAddress(Address address) { + this.address = address; + } + + @Keep + public long getBlock_count() { + return block_count; + } + + @Keep + public void setBlock_count(long block_count) { + this.block_count = block_count; + } + + @Keep + public BigInteger getMsg_count() { + return msg_count; + } + + @Keep + public void setMsg_count(BigInteger msg_count) { + this.msg_count = msg_count; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMRStatus{"); + sb.append("address=").append(address); + sb.append(", block_count=").append(block_count); + sb.append(", msg_count=").append(msg_count); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMVStatus.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMVStatus.java new file mode 100644 index 00000000..2c9a786d --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/BMVStatus.java @@ -0,0 +1,69 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.annotation.Keep; + +public class BMVStatus { + private long height; + private long offset; + private long last_height; + + @Keep + public BMVStatus() { + } + + @Keep + public long getHeight() { + return height; + } + + @Keep + public void setHeight(long height) { + this.height = height; + } + + @Keep + public long getOffset() { + return offset; + } + + @Keep + public void setOffset(long offset) { + this.offset = offset; + } + + @Keep + public long getLast_height() { + return last_height; + } + + @Keep + public void setLast_height(long last_height) { + this.last_height = last_height; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("BMVStatus{"); + sb.append("height=").append(height); + sb.append(", offset=").append(offset); + sb.append(", last_height=").append(last_height); + sb.append('}'); + return sb.toString(); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Balance.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Balance.java new file mode 100644 index 00000000..1e57b931 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/Balance.java @@ -0,0 +1,106 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class Balance { + + private BigInteger usable; + private BigInteger locked; + private BigInteger refundable; + + + public Balance(BigInteger usable, BigInteger locked, BigInteger refundable) { + this.usable = usable; + this.locked = locked; + this.refundable = refundable; + } + + public BigInteger getUsable() { + return usable; + } + + public void setUsable(BigInteger usable) { + this.usable = usable; + } + + public BigInteger getLocked() { + return locked; + } + + public void setLocked(BigInteger locked) { + this.locked = locked; + } + + public BigInteger getRefundable() { + return refundable; + } + + public void setRefundable(BigInteger refundable) { + this.refundable = refundable; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Balance{"); + sb.append("usable=").append(usable); + sb.append(", locked=").append(locked); + sb.append(", refundable=").append(refundable); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, Balance obj) { + obj.writeObject(writer); + } + + public static Balance readObject(ObjectReader reader) { + reader.beginList(); + //obj.setUsable(reader.readNullable(BigInteger.class)); + //obj.setLocked(reader.readNullable(BigInteger.class)); + //obj.setRefundable(reader.readNullable(BigInteger.class)); + Balance obj = new Balance(reader.readNullable(BigInteger.class),reader.readNullable(BigInteger.class),reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.writeNullable(this.getUsable()); + writer.writeNullable(this.getLocked()); + writer.writeNullable(this.getRefundable()); + writer.end(); + } + + public static Balance fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return Balance.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + Balance.writeObject(writer, this); + return writer.toByteArray(); + } + +} \ No newline at end of file diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCS.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCS.java new file mode 100644 index 00000000..5a2cff69 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCS.java @@ -0,0 +1,167 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.Address; +import score.annotation.External; +import score.annotation.Payable; + +import java.math.BigInteger; + +public interface NCS { + + /** + * Registers a wrapped token smart contract and id number of a supporting coin. + * Caller must be the owner + * + * {@code _name} It must be listed in the code name registry.. + * The native coin (ICX) must be registered through the constructor. + * + * @param _name A coin name. + */ + @External + void register(String _name); + + /** + * Return all supported coins names in other networks by the BSH contract + * + * @return An array of strings. + */ + @External(readonly = true) + String[] coinNames(); + + /** + * Return an _id number and a symbol of Coin whose name is the same with given _coinName. + * + * @implSpec Return nullempty if not found. + * + * @param _coinName Coin name. + * @return id of Native Coin Token. + */ + @External(readonly = true) + BigInteger coinId(String _coinName); + + /** + * Return a usable/locked balance of an account based on coinName. + * + * @implSpec Locked Balance means an amount of Coins/Wrapped Coins is currently + * at a pending state when a transfer tx is requested from this chain to another + * @implSpec Return 0 if not found + * + * @param _owner + * @param _coinName Coin name. + * @return Balance + * { + * "locked" : an amount of locked Coins/WrappedCoins, + * "refundable" : an amount of refundable Coins/WrappedCoins + * } + */ + @External(readonly = true) + Balance balanceOf(Address _owner, String _coinName); + + /** + * Return a list locked/usable balance of an account. + * + * @implSpec The order of request's coinNames must be the same with the order of return balance + * @implSpec Return 0 if not found. + * + * @param _owner + * @param _coinNames + * @return Balance[] + * [ + * { + * "locked" : an amount of locked Coins/WrappedCoins, + * "refundable" : an amount of refundable Coins/WrappedCoins + * } + * ] + */ + @External(readonly = true) + Balance[] balanceOfBatch(Address _owner, String[] _coinNames); + + /** + * Reclaim the coin's refundable balance by an owner. + * + * @apiNote Caller must be an owner of coin + * @implSpec This function only applies on native coin (not wrapped coins) + * The amount to claim must be smaller than refundable balance + * + * @param _coinName A given name of coin to be re-claim + * @param _value An amount of re-claiming + */ + @External + void reclaim(String _coinName, BigInteger _value); + + /** + * Allow users to deposit `msg.value` native coin into a BSH contract. + * + * @implSpec MUST specify msg.value + * + * @param _to An address that a user expects to receive an equivalent amount of tokens. + */ + @Payable + @External + void transferNativeCoin(String _to); + + /** + * Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSH contract. + * + * @apiNote Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by the operator. + * @implSpec It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + * + * @param _coinName A given name of coin that is equivalent to retrieve a wrapped Token Contract's address, i.e. list["Token A"] = 0x12345678 + * @param _value Transferring amount. + * @param _to Target address. + */ + @External + void transfer(String _coinName, BigInteger _value, String _to); + + /** + * Allows users to deposit `_values` amounts of coins `_coinName` from the `msg.sender` address into the BSH contract. + * + * The caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by the contract. It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + * The order of coinName and value should be matched + * + * @param _coinNames Given names of each coin that is equivalent to retrieve a wrapped Token Contract's address, i.e. list["Token A"] = 0x12345678 + * @param _values Transferring amounts per coin + * @param _to Target BTP Address. + */ + @Payable + @External + void transferBatch(String[] _coinNames, BigInteger[] _values, String _to); + + /** + * Sets a new transfer fee ratio. + * + * The caller must be the owner. + * The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. The feeNumetator should be less than FEE_DEMONINATOR and greater than 1 + * feeNumetator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + * + * @param _feeNumerator the fee numerator + */ + @External + void setFeeRatio(BigInteger _feeNumerator); + + /** + * Get transfer fee ratio. + * + * @return BigInteger the fee ratio + * + */ + @External(readonly = true) + BigInteger feeRatio(); + +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSEvents.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSEvents.java new file mode 100644 index 00000000..fc700104 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSEvents.java @@ -0,0 +1,56 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.Address; +import score.annotation.EventLog; + +import java.math.BigInteger; + +public interface NCSEvents { + + /** + * (EventLog) Sends a receipt to sender + * + * @param _from The {@code _from} sender. (Indexed) + * @param _to The {@code _to} receiver. + * @param _sn The {@code _sn} sequence number of the service message. + * @param _assets The {@code _assets} asset details that is the serialized data of AssetTransferDetail + */ + @EventLog(indexed = 1) + void TransferStart(Address _from, String _to, BigInteger _sn, byte[] _assets); + + /** + * (EventLog) Sends a receipt to sender to notify the transfer's result + * + * @param _sender The {@code _sender} account sends the service message. (Indexed) + * @param _sn The {@code _sn} sequence number of the service message. + * @param _code The {@code _code} response code. + * @param _msg The {@code _msg} response message. + */ + @EventLog(indexed = 1) + void TransferEnd(Address _sender, BigInteger _sn, BigInteger _code, byte[] _msg); + + /** + * Notify to the BSH owner that it has received unknown response + * + * @param _from The {@code _from} Network Address of source network. + * @param _sn The {@code _sn} sequence number of the service message. + */ + @EventLog(indexed = 1) + void UnknownResponse(String _from, BigInteger _sn); +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSException.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSException.java new file mode 100644 index 00000000..9a5e8d54 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSException.java @@ -0,0 +1,71 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.btp.lib.BTPException; + +public class NCSException extends BTPException.BSH { + + public NCSException(Code c) { + super(c, c.name()); + } + + public NCSException(Code c, String message) { + super(c, message); + } + + public static NCSException unknown(String message) { + return new NCSException(Code.Unknown, message); + } + + public static NCSException unauthorized() { + return new NCSException(Code.Unauthorized); + } + public static NCSException unauthorized(String message) { + return new NCSException(Code.Unauthorized, message); + } + + public static NCSException irc31Failure() { + return new NCSException(Code.IRC31Failure); + } + public static NCSException irc31Failure(String message) { + return new NCSException(Code.IRC31Failure, message); + } + + public static NCSException irc31Reverted() { + return new NCSException(Code.IRC31Reverted); + } + public static NCSException irc31Reverted(String message) { + return new NCSException(Code.IRC31Reverted, message); + } + + + //BTPException.BSH => 40 ~ 54 + public enum Code implements BTPException.Coded{ + Unknown(0), + Unauthorized(1), + IRC31Failure(2), + IRC31Reverted(3); + + final int code; + Code(int code){ this.code = code; } + + @Override + public int code() { return code; } + + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSMessage.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSMessage.java new file mode 100644 index 00000000..8d71a4b8 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSMessage.java @@ -0,0 +1,89 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +public class NCSMessage { + public static int REQUEST_COIN_TRANSFER = 0; + public static int REQUEST_COIN_REGISTER = 1; + public static int REPONSE_HANDLE_SERVICE = 2; + public static int UNKNOWN_TYPE = 3; + + private int serviceType; + private byte[] data; + + public int getServiceType() { + return serviceType; + } + + public void setServiceType(int serviceType) { + this.serviceType = serviceType; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("ServiceMessage{"); + sb.append("serviceType='").append(serviceType).append('\''); + sb.append(", data=").append(StringUtil.toString(data)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, NCSMessage obj) { + obj.writeObject(writer); + } + + public static NCSMessage readObject(ObjectReader reader) { + NCSMessage obj = new NCSMessage(); + reader.beginList(); + obj.setServiceType(reader.readInt()); + obj.setData(reader.readNullable(byte[].class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.write(this.getServiceType()); + writer.writeNullable(this.getData()); + writer.end(); + } + + public static NCSMessage fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return NCSMessage.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + NCSMessage.writeObject(writer, this); + return writer.toByteArray(); + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSProperties.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSProperties.java new file mode 100644 index 00000000..bf5c4646 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NCSProperties.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; + +import java.math.BigInteger; + +public class NCSProperties { + public static final NCSProperties DEFAULT; + + static { + DEFAULT = new NCSProperties(); + DEFAULT.setSn(BigInteger.ZERO); + DEFAULT.setFeeRatio(BigInteger.valueOf(10)); + } + + private BigInteger sn; + private BigInteger feeRatio; + + public BigInteger getSn() { + return sn; + } + + public void setSn(BigInteger sn) { + this.sn = sn; + } + + public BigInteger getFeeRatio() { + return feeRatio; + } + + public void setFeeRatio(BigInteger feeRatio) { + this.feeRatio = feeRatio; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("NCSProperties{"); + sb.append("sn=").append(sn); + sb.append(", feeRatio=").append(feeRatio); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, NCSProperties obj) { + obj.writeObject(writer); + } + + public static NCSProperties readObject(ObjectReader reader) { + NCSProperties obj = new NCSProperties(); + reader.beginList(); + obj.setSn(reader.readNullable(BigInteger.class)); + obj.setFeeRatio(reader.readNullable(BigInteger.class)); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(2); + writer.writeNullable(this.getSn()); + writer.writeNullable(this.getFeeRatio()); + writer.end(); + } + + public static NCSProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return NCSProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + NCSProperties.writeObject(writer, this); + return writer.toByteArray(); + } + +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NativeCoinService.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NativeCoinService.java new file mode 100644 index 00000000..7d209e73 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/NativeCoinService.java @@ -0,0 +1,583 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.btp.lib.BSH; +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.lib.OwnerManager; +import foundation.icon.btp.lib.OwnerManagerImpl; +import foundation.icon.score.util.ArrayUtil; +import foundation.icon.score.util.Logger; +import foundation.icon.score.util.StringUtil; +import score.*; +import score.annotation.EventLog; +import score.annotation.External; +import score.annotation.Optional; +import score.annotation.Payable; +import scorex.util.ArrayList; + +import java.math.BigInteger; +import java.util.List; + +public class NativeCoinService implements NCSEvents, BSH, OwnerManager { + private static final Logger logger = Logger.getLogger(NativeCoinService.class); + + public static final String SERVICE = "NativeCoinIRC2BSH"; + public static final BigInteger NATIVE_COIN_ID = BigInteger.ZERO; + public static final BigInteger FEE_DENOMINATOR = BigInteger.valueOf(10000); + protected static final Address ZERO_ADDRESS = new Address(new byte[Address.LENGTH]); + + // + private final Address bmc; + private final String net; + private final Address irc2; + private final String name; + private final String tokenName; + private final VarDB properties = Context.newVarDB("properties", NCSProperties.class); + + // + private final OwnerManager ownerManager = new OwnerManagerImpl("owners"); + + // + private final ArrayDB coinNames = Context.newArrayDB("coinNames", String.class); + private final BranchDB> balances = Context.newBranchDB("balances", Balance.class); + private final DictDB feeBalances = Context.newDictDB("feeBalances", BigInteger.class); + private final DictDB transactions = Context.newDictDB("transactions", TransferTransaction.class); + + public NativeCoinService(Address _bmc, Address _irc2, String _name, String _tokenName) { + bmc = _bmc; + BMCScoreInterface bmcInterface = new BMCScoreInterface(bmc); + //BTPAddress btpAddress = BTPAddress.valueOf(bmcInterface.getBtpAddress()); + String btpAddr = (String) Context.call(bmcInterface._getAddress(), "getBtpAddress"); + BTPAddress btpAddress = BTPAddress.valueOf(btpAddr); + net = btpAddress.net(); + irc2 = _irc2; + name = _name; + tokenName = _tokenName; + coinNames.add(_tokenName); + } + + public NCSProperties getProperties() { + return properties.getOrDefault(NCSProperties.DEFAULT); + } + + public void setProperties(NCSProperties properties) { + this.properties.set(properties); + } + + private boolean isRegistered(String name) { + if (tokenName.equals(name)) { + return true; + } + return false; + } + + static void require(boolean condition, String message) { + if (!condition) { + throw NCSException.unknown(message); + } + } + + @External(readonly = true) + public String[] coinNames() { + String[] names = new String[2]; + names[0] = name; + names[1] = tokenName; + return names; + } + + @External(readonly = true) + public Balance balanceOf(Address _owner, String _coinName) { + if (_owner.equals(Context.getAddress())) { + Balance balance = new Balance(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO); + balance.setRefundable(feeBalances.getOrDefault(_coinName, BigInteger.ZERO)); + return balance; + } else { + return getBalance(_coinName, _owner); + } + } + + @External + public void reclaim(String _coinName, BigInteger _value) { + require(_value.compareTo(BigInteger.ZERO) > 0, "_value must be positive"); + + Address owner = Context.getCaller(); + Balance balance = getBalance(_coinName, owner); + + if (balance.getRefundable().compareTo(BigInteger.ZERO) > 0) { + balance.setUsable(balance.getUsable().add(balance.getRefundable())); + balance.setRefundable(BigInteger.ZERO); + } + require(balance.getUsable().compareTo(_value) > -1, "invalid value"); + balance.setUsable(balance.getUsable().subtract(_value)); + setBalance(_coinName, owner, balance); + + if (name.equals(_coinName)) { + Context.transfer(owner, _value); + } else { + transfer(irc2, owner, _value, "Reclaim: transferring to Caller".getBytes()); + } + } + + @External + public void tokenFallback(Address from, BigInteger value, @Optional byte[] data) { + require(value.compareTo(BigInteger.ZERO) > 0, "Invalid Amount"); + require(Context.getCaller().equals(irc2), "Invalid Caller: Token Contract wrong"); + deposit(tokenName, from, value); + } + + @Payable + @External + public void transferNativeCoin(String _to) { + BigInteger value = Context.getValue(); + require(value != null && value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); + sendRequest(Context.getCaller(), BTPAddress.valueOf(_to), List.of(name), List.of(value)); + } + + @External + public void transfer(String _coinName, BigInteger _value, String _to) { + require(_value != null && _value.compareTo(BigInteger.ZERO) > 0, "Invalid amount"); + require(!name.equals(_coinName) && isRegistered(_coinName), "Not supported Token"); + + Address owner = Context.getCaller(); + Balance balance = getBalance(_coinName, owner); + require(balance.getUsable().compareTo(_value) >= 0, "Overdrawn: Insufficient Balance"); + sendRequest(owner, BTPAddress.valueOf(_to), List.of(_coinName), List.of(_value)); + } + + @EventLog(indexed = 1) + public void TransferStart(Address _from, String _to, BigInteger _sn, byte[] _assetDetails) { + } + + @EventLog(indexed = 1) + public void TransferEnd(Address _from, BigInteger _sn, BigInteger _code, byte[] _msg) { + } + + @EventLog(indexed = 1) + public void UnknownResponse(String _from, BigInteger _sn) { + } + + @External(readonly = true) + public TransferTransaction getTransaction(BigInteger _sn) { + return transactions.get(_sn); + } + + private void sendRequest(Address owner, BTPAddress to, List coinNames, List amounts) { + logger.println("sendRequest", "begin"); + NCSProperties properties = getProperties(); + + BigInteger feeRatio = properties.getFeeRatio(); + if (owner.equals(Context.getAddress())) { + feeRatio = BigInteger.ZERO; + } + int len = coinNames.size(); + AssetTransferDetail[] assetTransferDetails = new AssetTransferDetail[len]; + Asset[] assets = new Asset[len]; + for (int i = 0; i < len; i++) { + String coinName = coinNames.get(i); + BigInteger amount = amounts.get(i); + AssetTransferDetail assetTransferDetail = newAssetTransferDetail(coinName, amount, feeRatio); + lock(coinName, owner, amount); + assetTransferDetails[i] = assetTransferDetail; + assets[i] = new Asset(assetTransferDetail); + } + + TransferRequest request = new TransferRequest(); + request.setFrom(owner.toString()); + request.setTo(to.account()); + request.setAssets(assets); + + TransferTransaction transaction = new TransferTransaction(); + transaction.setFrom(owner.toString()); + transaction.setTo(to.toString()); + transaction.setAssets(assetTransferDetails); + + BigInteger sn = properties.getSn().add(BigInteger.ONE); + properties.setSn(sn); + setProperties(properties); + transactions.set(sn, transaction); + + sendMessage(to.net(), NCSMessage.REQUEST_COIN_TRANSFER, sn, request.toBytes()); + TransferStart(owner, to.toString(), sn, encode(assetTransferDetails)); + logger.println("sendRequest", "end"); + } + + static byte[] encode(AssetTransferDetail[] assetTransferDetails) { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + writer.beginList(assetTransferDetails.length); + for (AssetTransferDetail v : assetTransferDetails) { + //writer.write(v); + AssetTransferDetail.writeObject(writer, v); + } + writer.end(); + return writer.toByteArray(); + } + + private void sendMessage(String to, int serviceType, BigInteger sn, byte[] data) { + logger.println("sendMessage", "begin"); + NCSMessage message = new NCSMessage(); + message.setServiceType(serviceType); + message.setData(data); + Context.call(bmc, "sendMessage", to, SERVICE, sn, message.toBytes()); + logger.println("sendMessage", "end"); + } + + private void responseSuccess(String to, BigInteger sn) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_OK); + response.setMessage(TransferResponse.OK_MSG); + sendMessage(to, NCSMessage.REPONSE_HANDLE_SERVICE, sn, response.toBytes()); + } + + private void responseError(String to, BigInteger sn, String message) { + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(message); + sendMessage(to, NCSMessage.REPONSE_HANDLE_SERVICE, sn, response.toBytes()); + } + + @External + public void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg) { + require(Context.getCaller().equals(bmc), "Only BMC"); + + NCSMessage message = NCSMessage.fromBytes(_msg); + int serviceType = message.getServiceType(); + if (serviceType == NCSMessage.REQUEST_COIN_TRANSFER) { + TransferRequest request = TransferRequest.fromBytes(message.getData()); + handleRequest(request, _from, _sn); + } else if (serviceType == NCSMessage.REPONSE_HANDLE_SERVICE) { + TransferResponse response = TransferResponse.fromBytes(message.getData()); + handleResponse(_sn, response); + } else if (serviceType == NCSMessage.UNKNOWN_TYPE) { + // If receiving a RES_UNKNOWN_TYPE, ignore this message + // or re-send another correct message + UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage(TransferResponse.ERR_MSG_UNKNOWN_TYPE); + sendMessage(_from, NCSMessage.UNKNOWN_TYPE, _sn, response.toBytes()); + } + } + + @External + public void handleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg) { + require(Context.getCaller().equals(bmc), "Only BMC"); + TransferResponse response = new TransferResponse(); + response.setCode(TransferResponse.RC_ERR); + response.setMessage("BTPError [code:" + _code + ",msg:" + _msg); + handleResponse(_sn, response); + } + + @External + public void handleFeeGathering(String _fa, String _svc) { + require(Context.getCaller().equals(bmc), "Only BMC"); + BTPAddress from = BTPAddress.valueOf(_fa); + Address owner = Context.getAddress(); + + List coinNames = new ArrayList<>(); + List feeAmounts = new ArrayList<>(); + for (String coinName : coinNames()) { + BigInteger feeAmount = clearFee(coinName); + if (feeAmount.compareTo(BigInteger.ZERO) > 0) { + coinNames.add(coinName); + feeAmounts.add(feeAmount); + } + } + + if (coinNames.size() > 0) { + if (from.net().equals(net)) { + Address fa = Address.fromString(from.account()); + int idx = coinNames.indexOf(name); + if (idx >= 0) { + coinNames.remove(idx); + BigInteger feeAmount = feeAmounts.remove(idx); + Context.transfer(fa, feeAmount); + } + _transferBatch(fa, ArrayUtil.toStringArray(coinNames), ArrayUtil.toBigIntegerArray(feeAmounts)); + } else { + sendRequest(owner, from, coinNames, feeAmounts); + } + } + } + + private void _transferBatch(Address to, String[] coinNames, BigInteger[] amounts) { + logger.println("transferBatch", to, StringUtil.toString(coinNames), StringUtil.toString(amounts)); + for (int i = 0; i < coinNames.length; i++) { + _transfer(irc2, to, amounts[i]); + } + } + + private Balance getBalance(String coinName, Address owner) { + Balance balance = balances.at(coinName).get(owner); + if (balance == null) { + balance = new Balance(BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO); + return balance; + } + Balance newBal = new Balance(balance.getUsable(), balance.getLocked(), balance.getRefundable()); + return newBal; + } + + private void setBalance(String coinName, Address owner, Balance balance) { + Balance newBalance = new Balance(balance.getUsable(), balance.getLocked(), balance.getRefundable()); + balances.at(coinName).set(owner, newBalance); + } + + private void lock(String coinName, Address owner, BigInteger value) { + logger.println("lock", "coinName:", coinName, "owner:", owner, "value:", value); + Balance balance = getBalance(coinName, owner); + balance.setUsable(balance.getUsable().subtract(value)); + balance.setLocked(balance.getLocked().add(value)); + setBalance(coinName, owner, balance); + } + + private void unlock(String coinName, Address owner, BigInteger value) { + logger.println("unlock", "coinName:", coinName, "owner:", owner, "value:", value); + Balance balance = getBalance(coinName, owner); + balance.setLocked(balance.getLocked().subtract(value)); + setBalance(coinName, owner, balance); + } + + private void refund(String coinName, Address owner, BigInteger value) { + logger.println("refund", "coinName:", coinName, "owner:", owner, "value:", value); + //unlock and add refundable + Balance balance = getBalance(coinName, owner); + balance.setLocked(balance.getLocked().subtract(value)); + if (!owner.equals(Context.getAddress())) { + balance.setRefundable(balance.getRefundable().add(value)); + } + setBalance(coinName, owner, balance); + } + + private void deposit(String coinName, Address owner, BigInteger value) { + logger.println("deposit", "coinName:", coinName, "owner:", owner, "value:", value); + Balance balance = getBalance(coinName, owner); + balance.setUsable(balance.getUsable().add(value)); + setBalance(coinName, owner, balance); + } + + private void addFee(String coinName, BigInteger amount) { + BigInteger fee = feeBalances.getOrDefault(coinName, BigInteger.ZERO); + feeBalances.set(coinName, fee.add(amount)); + } + + private BigInteger clearFee(String coinName) { + BigInteger fee = feeBalances.getOrDefault(coinName, BigInteger.ZERO); + if (fee.compareTo(BigInteger.ZERO) > 0) { + feeBalances.set(coinName, BigInteger.ZERO); + } + return fee; + } + + private void handleRequest(TransferRequest request, String from, BigInteger sn) { + logger.println("handleRequest", "begin", "sn:", sn); + Address to; + try { + to = Address.fromString(request.getTo()); + } catch (IllegalArgumentException | NullPointerException e) { + throw NCSException.unknown(e.getMessage()); + } + + BigInteger nativeCoinTransferAmount = null; + Asset[] assets = request.getAssets(); + for (Asset asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + if (amount == null || amount.compareTo(BigInteger.ZERO) < 1) { + throw NCSException.unknown("Amount must be positive value"); + } + + if (tokenName.equals(coinName)) { + _transfer(irc2, to, amount); + } else if (name.equals(coinName)) { + nativeCoinTransferAmount = amount; + } else { + throw NCSException.unknown("Invalid Token"); + } + } + + if (nativeCoinTransferAmount != null) { + try { + Context.transfer(to, nativeCoinTransferAmount); + } catch (Exception e) { + throw NCSException.unknown("fail to transfer err:" + e.getMessage()); + } + } + + logger.println("handleRequest", "responseSuccess"); + responseSuccess(from, sn); + logger.println("handleRequest", "end"); + } + + private void handleResponse(BigInteger sn, TransferResponse response) { + logger.println("handleResponse", "begin", "sn:", sn); + TransferTransaction transaction = transactions.get(sn); + // ignore when not exists pending request + if (transaction != null) { + BigInteger code = response.getCode(); + Address owner = Address.fromString(transaction.getFrom()); + AssetTransferDetail[] assets = transaction.getAssets(); + + logger.println("handleResponse", "code:", code); + if (TransferResponse.RC_OK.equals(code)) { + for (AssetTransferDetail asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + BigInteger fee = asset.getFee(); + BigInteger locked = amount.add(fee); + boolean isNativeCoin = name.equals(coinName); + if (isNativeCoin || tokenName.equals(coinName)) { + unlock(coinName, owner, locked); + addFee(coinName, fee); + if (!isNativeCoin) { + _burn(irc2, amount); + } + } else { + //This should not happen + throw NCSException.unknown("invalid transaction, invalid coinName"); + } + } + } else { + for (AssetTransferDetail asset : assets) { + String coinName = asset.getCoinName(); + BigInteger amount = asset.getAmount(); + BigInteger fee = asset.getFee(); + BigInteger locked = amount.add(fee); + boolean isNativeCoin = name.equals(coinName); + if (isNativeCoin || tokenName.equals(coinName)) { + refund(coinName, owner, locked); + } else { + //This should not happen + throw NCSException.unknown("invalid transaction, invalid coinName"); + } + } + } + + transactions.set(sn, null); + TransferEnd(owner, sn, code, response.getMessage() != null ? response.getMessage().getBytes() : null); + } + logger.println("handleResponse", "end"); + } + + @External + public void setFeeRatio(BigInteger _feeNumerator) { + requireOwnerAccess(); + require(_feeNumerator.compareTo(BigInteger.ONE) >= 0 && + _feeNumerator.compareTo(FEE_DENOMINATOR) < 0, + "The feeNumetator should be less than FEE_DEMONINATOR and greater than 1"); + NCSProperties properties = getProperties(); + properties.setFeeRatio(_feeNumerator); + setProperties(properties); + } + + @External(readonly = true) + public BigInteger feeRatio() { + NCSProperties properties = getProperties(); + return properties.getFeeRatio(); + } + + private AssetTransferDetail newAssetTransferDetail(String coinName, BigInteger amount, BigInteger feeRatio) { + logger.println("newAssetTransferDetail", "begin"); + BigInteger fee = amount.multiply(feeRatio).divide(FEE_DENOMINATOR); + if (feeRatio.compareTo(BigInteger.ZERO) > 0 && fee.compareTo(BigInteger.ZERO) == 0) { + fee = BigInteger.ONE; + } + BigInteger transferAmount = amount.subtract(fee); + logger.println("newAssetTransferDetail", "amount:", amount, "fee:", fee); + if (transferAmount.compareTo(BigInteger.ZERO) < 1) { + throw NCSException.unknown("not enough value"); + } + AssetTransferDetail asset = new AssetTransferDetail(); + asset.setCoinName(coinName); + asset.setAmount(transferAmount); + asset.setFee(fee); + logger.println("newAssetTransferDetail", "end"); + return asset; + } + + private void _transfer(Address token, Address to, BigInteger amount) { + logger.println("transfer", to, amount); + try { + transfer(token, to, amount, "transfer to Receiver".getBytes()); + } catch (UserRevertedException e) { + logger.println("transfer", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("transfer", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + private void _burn(Address token, BigInteger amount) { + logger.println("burn", ZERO_ADDRESS, amount); + try { + transfer(token, ZERO_ADDRESS, amount, "Burn Transfer to Zero Address".getBytes()); + } catch (UserRevertedException e) { + logger.println("burn", "code:", e.getCode(), "msg:", e.getMessage()); + throw NCSException.irc31Reverted("code:" + e.getCode() + "msg:" + e.getMessage()); + } catch (IllegalArgumentException | RevertedException e) { + logger.println("burn", "Exception:", e.toString()); + throw NCSException.irc31Failure("Exception:" + e); + } + } + + + /* Delegate OwnerManager */ + private void requireOwnerAccess() { + if (!ownerManager.isOwner(Context.getCaller())) { + throw NCSException.unauthorized("require owner access"); + } + } + + @External + public void addOwner(Address _addr) { + try { + ownerManager.addOwner(_addr); + } catch (IllegalStateException e) { + throw NCSException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw NCSException.unknown(e.getMessage()); + } + } + + @External + public void removeOwner(Address _addr) { + try { + ownerManager.removeOwner(_addr); + } catch (IllegalStateException e) { + throw NCSException.unauthorized(e.getMessage()); + } catch (IllegalArgumentException e) { + throw NCSException.unknown(e.getMessage()); + } + } + + @External(readonly = true) + public Address[] getOwners() { + return ownerManager.getOwners(); + } + + @External(readonly = true) + public boolean isOwner(Address _addr) { + return ownerManager.isOwner(_addr); + } + + public void transfer(Address tokenAddr, Address _to, BigInteger _value, byte[] _data) { + Context.call(tokenAddr, "transfer", _to, _value, _data); + } + +} \ No newline at end of file diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferAsset.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferAsset.java new file mode 100644 index 00000000..73c9da60 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferAsset.java @@ -0,0 +1,74 @@ +package foundation.icon.btp.nativecoinIRC2; + +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class TransferAsset { + private String from; + private String to; + private List assets; + private List test; + + public TransferAsset(String from, String to, List assets) { + this.from = from; + this.to = to; + this.assets = assets; + } + + + public static void writeObject(ObjectWriter w, TransferAsset v) { + w.beginList(3); + w.write(v.getFrom()); + w.write(v.getTo()); + w.beginList(v.getAssets().size()); + for (int i = 0; i < v.getAssets().size(); i++) { + Asset.writeObject(w, v.getAssets().get(i)); + //w.write( v.getAsset()); + } + w.end(); + w.end(); + } + + public static TransferAsset readObject(ObjectReader r) { + r.beginList(); + String _from = r.readString(); + String _to = r.readString(); + List assets = new ArrayList<>(); + r.beginList(); + while (r.hasNext()) { + Asset _asset = Asset.readObject(r); + assets.add(_asset); + } + r.end(); + r.end(); + TransferAsset result = new TransferAsset(_from, _to, assets); + return result; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public void setTo(String to) { + this.to = to; + } + + public List getAssets() { + return assets; + } + + public void setAssets(List assets) { + this.assets = assets; + } +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferRequest.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferRequest.java new file mode 100644 index 00000000..7e9564e4 --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/TransferRequest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.nativecoinIRC2; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; + +import java.util.List; + +public class TransferRequest { + private String from; + private String to; + private Asset[] assets; + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getTo() { + return to; + } + + public Asset[] getAssets() { + return assets; + } + + public void setAssets(Asset[] assets) { + this.assets = assets; + } + + public void setTo(String to) { + this.to = to; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("TransferRequest{"); + sb.append("from='").append(from).append('\''); + sb.append(", to='").append(to).append('\''); + sb.append(", assets=").append(StringUtil.toString(assets)); + sb.append('}'); + return sb.toString(); + } + + public static void writeObject(ObjectWriter writer, TransferRequest obj) { + obj.writeObject(writer); + } + + public static TransferRequest readObject(ObjectReader reader) { + TransferRequest obj = new TransferRequest(); + reader.beginList(); + obj.setFrom(reader.readNullable(String.class)); + obj.setTo(reader.readNullable(String.class)); + if (reader.beginNullableList()) { + Asset[] assets = null; + List assetsList = new ArrayList<>(); + while(reader.hasNext()) { +// assetsList.add(reader.readNullable(Asset.class)); + assetsList.add(Asset.readObject(reader)); + } + assets = new Asset[assetsList.size()]; + for(int i=0; i balances = Context.newDictDB("balances", BigInteger.class); + + public IRC2Basic(String _name, String _symbol, int _decimals, BigInteger _initialSupply) { + this.name = _name; + this.symbol = _symbol; + this.decimals = _decimals; + + // decimals must be larger than 0 and less than 21 + Context.require(this.decimals >= 0); + Context.require(this.decimals <= 21); + Context.require(_initialSupply.compareTo(BigInteger.ZERO) >= 0); + _mint(Context.getCaller(), _initialSupply.multiply(pow10(_decimals))); + } + + private static BigInteger pow10(int exponent) { + BigInteger result = BigInteger.ONE; + for (int i = 0; i < exponent; i++) { + result = result.multiply(BigInteger.TEN); + } + return result; + } + + @External(readonly=true) + public String name() { + return name; + } + + @External(readonly=true) + public String symbol() { + return symbol; + } + + @External(readonly=true) + public int decimals() { + return decimals; + } + + @External(readonly=true) + public BigInteger totalSupply() { + return totalSupply; + } + + @External(readonly=true) + public BigInteger balanceOf(Address _owner) { + return safeGetBalance(_owner); + } + + @External + public void transfer(Address _to, BigInteger _value, @Optional byte[] _data) { + Address _from = Context.getCaller(); + + // check some basic requirements + Context.require(_value.compareTo(BigInteger.ZERO) >= 0); + Context.require(safeGetBalance(_from).compareTo(_value) >= 0); + + // adjust the balances + safeSetBalance(_from, safeGetBalance(_from).subtract(_value)); + safeSetBalance(_to, safeGetBalance(_to).add(_value)); + + // if the recipient is SCORE, call 'tokenFallback' to handle further operation + byte[] dataBytes = (_data == null) ? new byte[0] : _data; + if (_to.isContract()) { + Context.call(_to, "tokenFallback", _from, _value, dataBytes); + } + + // emit Transfer event + Transfer(_from, _to, _value, dataBytes); + } + + /** + * Creates `amount` tokens and assigns them to `owner`, increasing the total supply. + */ + protected void _mint(Address owner, BigInteger amount) { + Context.require(!ZERO_ADDRESS.equals(owner)); + Context.require(amount.compareTo(BigInteger.ZERO) >= 0); + + this.totalSupply = this.totalSupply.add(amount); + safeSetBalance(owner, safeGetBalance(owner).add(amount)); + Transfer(ZERO_ADDRESS, owner, amount, "mint".getBytes()); + } + + /** + * Destroys `amount` tokens from `owner`, reducing the total supply. + */ + protected void _burn(Address owner, BigInteger amount) { + Context.require(!ZERO_ADDRESS.equals(owner)); + Context.require(amount.compareTo(BigInteger.ZERO) >= 0); + Context.require(safeGetBalance(owner).compareTo(amount) >= 0); + + safeSetBalance(owner, safeGetBalance(owner).subtract(amount)); + this.totalSupply = this.totalSupply.subtract(amount); + Transfer(owner, ZERO_ADDRESS, amount, "burn".getBytes()); + } + + private BigInteger safeGetBalance(Address owner) { + return balances.getOrDefault(owner, BigInteger.ZERO); + } + + private void safeSetBalance(Address owner, BigInteger amount) { + balances.set(owner, amount); + } + + @EventLog(indexed=3) + public void Transfer(Address _from, Address _to, BigInteger _value, byte[] _data) {} +} diff --git a/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/irc2/IRC2ScoreInterface.java b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/irc2/IRC2ScoreInterface.java new file mode 100644 index 00000000..0531089d --- /dev/null +++ b/javascore/nativecoinIRC2/src/main/java/foundation/icon/btp/nativecoinIRC2/irc2/IRC2ScoreInterface.java @@ -0,0 +1,52 @@ +package foundation.icon.btp.nativecoinIRC2.irc2; + +import score.Address; +import score.Context; + +import java.math.BigInteger; + +public class IRC2ScoreInterface { + protected final Address address; + + public IRC2ScoreInterface(Address address) { + this.address = address; + } + + public Address _address() { + return this.address; + } + + public Address _check() { + return this.address; + } + + public BigInteger _balanceOf(Address _owner) { + return (BigInteger)Context.call( this.address, "balanceOf", _owner); + //return BigInteger.valueOf(1); + } + + public void transfer(Address _to, BigInteger _value, byte[] _data) { + Context.call(this.address, "transfer", _to, _value, _data); + } + + public void _Transfer(Address _from, Address _to, BigInteger _value, byte[] _data) { + throw new RuntimeException("not supported EventLog method"); + } + + public String _name() { + return (String)Context.call(this.address, "name"); + } + + public String _symbol() { + return (String)Context.call(this.address, "symbol"); + } + + public int _decimals() { + return (Integer)Context.call(this.address, "decimals"); + } + + public BigInteger _totalSupply() { + return Context.call(BigInteger.class, this.address, "totalSupply"); + } + +} diff --git a/javascore/nativecoinIRC2/src/test/java/foundation/icon/btp/nativecoinIRC2/IRC2NativeServiceHandler.java b/javascore/nativecoinIRC2/src/test/java/foundation/icon/btp/nativecoinIRC2/IRC2NativeServiceHandler.java new file mode 100644 index 00000000..628f6004 --- /dev/null +++ b/javascore/nativecoinIRC2/src/test/java/foundation/icon/btp/nativecoinIRC2/IRC2NativeServiceHandler.java @@ -0,0 +1,129 @@ +package foundation.icon.btp.nativecoinIRC2; + + +import com.iconloop.testsvc.Account; +import com.iconloop.testsvc.Score; +import com.iconloop.testsvc.ServiceManager; +import com.iconloop.testsvc.TestBase; +import foundation.icon.btp.nativecoinIRC2.irc2.IRC2Basic; +import org.junit.jupiter.api.*; +import score.ByteArrayObjectWriter; +import score.Context; +import scorex.util.ArrayList; + +import java.math.BigInteger; +import java.util.List; + +import static java.math.BigInteger.TEN; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class IRC2NativeServiceHandler extends TestBase { + final static String RLPn = "RLPn"; + final static String _svc = "NativeCoinIRC2BSH"; + final static String _net = "icon"; + final static String tokenName = "MOVR"; + final static String symbol = "MOVR"; + final static int decimals = 18; + final static BigInteger transferAmount = new BigInteger("10000000000000000000"); + private static final BigInteger initialSupply = BigInteger.valueOf(2000); + private static final BigInteger totalSupply = initialSupply.multiply(TEN.pow(decimals)); + private static final int REQUEST_TOKEN_TRANSFER = 0; + private static final int RESPONSE_HANDLE_SERVICE = 2; + private static final ServiceManager sm = getServiceManager(); + private static Account[] owners; + private static Score bsh; + private static Score token; + private static Score bmc; + + @BeforeAll + public static void setup() throws Exception { + // setup accounts and deploy + owners = new Account[5]; + for (int i = 0; i < owners.length; i++) { + owners[i] = sm.createAccount(100); + } + + bmc = sm.deploy(owners[0], BMCMock.class, "0x03.icon"); + token = sm.deploy(owners[0], IRC2Basic.class, tokenName, symbol, decimals, initialSupply); + bsh = sm.deploy(owners[0], NativeCoinService.class, bmc.getAddress(), token.getAddress(), "ICX", "MOVR"); + + BigInteger balance = (BigInteger) token.call("balanceOf", owners[0].getAddress()); + assertEquals(totalSupply, balance); + //fund user1 with tokens + token.invoke(owners[0], "transfer", owners[1].getAddress(), transferAmount.multiply(BigInteger.valueOf(100)), new byte[0]); + } + + + /** + * Secnario#: Transfer IRC2 tokens from Token contract to BSH via fallback - success + */ + @Order(1) + @Test + public void scenario1() { + String _to, _from; + _from = _to = "btp://0x97.bsc/0xa36a32c114ee13090e35cb086459a690f5c1f8e8"; + + bmc.invoke(owners[0], "addService", _svc, bsh.getAddress()); + //Add some enough token balance for BSH + Balance balanceBefore = ((Balance) bsh.call("balanceOf", owners[0].getAddress(), tokenName)); + BigInteger bshTokenBalanceBefore = (BigInteger) token.call("balanceOf", bsh.getAddress()); + //TransferAmount multiplied by 10 to reclaim unused coins later + token.invoke(owners[0], "transfer", bsh.getAddress(), transferAmount.multiply(BigInteger.valueOf(10)), new byte[0]); + //Fund BSH with more tokens by another user (owners[1]) for future funds for reclaim & credit + token.invoke(owners[1], "transfer", bsh.getAddress(), transferAmount.multiply(BigInteger.valueOf(100)), new byte[0]); + BigInteger bshTokenBalanceAfter = (BigInteger) token.call("balanceOf", bsh.getAddress()); + + //Initiate a transfer and check for locked balance + bsh.invoke(owners[0], "transfer", tokenName, transferAmount, _to); + Balance balanceAfterTransfer = ((Balance) bsh.call("balanceOf", owners[0].getAddress(), tokenName)); + assertEquals(balanceBefore.getLocked().add(transferAmount), balanceAfterTransfer.getLocked()); + + //BSH receives HandleBTPMessgae for token transfer request + BigInteger userBalanceBefore = (BigInteger) token.call("balanceOf", owners[1].getAddress()); + bmc.invoke(owners[0], "handleBTPMessage", _from, _svc, BigInteger.ZERO, handleBTPRequestBtpMsg(_from, owners[1].getAddress().toString())); + BigInteger userBalanceAfter = (BigInteger) token.call("balanceOf", owners[1].getAddress()); + assertEquals(userBalanceBefore.add(transferAmount), userBalanceAfter); + + //Send Success response after successfull transfer + Balance balanceBeforeSuccess = (Balance) bsh.call("balanceOf", owners[0].getAddress(), tokenName); + bmc.invoke(owners[0], "handleBTPMessage", _from, _svc, BigInteger.ONE, handleBTPResponseBtpMsg(0, "Transfer Success")); + Balance balanceAfterSuccess = (Balance) bsh.call("balanceOf", owners[0].getAddress(), tokenName); + assertEquals(balanceAfterSuccess.getLocked().add(transferAmount), balanceBeforeSuccess.getLocked()); + + //Reclaim unused available balance back to user + BigInteger bshTokenBalanceNow = (BigInteger) token.call("balanceOf", bsh.getAddress()); + bsh.invoke(owners[0], "reclaim", tokenName, balanceAfterSuccess.getUsable()); + Balance balanceAfterReclaim = (Balance) bsh.call("balanceOf", owners[0].getAddress(), tokenName); + assertEquals(balanceAfterReclaim.getUsable(), BigInteger.ZERO); + assertEquals(balanceAfterReclaim.getLocked(), BigInteger.ZERO); + } + + public byte[] handleBTPRequestBtpMsg(String from, String to) { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter(RLPn); + writer.beginList(2); + writer.write(REQUEST_TOKEN_TRANSFER);//ActionType + List assets = new ArrayList(); + assets.add(new Asset(tokenName, transferAmount)); + ByteArrayObjectWriter writerTa = Context.newByteArrayObjectWriter(RLPn); + TransferAsset _ta = new TransferAsset(from, to, assets); + TransferAsset.writeObject(writerTa, _ta); + writer.write(writerTa.toByteArray()); + writer.end(); + return writer.toByteArray(); + } + + public byte[] handleBTPResponseBtpMsg(int code, String msg) { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter(RLPn); + writer.beginList(2); + writer.write(RESPONSE_HANDLE_SERVICE);//ActionType + ByteArrayObjectWriter writerRespMsg = Context.newByteArrayObjectWriter(RLPn); + writerRespMsg.beginList(2); + writerRespMsg.write(code);//Code + writerRespMsg.write(msg);//Msg + writerRespMsg.end(); + writer.write(writerRespMsg.toByteArray()); + writer.end(); + return writer.toByteArray(); + } +} diff --git a/javascore/score-util/build.gradle b/javascore/score-util/build.gradle new file mode 100644 index 00000000..6f79e0ba --- /dev/null +++ b/javascore/score-util/build.gradle @@ -0,0 +1,15 @@ +apply plugin: 'java-library' + +dependencies { + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + + testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.7.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + testRuntimeOnly("foundation.icon:javaee-api:$javaeeVersion") +} + +test { + useJUnitPlatform() +} diff --git a/javascore/score-util/src/main/java/foundation/icon/score/util/ArrayUtil.java b/javascore/score-util/src/main/java/foundation/icon/score/util/ArrayUtil.java new file mode 100644 index 00000000..383754d5 --- /dev/null +++ b/javascore/score-util/src/main/java/foundation/icon/score/util/ArrayUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.util; + +import score.Address; + +import java.math.BigInteger; +import java.util.List; + +public class ArrayUtil { + + public static String[] toStringArray(List list) { + String[] arr = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + arr[i] = list.get(i); + } + return arr; + } + + public static BigInteger[] toBigIntegerArray(List list) { + BigInteger[] arr = new BigInteger[list.size()]; + for (int i = 0; i < list.size(); i++) { + arr[i] = list.get(i); + } + return arr; + } + + public static Address[] toAddressArray(List
list) { + Address[] arr = new Address[list.size()]; + for (int i = 0; i < list.size(); i++) { + arr[i] = list.get(i); + } + return arr; + } + + public static int matchCount(byte[] src, byte[] dst) { + int srcLen = src.length; + int dstLen = dst.length; + if (dstLen < srcLen) { + srcLen = dstLen; + } + for (int i = 0; i < srcLen; i++) { + if (src[i] != dst[i]) { + return i; + } + } + return srcLen; + } + + public static > void sort(T[] a) { + int len = a.length; + for (int i = 0; i < len; i++) { + T v = a[i]; + for (int j = i+1; j < len; j++) { + if (v.compareTo(a[j]) > 0) { + T t = v; + v = a[j]; + a[j] = t; + } + } + a[i] = v; + } + } + +} diff --git a/javascore/score-util/src/main/java/foundation/icon/score/util/BigIntegerUtil.java b/javascore/score-util/src/main/java/foundation/icon/score/util/BigIntegerUtil.java new file mode 100644 index 00000000..68c56439 --- /dev/null +++ b/javascore/score-util/src/main/java/foundation/icon/score/util/BigIntegerUtil.java @@ -0,0 +1,51 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.util; + +import score.Context; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.MathContext; + +public class BigIntegerUtil { + /** + * calculate multiply + * + * @param x the first value + * @param y the second value + * @return BigInteger multiply + */ + public static BigInteger multiply(BigInteger x, double y) { + double ret = StrictMath.floor(x.doubleValue() * y); + return new BigDecimal(ret).toBigInteger(); + } + + /** + * floor(x/y*pow(10^s))/pow(10^s) + * + * @param x the first value + * @param y the second value + * @param s scale factor + * @return double floor(x/y*pow(10^s))/pow(10^s) + */ + public static double floorDivide(BigInteger x, BigInteger y, int s) { + double scale = StrictMath.pow(10, s); + return StrictMath.floor(x.doubleValue() / y.doubleValue() * scale) / scale; + } + +} diff --git a/javascore/score-util/src/main/java/foundation/icon/score/util/ListUtil.java b/javascore/score-util/src/main/java/foundation/icon/score/util/ListUtil.java new file mode 100644 index 00000000..395ed239 --- /dev/null +++ b/javascore/score-util/src/main/java/foundation/icon/score/util/ListUtil.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.util; + +import java.util.List; + +public class ListUtil { + + @SuppressWarnings("rawtypes") + public static int matchCount(List src, List dst) { + int srcLen = src.size(); + int dstLen = dst.size(); + if (dstLen < srcLen) { + srcLen = dstLen; + } + for (int i = 0; i < srcLen; i++) { + if (!(src.get(i).equals(dst.get(i)))) { + return i; + } + } + return srcLen; + } + +} diff --git a/javascore/score-util/src/main/java/foundation/icon/score/util/Logger.java b/javascore/score-util/src/main/java/foundation/icon/score/util/Logger.java new file mode 100644 index 00000000..63b0c7f2 --- /dev/null +++ b/javascore/score-util/src/main/java/foundation/icon/score/util/Logger.java @@ -0,0 +1,115 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.util; + +import score.Context; +import scorex.util.HashMap; + +import java.util.Map; + +public class Logger { + static String loggerId(Class clazz) { + return clazz.getName(); + } + static Map loggers = new HashMap<>(); + + /** + * Return static Logger(clazz.getName()) + * + * @param clazz class + * @return Logger + */ + public static Logger getLogger(Class clazz) { + String id = loggerId(clazz); + Logger logger = loggers.get(id); + if(logger == null) { + logger = new Logger(id); + loggers.put(id, logger); + } + return logger; + } + + public static final String DELIMITER = " "; + final String id; + + public Logger(String id) { + this.id = id; + } + + /** + * Print list of message + * + * @param msg list of message + */ + public void println(String ... msg) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(this.id).append("]"); + for(String s : msg) { + sb.append(DELIMITER).append(s); + } + Context.println(sb.toString()); + } + + /** + * Print list of object + * + * @param prefix prefix + * @param objs list of object + */ + public void println(String prefix, Object ... objs) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(this.id); + if (prefix != null) { + sb.append(":").append(prefix); + } + sb.append("]"); + for(Object obj : objs) { + sb.append(DELIMITER).append(StringUtil.toString(obj)); + } + Context.println(sb.toString()); + } + + /** + * Print key, value with ClassName + * + * @param prefix prefix + * @param key key + * @param value value + */ + public void printKeyValue(String prefix, Object key, Object value) { + StringBuilder sb = new StringBuilder(); + sb.append("[").append(this.id).append(":").append(prefix).append("]"); + sb.append(DELIMITER).append("key: ").append(toStringWithClassName(key)); + sb.append(DELIMITER).append("value: ").append(toStringWithClassName(value)); + Context.println(sb.toString()); + } + + public static String toStringWithClassName(Object obj) { + if (obj == null) { + return "null"; + } else { + if (obj instanceof String) { + return (String)obj; + } else { + StringBuilder sb = new StringBuilder(); + sb.append("Class<").append(obj.getClass().getName()).append(">"); + sb.append(StringUtil.toString(obj)); + return sb.toString(); + } + } + } +} diff --git a/javascore/score-util/src/main/java/foundation/icon/score/util/StringUtil.java b/javascore/score-util/src/main/java/foundation/icon/score/util/StringUtil.java new file mode 100644 index 00000000..defedd54 --- /dev/null +++ b/javascore/score-util/src/main/java/foundation/icon/score/util/StringUtil.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.util; + +import scorex.util.ArrayList; +import scorex.util.StringTokenizer; + +import java.util.List; + +public class StringUtil { + public static String toLowerCamelCase(String str) { + String camelCase = toCamelCase(str); + if (camelCase.length() > 0) { + StringBuilder builder = new StringBuilder(); + builder.append(camelCase.substring(0, 1).toLowerCase()); + if (camelCase.length() > 1) { + builder.append(camelCase.substring(1)); + } + } + return camelCase; + } + + public static String toCamelCase(String str) { + StringBuilder builder = new StringBuilder(); + List words = tokenize(str, '_'); + for (String word : words) { + if (word.length() > 0) { + String lower = word.toLowerCase(); + builder.append(lower.substring(0, 1).toUpperCase()); + if (word.length() > 1) { + builder.append(lower.substring(1)); + } + } + } + return builder.toString(); + } + + public static List tokenize(String str, char... delimiters) { + List list = new ArrayList<>(); + StringTokenizer st = new StringTokenizer(str, new String(delimiters)); + while (st.hasMoreTokens()) { + list.add(st.nextToken()); + } + return list; + } + + private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); + + public static String bytesToHex(byte[] bytes) { + if (bytes == null) { + return null; + } + char[] hexChars = new char[bytes.length * 2]; + for (int i = 0; i < bytes.length; i++) { + int v = bytes[i] & 0xFF; + hexChars[i * 2] = HEX_ARRAY[v >>> 4]; + hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars); + } + + public static byte[] hexToBytes(String hexString) { + if (hexString == null) { + return null; + } + if (hexString.length() % 2 > 0) { + throw new IllegalArgumentException("hex cannot has odd length"); + } + int l = hexString.length()/2; + int j = 0; + byte[] bytes = new byte[l]; + for (int i = 0; i < l; i++) { + bytes[i] = (byte)((Character.digit(hexString.charAt(j++), 16) << 4) | + Character.digit(hexString.charAt(j++), 16) & 0xFF); + } + return bytes; + } + + public static boolean isAlphaNumeric(String str) { + for(char c : str.toCharArray()) { + if(!Character.isLetterOrDigit(c)) { + return false; + } + } + return true; + } + + public static String toString(Object obj) { + if (obj == null) { + return "null"; + } else { + return obj.toString(); + } + } + + public static String toString(byte[] arr) { + if (arr == null) { + return "null"; + } else { + return bytesToHex(arr); + } + } + + public static String toString(byte[][] arr) { + if (arr == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder("["); + if (arr.length > 0) { + sb.append(toString(arr[0])); + } + for(int i=1;i < arr.length;i++) { + sb.append(",").append(toString(arr[i])); + } + return sb.append("]").toString(); + } + } + + public static String toString(Object[] arr) { + if (arr == null) { + return "null"; + } else { + StringBuilder sb = new StringBuilder("["); + if (arr.length > 0) { + sb.append(toString(arr[0])); + } + for(int i=1;i < arr.length;i++) { + sb.append(",").append(toString(arr[i])); + } + return sb.append("]").toString(); + } + } +} diff --git a/javascore/settings.gradle b/javascore/settings.gradle new file mode 100644 index 00000000..38efd492 --- /dev/null +++ b/javascore/settings.gradle @@ -0,0 +1,10 @@ +rootProject.name = 'javascore' +include ( + 'score-util', + 'lib', + 'test-lib', + 'bmc', + 'bmv:icon', + 'nativecoin', + 'nativecoinIRC2' +) diff --git a/javascore/test-lib/build.gradle b/javascore/test-lib/build.gradle new file mode 100644 index 00000000..c9796eb1 --- /dev/null +++ b/javascore/test-lib/build.gradle @@ -0,0 +1,125 @@ +version = '0.1.0' + +apply plugin: 'java-library' + +dependencies { + //for mock package + compileOnly("foundation.icon:javaee-api:$javaeeVersion") + implementation("foundation.icon:javaee-scorex:$scorexVersion") + implementation project(':score-util') + implementation project(':lib') + + //for test package + implementation("org.junit.jupiter:junit-jupiter-api:5.7.2") + implementation("org.junit.jupiter:junit-jupiter-params:5.7.2") + runtimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2") + + annotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion") + implementation("foundation.icon:javaee-score-client:$scoreClientVersion") + implementation("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + implementation("foundation.icon:icon-sdk:$iconsdkVersion") + implementation("com.github.javafaker:javafaker:1.0.2") + + testImplementation("foundation.icon:javaee-unittest:$javaeeUnittestVersion") + testAnnotationProcessor("foundation.icon:javaee-score-client:$scoreClientVersion") +} + +import foundation.icon.gradle.plugins.javaee.task.OptimizedJar + +task optimizedJarMockBMC(type: OptimizedJar) { + mainClassName = 'foundation.icon.btp.mock.MockBMCImpl' + archiveName("bmc-mock-" + archiveVersion.get() + "." + archiveExtension.get()); + from { sourceSets.main.output } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +task optimizedJarMockBMV(type: OptimizedJar) { + mainClassName = 'foundation.icon.btp.mock.MockBMVImpl' + archiveName("bmv-mock-" + archiveVersion.get() + "." + archiveExtension.get()); + from { sourceSets.main.output } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +task optimizedJarMockBSH(type: OptimizedJar) { + mainClassName = 'foundation.icon.btp.mock.MockBSHImpl' + archiveName("bsh-mock-" + archiveVersion.get() + "." + archiveExtension.get()); + from { sourceSets.main.output } + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } { exclude "score/*" } + enableDebug = debugJar +} + +deployJar { + endpoints { + gangnam { + uri = 'https://gicon.net.solidwallet.io/api/v3' + nid = 7 + } + local { + uri = scoreTest.url + nid = scoreTest.parseNid(scoreTest.nid) + } + } + keystore = scoreTest.default.keyStore + password = scoreTest.default.resolvedKeyPassword + parameters { + arg('_net', '0xf1c22d.icon') + } +} + +test { + useJUnitPlatform { + if (!integrationTest) { + excludeTags("integration") + } + } + + options { + testLogging.showStandardStreams = true + //for ScoreIntegrationTest.client + systemProperty 'url', scoreTest.url + systemProperty 'nid', scoreTest.nid + + //for ScoreIntegrationTest.tester + systemProperty 'tester.keyStore', scoreTest.tester.keyStore + systemProperty 'tester.keySecret', scoreTest.tester.keySecret + + //for MockBMCTest.mockBMCClient + systemProperty 'bmc-mock.url', scoreTest.url + systemProperty 'bmc-mock.nid', scoreTest.nid + systemProperty 'bmc-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bmc-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bmc-mock.address', "cx4c513575af38ed8788b7b73729106e74fcf189d2" +// systemProperty 'bmc-mock.isUpdate', "true" + dependsOn "optimizedJarMockBMC" + systemProperty 'bmc-mock.scoreFilePath', tasks.getByPath("optimizedJarMockBMC").outputJarName + systemProperty 'bmc-mock.params._net', scoreTest.nid+'.icon' + + //for MockBMVTest.mockBMVClient + systemProperty 'bmv-mock.url', scoreTest.url + systemProperty 'bmv-mock.nid', scoreTest.nid + systemProperty 'bmv-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bmv-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bmv-mock.address', "cx0f549056aeebed9c06833bebfe8b17113958532f" +// systemProperty 'bmv-mock.isUpdate', "true" + dependsOn "optimizedJarMockBMV" + systemProperty 'bmv-mock.scoreFilePath', tasks.getByPath("optimizedJarMockBMV").outputJarName + + //for MockBSHTest.mockBSHClient + systemProperty 'bsh-mock.url', scoreTest.url + systemProperty 'bsh-mock.nid', scoreTest.nid + systemProperty 'bsh-mock.keyStore', scoreTest.default.keyStore + systemProperty 'bsh-mock.keyPassword', scoreTest.default.resolvedKeyPassword +// systemProperty 'bsh-mock.address', "cx47853622a461b34885c42d74e8354b03a3e88a42" +// systemProperty 'bsh-mock.isUpdate', "true" + dependsOn "optimizedJarMockBSH" + systemProperty 'bsh-mock.scoreFilePath', tasks.getByPath("optimizedJarMockBSH").outputJarName + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMC.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMC.java new file mode 100644 index 00000000..9c4fe8f8 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMC.java @@ -0,0 +1,63 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.score.client.ScoreClient; +import score.Address; +import score.annotation.EventLog; +import score.annotation.External; + +import java.math.BigInteger; + +/** + * for BMV, BSH + */ +@ScoreClient +public interface MockBMC { + @External + void setNet(String _net); + + @External(readonly = true) + String getNet(); + + //for BMV + @External + void intercallHandleRelayMessage(Address _addr, String _prev, BigInteger _seq, String _msg); + + @EventLog + void HandleRelayMessage(byte[] _ret); + + //for BSH + @External(readonly = true) + String getBtpAddress(); + + @External + void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg); + + @EventLog + void SendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg); + + @External + void intercallHandleBTPMessage(Address _addr, String _from, String _svc, BigInteger _sn, byte[] _msg); + + @External + void intercallHandleBTPError(Address _addr, String _src, String _svc, BigInteger _sn, long _code, String _msg); + + @External + void intercallHandleFeeGathering(Address _addr, String _fa, String _svc); + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMCImpl.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMCImpl.java new file mode 100644 index 00000000..08484825 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMCImpl.java @@ -0,0 +1,94 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BMVScoreInterface; +import foundation.icon.btp.lib.BSHScoreInterface; +import foundation.icon.btp.lib.BTPAddress; +import score.Address; +import score.Context; +import score.VarDB; +import score.annotation.EventLog; +import score.annotation.External; +import score.annotation.Optional; + +import java.math.BigInteger; + +public class MockBMCImpl implements MockBMC { + + private VarDB net = Context.newVarDB("net", String.class); + private String btpAddress; + + public MockBMCImpl(@Optional String _net) { + if (_net != null) { + setNet(_net); + } + } + + @External + public void setNet(String _net) { + net.set(_net); + btpAddress = new BTPAddress( + BTPAddress.PROTOCOL_BTP, _net, Context.getAddress().toString()).toString(); + } + + @External(readonly = true) + public String getNet() { + return net.get(); + } + + @External + public void intercallHandleRelayMessage(Address _addr, String _prev, BigInteger _seq, String _msg) { + BMVScoreInterface bmv = new BMVScoreInterface(_addr); + byte[][] ret = bmv.handleRelayMessage(btpAddress, _prev, _seq, _msg); + HandleRelayMessage(MockRelayMessage.toBytes(ret)); + } + + @EventLog + public void HandleRelayMessage(byte[] _ret) { } + + @External + public void sendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { + SendMessage(_to, _svc, _sn, _msg); + } + + @EventLog + public void SendMessage(String _to, String _svc, BigInteger _sn, byte[] _msg) { } + + @External(readonly = true) + public String getBtpAddress() { + return btpAddress; + } + + @External + public void intercallHandleBTPMessage(Address _addr, String _from, String _svc, BigInteger _sn, byte[] _msg) { + BSHScoreInterface bsh = new BSHScoreInterface(_addr); + bsh.handleBTPMessage(_from, _svc, _sn, _msg); + } + + @External + public void intercallHandleBTPError(Address _addr, String _src, String _svc, BigInteger _sn, long _code, String _msg) { + BSHScoreInterface bsh = new BSHScoreInterface(_addr); + bsh.handleBTPError(_src, _svc, _sn, _code, _msg); + } + + @External + public void intercallHandleFeeGathering(Address _addr, String _fa, String _svc) { + BSHScoreInterface bsh = new BSHScoreInterface(_addr); + bsh.handleFeeGathering(_fa, _svc); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMV.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMV.java new file mode 100644 index 00000000..53b96f91 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMV.java @@ -0,0 +1,49 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BMV; +import foundation.icon.score.client.ScoreClient; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.annotation.EventLog; +import score.annotation.External; +import scorex.util.ArrayList; + +import java.math.BigInteger; +import java.util.List; + +/** + * for BMC + */ +@ScoreClient +public interface MockBMV extends BMV { + + @External + void setHeight(long _height); + + @External + void setOffset(long _offset); + + @External + void setLast_height(long _last_height); + + @EventLog + void HandleRelayMessage(byte[] _ret); + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVImpl.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVImpl.java new file mode 100644 index 00000000..a0110dd7 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BMVStatus; +import foundation.icon.btp.lib.BTPException; +import foundation.icon.score.util.Logger; +import score.Context; +import score.VarDB; +import score.annotation.EventLog; +import score.annotation.External; + +import java.math.BigInteger; + +public class MockBMVImpl implements MockBMV { + private static final Logger logger = Logger.getLogger(MockBMVImpl.class); + + private final VarDB properties = Context.newVarDB("properties", MockBMVProperties.class); + + public MockBMVImpl() { + MockBMVProperties properties = getProperties(); + if (properties == null) { + setProperties(MockBMVProperties.DEFAULT); + } + } + + public MockBMVProperties getProperties() { + return properties.get(); + } + + public void setProperties(MockBMVProperties properties) { + this.properties.set(properties); + } + + @External + public void setHeight(long _height) { + MockBMVProperties properties = getProperties(); + properties.setHeight(_height); + setProperties(properties); + } + + @External + public void setOffset(long _offset) { + MockBMVProperties properties = getProperties(); + properties.setOffset(_offset); + setProperties(properties); + } + + @External + public void setLast_height(long _last_height) { + MockBMVProperties properties = getProperties(); + properties.setLast_height(_last_height); + setProperties(properties); + } + + @EventLog + public void HandleRelayMessage(byte[] _ret) {} + + @External + public byte[][] handleRelayMessage(String _bmc, String _prev, BigInteger _seq, String _msg) { + MockRelayMessage relayMessage = MockRelayMessage.fromBase64String(_msg); + Integer revertCode = relayMessage.getRevertCode(); + if (revertCode != null) { + String revertMsg = relayMessage.getRevertMessage(); + throw new BTPException.BMV(revertCode, + revertMsg == null ? revertCode.toString() : revertMsg); + } else { + MockBMVProperties properties = getProperties(); + boolean isUpdateProperties = false; + Long offset = relayMessage.getOffset(); + if (offset != null && offset != properties.getOffset()) { + logger.println("handleRelayMessage", "offset", properties.getOffset(), offset); + properties.setOffset(offset); + isUpdateProperties = true; + } + Long height = relayMessage.getHeight(); + if (height != null && height != properties.getHeight()) { + logger.println("handleRelayMessage", "height", properties.getHeight(), height); + properties.setHeight(height); + isUpdateProperties = true; + } + Long lastHeight = relayMessage.getLastHeight(); + if (lastHeight != null && lastHeight != properties.getLast_height()) { + logger.println("handleRelayMessage", "lastHeight", properties.getLast_height(), lastHeight); + properties.setLast_height(lastHeight); + isUpdateProperties = true; + } + if (isUpdateProperties) { + setProperties(properties); + } + + byte[][] ret = relayMessage.getBtpMessages(); + logger.println("handleRelayMessage", "btpMessages", + ret == null ? "null" : Integer.toString(ret.length)); + ret = ret == null ? new byte[0][] : ret; + HandleRelayMessage(MockRelayMessage.toBytes(ret)); + return ret; + } + } + + @External(readonly = true) + public BMVStatus getStatus() { + return getProperties(); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVProperties.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVProperties.java new file mode 100644 index 00000000..5b37a919 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBMVProperties.java @@ -0,0 +1,96 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BMVStatus; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import score.annotation.Keep; + +public class MockBMVProperties extends BMVStatus { + public static final MockBMVProperties DEFAULT; + + static { + DEFAULT = new MockBMVProperties(); + DEFAULT.setHeight(Context.getBlockHeight()); + DEFAULT.setOffset(Context.getBlockHeight()); + DEFAULT.setLast_height(Context.getBlockHeight()); + } + + public MockBMVProperties() { + super(); + } + + public MockBMVProperties(BMVStatus obj) { + super(); + this.setHeight(obj.getHeight()); + this.setOffset(obj.getOffset()); + this.setLast_height(obj.getLast_height()); + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MockBMVProperties{"); + sb.append('}').append(super.toString()); + return sb.toString(); + } + + @Keep + public static void writeObject(ObjectWriter writer, BMVStatus obj) { + MockBMVProperties.writeObject(writer, obj instanceof MockBMVProperties ? (MockBMVProperties)obj : new MockBMVProperties(obj)); + } + + public static void writeObject(ObjectWriter writer, MockBMVProperties obj) { + obj.writeObject(writer); + } + + public static MockBMVProperties readObject(ObjectReader reader) { + MockBMVProperties obj = new MockBMVProperties(); + reader.beginList(); + obj.setHeight(reader.readLong()); + obj.setOffset(reader.readLong()); + obj.setLast_height(reader.readLong()); + reader.end(); + return obj; + } + + public void writeObject(ObjectWriter writer) { + writer.beginList(3); + writer.write(this.getHeight()); + writer.write(this.getOffset()); + writer.write(this.getLast_height()); + writer.end(); + } + + public static MockBMVProperties fromBytes(byte[] bytes) { + ObjectReader reader = Context.newByteArrayObjectReader("RLPn", bytes); + return MockBMVProperties.readObject(reader); + } + + public byte[] toBytes() { + ByteArrayObjectWriter writer = Context.newByteArrayObjectWriter("RLPn"); + MockBMVProperties.writeObject(writer, this); + return writer.toByteArray(); + } + + public static byte[] toBytes(BMVStatus obj) { + return obj instanceof MockBMVProperties ? ((MockBMVProperties)obj).toBytes() : new MockBMVProperties(obj).toBytes(); + } + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSH.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSH.java new file mode 100644 index 00000000..5dd551bd --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSH.java @@ -0,0 +1,47 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BSH; +import foundation.icon.score.client.ScoreClient; +import score.Address; +import score.annotation.EventLog; +import score.annotation.External; + +import java.math.BigInteger; + +/** + * for BMC + */ +@ScoreClient +public interface MockBSH extends BSH { + + String SERVICE = "mock"; + + @External + void intercallSendMessage(Address _bmc, String _to, String _svc, BigInteger _sn, byte[] _msg); + + @EventLog + void HandleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg); + + @EventLog + void HandleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg); + + @EventLog + void HandleFeeGathering(String _fa, String _svc); + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSHImpl.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSHImpl.java new file mode 100644 index 00000000..db3ab452 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockBSHImpl.java @@ -0,0 +1,62 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BMCScoreInterface; +import foundation.icon.score.util.Logger; +import score.Address; +import score.annotation.EventLog; +import score.annotation.External; + +import java.math.BigInteger; + +public class MockBSHImpl implements MockBSH { + private static final Logger logger = Logger.getLogger(MockBSHImpl.class); + + public MockBSHImpl() { + } + + @External + public void handleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg) { + HandleBTPMessage(_from, _svc, _sn, _msg); + } + + @External + public void handleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg) { + HandleBTPError(_src, _svc, _sn, _code, _msg); + } + + @External + public void handleFeeGathering(String _fa, String _svc) { + HandleFeeGathering(_fa, _svc); + } + + @External + public void intercallSendMessage(Address _bmc, String _to, String _svc, BigInteger _sn, byte[] _msg) { + BMCScoreInterface bmc = new BMCScoreInterface(_bmc); + bmc.sendMessage(_to, _svc, _sn, _msg); + } + + @EventLog + public void HandleBTPMessage(String _from, String _svc, BigInteger _sn, byte[] _msg) { } + + @EventLog + public void HandleBTPError(String _src, String _svc, BigInteger _sn, long _code, String _msg) { } + + @EventLog + public void HandleFeeGathering(String _fa, String _svc) { } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockRelayMessage.java b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockRelayMessage.java new file mode 100644 index 00000000..8b08352f --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/mock/MockRelayMessage.java @@ -0,0 +1,190 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.score.util.StringUtil; +import score.ByteArrayObjectWriter; +import score.Context; +import score.ObjectReader; +import score.ObjectWriter; +import scorex.util.ArrayList; +import scorex.util.Base64; + +import java.util.List; + +public class MockRelayMessage { + private Long offset; + private Long height; + private Long lastHeight; + private byte[][] btpMessages; + private Integer revertCode; + private String revertMessage; + + public Long getOffset() { + return offset; + } + + public void setOffset(Long offset) { + this.offset = offset; + } + + public Long getHeight() { + return height; + } + + public void setHeight(Long height) { + this.height = height; + } + + public Long getLastHeight() { + return lastHeight; + } + + public void setLastHeight(Long lastHeight) { + this.lastHeight = lastHeight; + } + + public byte[][] getBtpMessages() { + return btpMessages; + } + + public void setBtpMessages(byte[][] btpMessages) { + this.btpMessages = btpMessages; + } + + public Integer getRevertCode() { + return revertCode; + } + + public void setRevertCode(Integer revertCode) { + this.revertCode = revertCode; + } + + public String getRevertMessage() { + return revertMessage; + } + + public void setRevertMessage(String revertMessage) { + this.revertMessage = revertMessage; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("MockRelayMessage{"); + sb.append("offset=").append(offset); + sb.append(", height=").append(height); + sb.append(", lastHeight=").append(lastHeight); + sb.append(", btpMessages=").append(StringUtil.toString(btpMessages)); + sb.append(", revertCode=").append(revertCode); + sb.append(", revertMessage='").append(revertMessage).append('\''); + sb.append('}'); + return sb.toString(); + } + + public String toBase64String() { + return new String(Base64.getUrlEncoder().encode(toBytes())); + } + + public static MockRelayMessage fromBase64String(String str) { + return fromBytes(Base64.getUrlDecoder().decode(str.getBytes())); + } + + public static void writeObject(ObjectWriter writer, MockRelayMessage obj) { + obj.writeObject(writer); + } + + public static MockRelayMessage readObject(ObjectReader reader) { + MockRelayMessage obj = new MockRelayMessage(); + reader.beginList(); + obj.setOffset(reader.readNullable(Long.class)); + obj.setHeight(reader.readNullable(Long.class)); + obj.setLastHeight(reader.readNullable(Long.class)); + if (reader.beginNullableList()) { + byte[][] btpMessages = null; + List btpMessagesList = new ArrayList<>(); + while(reader.hasNext()) { + btpMessagesList.add(reader.readNullable(byte[].class)); + } + btpMessages = new byte[btpMessagesList.size()][]; + for(int i=0; i bytesArrayList = new ArrayList<>(); + while(reader.hasNext()) { + bytesArrayList.add(reader.readNullable(byte[].class)); + } + byte[][] bytesArray = new byte[bytesArrayList.size()][]; + for(int i=0; i BTPException assertBTPException(T expected, Executable executable) { + return BTPException.of(assertUserRevert(expected, executable, + (e) -> String.format("assertBTPException expected: %s, actual: %s", expected, BTPException.of(e)))); + } + + public static BTPException assertBTPException(T expected, Executable executable, Function messageSupplier) { + return BTPException.of(assertUserRevert(expected, executable, (e) -> messageSupplier.apply(BTPException.of(e)))); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/BTPIntegrationTest.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/BTPIntegrationTest.java new file mode 100644 index 00000000..ed42676b --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/BTPIntegrationTest.java @@ -0,0 +1,54 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.TestInfo; + +public interface BTPIntegrationTest extends ScoreIntegrationTest { + @Override + default void internalBeforeEach(TestInfo testInfo) {} + + @Override + default void internalAfterEach(TestInfo testInfo) {} + + @Override + default void clearIfExists(TestInfo testInfo) {} + + interface Faker extends ScoreIntegrationTest.Faker { + + static String btpService() { + return faker.name().firstName(); + } + + static String btpNetwork() { + return "0x" + faker.crypto().sha256().substring(0, 6) + ".icon"; + } + + static BTPAddress btpLink() { + return new BTPAddress(BTPAddress.PROTOCOL_BTP, btpNetwork(), + ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT).toString()); + } + + static BTPAddress btpEoa() { + return new BTPAddress(BTPAddress.PROTOCOL_BTP, btpNetwork(), + ScoreIntegrationTest.Faker.address(Address.Type.EOA).toString()); + } + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPErrorEventLog.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPErrorEventLog.java new file mode 100644 index 00000000..0306141d --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPErrorEventLog.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class HandleBTPErrorEventLog { + static final String SIGNATURE = "HandleBTPError(str,str,int,int,str)"; + private String src; + private String svc; + private BigInteger sn; + private long code; + private String msg; + + public HandleBTPErrorEventLog(TransactionResult.EventLog el) { + src = el.getData().get(0); + svc = el.getData().get(1); + sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(2)); + code = IconJsonModule.NumberDeserializer.LONG.convert(el.getData().get(3)); + msg = el.getData().get(4); + } + + public String getSrc() { + return src; + } + + public String getSvc() { + return svc; + } + + public BigInteger getSn() { + return sn; + } + + public long getCode() { + return code; + } + + public String getMsg() { + return msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HandleBTPErrorEventLog{"); + sb.append("src='").append(src).append('\''); + sb.append(", svc='").append(svc).append('\''); + sb.append(", sn=").append(sn); + sb.append(", code=").append(code); + sb.append(", msg='").append(msg).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + HandleBTPErrorEventLog.SIGNATURE, + address, + HandleBTPErrorEventLog::new, + filter); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPMessageEventLog.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPMessageEventLog.java new file mode 100644 index 00000000..c5292a40 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleBTPMessageEventLog.java @@ -0,0 +1,78 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Predicate; + +public class HandleBTPMessageEventLog { + static final String SIGNATURE = "HandleBTPMessage(str,str,int,bytes)"; + private String from; + private String svc; + private BigInteger sn; + private byte[] msg; + + public HandleBTPMessageEventLog(TransactionResult.EventLog el) { + from = el.getData().get(0); + svc = el.getData().get(1); + sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert(el.getData().get(2)); + msg = IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert(el.getData().get(3)); + } + + public String getFrom() { + return from; + } + + public String getSvc() { + return svc; + } + + public BigInteger getSn() { + return sn; + } + + public byte[] getMsg() { + return msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HandleBTPMessageEventLog{"); + sb.append("from='").append(from).append('\''); + sb.append(", svc='").append(svc).append('\''); + sb.append(", sn=").append(sn); + sb.append(", msg=").append(StringUtil.toString(msg)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + HandleBTPMessageEventLog.SIGNATURE, + address, + HandleBTPMessageEventLog::new, + filter); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleFeeGatheringEventLog.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleFeeGatheringEventLog.java new file mode 100644 index 00000000..31dffeb2 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleFeeGatheringEventLog.java @@ -0,0 +1,61 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.util.List; +import java.util.function.Predicate; + +public class HandleFeeGatheringEventLog { + static final String SIGNATURE = "HandleFeeGathering(str,str)"; + private String fa; + private String svc; + + public HandleFeeGatheringEventLog(TransactionResult.EventLog el) { + fa = el.getData().get(0); + svc = el.getData().get(1); + } + + public String getFa() { + return fa; + } + + public String getSvc() { + return svc; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HandleFeeGatheringEventLog{"); + sb.append("fa='").append(fa).append('\''); + sb.append(", svc='").append(svc).append('\''); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + HandleFeeGatheringEventLog.SIGNATURE, + address, + HandleFeeGatheringEventLog::new, + filter); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleRelayMessageEventLog.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleRelayMessageEventLog.java new file mode 100644 index 00000000..91132658 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/HandleRelayMessageEventLog.java @@ -0,0 +1,59 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.btp.mock.MockRelayMessage; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; + +import java.util.List; +import java.util.function.Predicate; + +public class HandleRelayMessageEventLog { + static final String SIGNATURE = "HandleRelayMessage(bytes)"; + private byte[][] ret; + + public HandleRelayMessageEventLog(TransactionResult.EventLog el) { + this.ret = MockRelayMessage.toBytesArray( + IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert( + el.getData().get(0))); + } + + public byte[][] getRet() { + return ret; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("HandleRelayMessageEventLog{"); + sb.append("ret=").append(StringUtil.toString(ret)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + HandleRelayMessageEventLog.SIGNATURE, + address, + HandleRelayMessageEventLog::new, + filter); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMCIntegrationTest.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMCIntegrationTest.java new file mode 100644 index 00000000..a1a16dda --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMCIntegrationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.btp.mock.MockBMC; +import foundation.icon.btp.mock.MockBMCScoreClient; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.util.function.Consumer; + +public interface MockBMCIntegrationTest { + + DefaultScoreClient mockBMCClient = DefaultScoreClient.of("bmc-mock.", System.getProperties()); + MockBMC mockBMC = new MockBMCScoreClient(mockBMCClient); + + static Consumer eventLogChecker( + ScoreIntegrationTest.EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + mockBMCClient._address(), supplier, consumer); + } + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMVIntegrationTest.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMVIntegrationTest.java new file mode 100644 index 00000000..5daa39fb --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBMVIntegrationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.btp.mock.MockBMV; +import foundation.icon.btp.mock.MockBMVScoreClient; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.util.function.Consumer; + +public interface MockBMVIntegrationTest { + + DefaultScoreClient mockBMVClient = DefaultScoreClient.of("bmv-mock.", System.getProperties()); + MockBMV mockBMV = new MockBMVScoreClient(mockBMVClient); + + static Consumer eventLogChecker( + ScoreIntegrationTest.EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + mockBMVClient._address(), supplier, consumer); + } + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBSHIntegrationTest.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBSHIntegrationTest.java new file mode 100644 index 00000000..d5195cc8 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/MockBSHIntegrationTest.java @@ -0,0 +1,38 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.btp.mock.MockBSH; +import foundation.icon.btp.mock.MockBSHScoreClient; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.test.ScoreIntegrationTest; + +import java.util.function.Consumer; + +public interface MockBSHIntegrationTest { + + DefaultScoreClient mockBSHClient = DefaultScoreClient.of("bsh-mock.", System.getProperties()); + MockBSH mockBSH = new MockBSHScoreClient(mockBSHClient); + + static Consumer eventLogChecker( + ScoreIntegrationTest.EventLogsSupplier supplier, Consumer consumer) { + return ScoreIntegrationTest.eventLogChecker( + mockBSHClient._address(), supplier, consumer); + } + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/btp/test/SendMessageEventLog.java b/javascore/test-lib/src/main/java/foundation/icon/btp/test/SendMessageEventLog.java new file mode 100644 index 00000000..cbe5a7a6 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/btp/test/SendMessageEventLog.java @@ -0,0 +1,85 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.test; + +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.test.ScoreIntegrationTest; +import foundation.icon.score.util.StringUtil; + +import java.math.BigInteger; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Predicate; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SendMessageEventLog { + static final String SIGNATURE = "SendMessage(str,str,int,bytes)"; + private String to; + private String svc; + private BigInteger sn; + private byte[] msg; + + public SendMessageEventLog(TransactionResult.EventLog el) { + this.to = el.getData().get(0); + this.svc = el.getData().get(1); + this.sn = IconJsonModule.NumberDeserializer.BIG_INTEGER.convert( + el.getData().get(2)); + this.msg = IconJsonModule.ByteArrayDeserializer.BYTE_ARRAY.convert( + el.getData().get(3)); + } + + public String getTo() { + return to; + } + + public String getSvc() { + return svc; + } + + public BigInteger getSn() { + return sn; + } + + public byte[] getMsg() { + return msg; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("SendMessageEventLog{"); + sb.append("to='").append(to).append('\''); + sb.append(", svc='").append(svc).append('\''); + sb.append(", sn=").append(sn); + sb.append(", msg=").append(StringUtil.toString(msg)); + sb.append('}'); + return sb.toString(); + } + + public static List eventLogs( + TransactionResult txr, Address address, Predicate filter) { + return ScoreIntegrationTest.eventLogs(txr, + SendMessageEventLog.SIGNATURE, + address, + SendMessageEventLog::new, + filter); + } + +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/score/test/AssertRevertedException.java b/javascore/test-lib/src/main/java/foundation/icon/score/test/AssertRevertedException.java new file mode 100644 index 00000000..8b6fd393 --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/score/test/AssertRevertedException.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.test; + +import foundation.icon.jsonrpc.JsonrpcClient; +import foundation.icon.score.client.RevertedException; +import org.junit.jupiter.api.function.Executable; +import score.UserRevertException; +import score.UserRevertedException; + +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.*; + +public class AssertRevertedException { + + public static RevertedException assertReverted(int expected, Executable executable) { + return assertReverted(new RevertedException(expected, ""), executable); + } + + public static RevertedException assertReverted(RevertedException expected, Executable executable) { + RevertedException e = assertThrows(RevertedException.class, executable); + assertEquals(expected.getCode(), e.getCode(), + String.format("assertReverted expected: %d, actual: %d", expected.getCode(), e.getCode())); + return e; + } + + public static UserRevertedException assertUserReverted(int expected, Executable executable) { + return assertUserReverted(expected, executable, null); + } + + public static UserRevertedException assertUserReverted(int expected, Executable executable, Function messageSupplier) { + return assertUserReverted(new UserRevertedException(expected), executable, messageSupplier); + } + + public static UserRevertedException assertUserReverted(T expected, Executable executable, Function messageSupplier) { + UserRevertedException e = assertThrows(UserRevertedException.class, executable); + assertEquals(expected.getCode(), e.getCode(), messageSupplier == null ? + () -> String.format("assertUserReverted expected: %d, actual: %d", expected.getCode(), e.getCode()) : + () -> messageSupplier.apply(e)); + return e; + } + + public static UserRevertedException assertUserRevert(T expected, Executable executable, Function messageSupplier) { + return assertUserReverted(expected.getCode(), executable, messageSupplier == null ? + (e) -> String.format("assertUserRevertException expected:%s, actual: %s", expected, e.toString()) : + messageSupplier); + } + + public static UserRevertedException assertUserRevertedFromJsonrpcError(int expected, Executable executable, Function messageSupplier) { + return assertUserRevertedFromJsonrpcError(new UserRevertedException(expected), executable, messageSupplier); + } + + public static UserRevertedException assertUserRevertedFromJsonrpcError(T expected, Executable executable, Function messageSupplier) { + JsonrpcClient.JsonrpcError rpcError = assertThrows(JsonrpcClient.JsonrpcError.class, executable); + //ErrorCodeScore(30000) + UserReverted(32) + assertTrue(rpcError.getCode() <= -30032 && rpcError.getCode() > -31000); + UserRevertedException e = new UserRevertedException(-1 * ((int)rpcError.getCode() + 30032), rpcError.getMessage()); + assertEquals(expected.getCode(), e.getCode(), messageSupplier == null ? + () -> String.format("assertUserRevertedFromJsonrpcError expected: %d, actual: %d", expected.getCode(), e.getCode()) : + () -> messageSupplier.apply(e)); + return e; + } + + public static UserRevertedException assertUserRevertFromJsonrpcError(T expected, Executable executable, Function messageSupplier) { + return assertUserRevertedFromJsonrpcError(expected.getCode(), executable, messageSupplier == null ? + (e) -> String.format("assertUserRevertFromJsonrpcError expected:%s, actual: %s", expected, e.toString()) : + messageSupplier); + } +} diff --git a/javascore/test-lib/src/main/java/foundation/icon/score/test/ScoreIntegrationTest.java b/javascore/test-lib/src/main/java/foundation/icon/score/test/ScoreIntegrationTest.java new file mode 100644 index 00000000..cca9dfeb --- /dev/null +++ b/javascore/test-lib/src/main/java/foundation/icon/score/test/ScoreIntegrationTest.java @@ -0,0 +1,216 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.score.test; + +import foundation.icon.icx.KeyWallet; +import foundation.icon.icx.Wallet; +import foundation.icon.jsonrpc.Address; +import foundation.icon.jsonrpc.IconJsonModule; +import foundation.icon.jsonrpc.model.TransactionResult; +import foundation.icon.score.client.DefaultScoreClient; +import foundation.icon.score.client.RevertedException; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.function.Executable; +import score.UserRevertedException; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Random; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Tag("integration") +@TestMethodOrder(value = MethodOrderer.OrderAnnotation.class) +public interface ScoreIntegrationTest { + + void internalBeforeEach(TestInfo testInfo); + + void internalAfterEach(TestInfo testInfo); + + void clearIfExists(TestInfo testInfo); + + @BeforeEach + default void beforeEach(TestInfo testInfo) { + System.out.println("=".repeat(100)); + System.out.println("beforeEach start " + testInfo.getTestMethod().orElseThrow()); + System.out.println("clearIfExists start"); + clearIfExists(testInfo); + System.out.println("clearIfExists end"); + internalBeforeEach(testInfo); + System.out.println("beforeEach end " + testInfo.getTestMethod().orElseThrow()); + System.out.println("-".repeat(100)); + } + + @AfterEach + default void afterEach(TestInfo testInfo) { + System.out.println("-".repeat(100)); + System.out.println("afterEach start " + testInfo.getTestMethod().orElseThrow()); + internalAfterEach(testInfo); + System.out.println("clearIfExists start"); + clearIfExists(testInfo); + System.out.println("clearIfExists end"); + System.out.println("afterEach end " + testInfo.getTestMethod().orElseThrow()); + System.out.println("=".repeat(100)); + } + + static int indexOf(T[] array, T value) { + return indexOf(array, value::equals); + } + + static int indexOf(T[] array, Predicate predicate) { + for (int i = 0; i < array.length; i++) { + if (predicate.test(array[i])) { + return i; + } + } + return -1; + } + + static boolean contains(Map map, String key, Object value) { + return contains(map, key, value::equals); + } + + static boolean contains(Map map, String key, Predicate predicate) { + return map.containsKey(key) && predicate.test(map.get(key)); + } + + static List eventLogs(TransactionResult txr, + String signature, + Address scoreAddress, + Function mapperFunc, + Predicate filter) { + Predicate predicate = + (el) -> el.getIndexed().get(0).equals(signature); + if (scoreAddress != null) { + predicate = predicate.and((el) -> el.getScoreAddress().equals(scoreAddress)); + } + Stream stream = txr.getEventLogs().stream() + .filter(predicate) + .map(mapperFunc); + if(filter != null) { + stream = stream.filter(filter); + } + return stream.collect(Collectors.toList()); + } + + DefaultScoreClient client = new DefaultScoreClient( + DefaultScoreClient.url(System.getProperties()), + DefaultScoreClient.nid(System.getProperties()), + null, + null + ); + + static void waitByNumOfBlock(long numOfBlock) { + waitByHeight(client._lastBlockHeight().add(BigInteger.valueOf(numOfBlock))); + } + + static void waitByHeight(long waitHeight) { + waitByHeight(BigInteger.valueOf(waitHeight)); + } + + static void waitByHeight(BigInteger waitHeight) { + BigInteger height = client._lastBlockHeight(); + while (height.compareTo(waitHeight) < 0) { + System.out.println("height: "+height+", waitHeight: "+waitHeight); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + height = client._lastBlockHeight(); + } + } + + static void balanceCheck(Address address, BigInteger value, Executable executable) { + BigInteger balance = client._balance(address); + try { + executable.execute(); + } catch (UserRevertedException | RevertedException e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + assertEquals(balance.add(value), client._balance(address)); + } + + @FunctionalInterface + interface EventLogsSupplier { + List apply(TransactionResult txr, Address address, Predicate filter); + } + + static Consumer eventLogChecker( + Address address, EventLogsSupplier supplier, Consumer consumer) { + return (txr) -> { + List eventLogs = supplier.apply(txr, address, null); + assertEquals(1, eventLogs.size()); + if (consumer != null) { + consumer.accept(eventLogs.get(0)); + } + }; + } + + static Consumer eventLogsChecker( + Address address, EventLogsSupplier supplier, Consumer> consumer) { + return (txr) -> { + List eventLogs = supplier.apply(txr, address, null); + if (consumer != null) { + consumer.accept(eventLogs); + } + }; + } + + Wallet tester = getOrGenerateWallet("tester.", System.getProperties()); + static Wallet getOrGenerateWallet(String prefix, Properties properties) { + Wallet wallet = DefaultScoreClient.wallet(prefix, properties); + return wallet == null ? generateWallet() : wallet; + } + + static KeyWallet generateWallet() { + try { + return KeyWallet.create(); + } catch (InvalidAlgorithmParameterException | NoSuchAlgorithmException | NoSuchProviderException e) { + throw new RuntimeException(e); + } + } + + interface Faker { + com.github.javafaker.Faker faker = new com.github.javafaker.Faker(); + Random random = new Random(); + + static Address address(Address.Type type) { + byte[] body = IconJsonModule.hexToBytes( + faker.crypto().sha256().substring(0, (Address.LENGTH - 1) * 2)); + return new Address(type, body); + } + + static byte[] bytes(int length) { + byte[] bytes = new byte[length]; + random.nextBytes(bytes); + return bytes; + } + } +} diff --git a/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMCTest.java b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMCTest.java new file mode 100644 index 00000000..30752494 --- /dev/null +++ b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMCTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.test.*; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MockBMCTest implements BTPIntegrationTest, MockBMCIntegrationTest { + static BTPAddress btpAddress = new BTPAddress( + BTPAddress.PROTOCOL_BTP, + BTPIntegrationTest.Faker.btpLink().net(), + mockBMCClient._address().toString()); + static BTPAddress prevBtpAddress = BTPIntegrationTest.Faker.btpLink(); + static String prev = prevBtpAddress.toString(); + static BigInteger seq = BigInteger.ONE; + static byte[] msg = ScoreIntegrationTest.Faker.bytes(32); + static String to = prevBtpAddress.net(); + static String svc = BTPIntegrationTest.Faker.btpService(); + static BigInteger sn = BigInteger.ONE; + static long errCode = 1; + static String errMsg = "err" + svc; + static String fa = ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT).toString(); + + @Test + void btpAddress() { + mockBMC.setNet(btpAddress.net()); + assertEquals(btpAddress.net(), mockBMC.getNet()); + assertEquals(btpAddress.toString(), mockBMC.getBtpAddress()); + } + + @Test + void intercallBMVhandleRelayMessageShouldMakeEventLog() { + byte[][] msgs = new byte[][]{msg}; + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(msgs); + + ((MockBMCScoreClient) mockBMC).intercallHandleRelayMessage( + MockBMCIntegrationTest.eventLogChecker(HandleRelayMessageEventLog::eventLogs, + (el) -> assertArrayEquals(msgs, el.getRet())), + MockBMVIntegrationTest.mockBMVClient._address(), + prev, seq, relayMessage.toBase64String()); + } + + @Test + void sendMessageShouldMakeEventLog() { + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(new byte[][]{msg}); + + ((MockBMCScoreClient) mockBMC).sendMessage( + MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { + assertEquals(to, el.getTo()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(msg, el.getMsg()); + }), + to, svc, sn, msg); + } + + @Test + void handleBTPMessage() { + ((MockBMCScoreClient) mockBMC).intercallHandleBTPMessage( + MockBSHIntegrationTest.eventLogChecker(HandleBTPMessageEventLog::eventLogs, (el) -> { + assertEquals(to, el.getFrom()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(msg, el.getMsg()); + }), + MockBSHIntegrationTest.mockBSHClient._address(), + to, svc, sn, msg); + } + + @Test + void handleBTPError() { + ((MockBMCScoreClient) mockBMC).intercallHandleBTPError( + MockBSHIntegrationTest.eventLogChecker(HandleBTPErrorEventLog::eventLogs, (el) -> { + assertEquals(prev, el.getSrc()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertEquals(errCode, el.getCode()); + assertEquals(errMsg, el.getMsg()); + }), + MockBSHIntegrationTest.mockBSHClient._address(), + prev, svc, sn, errCode, errMsg); + } + + @Test + void handleFeeGathering() { + ((MockBMCScoreClient) mockBMC).intercallHandleFeeGathering( + MockBSHIntegrationTest.eventLogChecker(HandleFeeGatheringEventLog::eventLogs, (el) -> { + assertEquals(fa, el.getFa()); + assertEquals(svc, el.getSvc()); + }), + MockBSHIntegrationTest.mockBSHClient._address(), + fa, svc); + } +} \ No newline at end of file diff --git a/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMVTest.java b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMVTest.java new file mode 100644 index 00000000..72790076 --- /dev/null +++ b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBMVTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import com.github.javafaker.Number; +import foundation.icon.btp.lib.BMVStatus; +import foundation.icon.btp.lib.BTPException; +import foundation.icon.btp.test.AssertBTPException; +import foundation.icon.btp.test.BTPIntegrationTest; +import foundation.icon.btp.test.HandleRelayMessageEventLog; +import foundation.icon.btp.test.MockBMVIntegrationTest; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MockBMVTest implements BTPIntegrationTest, MockBMVIntegrationTest { + + static String bmc = BTPIntegrationTest.Faker.btpLink().toString(); + static String prev = BTPIntegrationTest.Faker.btpLink().toString(); + static BigInteger seq = BigInteger.ONE; + + @Test + void handleRelayMessageShouldMakeEventLog() { + List btpMessages = new ArrayList<>(); + btpMessages.add(bmc.getBytes()); + btpMessages.add(prev.getBytes()); + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setBtpMessages(btpMessages.toArray(new byte[btpMessages.size()][])); + + ((MockBMVScoreClient) mockBMV).handleRelayMessage( + MockBMVIntegrationTest.eventLogChecker(HandleRelayMessageEventLog::eventLogs, (el) -> { + assertArrayEquals(relayMessage.getBtpMessages(), el.getRet()); + }), + bmc, prev, seq, relayMessage.toBase64String()); + } + + @Test + void handleRelayMessageShouldRevert() { + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setRevertCode(1); + relayMessage.setRevertMessage("handleRelayMessageShouldRevert"); + //noinspection ThrowableNotThrown + AssertBTPException.assertBTPException( + new BTPException.BMV(relayMessage.getRevertCode(), relayMessage.getRevertMessage()), + () -> ((MockBMVScoreClient) mockBMV).handleRelayMessage( + (txr)->{}, + bmc, prev, seq, relayMessage.toBase64String()) + ); + } + + @Test + void handleRelayMessageShouldUpdateProperties() { + MockRelayMessage relayMessage = new MockRelayMessage(); + relayMessage.setHeight(3L); + relayMessage.setLastHeight(2L); + relayMessage.setOffset(1L); + + ((MockBMVScoreClient) mockBMV).handleRelayMessage( + MockBMVIntegrationTest.eventLogChecker(HandleRelayMessageEventLog::eventLogs, (el) -> { + assertArrayEquals(new byte[][]{}, el.getRet()); + }), + bmc, prev, seq, relayMessage.toBase64String()); + BMVStatus status = mockBMV.getStatus(); + assertEquals(relayMessage.getHeight(), status.getHeight()); + assertEquals(relayMessage.getLastHeight(), status.getLast_height()); + assertEquals(relayMessage.getOffset(), status.getOffset()); + } + + @Test + void updateProperties() { + Number number = com.github.javafaker.Faker.instance().number(); + long height = number.numberBetween(0, Long.MAX_VALUE); + mockBMV.setHeight(height); + assertEquals(height, mockBMV.getStatus().getHeight()); + + long lastHeight = number.numberBetween(0, Long.MAX_VALUE); + mockBMV.setLast_height(lastHeight); + assertEquals(lastHeight, mockBMV.getStatus().getLast_height()); + + long offset = number.numberBetween(0, Long.MAX_VALUE); + mockBMV.setOffset(offset); + assertEquals(offset, mockBMV.getStatus().getOffset()); + } + +} \ No newline at end of file diff --git a/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBSHTest.java b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBSHTest.java new file mode 100644 index 00000000..96654deb --- /dev/null +++ b/javascore/test-lib/src/test/java/foundation/icon/btp/mock/MockBSHTest.java @@ -0,0 +1,88 @@ +/* + * Copyright 2021 ICON Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package foundation.icon.btp.mock; + +import foundation.icon.btp.lib.BTPAddress; +import foundation.icon.btp.test.*; +import foundation.icon.jsonrpc.Address; +import foundation.icon.score.test.ScoreIntegrationTest; +import org.junit.jupiter.api.Test; + +import java.math.BigInteger; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MockBSHTest implements BTPIntegrationTest, MockBSHIntegrationTest { + static BTPAddress prevBtpAddress = BTPIntegrationTest.Faker.btpLink(); + static String prev = prevBtpAddress.toString(); + static byte[] msg = ScoreIntegrationTest.Faker.bytes(32); + static String to = prevBtpAddress.net(); + static String svc = BTPIntegrationTest.Faker.btpService(); + static BigInteger sn = BigInteger.ONE; + static long errCode = 1; + static String errMsg = "err" + svc; + static String fa = ScoreIntegrationTest.Faker.address(Address.Type.CONTRACT).toString(); + + @Test + void intercallSendMessage() { + ((MockBSHScoreClient) mockBSH).intercallSendMessage( + MockBMCIntegrationTest.eventLogChecker(SendMessageEventLog::eventLogs, (el) -> { + assertEquals(to, el.getTo()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(msg, el.getMsg()); + }), + MockBMCIntegrationTest.mockBMCClient._address(), + to, svc, sn, msg); + } + + @Test + void handleBTPMessageShouldMakeEventLog() { + ((MockBSHScoreClient) mockBSH).handleBTPMessage( + MockBSHIntegrationTest.eventLogChecker(HandleBTPMessageEventLog::eventLogs, (el) -> { + assertEquals(to, el.getFrom()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertArrayEquals(msg, el.getMsg()); + }), + to, svc, sn, msg); + } + + @Test + void handleBTPErrorShouldMakeEventLog() { + ((MockBSHScoreClient) mockBSH).handleBTPError( + MockBSHIntegrationTest.eventLogChecker(HandleBTPErrorEventLog::eventLogs, (el) -> { + assertEquals(prev, el.getSrc()); + assertEquals(svc, el.getSvc()); + assertEquals(sn, el.getSn()); + assertEquals(errCode, el.getCode()); + assertEquals(errMsg, el.getMsg()); + }), + prev, svc, sn, errCode, errMsg); + } + + @Test + void handleFeeGatheringShouldMakeEventLog() { + ((MockBSHScoreClient) mockBSH).handleFeeGathering( + MockBSHIntegrationTest.eventLogChecker(HandleFeeGatheringEventLog::eventLogs, (el) -> { + assertEquals(fa, el.getFa()); + assertEquals(svc, el.getSvc()); + }), + fa, svc); + } +} \ No newline at end of file diff --git a/scripts/dist_javascore.sh b/scripts/dist_javascore.sh new file mode 100644 index 00000000..60198f22 --- /dev/null +++ b/scripts/dist_javascore.sh @@ -0,0 +1,65 @@ +# /bin/sh +set -e + +JAVA_VERSION=11.0.11 +GRADLE_VERSION=6.7.1 + +install_javasdk() { + echo "Installing... JAVASDK" + if [ ! -d "$HOME/.sdkman" ]; then + curl -s "https://get.sdkman.io" | bash + fi + source $HOME/.sdkman/bin/sdkman-init.sh + sdk install java 11.0.11.hs-adpt + sdk default java 11.0.11.hs-adpt + sdk install gradle 6.7.1 + sdk default gradle 6.7.1 +} + +current_java_version=$(java --version | awk '/openjdk/ {print $2}') +if [ "$current_java_version" != "$JAVA_VERSION" ]; then + echo $current_java_version + install_javasdk +fi + +current_gradle_version=$(gradle -v | awk '/Gradle/ {print $2}') +if [ "$current_gradle_version" != "$GRADLE_VERSION" ]; then + install_javasdk +fi + +CONTRACTS_DIST_DIR=${CONTRACTS_DIST_DIR:-$PWD/build/contracts} +JAVASCORE_DIST_DIR=${JAVASCORE_DIST_DIR:-$CONTRACTS_DIST_DIR/javascore} +SOURCE_CODE_DIR=$JAVASCORE_DIST_DIR/sources + +rm -rf $JAVASCORE_DIST_DIR && mkdir -p "$SOURCE_CODE_DIR" +cp -r javascore/* $SOURCE_CODE_DIR/ + +# Overwrite the current BMV by the BMV without relaychain +cd $SOURCE_CODE_DIR && \ + ls -d ./bmv/* | grep -v "./bmv/icon" | xargs rm -rf && \ + tar -xzf bmv_without_relaychain.tar.gz + +### compiling +gradle --stop +cd $SOURCE_CODE_DIR/bmv/eventDecoder && gradle buildKusamaDecoder && gradle buildMoonriverDecoder +cd $SOURCE_CODE_DIR/bmv/parachain && gradle optimizedJar +cd $SOURCE_CODE_DIR/fee_aggregation && gradle optimizedJar +cd $SOURCE_CODE_DIR/lib && gradle build +cd $SOURCE_CODE_DIR/bmc && gradle optimizedJar +cd $SOURCE_CODE_DIR/nativecoin && gradle optimizedJar && gradle optimizedJarIRC31 +cd $SOURCE_CODE_DIR/nativecoinIRC2 && gradle optimizedJar && gradle optimizedJarIRC2 + +### packing +cp -rf $SOURCE_CODE_DIR/bmv/helper ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/bmv/parachain/build/libs/parachain-BMV-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/bmv/eventDecoder/build/libs/KusamaEventDecoder-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/bmv/eventDecoder/build/libs/MoonriverEventDecoder-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/fee_aggregation/build/libs/fee-aggregation-system-1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/bmc/build/libs/bmc-0.1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/nativecoin/build/libs/nativecoin-0.1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/nativecoin/build/libs/irc31-0.1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/nativecoinIRC2/build/libs/nativecoinIRC2-0.1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ +cp $SOURCE_CODE_DIR/nativecoinIRC2/build/libs/irc2-0.1.0-optimized.jar ${JAVASCORE_DIST_DIR}/ + +### cleaning +# rm -rf $SOURCE_CODE_DIR diff --git a/scripts/dist_solidity.sh b/scripts/dist_solidity.sh new file mode 100644 index 00000000..5aee5369 --- /dev/null +++ b/scripts/dist_solidity.sh @@ -0,0 +1,18 @@ +# /bin/sh +set -e + +CONTRACTS_DIST_DIR=${CONTRACTS_DIST_DIR:-$PWD/build/contracts} +SOLIDITY_DIST_DIR=${SOLIDITY_DIST_DIR:-$CONTRACTS_DIST_DIR/solidity} + +rm -rf $SOLIDITY_DIST_DIR +mkdir -p $SOLIDITY_DIST_DIR +cp -r solidity/bmc $SOLIDITY_DIST_DIR/ +cp -r solidity/bmv $SOLIDITY_DIST_DIR/ +cp -r solidity/bsh $SOLIDITY_DIST_DIR/ +cp -r solidity/nativecoinERC20 $SOLIDITY_DIST_DIR/ + +### cleaning +rm -rf $SOLIDITY_DIST_DIR/bmc/node_modules $SOLIDITY_DIST_DIR/bmc/test +rm -rf $SOLIDITY_DIST_DIR/bmv/node_modules $SOLIDITY_DIST_DIR/bmv/test +rm -rf $SOLIDITY_DIST_DIR/bsh/node_modules $SOLIDITY_DIST_DIR/bsh/test +rm -rf $SOLIDITY_DIST_DIR/nativecoinERC20/node_modules $SOLIDITY_DIST_DIR/nativecoinERC20/test \ No newline at end of file diff --git a/solidity/bmc/.prettierrc b/solidity/bmc/.prettierrc new file mode 100644 index 00000000..e368cfec --- /dev/null +++ b/solidity/bmc/.prettierrc @@ -0,0 +1,15 @@ +{ + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false, + "explicitTypes": "always" + } + } + ] +} diff --git a/solidity/bmc/.solhint.json b/solidity/bmc/.solhint.json new file mode 100644 index 00000000..ec279d46 --- /dev/null +++ b/solidity/bmc/.solhint.json @@ -0,0 +1,16 @@ +{ + "extends": ["solhint:recommended"], + "rules": { + "compiler-version": ["error", ">=0.5.0 <0.8.0"], + "prettier/prettier": "error", + "avoid-throw": "off", + "avoid-suicide": "error", + "avoid-sha3": "warn", + "no-inline-assembly": "off", + "func-visibility": ["warn", {"ignoreConstructors": true}], + "no-empty-blocks": "off", + "reason-string": "off" + }, + "plugins": ["prettier"] + } + \ No newline at end of file diff --git a/solidity/bmc/BMCBMVEventListener.js b/solidity/bmc/BMCBMVEventListener.js new file mode 100644 index 00000000..269a9177 --- /dev/null +++ b/solidity/bmc/BMCBMVEventListener.js @@ -0,0 +1,78 @@ +const Web3 = require('web3'); + +let web3Provider = new Web3.providers.WebsocketProvider("ws://localhost:9944"); +var web3Obj = new Web3(web3Provider); +async function init() { + var tx = await web3Obj.eth.getBlock("latest") + console.log(web3Obj.utils.keccak256('TransferEnd(address,uint256,uint256,string)')) + console.log(web3Obj.utils.keccak256('Debug(string,address,uint256)')) +} +init() + +var subscription = web3Obj.eth.subscribe('logs', { + //address: '0x746DFE0F96789e62CECeeA3CA2a9b5556b3AaD6c',//'0xbC7E488a8c459C9Bc32ccB31F951ed0d2A41b250',//address.solidity.BSH, //Smart contract address + fromBlock: 1, + //topics: [web3Obj.utils.keccak256('TransferEnd(address,uint256,uint256,string)')] //topics for events + topics: [web3Obj.utils.keccak256('Debug1(string,address,uint256)')] //topics for events +}, function (error, result) { + if (error) console.log(error); +}).on("data", function (trxData) { + console.log("Event received1111111111111", trxData); + for(const topic of trxData.topics){ + if(topic == web3Obj.utils.keccak256('Debug1(string,address,uint256)')){ + const result = web3Obj.eth.abi.decodeLog( + debugEventInputABI, + trxData.data, + trxData.topics + ); + console.log("Decoded debug data111111111111111: ", result); + } + } + //Code from here would be run immediately when event appeared +}); + + + +var subscription = web3Obj.eth.subscribe('logs', { + address: '0x5CC307268a1393AB9A764A20DACE848AB8275c46',//'0xbC7E488a8c459C9Bc32ccB31F951ed0d2A41b250',//address.solidity.BMC, //Smart contract address + fromBlock: 1, + //topics: [web3Obj.utils.keccak256('TransferEnd(address,uint256,uint256,string)')] //topics for events + topics: [web3Obj.utils.keccak256('Debug(string,address,uint256)')] //topics for events +}, function (error, result) { + if (error) console.log(error); +}).on("data", function (trxData) { + console.log("Event received2222222222222222", trxData); + //Code from here would be run immediately when event appeared + for(const topic of trxData.topics){ + if(topic == web3Obj.utils.keccak256('Debug(string,address,uint256)')){ + const result = web3Obj.eth.abi.decodeLog( + debugEventInputABI, + trxData.data, + trxData.topics + ); + console.log("Decoded debug data2222222222222: ", result); + } + } +}); +//event Debug(string _msg,address _user, uint256 _val); + +debugEventInputABI=[ + { + "indexed": false, + "internalType": "string", + "name": "_msg", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "_user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "_val", + "type": "uint256" + } +] \ No newline at end of file diff --git a/solidity/bmc/README.md b/solidity/bmc/README.md new file mode 100644 index 00000000..e40f3aa5 --- /dev/null +++ b/solidity/bmc/README.md @@ -0,0 +1,34 @@ +## Set up +Node >= 10.x +``` +$ node --version +v15.12.0 +``` +Install tools +``` +$ npm install --global yarn truffle@5.3.0 +``` +Install dependencies +``` +$ yarn +``` + +## Test +1. Run in a background process or seperate terminal window +``` +$ docker run --rm -p 9933:9933 -p 9944:9944 purestake/moonbeam:v0.9.2 --dev --ws-external --rpc-external +``` +2. Compile contracts +``` +$ yarn contract:compile +``` +3. Run unit and integration test +``` +$ yarn test +``` +- Run specific test +``` +$ yarn test:unit +$ yarn test:integration +``` + diff --git a/solidity/bmc/contracts/BMCManagement.sol b/solidity/bmc/contracts/BMCManagement.sol new file mode 100644 index 00000000..e6721750 --- /dev/null +++ b/solidity/bmc/contracts/BMCManagement.sol @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./interfaces/IBMCManagement.sol"; +import "./interfaces/IBMCPeriphery.sol"; +import "./interfaces/IBMV.sol"; +import "./libraries/ParseAddress.sol"; +import "./libraries/RLPEncode.sol"; +import "./libraries/RLPEncodeStruct.sol"; +import "./libraries/String.sol"; +import "./libraries/Types.sol"; +import "./libraries/Utils.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMCManagement is IBMCManagement, Initializable { + using ParseAddress for address; + using ParseAddress for string; + using RLPEncode for bytes; + using RLPEncode for string; + using RLPEncodeStruct for uint256; + using RLPEncodeStruct for Types.BMCService; + using String for string; + using Utils for uint256; + using Utils for string[]; + + mapping(address => bool) private _owners; + uint256 private numOfOwner; + + mapping(string => address) private bshServices; + mapping(string => address) private bmvServices; + mapping(address => Types.RelayStats) private relayStats; + mapping(string => string) private routes; + mapping(string => Types.Link) internal links; // should be private, temporarily set internal for testing + string[] private listBMVNames; + string[] private listBSHNames; + string[] private listRouteKeys; + string[] private listLinkNames; + address private bmcPeriphery; + + uint256 public serialNo; + + address[] private addrs; + + // Use to search by substring + mapping(string => string) private getRouteDstFromNet; + mapping(string => string) private getLinkFromNet; + mapping(string => Types.Tuple) private getLinkFromReachableNet; + + uint256 private constant BLOCK_INTERVAL_MSEC = 1000; + + modifier hasPermission { + require(_owners[msg.sender] == true, "BMCRevertUnauthorized"); + _; + } + + modifier onlyBMCPeriphery { + require(msg.sender == bmcPeriphery, "BMCRevertUnauthorized"); + _; + } + + function initialize() public initializer { + _owners[msg.sender] = true; + numOfOwner++; + } + + function setBMCPeriphery(address _addr) external override hasPermission { + require(_addr != address(0), "BMCRevertInvalidAddress"); + require(_addr != bmcPeriphery, "BMCRevertAlreadyExistsBMCPeriphery"); + bmcPeriphery = _addr; + } + + /***************************************************************************************** + Add Authorized Owner of Contract + - addOwner(): register additional Owner of this Contract + - removeOwner(): un-register existing Owner of this Contract. Unable to remove last + - isOwner(): checking Ownership of an arbitrary address + *****************************************************************************************/ + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override hasPermission { + _owners[_owner] = true; + numOfOwner++; + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override hasPermission { + require(numOfOwner > 1, "BMCRevertLastOwner"); + require(_owners[_owner] == true, "BMCRevertNotExistsPermission"); + delete _owners[_owner]; + numOfOwner--; + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return _owners[_owner]; + } + + /** + @notice Add the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + @param _addr Service's contract address + */ + function addService(string memory _svc, address _addr) + external + override + hasPermission + { + require(_addr != address(0), "BMCRevertInvalidAddress"); + require(bshServices[_svc] == address(0), "BMCRevertAlreadyExistsBSH"); + bshServices[_svc] = _addr; + listBSHNames.push(_svc); + } + + /** + @notice Unregisters the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + */ + function removeService(string memory _svc) external override hasPermission { + require(bshServices[_svc] != address(0), "BMCRevertNotExistsBSH"); + delete bshServices[_svc]; + listBSHNames.remove(_svc); + } + + /** + @notice Get registered services. + @return _servicers An array of Service. + */ + function getServices() + external + view + override + returns (Types.Service[] memory) + { + Types.Service[] memory services = + new Types.Service[](listBSHNames.length); + for (uint256 i = 0; i < listBSHNames.length; i++) { + services[i] = Types.Service( + listBSHNames[i], + bshServices[listBSHNames[i]] + ); + } + return services; + } + + /** + @notice Registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + @param _addr Address of BMV + */ + function addVerifier(string memory _net, address _addr) + external + override + hasPermission + { + require(bmvServices[_net] == address(0), "BMCRevertAlreadyExistsBMV"); + bmvServices[_net] = _addr; + listBMVNames.push(_net); + } + + /** + @notice Unregisters BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + */ + function removeVerifier(string memory _net) + external + override + hasPermission + { + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + delete bmvServices[_net]; + listBMVNames.remove(_net); + } + + /** + @notice Get registered verifiers. + @return _verifiers An array of Verifier. + */ + function getVerifiers() + external + view + override + returns (Types.Verifier[] memory) + { + Types.Verifier[] memory verifiers = + new Types.Verifier[](listBMVNames.length); + + for (uint256 i = 0; i < listBMVNames.length; i++) { + verifiers[i] = Types.Verifier( + listBMVNames[i], + bmvServices[listBMVNames[i]] + ); + } + return verifiers; + } + + /** + @notice Initializes status information for the link. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function addLink(string calldata _link) external override hasPermission { + (string memory _net, ) = _link.splitBTPAddress(); + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + require( + links[_link].isConnected == false, + "BMCRevertAlreadyExistsLink" + ); + links[_link] = Types.Link( + new address[](0), + new string[](0), + 0, + 0, + BLOCK_INTERVAL_MSEC, + 0, + 10, + 3, + 0, + 0, + 0, + 0, + true + ); + + // propagate an event "LINK" + propagateInternal("Link", _link); + + string[] memory _links = listLinkNames; + + listLinkNames.push(_link); + getLinkFromNet[_net] = _link; + + // init link + sendInternal(_link, "Init", _links); + } + + /** + @notice Removes the link and status information. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function removeLink(string calldata _link) external override hasPermission { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + delete links[_link]; + (string memory _net, ) = _link.splitBTPAddress(); + delete getLinkFromNet[_net]; + propagateInternal("Unlink", _link); + listLinkNames.remove(_link); + } + + /** + @notice Get registered links. + @return _links An array of links ( BTP Addresses of the BMCs ). + */ + function getLinks() external view override returns (string[] memory) { + return listLinkNames; + } + + function setLink( + string memory _link, + uint256 _blockInterval, + uint256 _maxAggregation, + uint256 _delayLimit + ) external override hasPermission { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + require( + _maxAggregation >= 1 && _delayLimit >= 1, + "BMCRevertInvalidParam" + ); + Types.Link memory link = links[_link]; + uint256 _scale = link.blockIntervalSrc.getScale(link.blockIntervalDst); + bool resetRotateHeight = false; + if (link.maxAggregation.getRotateTerm(_scale) == 0) { + resetRotateHeight = true; + } + link.blockIntervalDst = _blockInterval; + link.maxAggregation = _maxAggregation; + link.delayLimit = _delayLimit; + + _scale = link.blockIntervalSrc.getScale(_blockInterval); + uint256 _rotateTerm = _maxAggregation.getRotateTerm(_scale); + if (resetRotateHeight && _rotateTerm > 0) { + link.rotateHeight = block.number + _rotateTerm; + link.rxHeight = block.number; + string memory _net; + (_net, ) = _link.splitBTPAddress(); + (link.rxHeightSrc, , ) = IBMV(bmvServices[_net]).getStatus(); + } + links[_link] = link; + } + + function rotateRelay( + string memory _link, + uint256 _currentHeight, + uint256 _relayMsgHeight, + bool _hasMsg + ) external override onlyBMCPeriphery returns (address) { + /* + @dev Solidity does not support calculate rational numbers/floating numbers + thus, a division of _blockIntervalSrc and _blockIntervalDst should be + scaled by 10^6 to minimize proportional error + */ + Types.Link memory link = links[_link]; + uint256 _scale = link.blockIntervalSrc.getScale(link.blockIntervalDst); + uint256 _rotateTerm = link.maxAggregation.getRotateTerm(_scale); + uint256 _baseHeight; + uint256 _rotateCount; + if (_rotateTerm > 0) { + if (_hasMsg) { + // Note that, Relay has to relay this event immediately to BMC + // upon receiving this event. However, Relay is allowed to hold + // no later than 'delay_limit'. Thus, guessHeight comes up + // Arrival time of BTP Message identified by a block height + // BMC starts guessing when an event of 'RelayMessage' was thrown by another BMC + // which is 'guessHeight' and the time BMC receiving this event is 'currentHeight' + // If there is any delay, 'guessHeight' is likely less than 'currentHeight' + uint256 _guessHeight = + link.rxHeight + + uint256((_relayMsgHeight - link.rxHeightSrc) * 10**6) + .ceilDiv(_scale) - + 1; + + if (_guessHeight > _currentHeight) { + _guessHeight = _currentHeight; + } + // Python implementation as: + // rotate_count = math.ceil((guess_height - self.rotate_height)/rotate_term) + // the following code re-write it with using unsigned integer + if (_guessHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _guessHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_guessHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + // No need to check this if using unsigned integer as above + // if (_rotateCount < 0) { + // _rotateCount = 0; + // } + + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + /* Python implementation as: + // skip_count = math.ceil((current_height - guess_height)/self.delay_limit) - 1 + // In case that 'current_height' = 'guess_height' + // it might have an error calculation if using unsigned integer + // Thus, 'skipCount - 1' is moved into if_statement + // For example: + // + 'currentHeight' = 'guessHeight' + // => skipCount = 0 + // => no delay + // + 'currentHeight' > 'guessHeight' and 'currentHeight' - 'guessHeight' <= 'delay_limit' + // => ceil(('currentHeight' - 'guessHeight') / 'delay_limit') = 1 + // => skipCount = skipCount - 1 = 0 + // => not out of 'delay_limit' + // => accepted + // + 'currentHeight' > 'guessHeight' and 'currentHeight' - 'guessHeight' > 'delay_limit' + // => ceil(('currentHeight' - 'guessHeight') / 'delay_limit') = 2 + // => skipCount = skipCount - 1 = 1 + // => out of 'delay_limit' + // => rejected and move to next Relay + */ + uint256 _skipCount = + (_currentHeight - _guessHeight).ceilDiv(link.delayLimit); + + if (_skipCount > 0) { + _skipCount = _skipCount - 1; + _rotateCount += _skipCount; + _baseHeight = _currentHeight; + } + link.rxHeight = _currentHeight; + link.rxHeightSrc = _relayMsgHeight; + links[_link] = link; + } else { + if (_currentHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _currentHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_currentHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + } + return rotate(_link, _rotateTerm, _rotateCount, _baseHeight); + } + return address(0); + } + + function rotate( + string memory _link, + uint256 _rotateTerm, + uint256 _rotateCount, + uint256 _baseHeight + ) internal returns (address) { + Types.Link memory link = links[_link]; + if (_rotateTerm > 0 && _rotateCount > 0) { + link.rotateHeight = _baseHeight + _rotateTerm; + link.relayIdx = link.relayIdx + _rotateCount; + if (link.relayIdx >= link.relays.length) { + link.relayIdx = link.relayIdx % link.relays.length; + } + links[_link] = link; + } + return link.relays[link.relayIdx]; + } + + function propagateInternal( + string memory _serviceType, + string calldata _link + ) private { + bytes memory _rlpBytes; + _rlpBytes = abi.encodePacked(_rlpBytes, _link.encodeString()); + + _rlpBytes = abi.encodePacked( + _rlpBytes.length.addLength(false), + _rlpBytes + ); + + // encode payload + _rlpBytes = abi + .encodePacked(_rlpBytes.length.addLength(false), _rlpBytes) + .encodeBytes(); + + for (uint256 i = 0; i < listLinkNames.length; i++) { + if (links[listLinkNames[i]].isConnected) { + (string memory _net, ) = listLinkNames[i].splitBTPAddress(); + IBMCPeriphery(bmcPeriphery).sendMessage( + _net, + "bmc", + 0, + Types.BMCService(_serviceType, _rlpBytes).encodeBMCService() + ); + } + } + } + + function sendInternal( + string memory _target, + string memory _serviceType, + string[] memory _links + ) private { + bytes memory _rlpBytes; + if (_links.length == 0) { + _rlpBytes = abi.encodePacked(RLPEncodeStruct.LIST_SHORT_START); + } else { + for (uint256 i = 0; i < _links.length; i++) + _rlpBytes = abi.encodePacked(_rlpBytes, _links[i].encodeString()); + // encode target's reachable list + _rlpBytes = abi.encodePacked( + _rlpBytes.length.addLength(false), + _rlpBytes + ); + } + // encode payload + _rlpBytes = abi + .encodePacked(_rlpBytes.length.addLength(false), _rlpBytes) + .encodeBytes(); + + (string memory _net, ) = _target.splitBTPAddress(); + IBMCPeriphery(bmcPeriphery).sendMessage( + _net, + "bmc", + 0, + Types.BMCService(_serviceType, _rlpBytes).encodeBMCService() + ); + } + + /** + @notice Add route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + @param _link BTP Address of the next BMC for the destination + */ + function addRoute(string memory _dst, string memory _link) + external + override + hasPermission + { + require(bytes(routes[_dst]).length == 0, "BTPRevertAlreadyExistRoute"); + // Verify _dst and _link format address + // these two strings must follow BTP format address + // If one of these is failed, revert() + (string memory _net, ) = _dst.splitBTPAddress(); + _link.splitBTPAddress(); + + routes[_dst] = _link; // map _dst to _link + listRouteKeys.push(_dst); // push _dst key into an array of route keys + getRouteDstFromNet[_net] = _dst; + } + + /** + @notice Remove route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + */ + function removeRoute(string memory _dst) external override hasPermission { + // @dev No need to check if _dst is a valid BTP format address + // since it was checked when adding route at the beginning + // If _dst does not match, revert() + require(bytes(routes[_dst]).length != 0, "BTPRevertNotExistRoute"); + delete routes[_dst]; + (string memory _net, ) = _dst.splitBTPAddress(); + delete getRouteDstFromNet[_net]; + listRouteKeys.remove(_dst); + } + + /** + @notice Get routing information. + @return _routes An array of Route. + */ + function getRoutes() external view override returns (Types.Route[] memory) { + Types.Route[] memory _routes = new Types.Route[](listRouteKeys.length); + for (uint256 i = 0; i < listRouteKeys.length; i++) { + _routes[i] = Types.Route( + listRouteKeys[i], + routes[listRouteKeys[i]] + ); + } + return _routes; + } + + /** + @notice Registers relay for the network. + @dev Called by the Relay-Operator to manage the BTP network. + @param _link BTP Address of connected BMC + @param _addr the address of Relay + */ + function addRelay(string memory _link, address[] memory _addr) + external + override + hasPermission + { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + links[_link].relays = _addr; + for (uint256 i = 0; i < _addr.length; i++) + relayStats[_addr[i]] = Types.RelayStats(_addr[i], 0, 0); + } + + /** + @notice Unregisters Relay for the network. + @dev Called by the Relay-Operator to manage the BTP network. + @param _link BTP Address of connected BMC + @param _addr the address of Relay + */ + function removeRelay(string memory _link, address _addr) + external + override + hasPermission + { + require( + links[_link].isConnected == true && links[_link].relays.length != 0, + "BMCRevertUnauthorized" + ); + for (uint256 i = 0; i < links[_link].relays.length; i++) { + if (links[_link].relays[i] != _addr) { + addrs.push(links[_link].relays[i]); + } + } + links[_link].relays = addrs; + delete addrs; + } + + /** + @notice Get registered relays. + @param _link BTP Address of the connected BMC. + @return _relayes A list of relays. + */ + + function getRelays(string memory _link) + external + view + override + returns (address[] memory) + { + return links[_link].relays; + } + + /******************************* Use for BMC Service *************************************/ + function getBshServiceByName(string memory _serviceName) + external + view + override + returns (address) + { + return bshServices[_serviceName]; + } + + function getBmvServiceByNet(string memory _net) + external + view + override + returns (address) + { + return bmvServices[_net]; + } + + function getLink(string memory _to) + external + view + override + returns (Types.Link memory) + { + return links[_to]; + } + + function getLinkRxSeq(string calldata _prev) + external + view + override + returns (uint256) + { + return links[_prev].rxSeq; + } + + function getLinkTxSeq(string calldata _prev) + external + view + override + returns (uint256) + { + return links[_prev].txSeq; + } + + function getLinkRelays(string calldata _prev) + external + view + override + returns (address[] memory) + { + return links[_prev].relays; + } + + function getRelayStatusByLink(string memory _prev) + external + view + override + returns (Types.RelayStats[] memory _relays) + { + _relays = new Types.RelayStats[](links[_prev].relays.length); + for (uint256 i = 0; i < links[_prev].relays.length; i++) { + _relays[i] = relayStats[links[_prev].relays[i]]; + } + } + + function updateLinkRxSeq(string calldata _prev, uint256 _val) + external + override + onlyBMCPeriphery + { + links[_prev].rxSeq += _val; + } + + function updateLinkTxSeq(string memory _prev) + external + override + onlyBMCPeriphery + { + links[_prev].txSeq++; + } + + function updateLinkReachable(string memory _prev, string[] memory _to) + external + override + onlyBMCPeriphery + { + for (uint256 i = 0; i < _to.length; i++) { + links[_prev].reachable.push(_to[i]); + (string memory _net, ) = _to[i].splitBTPAddress(); + getLinkFromReachableNet[_net] = Types.Tuple(_prev, _to[i]); + } + } + + function deleteLinkReachable(string memory _prev, uint256 _index) + external + override + onlyBMCPeriphery + { + (string memory _net, ) = + links[_prev].reachable[_index].splitBTPAddress(); + delete getLinkFromReachableNet[_net]; + delete links[_prev].reachable[_index]; + links[_prev].reachable[_index] = links[_prev].reachable[ + links[_prev].reachable.length - 1 + ]; + links[_prev].reachable.pop(); + } + + function updateRelayStats( + address relay, + uint256 _blockCountVal, + uint256 _msgCountVal + ) external override onlyBMCPeriphery { + relayStats[relay].blockCount += _blockCountVal; + relayStats[relay].msgCount += _msgCountVal; + } + + function resolveRoute(string memory _dstNet) + external + view + override + onlyBMCPeriphery + returns (string memory, string memory) + { + // search in routes + string memory _dst = getRouteDstFromNet[_dstNet]; + if (bytes(_dst).length != 0) return (routes[_dst], _dst); + + // search in links + _dst = getLinkFromNet[_dstNet]; + if (bytes(_dst).length != 0) return (_dst, _dst); + + // search link by reachable net + Types.Tuple memory res = getLinkFromReachableNet[_dstNet]; + + require( + bytes(res._to).length > 0, + string("BMCRevertUnreachable: ").concat(_dstNet).concat( + " is unreachable" + ) + ); + return (res._prev, res._to); + } + /*******************************************************************************************/ +} diff --git a/solidity/bmc/contracts/BMCPeriphery.sol b/solidity/bmc/contracts/BMCPeriphery.sol new file mode 100644 index 00000000..eca3f13c --- /dev/null +++ b/solidity/bmc/contracts/BMCPeriphery.sol @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./interfaces/IBSH.sol"; +import "./interfaces/IBMCPeriphery.sol"; +import "./interfaces/IBMCManagement.sol"; +import "./interfaces/IBMV.sol"; +import "./libraries/ParseAddress.sol"; +import "./libraries/RLPDecodeStruct.sol"; +import "./libraries/RLPEncodeStruct.sol"; +import "./libraries/String.sol"; +import "./libraries/Types.sol"; +import "./libraries/Utils.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMCPeriphery is IBMCPeriphery, Initializable { + using String for string; + using ParseAddress for address; + using RLPDecodeStruct for bytes; + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.Response; + using Utils for uint256; + + uint256 internal constant UNKNOWN_ERR = 0; + uint256 internal constant BMC_ERR = 10; + uint256 internal constant BMV_ERR = 25; + uint256 internal constant BSH_ERR = 40; + + string private bmcBtpAddress; // a network address BMV, i.e. btp://1234.pra/0xabcd + address private bmcManagement; + + function initialize(string memory _network, address _bmcManagementAddr) + public + initializer + { + bmcBtpAddress = string("btp://").concat(_network).concat("/").concat( + address(this).toString() + ); + bmcManagement = _bmcManagementAddr; + } + + event Message( + string _next, // an address of the next BMC (it could be a destination BMC) + uint256 _seq, // a sequence number of BMC (NOT sequence number of BSH) + bytes _msg + ); + + // emit errors in BTP messages processing + event ErrorOnBTPError( + string _svc, + int256 _sn, + uint256 _code, + string _errMsg, + uint256 _svcErrCode, + string _svcErrMsg + ); + + function getBmcBtpAddress() external view override returns (string memory) { + return bmcBtpAddress; + } + + /** + @notice Verify and decode RelayMessage with BMV, and dispatch BTP Messages to registered BSHs + @dev Caller must be a registered relayer. + @param _prev BTP Address of the BMC generates the message + @param _msg base64 encoded string of serialized bytes of Relay Message refer RelayMessage structure + */ + function handleRelayMessage(string calldata _prev, string calldata _msg) + external + override + { + bytes[] memory serializedMsgs = decodeMsgAndValidateRelay(_prev, _msg); + + // dispatch BTP Messages + Types.BMCMessage memory _message; + for (uint256 i = 0; i < serializedMsgs.length; i++) { + try this.decodeBTPMessage(serializedMsgs[i]) returns ( + Types.BMCMessage memory _decoded + ) { + _message = _decoded; + } catch { + // ignore BTPMessage parse failure + continue; + } + + if (_message.dst.compareTo(bmcBtpAddress)) { + handleMessage(_prev, _message); + } else { + (string memory _net, ) = _message.dst.splitBTPAddress(); + try IBMCManagement(bmcManagement).resolveRoute(_net) returns ( + string memory _nextLink, + string memory + ) { + _sendMessage(_nextLink, serializedMsgs[i]); + } catch Error(string memory _error) { + _sendError(_prev, _message, BMC_ERR, _error); + } + } + } + IBMCManagement(bmcManagement).updateLinkRxSeq( + _prev, + serializedMsgs.length + ); + } + + function decodeMsgAndValidateRelay( + string calldata _prev, + string calldata _msg + ) internal returns (bytes[] memory) { + (string memory _net, ) = _prev.splitBTPAddress(); + address _bmvAddr = + IBMCManagement(bmcManagement).getBmvServiceByNet(_net); + + require(_bmvAddr != address(0), "BMCRevertNotExistsBMV"); + (uint256 _prevHeight, , ) = IBMV(_bmvAddr).getStatus(); + + // decode and verify relay message + bytes[] memory serializedMsgs = + IBMV(_bmvAddr).handleRelayMessage( + bmcBtpAddress, + _prev, + IBMCManagement(bmcManagement).getLinkRxSeq(_prev), + bytes(_msg) + ); + + // rotate and check valid relay + (uint256 _height, uint256 _lastHeight, ) = IBMV(_bmvAddr).getStatus(); + address relay = + IBMCManagement(bmcManagement).rotateRelay( + _prev, + block.number, + _lastHeight, + serializedMsgs.length > 0 + ); + + if (relay == address(0)) { + address[] memory relays = + IBMCManagement(bmcManagement).getLinkRelays(_prev); + bool check; + for (uint256 i = 0; i < relays.length; i++) + if (msg.sender == relays[i]) { + check = true; + break; + } + require(check, "BMCRevertUnauthorized: not registered relay"); + relay = msg.sender; + } else if (relay != msg.sender) + revert("BMCRevertUnauthorized: invalid relay"); + + IBMCManagement(bmcManagement).updateRelayStats( + relay, + _height - _prevHeight, + serializedMsgs.length + ); + return serializedMsgs; + } + + // @dev Despite this function was set as external, it should be called internally + // since Solidity does not allow using try_catch with internal function + // this solution can solve the issue + function decodeBTPMessage(bytes memory _rlp) + external + pure + returns (Types.BMCMessage memory) + { + return _rlp.decodeBMCMessage(); + } + + function handleMessage(string calldata _prev, Types.BMCMessage memory _msg) + internal + { + address _bshAddr; + if (_msg.svc.compareTo("bmc")) { + Types.BMCService memory _sm; + try this.tryDecodeBMCService(_msg.message) returns ( + Types.BMCService memory res + ) { + _sm = res; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + return; + } + + if (_sm.serviceType.compareTo("FeeGathering")) { + Types.GatherFeeMessage memory _gatherFee; + try this.tryDecodeGatherFeeMessage(_sm.payload) returns ( + Types.GatherFeeMessage memory res + ) { + _gatherFee = res; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + return; + } + + for (uint256 i = 0; i < _gatherFee.svcs.length; i++) { + _bshAddr = IBMCManagement(bmcManagement) + .getBshServiceByName(_gatherFee.svcs[i]); + // If 'svc' not found, ignore + if (_bshAddr != address(0)) { + try + IBSH(_bshAddr).handleFeeGathering( + _gatherFee.fa, + _gatherFee.svcs[i] + ) + {} catch { + // If BSH contract throws a revert error, ignore and continue + } + } + } + } else if (_sm.serviceType.compareTo("Link")) { + string memory _to = _sm.payload.decodePropagateMessage(); + Types.Link memory link = + IBMCManagement(bmcManagement).getLink(_prev); + bool check; + if (link.isConnected) { + for (uint256 i = 0; i < link.reachable.length; i++) + if (_to.compareTo(link.reachable[i])) { + check = true; + break; + } + if (!check) { + string[] memory _links = new string[](1); + _links[0] = _to; + IBMCManagement(bmcManagement).updateLinkReachable( + _prev, + _links + ); + } + } + } else if (_sm.serviceType.compareTo("Unlink")) { + string memory _to = _sm.payload.decodePropagateMessage(); + Types.Link memory link = + IBMCManagement(bmcManagement).getLink(_prev); + if (link.isConnected) { + for (uint256 i = 0; i < link.reachable.length; i++) { + if (_to.compareTo(link.reachable[i])) + IBMCManagement(bmcManagement).deleteLinkReachable( + _prev, + i + ); + } + } + } else if (_sm.serviceType.compareTo("Init")) { + string[] memory _links = _sm.payload.decodeInitMessage(); + IBMCManagement(bmcManagement).updateLinkReachable( + _prev, + _links + ); + } else if (_sm.serviceType.compareTo("Sack")) { + // skip this case since it has been removed from internal services + } else revert("BMCRevert: not exists internal handler"); + } else { + _bshAddr = IBMCManagement(bmcManagement).getBshServiceByName( + _msg.svc + ); + if (_bshAddr == address(0)) { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertNotExistsBSH"); + return; + } + + if (_msg.sn >= 0) { + (string memory _net, ) = _msg.src.splitBTPAddress(); + try + IBSH(_bshAddr).handleBTPMessage( + _net, + _msg.svc, + uint256(_msg.sn), + _msg.message + ) + {} catch Error(string memory _error) { + _sendError(_prev, _msg, BSH_ERR, _error); + } + } else { + Types.Response memory _errMsg = _msg.message.decodeResponse(); + try + IBSH(_bshAddr).handleBTPError( + _msg.src, + _msg.svc, + uint256(_msg.sn * -1), + _errMsg.code, + _errMsg.message + ) + {} catch Error(string memory _error) { + emit ErrorOnBTPError( + _msg.svc, + _msg.sn * -1, + _errMsg.code, + _errMsg.message, + BSH_ERR, + _error + ); + } catch (bytes memory _error) { + emit ErrorOnBTPError( + _msg.svc, + _msg.sn * -1, + _errMsg.code, + _errMsg.message, + UNKNOWN_ERR, + string(_error) + ); + } + } + } + } + + // @dev Solidity does not allow using try_catch with internal function + // Thus, work-around solution is the followings + // If there is any error throwing, BMC contract can catch it, then reply back a RC_ERR Response + function tryDecodeBMCService(bytes calldata _msg) + external + pure + returns (Types.BMCService memory) + { + return _msg.decodeBMCService(); + } + + function tryDecodeGatherFeeMessage(bytes calldata _msg) + external + pure + returns (Types.GatherFeeMessage memory) + { + return _msg.decodeGatherFeeMessage(); + } + + function _sendMessage(string memory _to, bytes memory _serializedMsg) + internal + { + IBMCManagement(bmcManagement).updateLinkTxSeq(_to); + emit Message( + _to, + IBMCManagement(bmcManagement).getLinkTxSeq(_to), + _serializedMsg + ); + } + + function _sendError( + string calldata _prev, + Types.BMCMessage memory _message, + uint256 _errCode, + string memory _errMsg + ) internal { + if (_message.sn > 0) { + bytes memory _serializedMsg = + Types + .BMCMessage( + bmcBtpAddress, + _message + .src, + _message + .svc, + _message.sn * -1, + Types.Response(_errCode, _errMsg).encodeResponse() + ) + .encodeBMCMessage(); + _sendMessage(_prev, _serializedMsg); + } + } + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string memory _to, + string memory _svc, + uint256 _sn, + bytes memory _msg + ) external override { + require( + msg.sender == bmcManagement || + IBMCManagement(bmcManagement).getBshServiceByName(_svc) == + msg.sender, + "BMCRevertUnauthorized" + ); + require(_sn >= 0, "BMCRevertInvalidSN"); + // In case BSH sends a REQUEST_COIN_TRANSFER, + // but '_to' is a network which is not supported by BMC + // revert() therein + if ( + IBMCManagement(bmcManagement).getBmvServiceByNet(_to) == address(0) + ) { + revert("BMCRevertNotExistsBMV"); + } + (string memory _nextLink, string memory _dst) = + IBMCManagement(bmcManagement).resolveRoute(_to); + bytes memory _rlp = + Types + .BMCMessage(bmcBtpAddress, _dst, _svc, int256(_sn), _msg) + .encodeBMCMessage(); + _sendMessage(_nextLink, _rlp); + } + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC. + @return tx_seq Next sequence number of the next sending message. + @return rx_seq Next sequence number of the message to receive. + @return verifier VerifierStatus Object contains status information of the BMV. + */ + function getStatus(string calldata _link) + public + view + override + returns (Types.LinkStats memory _linkStats) + { + Types.Link memory link = IBMCManagement(bmcManagement).getLink(_link); + require(link.isConnected == true, "BMCRevertNotExistsLink"); + Types.RelayStats[] memory _relays = + IBMCManagement(bmcManagement).getRelayStatusByLink(_link); + (string memory _net, ) = _link.splitBTPAddress(); + uint256 _height; + uint256 _offset; + uint256 _lastHeight; + (_height, _offset, _lastHeight) = IBMV( + IBMCManagement(bmcManagement).getBmvServiceByNet(_net) + ) + .getStatus(); + uint256 _rotateTerm = + link.maxAggregation.getRotateTerm( + link.blockIntervalSrc.getScale(link.blockIntervalDst) + ); + return + Types.LinkStats( + link.rxSeq, + link.txSeq, + Types.VerifierStats(_height, _offset, _lastHeight, ""), + _relays, + link.relayIdx, + link.rotateHeight, + _rotateTerm, + link.delayLimit, + link.maxAggregation, + link.rxHeightSrc, + link.rxHeight, + link.blockIntervalSrc, + link.blockIntervalDst, + block.number + ); + } +} diff --git a/solidity/bmc/contracts/interfaces/IBMCManagement.sol b/solidity/bmc/contracts/interfaces/IBMCManagement.sol new file mode 100644 index 00000000..16f688e3 --- /dev/null +++ b/solidity/bmc/contracts/interfaces/IBMCManagement.sol @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; + +interface IBMCManagement { + /** + @notice Update BMC periphery. + @dev Caller must be an Onwer of BTP network + @param _addr Address of a new periphery. + */ + function setBMCPeriphery(address _addr) external; + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external; + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external; + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view returns (bool); + + /** + @notice Add the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + @param _addr Service's contract address + */ + function addService(string memory _svc, address _addr) external; + + /** + @notice De-registers the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + */ + function removeService(string calldata _svc) external; + + /** + @notice Registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + @param _addr Address of BMV + */ + function addVerifier(string calldata _net, address _addr) external; + + /** + @notice De-registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + */ + function removeVerifier(string calldata _net) external; + + /** + @notice Initializes status information for the link. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function addLink(string calldata _link) external; + + /** + @notice Set the link and status information. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + @param _blockInterval Block interval of a connected link + @param _maxAggregation Set max aggreation of a connected link + @param _delayLimit Set delay limit of a connected link + */ + function setLink( + string calldata _link, + uint256 _blockInterval, + uint256 _maxAggregation, + uint256 _delayLimit + ) external; + + /** + @notice Removes the link and status information. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function removeLink(string calldata _link) external; + + /** + @notice Add route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + @param _link BTP Address of the next BMC for the destination + */ + function addRoute(string calldata _dst, string calldata _link) external; + + /** + @notice Remove route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + */ + function removeRoute(string calldata _dst) external; + + /** + @notice Registers relay for the network. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + @param _addrs A list of Relays + */ + function addRelay(string calldata _link, address[] memory _addrs) external; + + /** + @notice Unregisters Relay for the network. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + @param _addrs A list of Relays + */ + function removeRelay(string calldata _link, address _addrs) external; + + /** + @notice Get registered services. + @return _servicers An array of Service. + */ + function getServices() + external + view + returns (Types.Service[] memory _servicers); + + /** + @notice Get registered verifiers. + @return _verifiers An array of Verifier. + */ + function getVerifiers() + external + view + returns (Types.Verifier[] memory _verifiers); + + /** + @notice Get registered links. + @return _links An array of links ( BTP Addresses of the BMCs ). + */ + function getLinks() external view returns (string[] memory _links); + + /** + @notice Get routing information. + @return _routes An array of Route. + */ + function getRoutes() external view returns (Types.Route[] memory _routes); + + /** + @notice Get registered relays. + @param _link BTP Address of the connected BMC. + @return _relayes A list of relays. + */ + function getRelays(string calldata _link) + external + view + returns (address[] memory _relayes); + + /** + @notice Get BSH services by name. Only called by BMC periphery. + @param _serviceName BSH service name + @return BSH service address + */ + function getBshServiceByName(string memory _serviceName) + external + view + returns (address); + + /** + @notice Get BMV services by net. Only called by BMC periphery. + @param _net net of the connected network + @return BMV service address + */ + function getBmvServiceByNet(string memory _net) + external + view + returns (address); + + /** + @notice Get link info. Only called by BMC periphery. + @param _to link's BTP address + @return Link info + */ + function getLink(string memory _to) + external + view + returns (Types.Link memory); + + /** + @notice Get rotation sequence by link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @return Rotation sequence + */ + function getLinkRxSeq(string calldata _prev) + external + view + returns (uint256); + + /** + @notice Get transaction sequence by link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @return Transaction sequence + */ + function getLinkTxSeq(string calldata _prev) + external + view + returns (uint256); + + /** + @notice Get relays by link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @return List of relays' addresses + */ + function getLinkRelays(string calldata _prev) + external + view + returns (address[] memory); + + /** + @notice Get relays status by link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @return Relay status of all relays + */ + function getRelayStatusByLink(string memory _prev) + external + view + returns (Types.RelayStats[] memory); + + /** + @notice Update rotation sequence by link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @param _val increment value + */ + function updateLinkRxSeq(string calldata _prev, uint256 _val) external; + + /** + @notice Increase transaction sequence by 1. + @param _prev BTP Address of the previous BMC + */ + function updateLinkTxSeq(string memory _prev) external; + + /** + @notice Add a reachable BTP address to link. Only called by BMC periphery. + @param _prev BTP Address of the previous BMC + @param _to BTP Address of the reachable + */ + function updateLinkReachable(string memory _prev, string[] memory _to) + external; + + /** + @notice Remove a reachable BTP address. Only called by BMC periphery. + @param _index reachable index to remove + */ + function deleteLinkReachable(string memory _prev, uint256 _index) external; + + /** + @notice Update relay status. Only called by BMC periphery. + @param _relay relay address + @param _blockCountVal increment value for block counter + @param _msgCountVal increment value for message counter + */ + function updateRelayStats( + address _relay, + uint256 _blockCountVal, + uint256 _msgCountVal + ) external; + + /** + @notice resolve next BMC. Only called by BMC periphery. + @param _dstNet net of BTP network address + @return BTP address of next BMC and destinated BMC + */ + function resolveRoute(string memory _dstNet) + external + view + returns (string memory, string memory); + + /** + @notice rotate relay for relay address. Only called by BMC periphery. + @param _link BTP network address of connected BMC + @param _currentHeight current block height of MTA from BMV + @param _relayMsgHeight block height of last relayed BTP Message + @param _hasMsg check if message exists + @return relay address + */ + function rotateRelay( + string memory _link, + uint256 _currentHeight, + uint256 _relayMsgHeight, + bool _hasMsg + ) external returns (address); +} diff --git a/solidity/bmc/contracts/interfaces/IBMCPeriphery.sol b/solidity/bmc/contracts/interfaces/IBMCPeriphery.sol new file mode 100644 index 00000000..78787903 --- /dev/null +++ b/solidity/bmc/contracts/interfaces/IBMCPeriphery.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; + +interface IBMCPeriphery { + /** + @notice Get BMC BTP address + */ + function getBmcBtpAddress() external view returns (string memory); + + /** + @notice Verify and decode RelayMessage with BMV, and dispatch BTP Messages to registered BSHs + @dev Caller must be a registered relayer. + @param _prev BTP Address of the BMC generates the message + @param _msg base64 encoded string of serialized bytes of Relay Message refer RelayMessage structure + */ + function handleRelayMessage(string calldata _prev, string calldata _msg) + external; + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string calldata _to, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC + @return _linkStats The link status + */ + function getStatus(string calldata _link) + external + view + returns (Types.LinkStats memory _linkStats); +} diff --git a/solidity/bmc/contracts/interfaces/IBMV.sol b/solidity/bmc/contracts/interfaces/IBMV.sol new file mode 100644 index 00000000..8ed816e4 --- /dev/null +++ b/solidity/bmc/contracts/interfaces/IBMV.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBMV { + /** + @return base64EncodedMTA Base64 encode of Merkle Tree + */ + function getMTA() external view returns (string memory); + + /** + @return addr connected BMC address + */ + function getConnectedBMC() external view returns (address); + + /** + @return net network address of the blockchain + */ + function getNetAddress() external view returns (string memory); + + /** + @return serializedHash hash of RLP encode from given list of validators + @return addresses list of validators' addresses + */ + function getValidators() external view returns (bytes32, address[] memory); + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + returns ( + uint256 height, + uint256 offset, + uint256 lastHeight + ); + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes calldata _msg + ) external returns (bytes[] memory); +} diff --git a/solidity/bmc/contracts/interfaces/IBSH.sol b/solidity/bmc/contracts/interfaces/IBSH.sol new file mode 100644 index 00000000..a43ee9ad --- /dev/null +++ b/solidity/bmc/contracts/interfaces/IBSH.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +interface IBSH { + /** + @notice Handle BTP Message from other blockchain. + @dev Accept the message only from the BMC. + Every BSH must implement this function + @param _from Network Address of source network + @param _svc Name of the service + @param _sn Serial number of the message + @param _msg Serialized bytes of ServiceMessage + */ + + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /** + @notice Handle the error on delivering the message. + @dev Accept the error only from the BMC. + Every BSH must implement this function + @param _src BTP Address of BMC generates the error + @param _svc Name of the service + @param _sn Serial number of the original message + @param _code Code of the error + @param _msg Message of the error + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external; + + /** + @notice Handle Gather Fee Request from ICON. + @dev Every BSH must implement this function + @param _fa BTP Address of Fee Aggregator in ICON + @param _svc Name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external; +} diff --git a/solidity/bmc/contracts/libraries/DecodeBase64.sol b/solidity/bmc/contracts/libraries/DecodeBase64.sol new file mode 100644 index 00000000..283540d5 --- /dev/null +++ b/solidity/bmc/contracts/libraries/DecodeBase64.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title DecodeBase64 + * @dev A simple Base64 decoding library. + * @author Quang Tran + */ + +library DecodeBase64 { + function decode(string memory _str) internal pure returns (bytes memory) { + bytes memory _bs = bytes(_str); + uint256 remove = 0; + if (_bs[_bs.length - 1] == "=" && _bs[_bs.length - 2] == "=") { + remove += 2; + } else if (_bs[_bs.length - 1] == "=") { + remove++; + } + uint256 resultLength = (_bs.length / 4) * 3 - remove; + bytes memory result = new bytes(resultLength); + + uint256 i = 0; + uint256 j = 0; + for (; i + 4 < _bs.length; i += 4) { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[i]), + mapBase64Char(_bs[i + 1]), + mapBase64Char(_bs[i + 2]), + mapBase64Char(_bs[i + 3]) + ); + j += 3; + } + if (remove == 1) { + (result[j], result[j + 1], ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + 0 + ); + } else if (remove == 2) { + (result[j], , ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + 0, + 0 + ); + } else { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + mapBase64Char(_bs[_bs.length - 1]) + ); + } + return result; + } + + function mapBase64Char(bytes1 _char) private pure returns (uint8) { + // solhint-disable-next-line + uint8 A = 0; + uint8 a = 26; + uint8 zero = 52; + if (uint8(_char) == 45) { + return 62; + } else if (uint8(_char) == 95) { + return 63; + } else if (uint8(_char) >= 48 && uint8(_char) <= 57) { + return zero + (uint8(_char) - 48); + } else if (uint8(_char) >= 65 && uint8(_char) <= 90) { + return A + (uint8(_char) - 65); + } else if (uint8(_char) >= 97 && uint8(_char) <= 122) { + return a + (uint8(_char) - 97); + } + return 0; + } + + function decode4( + uint256 a0, + uint256 a1, + uint256 a2, + uint256 a3 + ) + private + pure + returns ( + bytes1, + bytes1, + bytes1 + ) + { + uint256 n = + ((a0 & 63) << 18) | + ((a1 & 63) << 12) | + ((a2 & 63) << 6) | + (a3 & 63); + uint256 b0 = (n >> 16) & 255; + uint256 b1 = (n >> 8) & 255; + uint256 b2 = (n) & 255; + return (bytes1(uint8(b0)), bytes1(uint8(b1)), bytes1(uint8(b2))); + } +} diff --git a/solidity/bmc/contracts/libraries/EncodeBase64.sol b/solidity/bmc/contracts/libraries/EncodeBase64.sol new file mode 100644 index 00000000..5a09250f --- /dev/null +++ b/solidity/bmc/contracts/libraries/EncodeBase64.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title EncodeBase64 + * @dev A simple Base64 encoding library. + * The original library was modified to make it work for our case + * For more info, please check the link: https://github.com/OpenZeppelin/solidity-jwt.git + */ +library EncodeBase64 { + bytes private constant BASE64URLCHARS = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + function encode(bytes memory _bs) internal pure returns (string memory) { + // bytes memory _bs = bytes(_str); + uint256 rem = _bs.length % 3; + uint256 resLength; + if (_bs.length % 3 != 0) { + resLength = (_bs.length / 3) * 4 + 4; + } else { + resLength = (_bs.length / 3) * 4; + } + + // uint256 res_length = (_bs.length + 2) / 3 * 4 - ((3 - rem) % 3); + bytes memory res = new bytes(resLength); + uint256 i = 0; + uint256 j = 0; + + for (; i + 3 <= _bs.length; i += 3) { + (res[j], res[j + 1], res[j + 2], res[j + 3]) = encode3( + uint8(_bs[i]), + uint8(_bs[i + 1]), + uint8(_bs[i + 2]) + ); + j += 4; + } + + if (rem != 0) { + uint8 la0 = uint8(_bs[_bs.length - rem]); + uint8 la1 = 0; + if (rem == 2) { + la1 = uint8(_bs[_bs.length - 1]); + } + (bytes1 b0, bytes1 b1, bytes1 b2, ) = encode3(la0, la1, 0); + res[j] = b0; + res[j + 1] = b1; + if (rem == 1) { + res[j + 2] = "="; + res[j + 3] = "="; + } else if (rem == 2) { + res[j + 2] = b2; + res[j + 3] = "="; + } + } + return string(res); + } + + function encode3( + uint256 a0, + uint256 a1, + uint256 a2 + ) + private + pure + returns ( + bytes1 b0, + bytes1 b1, + bytes1 b2, + bytes1 b3 + ) + { + uint256 n = (a0 << 16) | (a1 << 8) | a2; + uint256 c0 = (n >> 18) & 63; + uint256 c1 = (n >> 12) & 63; + uint256 c2 = (n >> 6) & 63; + uint256 c3 = (n) & 63; + b0 = BASE64URLCHARS[c0]; + b1 = BASE64URLCHARS[c1]; + b2 = BASE64URLCHARS[c2]; + b3 = BASE64URLCHARS[c3]; + } +} diff --git a/solidity/bmc/contracts/libraries/ParseAddress.sol b/solidity/bmc/contracts/libraries/ParseAddress.sol new file mode 100644 index 00000000..8cebaf49 --- /dev/null +++ b/solidity/bmc/contracts/libraries/ParseAddress.sol @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Utility library of inline functions on addresses + */ +library ParseAddress { + /** + * @dev Get a checksummed string hex representation of an account address. + * @param account address The account to get the checksum for. + * @return The checksummed account string in ASCII format. Note that leading + * "0x" is not included. + */ + function toString(address account) internal pure returns (string memory) { + // call internal function for converting an account to a checksummed string. + return _toChecksumString(account); + } + + /** + * @dev Get a fixed-size array of whether or not each character in an account + * will be capitalized in the checksum. + * @param account address The account to get the checksum capitalization + * information for. + * @return A fixed-size array of booleans that signify if each character or + * "nibble" of the hex encoding of the address will be capitalized by the + * checksum. + */ + function getChecksumCapitalizedCharacters(address account) + internal + pure + returns (bool[40] memory) + { + // call internal function for computing characters capitalized in checksum. + return _toChecksumCapsFlags(account); + } + + /** + * @dev Determine whether a string hex representation of an account address + * matches the ERC-55 checksum of that address. + * @param accountChecksum string The checksummed account string in ASCII + * format. Note that a leading "0x" MUST NOT be included. + * @return A boolean signifying whether or not the checksum is valid. + */ + function isChecksumValid(string calldata accountChecksum) + internal + pure + returns (bool) + { + // call internal function for validating checksum strings. + return _isChecksumValid(accountChecksum); + } + + function _toChecksumString(address account) + internal + pure + returns (string memory asciiString) + { + // convert the account argument from address to bytes. + bytes20 data = bytes20(account); + + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + bool leftCaps; + bool rightCaps; + uint8 asciiOffset; + + // get the capitalized characters in the actual checksum. + bool[40] memory caps = _toChecksumCapsFlags(account); + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // locate and extract each capitalization status. + leftCaps = caps[2 * i]; + rightCaps = caps[2 * i + 1]; + + // get the offset from nibble value to ascii character for left nibble. + asciiOffset = _getAsciiOffset(leftNibble, leftCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i] = byte(leftNibble + asciiOffset); + + // get the offset from nibble value to ascii character for right nibble. + asciiOffset = _getAsciiOffset(rightNibble, rightCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i + 1] = byte(rightNibble + asciiOffset); + } + + return string(abi.encodePacked("0x", string(asciiBytes))); + } + + function _toChecksumCapsFlags(address account) + internal + pure + returns (bool[40] memory characterCapitalized) + { + // convert the address to bytes. + bytes20 a = bytes20(account); + + // hash the address (used to calculate checksum). + bytes32 b = keccak256(abi.encodePacked(_toAsciiString(a))); + + // declare variable types. + uint8 leftNibbleAddress; + uint8 rightNibbleAddress; + uint8 leftNibbleHash; + uint8 rightNibbleHash; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i; i < a.length; i++) { + // locate the byte and extract each nibble for the address and the hash. + rightNibbleAddress = uint8(a[i]) % 16; + leftNibbleAddress = (uint8(a[i]) - rightNibbleAddress) / 16; + rightNibbleHash = uint8(b[i]) % 16; + leftNibbleHash = (uint8(b[i]) - rightNibbleHash) / 16; + + characterCapitalized[2 * i] = (leftNibbleAddress > 9 && + leftNibbleHash > 7); + characterCapitalized[2 * i + 1] = (rightNibbleAddress > 9 && + rightNibbleHash > 7); + } + } + + function _isChecksumValid(string memory provided) + internal + pure + returns (bool ok) + { + // convert the provided string into account type. + address account = _toAddress(provided); + + // return false in the event the account conversion returned null address. + if (account == address(0)) { + // ensure that provided address is not also the null address first. + bytes memory b = bytes(provided); + for (uint256 i; i < b.length; i++) { + if (b[i] != hex"30") { + return false; + } + } + } + + // get the capitalized characters in the actual checksum. + string memory actual = _toChecksumString(account); + + // compare provided string to actual checksum string to test for validity. + return (keccak256(abi.encodePacked(actual)) == + keccak256(abi.encodePacked(provided))); + } + + function _getAsciiOffset(uint8 nibble, bool caps) + internal + pure + returns (uint8 offset) + { + // to convert to ascii characters, add 48 to 0-9, 55 to A-F, & 87 to a-f. + if (nibble < 10) { + offset = 48; + } else if (caps) { + offset = 55; + } else { + offset = 87; + } + } + + function _toAddress(string memory account) + internal + pure + returns (address accountAddress) + { + // convert the account argument from address to bytes. + bytes memory accountBytes = bytes(account); + + // create a new fixed-size byte array for the ascii bytes of the address. + bytes memory accountAddressBytes = new bytes(20); + + // declare variable types. + uint8 b; + uint8 nibble; + uint8 asciiOffset; + + // only proceed if the provided string has a length of 40. + if (accountBytes.length == 40) { + for (uint256 i; i < 40; i++) { + // get the byte in question. + b = uint8(accountBytes[i]); + + // ensure that the byte is a valid ascii character (0-9, A-F, a-f) + if (b < 48) return address(0); + if (57 < b && b < 65) return address(0); + if (70 < b && b < 97) return address(0); + if (102 < b) return address(0); //bytes(hex""); + + // find the offset from ascii encoding to the nibble representation. + if (b < 65) { + // 0-9 + asciiOffset = 48; + } else if (70 < b) { + // a-f + asciiOffset = 87; + } else { + // A-F + asciiOffset = 55; + } + + // store left nibble on even iterations, then store byte on odd ones. + if (i % 2 == 0) { + nibble = b - asciiOffset; + } else { + accountAddressBytes[(i - 1) / 2] = ( + byte(16 * nibble + (b - asciiOffset)) + ); + } + } + + // pack up the fixed-size byte array and cast it to accountAddress. + bytes memory packed = abi.encodePacked(accountAddressBytes); + assembly { + accountAddress := mload(add(packed, 20)) + } + } + } + + // based on https://ethereum.stackexchange.com/a/56499/48410 + function _toAsciiString(bytes20 data) + internal + pure + returns (string memory asciiString) + { + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // to convert to ascii characters, add 48 to 0-9 and 87 to a-f. + asciiBytes[2 * i] = byte(leftNibble + (leftNibble < 10 ? 48 : 87)); + asciiBytes[2 * i + 1] = byte( + rightNibble + (rightNibble < 10 ? 48 : 87) + ); + } + + return string(asciiBytes); + } +} diff --git a/solidity/bmc/contracts/libraries/RLPDecode.sol b/solidity/bmc/contracts/libraries/RLPDecode.sol new file mode 100644 index 00000000..a7764aa9 --- /dev/null +++ b/solidity/bmc/contracts/libraries/RLPDecode.sol @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Change supporting solidity compiler version + * The original code can be found via this link: https://github.com/hamdiallam/Solidity-RLP.git + */ + +library RLPDecode { + uint8 private constant STRING_SHORT_START = 0x80; + uint8 private constant STRING_LONG_START = 0xb8; + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf8; + uint8 private constant WORD_SIZE = 32; + + struct RLPItem { + uint256 len; + uint256 memPtr; + } + + struct Iterator { + RLPItem item; // Item that's being iterated over. + uint256 nextPtr; // Position of the next item in the list. + } + + /* + * @dev Returns the next element in the iteration. Reverts if it has not next element. + * @param self The iterator. + * @return The next element in the iteration. + */ + function next(Iterator memory self) internal pure returns (RLPItem memory) { + require(hasNext(self), "Must have next elements"); + + uint256 ptr = self.nextPtr; + uint256 itemLength = _itemLength(ptr); + self.nextPtr = ptr + itemLength; + + return RLPItem(itemLength, ptr); + } + + /* + * @dev Returns true if the iteration has more elements. + * @param self The iterator. + * @return true if the iteration has more elements. + */ + function hasNext(Iterator memory self) internal pure returns (bool) { + RLPItem memory item = self.item; + return self.nextPtr < item.memPtr + item.len; + } + + /* + * @param item RLP encoded bytes + */ + function toRlpItem(bytes memory item) + internal + pure + returns (RLPItem memory) + { + uint256 memPtr; + assembly { + memPtr := add(item, 0x20) + } + + return RLPItem(item.length, memPtr); + } + + /* + * @dev Create an iterator. Reverts if item is not a list. + * @param self The RLP item. + * @return An 'Iterator' over the item. + */ + function iterator(RLPItem memory self) + internal + pure + returns (Iterator memory) + { + require(isList(self), "Must be a list"); + + uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); + return Iterator(self, ptr); + } + + /* + * @param item RLP encoded bytes + */ + function rlpLen(RLPItem memory item) internal pure returns (uint256) { + return item.len; + } + + /* + * @param item RLP encoded bytes + */ + function payloadLen(RLPItem memory item) internal pure returns (uint256) { + return item.len - _payloadOffset(item.memPtr); + } + + /* + * @param item RLP encoded list in bytes + */ + function toList(RLPItem memory item) + internal + pure + returns (RLPItem[] memory) + { + require(isList(item), "Must be a list"); + + uint256 items = numItems(item); + RLPItem[] memory result = new RLPItem[](items); + + uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 dataLen; + for (uint256 i = 0; i < items; i++) { + dataLen = _itemLength(memPtr); + result[i] = RLPItem(dataLen, memPtr); + memPtr = memPtr + dataLen; + } + + return result; + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.len == 0) return false; + + uint8 byte0; + uint256 memPtr = item.memPtr; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /** RLPItem conversions into data types **/ + + // @returns raw rlp encoding in bytes + function toRlpBytes(RLPItem memory item) + internal + pure + returns (bytes memory) + { + bytes memory result = new bytes(item.len); + if (result.length == 0) return result; + + uint256 ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.memPtr, ptr, item.len); + return result; + } + + // any non-zero byte except "0x80" is considered true + function toBoolean(RLPItem memory item) internal pure returns (bool) { + require(item.len == 1, "Must have length 1"); + uint256 result; + uint256 memPtr = item.memPtr; + assembly { + result := byte(0, mload(memPtr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function toAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + require(item.len == 21, "Must have length 21"); + + return address(uint160(toUint(item))); + } + + function toUint(RLPItem memory item) internal pure returns (uint256) { + require(item.len > 0 && item.len <= 33, "Invalid uint number"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; + + uint256 result; + uint256 memPtr = item.memPtr + offset; + assembly { + result := mload(memPtr) + + // shfit to the correct location if neccesary + if lt(len, 32) { + result := div(result, exp(256, sub(32, len))) + } + } + + return result; + } + + function toInt(RLPItem memory item) internal pure returns (int256) { + if ((toBytes(item)[0] & 0x80) == 0x80) { + return int256(toUint(item) - 2**(toBytes(item).length * 8)); + } + + return int256(toUint(item)); + } + + // enforces 32 byte length + function toUintStrict(RLPItem memory item) internal pure returns (uint256) { + // one byte prefix + require(item.len == 33, "Must have length 33"); + + uint256 result; + uint256 memPtr = item.memPtr + 1; + assembly { + result := mload(memPtr) + } + + return result; + } + + function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + require(item.len > 0, "Invalid length"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; // data length + bytes memory result = new bytes(len); + + uint256 destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(item.memPtr + offset, destPtr, len); + return result; + } + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) private pure returns (uint256) { + if (item.len == 0) return 0; + + uint256 count = 0; + uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 endPtr = item.memPtr + item.len; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint256 memPtr) private pure returns (uint256) { + uint256 itemLen; + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) itemLen = 1; + else if (byte0 < STRING_LONG_START) + itemLen = byte0 - STRING_SHORT_START + 1; + else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + memPtr := add(memPtr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + memPtr := add(memPtr, 1) + + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint256 memPtr) private pure returns (uint256) { + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) return 0; + else if ( + byte0 < STRING_LONG_START || + (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START) + ) return 1; + else if (byte0 < LIST_SHORT_START) + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + else return byte0 - (LIST_LONG_START - 1) + 1; + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param len Amount of memory to copy from the source + */ + function copy( + uint256 src, + uint256 dest, + uint256 len + ) private pure { + if (len == 0) return; + + // copy as many word sizes as possible + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + // left over bytes. Mask is used to remove unwanted bytes from the word + uint256 mask = 256**(WORD_SIZE - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } +} diff --git a/solidity/bmc/contracts/libraries/RLPDecodeStruct.sol b/solidity/bmc/contracts/libraries/RLPDecodeStruct.sol new file mode 100644 index 00000000..4f461ca2 --- /dev/null +++ b/solidity/bmc/contracts/libraries/RLPDecodeStruct.sol @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./Types.sol"; + +library RLPDecodeStruct { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using RLPDecode for bytes; + + using RLPDecodeStruct for bytes; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function decodeBMCService(bytes memory _rlp) + internal + pure + returns (Types.BMCService memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCService( + string(ls[0].toBytes()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeGatherFeeMessage(bytes memory _rlp) + internal + pure + returns (Types.GatherFeeMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory subList = ls[1].toList(); + string[] memory _svcs = new string[](subList.length); + for (uint256 i = 0; i < subList.length; i++) { + _svcs[i] = string(subList[i].toBytes()); + } + return Types.GatherFeeMessage(string(ls[0].toBytes()), _svcs); + } + + function decodePropagateMessage(bytes memory _rlp) + internal + pure + returns (string memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return string(ls[0].toBytes()); + } + + function decodeInitMessage(bytes memory _rlp) + internal + pure + returns (string[] memory _links) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory rlpLinks = ls[0].toList(); + _links = new string[](rlpLinks.length); + for (uint256 i = 0; i < rlpLinks.length; i++) + _links[i] = string(rlpLinks[i].toBytes()); + } + + function decodeRegisterCoin(bytes memory _rlp) + internal + pure + returns (Types.RegisterCoin memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.RegisterCoin( + string(ls[0].toBytes()), + ls[1].toUint(), + string(ls[2].toBytes()) + ); + } + + function decodeBMCMessage(bytes memory _rlp) + internal + pure + returns (Types.BMCMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCMessage( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + string(ls[2].toBytes()), + ls[3].toInt(), + ls[4].toBytes() // bytes array of RLPEncode(ServiceMessage) + ); + } + + function decodeServiceMessage(bytes memory _rlp) + internal + pure + returns (Types.ServiceMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.ServiceMessage( + Types.ServiceType(ls[0].toUint()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeTransferCoinMsg(bytes memory _rlp) + internal + pure + returns (Types.TransferCoin memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + Types.Asset[] memory assets = new Types.Asset[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpAssets = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + assets[i] = Types.Asset( + string(rlpAssets[i].toList()[0].toBytes()), + rlpAssets[i].toList()[1].toUint() + ); + } + return + Types.TransferCoin( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + assets + ); + } + + function decodeResponse(bytes memory _rlp) + internal + pure + returns (Types.Response memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return Types.Response(ls[0].toUint(), string(ls[1].toBytes())); + } + + function decodeBlockHeader(bytes memory _rlp) + internal + pure + returns (Types.BlockHeader memory) + { + // Decode RLP bytes into a list of items + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bool isSPREmpty = true; + if (ls[10].toBytes().length == 0) { + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR("", "", ""), + isSPREmpty + ); + } + RLPDecode.RLPItem[] memory subList = + ls[10].toBytes().toRlpItem().toList(); + isSPREmpty = false; + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR( + subList[0].toBytes(), + subList[1].toBytes(), + subList[2].toBytes() + ), + isSPREmpty + ); + } + + // Votes item consists of: + // round as integer + // blockPartSetID is a list that consists of two items - integer and bytes + // and TS[] ts_list (an array of list) + function decodeVotes(bytes memory _rlp) + internal + pure + returns (Types.Votes memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.TS[] memory tsList = new Types.TS[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpTs = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + tsList[i] = Types.TS( + rlpTs[i].toList()[0].toUint(), + rlpTs[i].toList()[1].toBytes() + ); + } + return + Types.Votes( + ls[0].toUint(), + Types.BPSI( + ls[1].toList()[0].toUint(), + ls[1].toList()[1].toBytes() + ), + tsList + ); + } + + // Wait for confirmation + function decodeBlockWitness(bytes memory _rlp) + internal + pure + returns (Types.BlockWitness memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + bytes[] memory witnesses = new bytes[](ls[1].toList().length); + // witnesses is an array of hash of leaf node + // The array size may also vary, thus loop is needed therein + for (uint256 i = 0; i < ls[1].toList().length; i++) { + witnesses[i] = ls[1].toList()[i].toBytes(); + } + return Types.BlockWitness(ls[0].toUint(), witnesses); + } + + function decodeEventProof(bytes memory _rlp) + internal + pure + returns (Types.EventProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory data = ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory eventMptNode = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + eventMptNode[i] = data[i].toBytes(); + } + return Types.EventProof(ls[0].toUint(), eventMptNode); + } + + function decodeBlockUpdate(bytes memory _rlp) + internal + pure + returns (Types.BlockUpdate memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + // Types.BlockHeader memory _bh; + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.Votes memory _v = ls[1].toBytes().decodeVotes(); + // Types.Votes memory _v; + + // BlockUpdate may or may not include the RLP of addresses of validators + // In that case, RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START == 0xF800 + // Thus, length of data will be 0. Therein, loop will be skipped + // and the _validators[] will be empty + // Otherwise, executing normally to read and assign value into the array _validators[] + bytes[] memory _validators; + if (ls[2].toBytes().length != 0) { + _validators = new bytes[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _validators[i] = ls[2].toList()[i].toBytes(); + } + } + return Types.BlockUpdate(_bh, _v, _validators); + } + + function decodeReceiptProof(bytes memory _rlp) + internal + pure + returns (Types.ReceiptProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory receiptList = + ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory txReceipts = new bytes[](receiptList.length); + for (uint256 i = 0; i < receiptList.length; i++) { + txReceipts[i] = receiptList[i].toBytes(); + } + + Types.EventProof[] memory _ep = + new Types.EventProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _ep[i] = Types.EventProof( + ls[2].toList()[i].toList()[0].toUint(), + ls[2].toList()[i].toList()[1].toBytes().decodeEventLog() + ); + } + + return Types.ReceiptProof(ls[0].toUint(), txReceipts, _ep); + } + + function decodeEventLog(bytes memory _rlp) + internal + pure + returns (bytes[] memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bytes[] memory eventMptNode = new bytes[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + eventMptNode[i] = ls[i].toBytes(); + } + return eventMptNode; + } + + function decodeBlockProof(bytes memory _rlp) + internal + pure + returns (Types.BlockProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.BlockWitness memory _bw = ls[1].toBytes().decodeBlockWitness(); + + return Types.BlockProof(_bh, _bw); + } + + function decodeRelayMessage(bytes memory _rlp) + internal + pure + returns (Types.RelayMessage memory) + { + // _rlp.toRlpItem() removes the LIST_HEAD_START of RelayMessage + // then .toList() to itemize all fields in the RelayMessage + // which are [RLP_ENCODE(BlockUpdate)], RLP_ENCODE(BlockProof), and + // the RLP_ENCODE(ReceiptProof) + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + // return ( + // ls[0].toList()[0].toBytes().toRlpItem().toList()[1].toBytes().toRlpItem().toList()[2].toList()[0].toList()[1].toBytes() + // ); + + // If [RLP_ENCODE(BlockUpdate)] was empty, it should be started by 0xF800 + // therein, ls[0].toBytes() will be null (length = 0) + // Otherwise, create an array of BlockUpdate struct to decode + Types.BlockUpdate[] memory _buArray; + if (ls[0].toBytes().length != 0) { + _buArray = new Types.BlockUpdate[](ls[0].toList().length); + for (uint256 i = 0; i < ls[0].toList().length; i++) { + // Each of items inside an array [RLP_ENCODE(BlockUpdate)] + // is a string which defines RLP_ENCODE(BlockUpdate) + // that contains a LIST_HEAD_START and multiple RLP of data + // ls[0].toList()[i].toBytes() returns bytes presentation of + // RLP_ENCODE(BlockUpdate) + _buArray[i] = ls[0].toList()[i].toBytes().decodeBlockUpdate(); + } + } + bool isBPEmpty = true; + Types.BlockProof memory _bp; + // If RLP_ENCODE(BlockProof) is omitted, + // ls[1].toBytes() should be null (length = 0) + if (ls[1].toBytes().length != 0) { + _bp = ls[1].toBytes().decodeBlockProof(); + isBPEmpty = false; // add this field into RelayMessage + // to specify whether BlockProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + + bool isRPEmpty = true; + Types.ReceiptProof[] memory _rp; + // If [RLP_ENCODE(ReceiptProof)] is omitted, + // ls[2].toBytes() should be null (length = 0) + if (ls[2].toBytes().length != 0) { + _rp = new Types.ReceiptProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _rp[i] = ls[2].toList()[i].toBytes().decodeReceiptProof(); + } + isRPEmpty = false; // add this field into RelayMessage + // to specify whether ReceiptProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + return Types.RelayMessage(_buArray, _bp, isBPEmpty, _rp, isRPEmpty); + } +} diff --git a/solidity/bmc/contracts/libraries/RLPEncode.sol b/solidity/bmc/contracts/libraries/RLPEncode.sol new file mode 100644 index 00000000..3314b087 --- /dev/null +++ b/solidity/bmc/contracts/libraries/RLPEncode.sol @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title RLPEncode + * @dev A simple RLP encoding library. + * @author Bakaoh + * The original code was modified. For more info, please check the link: + * https://github.com/bakaoh/solidity-rlp-encode.git + */ +library RLPEncode { + int8 internal constant MAX_INT8 = type(int8).max; + int16 internal constant MAX_INT16 = type(int16).max; + int24 internal constant MAX_INT24 = type(int24).max; + int32 internal constant MAX_INT32 = type(int32).max; + int40 internal constant MAX_INT40 = type(int40).max; + int48 internal constant MAX_INT48 = type(int48).max; + int56 internal constant MAX_INT56 = type(int56).max; + int64 internal constant MAX_INT64 = type(int64).max; + int72 internal constant MAX_INT72 = type(int72).max; + int80 internal constant MAX_INT80 = type(int80).max; + int88 internal constant MAX_INT88 = type(int88).max; + int96 internal constant MAX_INT96 = type(int96).max; + int104 internal constant MAX_INT104 = type(int104).max; + int112 internal constant MAX_INT112 = type(int112).max; + int120 internal constant MAX_INT120 = type(int120).max; + int128 internal constant MAX_INT128 = type(int128).max; + + uint8 internal constant MAX_UINT8 = type(uint8).max; + uint16 internal constant MAX_UINT16 = type(uint16).max; + uint24 internal constant MAX_UINT24 = type(uint24).max; + uint32 internal constant MAX_UINT32 = type(uint32).max; + uint40 internal constant MAX_UINT40 = type(uint40).max; + uint48 internal constant MAX_UINT48 = type(uint48).max; + uint56 internal constant MAX_UINT56 = type(uint56).max; + uint64 internal constant MAX_UINT64 = type(uint64).max; + uint72 internal constant MAX_UINT72 = type(uint72).max; + uint80 internal constant MAX_UINT80 = type(uint80).max; + uint88 internal constant MAX_UINT88 = type(uint88).max; + uint96 internal constant MAX_UINT96 = type(uint96).max; + uint104 internal constant MAX_UINT104 = type(uint104).max; + uint112 internal constant MAX_UINT112 = type(uint112).max; + uint120 internal constant MAX_UINT120 = type(uint120).max; + uint128 internal constant MAX_UINT128 = type(uint128).max; + + /* + * Internal functions + */ + + /** + * @dev RLP encodes a byte string. + * @param self The byte string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeBytes(bytes memory self) + internal + pure + returns (bytes memory) + { + bytes memory encoded; + if (self.length == 1 && uint8(self[0]) <= 128) { + encoded = self; + } else { + encoded = concat(encodeLength(self.length, 128), self); + } + return encoded; + } + + /** + * @dev RLP encodes a list of RLP encoded byte byte strings. + * @param self The list of RLP encoded byte strings. + * @return The RLP encoded list of items in bytes. + */ + function encodeList(bytes[] memory self) + internal + pure + returns (bytes memory) + { + bytes memory list = flatten(self); + return concat(encodeLength(list.length, 192), list); + } + + /** + * @dev RLP encodes a string. + * @param self The string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeString(string memory self) + internal + pure + returns (bytes memory) + { + return encodeBytes(bytes(self)); + } + + /** + * @dev RLP encodes an address. + * @param self The address to encode. + * @return The RLP encoded address in bytes. + */ + function encodeAddress(address self) internal pure returns (bytes memory) { + bytes memory inputBytes; + assembly { + let m := mload(0x40) + mstore( + add(m, 20), + xor(0x140000000000000000000000000000000000000000, self) + ) + mstore(0x40, add(m, 52)) + inputBytes := m + } + return encodeBytes(inputBytes); + } + + /** + * @dev RLP encodes a uint. + * @param self The uint to encode. + * @return The RLP encoded uint in bytes. + */ + function encodeUint(uint256 self) internal pure returns (bytes memory) { + uint nBytes = bitLength(self)/8 + 1; + bytes memory uintBytes = encodeUintByLength(self); + if (nBytes - uintBytes.length > 0) { + uintBytes = abi.encodePacked(bytes1(0), uintBytes); + } + return encodeBytes(uintBytes); + } + + /** + * @dev convert int to strict bytes. + * @notice only handle to int128 due to contract code size limit + * @param n The int to convert. + * @return The int in strict bytes without padding. + */ + function intToStrictBytes(int256 n) internal pure returns (bytes memory) { + if (-MAX_INT8 - 1 <= n && n <= MAX_INT8) { + return abi.encodePacked(int8(n)); + } else if (-MAX_INT16 - 1 <= n && n <= MAX_INT16) { + return abi.encodePacked(int16(n)); + } else if (-MAX_INT24 - 1 <= n && n <= MAX_INT24) { + return abi.encodePacked(int24(n)); + } else if (-MAX_INT32 - 1 <= n && n <= MAX_INT32) { + return abi.encodePacked(int32(n)); + } else if (-MAX_INT40 - 1 <= n && n <= MAX_INT40) { + return abi.encodePacked(int40(n)); + } else if (-MAX_INT48 - 1 <= n && n <= MAX_INT48) { + return abi.encodePacked(int48(n)); + } else if (-MAX_INT56 - 1 <= n && n <= MAX_INT56) { + return abi.encodePacked(int56(n)); + } else if (-MAX_INT64 - 1 <= n && n <= MAX_INT64) { + return abi.encodePacked(int64(n)); + } else if (-MAX_INT72 - 1 <= n && n <= MAX_INT72) { + return abi.encodePacked(int72(n)); + } else if (-MAX_INT80 - 1 <= n && n <= MAX_INT80) { + return abi.encodePacked(int80(n)); + } else if (-MAX_INT88 - 1 <= n && n <= MAX_INT88) { + return abi.encodePacked(int88(n)); + } else if (-MAX_INT96 - 1 <= n && n <= MAX_INT96) { + return abi.encodePacked(int96(n)); + } else if (-MAX_INT104 - 1 <= n && n <= MAX_INT104) { + return abi.encodePacked(int104(n)); + } else if (-MAX_INT112 - 1 <= n && n <= MAX_INT112) { + return abi.encodePacked(int112(n)); + } else if (-MAX_INT120 - 1 <= n && n <= MAX_INT120) { + return abi.encodePacked(int120(n)); + } + require(-MAX_INT128 - 1 <= n && n <= MAX_INT128, "outOfBounds: [-2^128-1, 2^128]"); + return abi.encodePacked(int128(n)); + } + + /** + * @dev RLP encodes an int. + * @param self The int to encode. + * @return The RLP encoded int in bytes. + */ + function encodeInt(int256 self) internal pure returns (bytes memory) { + return encodeBytes(intToStrictBytes(self)); + } + + /** + * @dev RLP encodes a bool. + * @param self The bool to encode. + * @return The RLP encoded bool in bytes. + */ + function encodeBool(bool self) internal pure returns (bytes memory) { + bytes memory encoded = new bytes(1); + encoded[0] = (self ? bytes1(0x01) : bytes1(0x00)); + return encoded; + } + + /* + * Private functions + */ + + /** + * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param len The length of the string or the payload. + * @param offset 128 if item is string, 192 if item is list. + * @return RLP encoded bytes. + */ + function encodeLength(uint256 len, uint256 offset) + private + pure + returns (bytes memory) + { + bytes memory encoded; + if (len < 56) { + encoded = new bytes(1); + encoded[0] = bytes32(len + offset)[31]; + } else { + uint256 lenLen; + uint256 i = 1; + while (len / i != 0) { + lenLen++; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes32(lenLen + offset + 55)[31]; + for (i = 1; i <= lenLen; i++) { + encoded[i] = bytes32((len / (256**(lenLen - i))) % 256)[31]; + } + } + return encoded; + } + + /** + * @dev Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function toBinary(uint256 _x) private pure returns (bytes memory) { + // Modify library to make it work properly when _x = 0 + if (_x == 0) { + return abi.encodePacked(uint8(_x)); + } + bytes memory b = new bytes(32); + assembly { + mstore(add(b, 32), _x) + } + uint256 i; + for (i = 0; i < 32; i++) { + if (b[i] != 0) { + break; + } + } + bytes memory res = new bytes(32 - i); + for (uint256 j = 0; j < res.length; j++) { + res[j] = b[i++]; + } + return res; + } + + /** + * @dev Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function memcpy( + uint256 _dest, + uint256 _src, + uint256 _len + ) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask = 256**(32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * @dev Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return The flattened byte string. + */ + function flatten(bytes[] memory _list) private pure returns (bytes memory) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i; + for (i = 0; i < _list.length; i++) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; i++) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } + + /** + * @dev Concatenates two bytes. + * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. + * @param _preBytes First byte string. + * @param _postBytes Second byte string. + * @return Both byte string combined. + */ + function concat(bytes memory _preBytes, bytes memory _postBytes) + private + pure + returns (bytes memory) + { + bytes memory tempBytes; + + assembly { + tempBytes := mload(0x40) + + let length := mload(_preBytes) + mstore(tempBytes, length) + + let mc := add(tempBytes, 0x20) + let end := add(mc, length) + + for { + let cc := add(_preBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + length := mload(_postBytes) + mstore(tempBytes, add(length, mload(tempBytes))) + + mc := end + end := add(mc, length) + + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore( + 0x40, + and( + add(add(end, iszero(add(length, mload(_preBytes)))), 31), + not(31) + ) + ) + } + + return tempBytes; + } + + /** + * @dev convert uint to strict bytes. + * @notice only handle to uint128 due to contract code size limit + * @param length The uint to convert. + * @return The uint in strict bytes without padding. + */ + function encodeUintByLength(uint256 length) + internal + pure + returns (bytes memory) + { + if (length < MAX_UINT8) { + return abi.encodePacked(uint8(length)); + } else if (length >= MAX_UINT8 && length < MAX_UINT16) { + return abi.encodePacked(uint16(length)); + } else if (length >= MAX_UINT16 && length < MAX_UINT24) { + return abi.encodePacked(uint24(length)); + } else if (length >= MAX_UINT24 && length < MAX_UINT32) { + return abi.encodePacked(uint32(length)); + } else if (length >= MAX_UINT32 && length < MAX_UINT40) { + return abi.encodePacked(uint40(length)); + } else if (length >= MAX_UINT40 && length < MAX_UINT48) { + return abi.encodePacked(uint48(length)); + } else if (length >= MAX_UINT48 && length < MAX_UINT56) { + return abi.encodePacked(uint56(length)); + } else if (length >= MAX_UINT56 && length < MAX_UINT64) { + return abi.encodePacked(uint64(length)); + } else if (length >= MAX_UINT64 && length < MAX_UINT72) { + return abi.encodePacked(uint72(length)); + } else if (length >= MAX_UINT72 && length < MAX_UINT80) { + return abi.encodePacked(uint80(length)); + } else if (length >= MAX_UINT80 && length < MAX_UINT88) { + return abi.encodePacked(uint88(length)); + } else if (length >= MAX_UINT88 && length < MAX_UINT96) { + return abi.encodePacked(uint96(length)); + } else if (length >= MAX_UINT96 && length < MAX_UINT104) { + return abi.encodePacked(uint104(length)); + } else if (length >= MAX_UINT104 && length < MAX_UINT112) { + return abi.encodePacked(uint112(length)); + } else if (length >= MAX_UINT112 && length < MAX_UINT120) { + return abi.encodePacked(uint120(length)); + } + require(length >= MAX_UINT120 && length < MAX_UINT128, "outOfBounds: [0, 2^128]"); + return abi.encodePacked(uint128(length)); + } + + function bitLength(uint256 n) internal pure returns (uint256) { + uint256 count; + while (n != 0) { + count += 1; + n >>= 1; + } + return count; + } +} diff --git a/solidity/bmc/contracts/libraries/RLPEncodeStruct.sol b/solidity/bmc/contracts/libraries/RLPEncodeStruct.sol new file mode 100644 index 00000000..ee3e7f9c --- /dev/null +++ b/solidity/bmc/contracts/libraries/RLPEncodeStruct.sol @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPEncode.sol"; +import "./Types.sol"; + +library RLPEncodeStruct { + using RLPEncode for bytes; + using RLPEncode for string; + using RLPEncode for uint256; + using RLPEncode for int256; + using RLPEncode for address; + + using RLPEncodeStruct for Types.BlockHeader; + using RLPEncodeStruct for Types.BlockWitness; + using RLPEncodeStruct for Types.BlockUpdate; + using RLPEncodeStruct for Types.BlockProof; + using RLPEncodeStruct for Types.EventProof; + using RLPEncodeStruct for Types.ReceiptProof; + using RLPEncodeStruct for Types.Votes; + using RLPEncodeStruct for Types.RelayMessage; + + uint8 internal constant LIST_SHORT_START = 0xc0; + uint8 internal constant LIST_LONG_START = 0xf7; + + function encodeBMCService(Types.BMCService memory _bs) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked(_bs.serviceType.encodeString(), _bs.payload); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeGatherFeeMessage(Types.GatherFeeMessage memory _gfm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _gfm.svcs.length; i++) { + temp = _gfm.svcs[i].encodeString(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _gfm.fa.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeRegisterCoin(Types.RegisterCoin memory _rc) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _rc.coinName.encodeString(), + _rc.id.encodeUint(), + _rc.symbol.encodeString() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBMCMessage(Types.BMCMessage memory _bm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bm.src.encodeString(), + _bm.dst.encodeString(), + _bm.svc.encodeString(), + _bm.sn.encodeInt(), + _bm.message.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeServiceMessage(Types.ServiceMessage memory _sm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + uint256(_sm.serviceType).encodeUint(), + _sm.data.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeTransferCoinMsg(Types.TransferCoin memory _data) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _data.assets.length; i++) { + temp = abi.encodePacked( + _data.assets[i].coinName.encodeString(), + _data.assets[i].value.encodeUint() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + _rlp = abi.encodePacked( + _data.from.encodeString(), + _data.to.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeResponse(Types.Response memory _res) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _res.code.encodeUint(), + _res.message.encodeString() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockHeader(Types.BlockHeader memory _bh) + internal + pure + returns (bytes memory) + { + // Serialize the first 10 items in the BlockHeader + // patchTxHash and txHash might be empty. + // In that case, encoding these two items gives the result as 0xF800 + // Similarly, logsBloom might be also empty + // But, encoding this item gives the result as 0x80 + bytes memory _rlp = + abi.encodePacked( + _bh.version.encodeUint(), + _bh.height.encodeUint(), + _bh.timestamp.encodeUint(), + _bh.proposer.encodeBytes(), + _bh.prevHash.encodeBytes(), + _bh.voteHash.encodeBytes(), + _bh.nextValidators.encodeBytes() + ); + bytes memory temp1; + if (_bh.patchTxHash.length != 0) { + temp1 = _bh.patchTxHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1); + + if (_bh.txHash.length != 0) { + temp1 = _bh.txHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1, _bh.logsBloom.encodeBytes()); + bytes memory temp2; + // SPR struct could be an empty struct + // In that case, serialize(SPR) = 0xF800 + if (_bh.isSPREmpty) { + temp2 = emptyListHeadStart(); + } else { + // patchReceiptHash and receiptHash might be empty + // In that case, encoding these two items gives the result as 0xF800 + if (_bh.spr.patchReceiptHash.length != 0) { + temp1 = _bh.spr.patchReceiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(_bh.spr.stateHash.encodeBytes(), temp1); + + if (_bh.spr.receiptHash.length != 0) { + temp1 = _bh.spr.receiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(temp2, temp1); + temp2 = abi + .encodePacked(addLength(temp2.length, false), temp2) + .encodeBytes(); + } + _rlp = abi.encodePacked(_rlp, temp2); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeVotes(Types.Votes memory _vote) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + + // First, serialize an array of TS + for (uint256 i = 0; i < _vote.ts.length; i++) { + temp = abi.encodePacked( + _vote.ts[i].timestamp.encodeUint(), + _vote.ts[i].signature.encodeBytes() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + + // Next, serialize the blockPartSetID + temp = abi.encodePacked( + _vote.blockPartSetID.n.encodeUint(), + _vote.blockPartSetID.b.encodeBytes() + ); + // Combine all of them + _rlp = abi.encodePacked( + _vote.round.encodeUint(), + addLength(temp.length, false), + temp, + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockWitness(Types.BlockWitness memory _bw) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _bw.witnesses.length; i++) { + temp = _bw.witnesses[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _bw.height.encodeUint(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeEventProof(Types.EventProof memory _ep) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _ep.eventMptNode.length; i++) { + temp = _ep.eventMptNode[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + _rlp = abi.encodePacked(_ep.index.encodeUint(), _rlp); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockUpdate(Types.BlockUpdate memory _bu) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // In the case that _validators[] is an empty array, loop will be skipped + // and RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START (0xF800) instead + if (_bu.validators.length != 0) { + for (uint256 i = 0; i < _bu.validators.length; i++) { + temp = _bu.validators[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + } else { + _rlp = emptyListHeadStart(); + } + + _rlp = abi.encodePacked( + _bu.bh.encodeBlockHeader().encodeBytes(), + _bu.votes.encodeVotes().encodeBytes(), + _rlp + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeReceiptProof(Types.ReceiptProof memory _rp) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // Serialize [bytes] which are transaction receipts + for (uint256 i = 0; i < _rp.txReceipts.length; i++) { + temp = _rp.txReceipts[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + bytes memory eventProof; + for (uint256 i = 0; i < _rp.ep.length; i++) { + temp = _rp.ep[i].encodeEventProof(); + eventProof = abi.encodePacked(eventProof, temp); + } + _rlp = abi.encodePacked( + _rp.index.encodeUint(), + _rlp, + addLength(eventProof.length, false), + eventProof + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockProof(Types.BlockProof memory _bp) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bp.bh.encodeBlockHeader().encodeBytes(), + _bp.bw.encodeBlockWitness().encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeRelayMessage(Types.RelayMessage memory _rm) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + if (_rm.buArray.length != 0) { + for (uint256 i = 0; i < _rm.buArray.length; i++) { + temp = _rm.buArray[i].encodeBlockUpdate().encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked(addLength(_rlp.length, false), _rlp); + } else { + _rlp = emptyListShortStart(); + } + + if (_rm.isBPEmpty == false) { + temp = _rm.bp.encodeBlockProof(); + } else { + temp = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp); + + bytes memory receiptProof; + if (_rm.isRPEmpty == false) { + for (uint256 i = 0; i < _rm.rp.length; i++) { + temp = _rm.rp[i].encodeReceiptProof().encodeBytes(); + receiptProof = abi.encodePacked(receiptProof, temp); + } + receiptProof = abi.encodePacked( + addLength(receiptProof.length, false), + receiptProof + ); + } else { + receiptProof = emptyListShortStart(); + } + _rlp = abi.encodePacked(_rlp, receiptProof); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + // Adding LIST_HEAD_START by length + // There are two cases: + // 1. List contains less than or equal 55 elements (total payload of the RLP) -> LIST_HEAD_START = LIST_SHORT_START + [0-55] = [0xC0 - 0xF7] + // 2. List contains more than 55 elements: + // - Total Payload = 512 elements = 0x0200 + // - Length of Total Payload = 2 + // => LIST_HEAD_START = \x (LIST_LONG_START + length of Total Payload) \x (Total Payload) = \x(F7 + 2) \x(0200) = \xF9 \x0200 = 0xF90200 + function addLength(uint256 length, bool isLongList) + internal + pure + returns (bytes memory) + { + if (length > 55 && !isLongList) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(length); + return + abi.encodePacked( + addLength(payLoadSize.length, true), + payLoadSize + ); + } else if (length <= 55 && !isLongList) { + return abi.encodePacked(uint8(LIST_SHORT_START + length)); + } + return abi.encodePacked(uint8(LIST_LONG_START + length)); + } + + function emptyListHeadStart() internal pure returns (bytes memory) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(0); + return + abi.encodePacked( + abi.encodePacked(uint8(LIST_LONG_START + payLoadSize.length)), + payLoadSize + ); + } + + function emptyListShortStart() internal pure returns (bytes memory) { + return abi.encodePacked(LIST_SHORT_START); + } +} diff --git a/solidity/bmc/contracts/libraries/String.sol b/solidity/bmc/contracts/libraries/String.sol new file mode 100644 index 00000000..89da00aa --- /dev/null +++ b/solidity/bmc/contracts/libraries/String.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * String Library + * + * This is a simple library of string functions which try to simplify + * string operations in solidity. + * + * Please be aware some of these functions can be quite gas heavy so use them only when necessary + * + * The original library was modified. If you want to know more about the original version + * please check this link: https://github.com/willitscale/solidity-util.git + */ +library String { + /** + * splitBTPAddress + * + * Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + * into Network_address (1234.iconee) and Server_address (0x123456789) + * + * @param _base String base BTP Address format to be split + * @dev _base must follow a BTP Address format + * + * @return string, string The resulting strings of Network_address and Server_address + */ + function splitBTPAddress(string memory _base) + internal + pure + returns (string memory, string memory) + { + string[] memory temp = split(_base, "/"); + return (temp[2], temp[3]); + } + + /** + * Concat + * + * Appends two strings together and returns a new value + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string which will be the concatenated + * prefix + * @param _value The value to be the concatenated suffix + * @return string The resulting string from combinging the base and value + */ + function concat(string memory _base, string memory _value) + internal + pure + returns (string memory) + { + return string(abi.encodePacked(_base, _value)); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function indexOf(string memory _base, string memory _value) + internal + pure + returns (int256) + { + return _indexOf(_base, _value, 0); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string starting + * from a defined offset + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @param _offset The starting point to start searching from which can start + * from 0, but must not exceed the length of the string + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function _indexOf( + string memory _base, + string memory _value, + uint256 _offset + ) internal pure returns (int256) { + bytes memory _baseBytes = bytes(_base); + bytes memory _valueBytes = bytes(_value); + + assert(_valueBytes.length == 1); + + for (uint256 i = _offset; i < _baseBytes.length; i++) { + if (_baseBytes[i] == _valueBytes[0]) { + return int256(i); + } + } + + return -1; + } + + /** + * Length + * + * Returns the length of the specified string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string to be measured + * @return uint The length of the passed string + */ + function length(string memory _base) internal pure returns (uint256) { + bytes memory _baseBytes = bytes(_base); + return _baseBytes.length; + } + + /* + * String Split (Very high gas cost) + * + * Splits a string into an array of strings based off the delimiter value. + * Please note this can be quite a gas expensive function due to the use of + * storage so only use if really required. + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string value to be split. + * @param _value The delimiter to split the string on which must be a single + * character + * @return string[] An array of values split based off the delimiter, but + * do not container the delimiter. + */ + function split(string memory _base, string memory _value) + internal + pure + returns (string[] memory splitArr) + { + bytes memory _baseBytes = bytes(_base); + + uint256 _offset = 0; + uint256 _splitsCount = 1; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) break; + else { + _splitsCount++; + _offset = uint256(_limit) + 1; + } + } + + splitArr = new string[](_splitsCount); + + _offset = 0; + _splitsCount = 0; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) { + _limit = int256(_baseBytes.length); + } + + string memory _tmp = new string(uint256(_limit) - _offset); + bytes memory _tmpBytes = bytes(_tmp); + + uint256 j = 0; + for (uint256 i = _offset; i < uint256(_limit); i++) { + _tmpBytes[j++] = _baseBytes[i]; + } + _offset = uint256(_limit) + 1; + splitArr[_splitsCount++] = string(_tmpBytes); + } + return splitArr; + } + + /** + * Compare To + * + * Compares the characters of two strings, to ensure that they have an + * identical footprint + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string base to compare against + * @param _value The string the base is being compared to + * @return bool Simply notates if the two string have an equivalent + */ + function compareTo(string memory _base, string memory _value) + internal + pure + returns (bool) + { + if ( + keccak256(abi.encodePacked(_base)) == + keccak256(abi.encodePacked(_value)) + ) { + return true; + } + return false; + } +} diff --git a/solidity/bmc/contracts/libraries/Types.sol b/solidity/bmc/contracts/libraries/Types.sol new file mode 100644 index 00000000..8e6e6d2d --- /dev/null +++ b/solidity/bmc/contracts/libraries/Types.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Types { + /** + * @Notice List of ALL Struct being used to Encode and Decode RLP Messages + */ + + // SPR = State Hash + Pathch Receipt Hash + Receipt Hash + struct SPR { + bytes stateHash; + bytes patchReceiptHash; + bytes receiptHash; + } + + struct BlockHeader { + uint256 version; + uint256 height; + uint256 timestamp; + bytes proposer; + bytes prevHash; + bytes voteHash; + bytes nextValidators; + bytes patchTxHash; + bytes txHash; + bytes logsBloom; + SPR spr; + bool isSPREmpty; // add to check whether SPR is an empty struct, not include in RLP thereafter + } + + // TS = Timestamp + Signature + struct TS { + uint256 timestamp; + bytes signature; + } + + // BPSI = blockPartSetID + struct BPSI { + uint256 n; + bytes b; + } + + struct Votes { + uint256 round; + BPSI blockPartSetID; + TS[] ts; + } + + struct BlockWitness { + uint256 height; + bytes[] witnesses; + } + + struct EventProof { + uint256 index; + bytes[] eventMptNode; + } + + struct BlockUpdate { + BlockHeader bh; + Votes votes; + bytes[] validators; + } + + struct ReceiptProof { + uint256 index; + bytes[] txReceipts; + EventProof[] ep; + } + + struct BlockProof { + BlockHeader bh; + BlockWitness bw; + } + + struct RelayMessage { + BlockUpdate[] buArray; + BlockProof bp; + bool isBPEmpty; // add to check in a case BlockProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + ReceiptProof[] rp; + bool isRPEmpty; // add to check in a case ReceiptProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + } + + /** + * @Notice List of ALL Structs being used by a BSH contract + */ + enum ServiceType { + REQUEST_COIN_TRANSFER, + REQUEST_COIN_REGISTER, + REPONSE_HANDLE_SERVICE, + UNKNOWN_TYPE + } + + struct PendingTransferCoin { + string from; + string to; + string coinName; + uint256 value; + uint256 fee; + } + + struct TransferCoin { + string from; + string to; + Asset[] assets; + } + + struct Asset { + string coinName; + uint256 value; + } + + struct AssetTransferDetail { + string coinName; + uint256 value; + uint256 fee; + } + + struct RegisterCoin { + string coinName; + uint256 id; + string symbol; + } + + struct Response { + uint256 code; + string message; + } + + struct ServiceMessage { + ServiceType serviceType; + bytes data; + } + + struct Coin { + uint256 id; + string symbol; + uint256 decimals; + } + + struct Balance { + uint256 lockedBalance; + uint256 refundableBalance; + } + + struct Request { + string serviceName; + address bsh; + } + + /** + * @Notice List of ALL Structs being used by a BMC contract + */ + struct VerifierStats { + uint256 heightMTA; // MTA = Merkle Trie Accumulator + uint256 offsetMTA; + uint256 lastHeight; // Block height of last verified message which is BTP-Message contained + bytes extra; + } + + struct Service { + string svc; + address addr; + } + + struct Verifier { + string net; + address addr; + } + + struct Route { + string dst; // BTP Address of destination BMC + string next; // BTP Address of a BMC before reaching dst BMC + } + + struct Link { + address[] relays; // Address of multiple Relays handle for this link network + string[] reachable; // A BTP Address of the next BMC that can be reach using this link + uint256 rxSeq; + uint256 txSeq; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 maxAggregation; + uint256 delayLimit; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rxHeight; + uint256 rxHeightSrc; + bool isConnected; + } + + struct LinkStats { + uint256 rxSeq; + uint256 txSeq; + VerifierStats verifier; + RelayStats[] relays; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rotateTerm; + uint256 delayLimit; + uint256 maxAggregation; + uint256 rxHeightSrc; + uint256 rxHeight; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 currentHeight; + } + + struct RelayStats { + address addr; + uint256 blockCount; + uint256 msgCount; + } + + struct BMCMessage { + string src; // an address of BMC (i.e. btp://1234.PARA/0x1234) + string dst; // an address of destination BMC + string svc; // service name of BSH + int256 sn; // sequence number of BMC + bytes message; // serialized Service Message from BSH + } + + struct BMCService { + string serviceType; + bytes payload; + } + + struct GatherFeeMessage { + string fa; // BTP address of Fee Aggregator + string[] svcs; // a list of services + } + + struct Tuple { + string _prev; + string _to; + } +} diff --git a/solidity/bmc/contracts/libraries/Utils.sol b/solidity/bmc/contracts/libraries/Utils.sol new file mode 100644 index 00000000..162ea81c --- /dev/null +++ b/solidity/bmc/contracts/libraries/Utils.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +import "./String.sol"; + +library Utils { + using String for string; + + /** + @notice this function return a ceiling value of division + @dev No need to check validity of num2 (num2 != 0) + @dev It is checked before calling this function + */ + function ceilDiv(uint256 num1, uint256 num2) + internal + pure + returns (uint256) + { + if (num1 % num2 == 0) { + return num1 / num2; + } + return (num1 / num2) + 1; + } + + function getScale(uint256 _blockIntervalSrc, uint256 _blockIntervalDst) + internal + pure + returns (uint256) + { + if (_blockIntervalSrc < 1 || _blockIntervalDst < 1) { + return 0; + } + return ceilDiv(_blockIntervalSrc * 10**6, _blockIntervalDst); + } + + function getRotateTerm(uint256 _maxAggregation, uint256 _scale) + internal + pure + returns (uint256) + { + if (_scale > 0) { + return ceilDiv(_maxAggregation * 10**6, _scale); + } + return 0; + } + + function remove(string[] storage arr, string memory _str) internal { + for (uint256 i = 0; i < arr.length; i++) + if (arr[i].compareTo(_str)) { + arr[i] = arr[arr.length - 1]; + arr.pop(); + break; + } + } +} diff --git a/solidity/bmc/contracts/test/BMCManagementV2.sol b/solidity/bmc/contracts/test/BMCManagementV2.sol new file mode 100644 index 00000000..e21ca80f --- /dev/null +++ b/solidity/bmc/contracts/test/BMCManagementV2.sol @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBMCManagement.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../interfaces/IBMV.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; +import "../libraries/Utils.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMCManagementV2 is IBMCManagement, Initializable { + using ParseAddress for address; + using ParseAddress for string; + using RLPEncodeStruct for Types.BMCService; + using String for string; + using Utils for uint256; + using Utils for string[]; + + mapping(address => bool) private _owners; + uint256 private numOfOwner; + + mapping(string => address) private bshServices; + mapping(string => address) private bmvServices; + mapping(address => Types.RelayStats) private relayStats; + mapping(string => string) private routes; + mapping(string => Types.Link) internal links; // should be private, temporarily set internal for testing + string[] private listBMVNames; + string[] private listBSHNames; + string[] private listRouteKeys; + string[] private listLinkNames; + address private bmcPeriphery; + + uint256 public serialNo; + + address[] private addrs; + + // Use to search by substring + mapping(string => string) private getRouteDstFromNet; + mapping(string => string) private getLinkFromNet; + mapping(string => Types.Tuple) private getLinkFromReachableNet; + + uint256 private constant BLOCK_INTERVAL_MSEC = 1000; + + modifier hasPermission { + // As Soliditty Security Consideration mentioned: https://docs.soliditylang.org/en/v0.6.2/security-considerations.html + // tx.origin should not be used in checking authorization + // However, PyScore implementation have used both 'msg.sender' and 'tx.orgin' + // Thus, this code just follows it + require( + _owners[msg.sender] == true || _owners[tx.origin] == true, + "BMCRevertUnauthorized" + ); + _; + } + + modifier onlyBMCPeriphery { + require(msg.sender == bmcPeriphery, "BMCRevertUnauthorized"); + _; + } + + function initialize() public initializer { + _owners[msg.sender] = true; + numOfOwner++; + } + + function setBMCPeriphery(address _addr) external override hasPermission { + bmcPeriphery = _addr; + } + + /***************************************************************************************** + Add Authorized Owner of Contract + - addOwner(): register additional Owner of this Contract + - removeOwner(): un-register existing Owner of this Contract. Unable to remove last + - isOwner(): checking Ownership of an arbitrary address + *****************************************************************************************/ + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override hasPermission { + _owners[_owner] = true; + numOfOwner++; + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override hasPermission { + require(numOfOwner > 1, "BMCRevertLastOwner"); + require(_owners[_owner] == true, "BMCRevertNotExistsPermission"); + delete _owners[_owner]; + numOfOwner--; + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return _owners[_owner]; + } + + /** + @notice Add the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + @param _addr Service's contract address + */ + function addService(string memory _svc, address _addr) + external + override + hasPermission + { + require(_addr != address(0), "BMCRevertInvalidAddress"); + require(bshServices[_svc] == address(0), "BMCRevertAlreadyExistsBSH"); + bshServices[_svc] = _addr; + listBSHNames.push(_svc); + } + + /** + @notice Unregisters the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + */ + function removeService(string memory _svc) external override hasPermission { + require(bshServices[_svc] != address(0), "BMCRevertNotExistsBSH"); + delete bshServices[_svc]; + listBSHNames.remove(_svc); + } + + /** + @notice Get registered services. + @return _servicers An array of Service. + */ + function getServices() + external + view + override + returns (Types.Service[] memory) + { + Types.Service[] memory services = + new Types.Service[](listBSHNames.length); + for (uint256 i = 0; i < listBSHNames.length; i++) { + services[i] = Types.Service( + listBSHNames[i], + bshServices[listBSHNames[i]] + ); + } + return services; + } + + /** + @notice Registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + @param _addr Address of BMV + */ + function addVerifier(string memory _net, address _addr) + external + override + hasPermission + { + require(bmvServices[_net] == address(0), "BMCRevertAlreadyExistsBMV"); + bmvServices[_net] = _addr; + listBMVNames.push(_net); + } + + /** + @notice Unregisters BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + */ + function removeVerifier(string memory _net) + external + override + hasPermission + { + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + delete bmvServices[_net]; + listBMVNames.remove(_net); + } + + /** + @notice Get registered verifiers. + @return _verifiers An array of Verifier. + */ + function getVerifiers() + external + view + override + returns (Types.Verifier[] memory) + { + Types.Verifier[] memory verifiers = + new Types.Verifier[](listBMVNames.length); + + for (uint256 i = 0; i < listBMVNames.length; i++) { + verifiers[i] = Types.Verifier( + listBMVNames[i], + bmvServices[listBMVNames[i]] + ); + } + return verifiers; + } + + /** + @notice Initializes status information for the link. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function addLink(string calldata _link) external override hasPermission { + (string memory _net, ) = _link.splitBTPAddress(); + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + require( + links[_link].isConnected == false, + "BMCRevertAlreadyExistsLink" + ); + links[_link] = Types.Link( + new address[](0), + new string[](0), + 0, + 0, + BLOCK_INTERVAL_MSEC, + 0, + 10, + 3, + 0, + 0, + 0, + 0, + true + ); + listLinkNames.push(_link); + getLinkFromNet[_net] = _link; + + // propagate an event "LINK" + propagateEvent("Link", _link); + } + + /** + @notice Removes the link and status information. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function removeLink(string calldata _link) external override hasPermission { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + delete links[_link]; + (string memory _net, ) = _link.splitBTPAddress(); + delete getLinkFromNet[_net]; + propagateEvent("Unlink", _link); + listLinkNames.remove(_link); + } + + /** + @notice Get registered links. + @return _links An array of links ( BTP Addresses of the BMCs ). + */ + function getLinks() external view override returns (string[] memory) { + return listLinkNames; + } + + function setLink( + string memory _link, + uint256 _blockInterval, + uint256 _maxAggregation, + uint256 _delayLimit + ) external override hasPermission { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + require( + _maxAggregation >= 1 && _delayLimit >= 1, + "BMCRevertInvalidParam" + ); + Types.Link memory link = links[_link]; + uint256 _scale = link.blockIntervalSrc.getScale(link.blockIntervalDst); + bool resetRotateHeight = false; + if (link.maxAggregation.getRotateTerm(_scale) == 0) { + resetRotateHeight = true; + } + link.blockIntervalDst = _blockInterval; + link.maxAggregation = _maxAggregation; + link.delayLimit = _delayLimit; + + _scale = link.blockIntervalSrc.getScale(_blockInterval); + uint256 _rotateTerm = _maxAggregation.getRotateTerm(_scale); + if (resetRotateHeight && _rotateTerm > 0) { + link.rotateHeight = block.number + _rotateTerm; + link.rxHeight = block.number; + string memory _net; + (_net, ) = _link.splitBTPAddress(); + (link.rxHeightSrc, , ) = IBMV(bmvServices[_net]).getStatus(); + } + links[_link] = link; + } + + function rotateRelay( + string memory _link, + uint256 _currentHeight, + uint256 _relayMsgHeight, + bool _hasMsg + ) external override onlyBMCPeriphery returns (address) { + /* + @dev Solidity does not support calculate rational numbers/floating numbers + thus, a division of _blockIntervalSrc and _blockIntervalDst should be + scaled by 10^6 to minimize proportional error + */ + Types.Link memory link = links[_link]; + uint256 _scale = link.blockIntervalSrc.getScale(link.blockIntervalDst); + uint256 _rotateTerm = link.maxAggregation.getRotateTerm(_scale); + uint256 _baseHeight; + uint256 _rotateCount; + if (_rotateTerm > 0) { + if (_hasMsg) { + // Note that, Relay has to relay this event immediately to BMC + // upon receiving this event. However, Relay is allowed to hold + // no later than 'delay_limit'. Thus, guessHeight comes up + // Arrival time of BTP Message identified by a block height + // BMC starts guessing when an event of 'RelayMessage' was thrown by another BMC + // which is 'guessHeight' and the time BMC receiving this event is 'currentHeight' + // If there is any delay, 'guessHeight' is likely less than 'currentHeight' + uint256 _guessHeight = + link.rxHeight + + uint256((_relayMsgHeight - link.rxHeightSrc) * 10**6) + .ceilDiv(_scale) - + 1; + + if (_guessHeight > _currentHeight) { + _guessHeight = _currentHeight; + } + // Python implementation as: + // rotate_count = math.ceil((guess_height - self.rotate_height)/rotate_term) + // the following code re-write it with using unsigned integer + if (_guessHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _guessHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_guessHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + // No need to check this if using unsigned integer as above + // if (_rotateCount < 0) { + // _rotateCount = 0; + // } + + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + /* Python implementation as: + // skip_count = math.ceil((current_height - guess_height)/self.delay_limit) - 1 + // In case that 'current_height' = 'guess_height' + // it might have an error calculation if using unsigned integer + // Thus, 'skipCount - 1' is moved into if_statement + // For example: + // + 'currentHeight' = 'guessHeight' + // => skipCount = 0 + // => no delay + // + 'currentHeight' > 'guessHeight' and 'currentHeight' - 'guessHeight' <= 'delay_limit' + // => ceil(('currentHeight' - 'guessHeight') / 'delay_limit') = 1 + // => skipCount = skipCount - 1 = 0 + // => not out of 'delay_limit' + // => accepted + // + 'currentHeight' > 'guessHeight' and 'currentHeight' - 'guessHeight' > 'delay_limit' + // => ceil(('currentHeight' - 'guessHeight') / 'delay_limit') = 2 + // => skipCount = skipCount - 1 = 1 + // => out of 'delay_limit' + // => rejected and move to next Relay + */ + uint256 _skipCount = + (_currentHeight - _guessHeight).ceilDiv(link.delayLimit); + + if (_skipCount > 0) { + _skipCount = _skipCount - 1; + _rotateCount += _skipCount; + _baseHeight = _currentHeight; + } + link.rxHeight = _currentHeight; + link.rxHeightSrc = _relayMsgHeight; + links[_link] = link; + } else { + if (_currentHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _currentHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_currentHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + } + return rotate(_link, _rotateTerm, _rotateCount, _baseHeight); + } + return address(0); + } + + function rotate( + string memory _link, + uint256 _rotateTerm, + uint256 _rotateCount, + uint256 _baseHeight + ) internal returns (address) { + Types.Link memory link = links[_link]; + if (_rotateTerm > 0 && _rotateCount > 0) { + link.rotateHeight = _baseHeight + _rotateTerm; + link.relayIdx = link.relayIdx + _rotateCount; + if (link.relayIdx >= link.relays.length) { + link.relayIdx = link.relayIdx % link.relays.length; + } + links[_link] = link; + } + return link.relays[link.relayIdx]; + } + + function propagateEvent(string memory _eventType, string calldata _link) + private + { + string memory _net; + for (uint256 i = 0; i < listLinkNames.length; i++) { + if (links[listLinkNames[i]].isConnected) { + (_net, ) = listLinkNames[i].splitBTPAddress(); + IBMCPeriphery(bmcPeriphery).sendMessage( + _net, + "bmc", + 0, + Types + .BMCService(_eventType, bytes(_link)) + .encodeBMCService() + ); + } + } + } + + /** + @notice Add route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + @param _link BTP Address of the next BMC for the destination + */ + function addRoute(string memory _dst, string memory _link) + external + override + hasPermission + { + require(bytes(routes[_dst]).length == 0, "BTPRevertAlreadyExistRoute"); + // Verify _dst and _link format address + // these two strings must follow BTP format address + // If one of these is failed, revert() + (string memory _net, ) = _dst.splitBTPAddress(); + _link.splitBTPAddress(); + + routes[_dst] = _link; // map _dst to _link + listRouteKeys.push(_dst); // push _dst key into an array of route keys + getRouteDstFromNet[_net] = _link; + } + + /** + @notice Remove route to the BMC. + @dev Caller must be an operator of BTP network. + @param _dst BTP Address of the destination BMC + */ + function removeRoute(string memory _dst) external override hasPermission { + // @dev No need to check if _dst is a valid BTP format address + // since it was checked when adding route at the beginning + // If _dst does not match, revert() + require(bytes(routes[_dst]).length != 0, "BTPRevertNotExistRoute"); + delete routes[_dst]; + (string memory _net, ) = _dst.splitBTPAddress(); + delete getRouteDstFromNet[_net]; + listRouteKeys.remove(_dst); + } + + /** + @notice Get routing information. + @return _routes An array of Route. + */ + function getRoutes() external view override returns (Types.Route[] memory) { + Types.Route[] memory _routes = new Types.Route[](listRouteKeys.length); + for (uint256 i = 0; i < listRouteKeys.length; i++) { + _routes[i] = Types.Route( + listRouteKeys[i], + routes[listRouteKeys[i]] + ); + } + return _routes; + } + + /** + @notice Registers relay for the network. + @dev Called by the Relay-Operator to manage the BTP network. + @param _link BTP Address of connected BMC + @param _addr the address of Relay + */ + function addRelay(string memory _link, address[] memory _addr) + external + override + hasPermission + { + require(links[_link].isConnected == true, "BMCRevertNotExistsLink"); + links[_link].relays = _addr; + for (uint256 i = 0; i < _addr.length; i++) + relayStats[_addr[i]] = Types.RelayStats(_addr[i], 0, 0); + } + + /** + @notice Unregisters Relay for the network. + @dev Called by the Relay-Operator to manage the BTP network. + @param _link BTP Address of connected BMC + @param _addr the address of Relay + */ + function removeRelay(string memory _link, address _addr) + external + override + hasPermission + { + require( + links[_link].isConnected == true && links[_link].relays.length != 0, + "BMCRevertUnauthorized" + ); + for (uint256 i = 0; i < links[_link].relays.length; i++) { + if (links[_link].relays[i] != _addr) { + addrs.push(links[_link].relays[i]); + } + } + links[_link].relays = addrs; + delete addrs; + } + + /** + @notice Get registered relays. + @param _link BTP Address of the connected BMC. + @return _relayes A list of relays. + */ + + function getRelays(string memory _link) + external + view + override + returns (address[] memory) + { + return links[_link].relays; + } + + /******************************* Use for BMC Service *************************************/ + function getBshServiceByName(string memory) + external + pure + override + returns (address) + { + return address(0); + } + + function getBmvServiceByNet(string memory _net) + external + view + override + returns (address) + { + return bmvServices[_net]; + } + + function getLink(string memory _to) + external + view + override + returns (Types.Link memory) + { + return links[_to]; + } + + function getLinkRxSeq(string calldata _prev) + external + view + override + returns (uint256) + { + return links[_prev].rxSeq; + } + + function getLinkTxSeq(string calldata _prev) + external + view + override + returns (uint256) + { + return links[_prev].txSeq; + } + + function getLinkRelays(string calldata _prev) + external + view + override + returns (address[] memory) + { + return links[_prev].relays; + } + + function getRelayStatusByLink(string memory _prev) + external + view + override + returns (Types.RelayStats[] memory _relays) + { + _relays = new Types.RelayStats[](links[_prev].relays.length); + for (uint256 i = 0; i < links[_prev].relays.length; i++) { + _relays[i] = relayStats[links[_prev].relays[i]]; + } + } + + function updateLinkRxSeq(string calldata _prev, uint256 _val) + external + override + onlyBMCPeriphery + { + links[_prev].rxSeq += _val; + } + + function updateLinkTxSeq(string memory _prev) + external + override + onlyBMCPeriphery + { + links[_prev].txSeq++; + } + + function updateLinkReachable(string memory _prev, string[] memory _to) + external + override + onlyBMCPeriphery + { + for (uint256 i = 0; i < _to.length; i++) { + links[_prev].reachable.push(_to[i]); + (string memory _net, ) = _to[i].splitBTPAddress(); + getLinkFromReachableNet[_net] = Types.Tuple(_prev, _to[i]); + } + } + + function deleteLinkReachable(string memory _prev, uint256 _index) + external + override + onlyBMCPeriphery + { + (string memory _net, ) = + links[_prev].reachable[_index].splitBTPAddress(); + delete getLinkFromReachableNet[_net]; + delete links[_prev].reachable[_index]; + links[_prev].reachable[_index] = links[_prev].reachable[ + links[_prev].reachable.length - 1 + ]; + links[_prev].reachable.pop(); + } + + function updateRelayStats( + address relay, + uint256 _blockCountVal, + uint256 _msgCountVal + ) external override onlyBMCPeriphery { + relayStats[relay].blockCount += _blockCountVal; + relayStats[relay].msgCount += _msgCountVal; + } + + function resolveRoute(string memory _dstNet) + external + view + override + onlyBMCPeriphery + returns (string memory, string memory) + { + // search in routes + string memory _dst = getRouteDstFromNet[_dstNet]; + if (bytes(_dst).length != 0) return (routes[_dst], _dst); + + // search in links + _dst = getLinkFromNet[_dstNet]; + if (bytes(_dst).length != 0) return (_dst, _dst); + + // search link by reachable net + Types.Tuple memory res = getLinkFromReachableNet[_dstNet]; + + require( + bytes(res._to).length > 0, + string("BMCRevertUnreachable: ").concat(_dstNet).concat( + " is unreachable" + ) + ); + return (res._prev, res._to); + } + /*******************************************************************************************/ +} diff --git a/solidity/bmc/contracts/test/BMCPeripheryV2.sol b/solidity/bmc/contracts/test/BMCPeripheryV2.sol new file mode 100644 index 00000000..464a9460 --- /dev/null +++ b/solidity/bmc/contracts/test/BMCPeripheryV2.sol @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBSH.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../interfaces/IBMCManagement.sol"; +import "../interfaces/IBMV.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; +import "../libraries/Utils.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMCPeripheryV2 is IBMCPeriphery, Initializable { + using String for string; + using ParseAddress for address; + using RLPDecodeStruct for bytes; + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.Response; + using Utils for uint256; + + uint256 internal constant UNKNOWN_ERR = 0; + uint256 internal constant BMC_ERR = 10; + uint256 internal constant BMV_ERR = 25; + uint256 internal constant BSH_ERR = 40; + + string private bmcBtpAddress; // a network address BMV, i.e. btp://1234.pra/0xabcd + address private bmcManagement; + + function initialize(string memory _network, address _bmcManagementAddr) + public + initializer + { + bmcBtpAddress = string("btp://").concat(_network).concat("/").concat( + address(this).toString() + ); + bmcManagement = _bmcManagementAddr; + } + + event Message( + string _next, // an address of the next BMC (it could be a destination BMC) + uint256 _seq, // a sequence number of BMC (NOT sequence number of BSH) + bytes _msg + ); + + // emit errors in BTP messages processing + event ErrorOnBTPError( + string _svc, + int256 _sn, + uint256 _code, + string _errMsg, + uint256 _svcErrCode, + string _svcErrMsg + ); + + function getBmcBtpAddress() external view override returns (string memory) { + return bmcBtpAddress; + } + + /** + @notice Verify and decode RelayMessage with BMV, and dispatch BTP Messages to registered BSHs + @dev Caller must be a registered relayer. + @param _prev BTP Address of the BMC generates the message + @param _msg base64 encoded string of serialized bytes of Relay Message refer RelayMessage structure + */ + function handleRelayMessage(string calldata _prev, string calldata _msg) + external + override + { + bytes[] memory serializedMsgs = decodeMsgAndValidateRelay(_prev, _msg); + string memory _net; + + // dispatch BTP Messages + Types.BMCMessage memory _message; + for (uint256 i = 0; i < serializedMsgs.length; i++) { + try this.decodeBTPMessage(serializedMsgs[i]) returns ( + Types.BMCMessage memory _decoded + ) { + _message = _decoded; + } catch { + // ignore BTPMessage parse failure? + continue; + } + + if (_message.dst.compareTo(bmcBtpAddress)) { + handleMessage(_prev, _message); + } else { + (_net, ) = _message.dst.splitBTPAddress(); + try IBMCManagement(bmcManagement).resolveRoute(_net) returns ( + string memory _nextLink, + string memory + ) { + _sendMessage(_nextLink, serializedMsgs[i]); + } catch Error(string memory _error) { + _sendError(_prev, _message, BMC_ERR, _error); + } + } + } + IBMCManagement(bmcManagement).updateLinkRxSeq( + _prev, + serializedMsgs.length + ); + } + + function decodeMsgAndValidateRelay( + string calldata _prev, + string calldata _msg + ) internal returns (bytes[] memory) { + (string memory _net, ) = _prev.splitBTPAddress(); + address _bmvAddr = + IBMCManagement(bmcManagement).getBmvServiceByNet(_net); + + require(_bmvAddr != address(0), "BMCRevertNotExistsBMV"); + (uint256 _prevHeight, , ) = IBMV(_bmvAddr).getStatus(); + + // decode and verify relay message + bytes[] memory serializedMsgs = + IBMV(_bmvAddr).handleRelayMessage( + bmcBtpAddress, + _prev, + IBMCManagement(bmcManagement).getLinkRxSeq(_prev), + bytes(_msg) + ); + + // rotate and check valid relay + (uint256 _height, uint256 _lastHeight, ) = IBMV(_bmvAddr).getStatus(); + address relay = + IBMCManagement(bmcManagement).rotateRelay( + _prev, + block.number, + _lastHeight, + serializedMsgs.length > 0 + ); + + if (relay == address(0)) { + address[] memory relays = + IBMCManagement(bmcManagement).getLinkRelays(_prev); + bool check; + for (uint256 i = 0; i < relays.length; i++) + if (msg.sender == relays[i]) { + check = true; + break; + } + require(check, "BMCRevertUnauthorized: not registered relay"); + relay = msg.sender; + } else if (relay != msg.sender) + revert("BMCRevertUnauthorized: invalid relay"); + + IBMCManagement(bmcManagement).updateRelayStats( + relay, + _height - _prevHeight, + serializedMsgs.length + ); + return serializedMsgs; + } + + // @dev Despite this function was set as external, it should be called internally + // since Solidity does not allow using try_catch with internal function + // this solution can solve the issue + function decodeBTPMessage(bytes memory _rlp) + external + pure + returns (Types.BMCMessage memory) + { + return _rlp.decodeBMCMessage(); + } + + function handleMessage(string calldata _prev, Types.BMCMessage memory _msg) + internal + { + address _bshAddr; + if (_msg.svc.compareTo("bmc")) { + Types.BMCService memory _sm; + try this.tryDecodeBMCService(_msg.message) returns ( + Types.BMCService memory res + ) { + _sm = res; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + return; + } + + if (_sm.serviceType.compareTo("FeeGathering")) { + Types.GatherFeeMessage memory _gatherFee; + try this.tryDecodeGatherFeeMessage(_sm.payload) returns ( + Types.GatherFeeMessage memory res + ) { + _gatherFee = res; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + return; + } + + for (uint256 i = 0; i < _gatherFee.svcs.length; i++) { + _bshAddr = IBMCManagement(bmcManagement) + .getBshServiceByName(_gatherFee.svcs[i]); + // If 'svc' not found, ignore + if (_bshAddr != address(0)) { + try + IBSH(_bshAddr).handleFeeGathering( + _gatherFee.fa, + _gatherFee.svcs[i] + ) + {} catch { + // If BSH contract throws a revert error, ignore and continue + } + } + } + } else if (_sm.serviceType.compareTo("Link")) { + string memory _to = _sm.payload.decodePropagateMessage(); + Types.Link memory link = + IBMCManagement(bmcManagement).getLink(_prev); + bool check; + if (link.isConnected) { + for (uint256 i = 0; i < link.reachable.length; i++) + if (_to.compareTo(link.reachable[i])) { + check = true; + break; + } + if (!check) { + string[] memory _links = new string[](1); + _links[0] = _to; + IBMCManagement(bmcManagement).updateLinkReachable( + _prev, + _links + ); + } + } + } else if (_sm.serviceType.compareTo("Unlink")) { + string memory _to = _sm.payload.decodePropagateMessage(); + Types.Link memory link = + IBMCManagement(bmcManagement).getLink(_prev); + if (link.isConnected) { + for (uint256 i = 0; i < link.reachable.length; i++) { + if (_to.compareTo(link.reachable[i])) + IBMCManagement(bmcManagement).deleteLinkReachable( + _to, + i + ); + } + } + } else if (_sm.serviceType.compareTo("Init")) { + string[] memory _links = _sm.payload.decodeInitMessage(); + IBMCManagement(bmcManagement).updateLinkReachable( + _prev, + _links + ); + } else if (_sm.serviceType.compareTo("Sack")) { + // skip this case since it has been removed from internal services + } else revert("BMCRevert: not exists internal handler"); + } else { + _bshAddr = IBMCManagement(bmcManagement).getBshServiceByName( + _msg.svc + ); + if (_bshAddr == address(0)) { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertNotExistsBSH"); + return; + } + + if (_msg.sn >= 0) { + (string memory _net, ) = _msg.src.splitBTPAddress(); + try + IBSH(_bshAddr).handleBTPMessage( + _net, + _msg.svc, + uint256(_msg.sn), + _msg.message + ) + {} catch Error(string memory _error) { + _sendError(_prev, _msg, BSH_ERR, _error); + } + } else { + Types.Response memory _errMsg = _msg.message.decodeResponse(); + try + IBSH(_bshAddr).handleBTPError( + _msg.src, + _msg.svc, + uint256(_msg.sn * -1), + _errMsg.code, + _errMsg.message + ) + {} catch Error(string memory _error) { + emit ErrorOnBTPError( + _msg.svc, + _msg.sn * -1, + _errMsg.code, + _errMsg.message, + BSH_ERR, + _error + ); + } catch (bytes memory _error) { + emit ErrorOnBTPError( + _msg.svc, + _msg.sn * -1, + _errMsg.code, + _errMsg.message, + UNKNOWN_ERR, + string(_error) + ); + } + } + } + } + + // @dev Solidity does not allow using try_catch with internal function + // Thus, work-around solution is the followings + // If there is any error throwing, BMC contract can catch it, then reply back a RC_ERR Response + function tryDecodeBMCService(bytes calldata _msg) + external + pure + returns (Types.BMCService memory) + { + return _msg.decodeBMCService(); + } + + function tryDecodeGatherFeeMessage(bytes calldata _msg) + external + pure + returns (Types.GatherFeeMessage memory) + { + return _msg.decodeGatherFeeMessage(); + } + + function _sendMessage(string memory _to, bytes memory _serializedMsg) + internal + { + IBMCManagement(bmcManagement).updateLinkTxSeq(_to); + emit Message( + _to, + IBMCManagement(bmcManagement).getLinkTxSeq(_to), + _serializedMsg + ); + } + + function _sendError( + string calldata _prev, + Types.BMCMessage memory _message, + uint256 _errCode, + string memory _errMsg + ) internal { + if (_message.sn > 0) { + bytes memory _serializedMsg = + Types + .BMCMessage( + bmcBtpAddress, + _message + .src, + _message + .svc, + _message.sn * -1, + Types.Response(_errCode, _errMsg).encodeResponse() + ) + .encodeBMCMessage(); + _sendMessage(_prev, _serializedMsg); + } + } + + function sendMessage( + string memory, + string memory, + uint256, + bytes memory + ) external pure override { + revert("Upgrade successfully"); + } + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC. + @return tx_seq Next sequence number of the next sending message. + @return rx_seq Next sequence number of the message to receive. + @return verifier VerifierStatus Object contains status information of the BMV. + */ + function getStatus(string calldata _link) + public + view + override + returns (Types.LinkStats memory _linkStats) + { + Types.Link memory link = IBMCManagement(bmcManagement).getLink(_link); + require(link.isConnected == true, "BMCRevertNotExistsLink"); + Types.RelayStats[] memory _relays = + IBMCManagement(bmcManagement).getRelayStatusByLink(_link); + (string memory _net, ) = _link.splitBTPAddress(); + uint256 _height; + uint256 _offset; + uint256 _lastHeight; + (_height, _offset, _lastHeight) = IBMV( + IBMCManagement(bmcManagement).getBmvServiceByNet(_net) + ) + .getStatus(); + uint256 _rotateTerm = + link.maxAggregation.getRotateTerm( + link.blockIntervalSrc.getScale(link.blockIntervalDst) + ); + return + Types.LinkStats( + link.rxSeq, + link.txSeq, + Types.VerifierStats(_height, _offset, _lastHeight, ""), + _relays, + link.relayIdx, + link.rotateHeight, + _rotateTerm, + link.delayLimit, + link.maxAggregation, + link.rxHeightSrc, + link.rxHeight, + link.blockIntervalSrc, + link.blockIntervalDst, + block.number + ); + } +} diff --git a/solidity/bmc/contracts/test/MockBMCManagement.sol b/solidity/bmc/contracts/test/MockBMCManagement.sol new file mode 100644 index 00000000..85229534 --- /dev/null +++ b/solidity/bmc/contracts/test/MockBMCManagement.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../BMCManagement.sol"; +import "../libraries/Utils.sol"; + +contract MockBMCManagement is BMCManagement { + using Utils for uint256; + + struct RelayInfo { + address r; + uint256 cb; + uint256 rh; + } + + RelayInfo private relay; + + function _rotateRelay( + string memory _link, + uint256 _currentHeight, + uint256 _relayMsgHeight, + bool _hasMsg + ) internal returns (address) { + Types.Link memory link = links[_link]; + uint256 _scale = link.blockIntervalSrc.getScale(link.blockIntervalDst); + uint256 _rotateTerm = link.maxAggregation.getRotateTerm(_scale); + uint256 _baseHeight; + uint256 _rotateCount; + if (_rotateTerm > 0) { + if (_hasMsg) { + uint256 _guessHeight = + link.rxHeight + + uint256((_relayMsgHeight - link.rxHeightSrc) * 10**6) + .ceilDiv(_scale) - + 1; + + if (_guessHeight > _currentHeight) { + _guessHeight = _currentHeight; + } + + if (_guessHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _guessHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_guessHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + + uint256 _skipCount = + (_currentHeight - _guessHeight).ceilDiv(link.delayLimit); + + if (_skipCount > 0) { + _skipCount = _skipCount - 1; + _rotateCount += _skipCount; + _baseHeight = _currentHeight; + } + link.rxHeight = _currentHeight; + link.rxHeightSrc = _relayMsgHeight; + links[_link] = link; + } else { + if (_currentHeight < link.rotateHeight) { + _rotateCount = + (link.rotateHeight - _currentHeight).ceilDiv( + _rotateTerm + ) - + 1; + } else { + _rotateCount = (_currentHeight - link.rotateHeight).ceilDiv( + _rotateTerm + ); + } + _baseHeight = + link.rotateHeight + + ((_rotateCount - 1) * _rotateTerm); + } + return rotate(_link, _rotateTerm, _rotateCount, _baseHeight); + } + return address(0); + } + + function relayRotation( + string memory _link, + uint256 _relayMsgHeight, + bool hasMsg + ) external { + address r = _rotateRelay(_link, block.number, _relayMsgHeight, hasMsg); + Types.Link memory link = links[_link]; + relay = RelayInfo(r, block.number, link.rotateHeight); + } + + function getRelay() external view returns (RelayInfo memory) { + return relay; + } + + function mineOneBlock() external {} +} diff --git a/solidity/bmc/contracts/test/MockBMV.sol b/solidity/bmc/contracts/test/MockBMV.sol new file mode 100644 index 00000000..fbc9cb98 --- /dev/null +++ b/solidity/bmc/contracts/test/MockBMV.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBMV.sol"; +import "../libraries/DecodeBase64.sol"; + +contract MockBMV is IBMV { + using DecodeBase64 for string; + + struct MTA { + uint256 height; + bytes32[] roots; + uint256 offset; + uint256 rootsSize; + uint256 cacheSize; + bytes32[] cache; + bool isAllowNewerWitness; + } + + MTA internal mta; + uint256 internal lastBTPHeight; + + /** + @return base64EncodedMTA Base64 encode of Merkle Tree + */ + function getMTA() + external + view + override + returns (string memory base64EncodedMTA) + {} + + /** + @return addr connected BMC address + */ + function getConnectedBMC() external view override returns (address addr) {} + + /** + @return net network address of the blockchain + */ + function getNetAddress() + external + view + override + returns (string memory net) + {} + + /** + @return serializedHash hash of RLP encode from given list of validators + @return addresses list of validators' addresses + */ + function getValidators() + external + view + override + returns (bytes32 serializedHash, address[] memory addresses) + {} + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + override + returns ( + uint256 height, + uint256 offset, + uint256 lastHeight + ) + { + return (mta.height, mta.offset, lastBTPHeight); + } + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string memory, + string memory, + uint256, + bytes calldata _msg + ) external pure override returns (bytes[] memory) { + bytes[] memory btpMsgs = new bytes[](1); + btpMsgs[0] = _msg; + return btpMsgs; + } + + function setStatus( + uint256 _height, + uint256 _offset, + uint256 _lastHeight + ) external { + mta.height = _height; + mta.offset = _offset; + lastBTPHeight = _lastHeight; + } +} diff --git a/solidity/bmc/contracts/test/MockBSH.sol b/solidity/bmc/contracts/test/MockBSH.sol new file mode 100644 index 00000000..d34c392b --- /dev/null +++ b/solidity/bmc/contracts/test/MockBSH.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBSH.sol"; + +contract MockBSH is IBSH { + constructor() {} + + function handleBTPMessage( + string calldata, + string calldata, + uint256 _sn, + bytes calldata + ) external pure override { + require(_sn != 1000, "Mocking error message on handleBTPMessage"); + } + + function handleBTPError( + string calldata, + string calldata, + uint256 _sn, + uint256, + string calldata + ) external pure override { + require(_sn != 1000, "Mocking error message on handleBTPError"); + assert(_sn != 100); // mocking invalid opcode + } + + /** + @notice Handle Gather Fee Request from ICON. + @dev Every BSH must implement this function + @param _fa BTP Address of Fee Aggregator in ICON + @param _svc Name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override + {} +} diff --git a/solidity/bmc/contracts/test/TestLibRLP.sol b/solidity/bmc/contracts/test/TestLibRLP.sol new file mode 100644 index 00000000..fa084c7b --- /dev/null +++ b/solidity/bmc/contracts/test/TestLibRLP.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/RLPEncode.sol"; +import "../libraries/RLPDecode.sol"; + +contract TestLibRLP { + using RLPEncode for *; + using RLPDecode for *; + + function encodeBytes(bytes memory self) + external + pure + returns (bytes memory) + { + return self.encodeBytes(); + } + + function encodeString(string memory self) + external + pure + returns (bytes memory) + { + return self.encodeString(); + } + + function encodeAddress(address self) + external + pure + returns (bytes memory) + { + return self.encodeAddress(); + } + + function encodeUint(uint256 self) + external + pure + returns (bytes memory) + { + return self.encodeUint(); + } + + function encodeInt(int256 self) + external + pure + returns (bytes memory) + { + return self.encodeInt(); + } + + function encodeBool(bool self) + external + pure + returns (bytes memory) + { + return self.encodeBool(); + } + + function encodeList(bytes[] memory self) + external + pure + returns (bytes memory) + { + return self.encodeList(); + } + + function decodeBytes(bytes memory rlpBytes) + external + pure + returns (bytes memory) + { + return rlpBytes.toRlpItem().toBytes(); + } + + function decodeString(bytes memory rlpBytes) + external + pure + returns (string memory) + { + return string(rlpBytes.toRlpItem().toBytes()); + } + + function decodeAddress(bytes memory rlpBytes) + external + pure + returns (address) + { + return rlpBytes.toRlpItem().toAddress(); + } + + function decodeUint(bytes memory rlpBytes) + external + pure + returns (uint256) + { + return rlpBytes.toRlpItem().toUint(); + } + + function decodeInt(bytes memory rlpBytes) + external + pure + returns (int256) + { + return rlpBytes.toRlpItem().toInt(); + } + + function decodeBool(bytes memory rlpBytes) + external + pure + returns (bool) + { + return rlpBytes.toRlpItem().toBoolean(); + } + + function decodeList(bytes memory rlpBytes) + external + pure + returns (bytes[] memory) + { + RLPDecode.RLPItem[] memory rlpItems = rlpBytes.toRlpItem().toList(); + bytes[] memory rlpBytesList = new bytes[](rlpItems.length); + for (uint256 i = 0; i < rlpItems.length; i++) + rlpBytesList[i] = rlpItems[i].toRlpBytes(); + return rlpBytesList; + } +} \ No newline at end of file diff --git a/solidity/bmc/migrations/1_deploy_bmc.js b/solidity/bmc/migrations/1_deploy_bmc.js new file mode 100644 index 00000000..1274a33f --- /dev/null +++ b/solidity/bmc/migrations/1_deploy_bmc.js @@ -0,0 +1,13 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const BMCPeriphery = artifacts.require('BMCPeriphery'); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +module.exports = async function (deployer, network) { + if (network !== "development") { + await deployProxy(BMCManagement, { deployer }); + await deployProxy(BMCPeriphery, [process.env.BMC_BTP_NET, BMCManagement.address], { deployer }); + + const bmcManagement = await BMCManagement.deployed(); + await bmcManagement.setBMCPeriphery(BMCPeriphery.address); + } +}; \ No newline at end of file diff --git a/solidity/bmc/package.json b/solidity/bmc/package.json new file mode 100644 index 00000000..e1784561 --- /dev/null +++ b/solidity/bmc/package.json @@ -0,0 +1,36 @@ +{ + "name": "pra-bmc", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@openzeppelin/contracts-upgradeable": "3.4.1-solc-0.7-2", + "@openzeppelin/truffle-upgrades": "^1.7.0", + "@truffle/hdwallet-provider": "^1.4.0" + }, + "devDependencies": { + "chai": "^4.3.4", + "husky": "^6.0.0", + "lodash": "^4.17.21", + "prettier": "^2.2.1", + "prettier-plugin-solidity": "^1.0.0-beta.7", + "rlp": "^2.2.6", + "solhint": "^3.3.4", + "solhint-plugin-prettier": "^0.0.5", + "truffle-assertions": "^0.9.2", + "urlsafe-base64": "^1.0.0" + }, + "scripts": { + "linter": "./node_modules/.bin/solhint -f table ./contracts/**/*.sol -f table ./contracts/*.sol", + "prettier": "./node_modules/.bin/prettier --write ./contracts -l", + "contract:compile": "truffle compile --all", + "test": "yarn test:unit && yarn test:integration", + "test:unit": "rm -rf .openzeppelin && truffle test test/unit/*.js", + "test:integration" : "rm -rf .openzeppelin && truffle test test/integration/*.js" + + }, + "husky": { + "hooks": { + "pre-push": "yarn linter && yarn prettier" + } + } +} diff --git a/solidity/bmc/scripts/add_bsh_service.js b/solidity/bmc/scripts/add_bsh_service.js new file mode 100644 index 00000000..b00144f0 --- /dev/null +++ b/solidity/bmc/scripts/add_bsh_service.js @@ -0,0 +1,12 @@ +const BMCManagement = artifacts.require('BMCManagement'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + console.log(await bmcManagement.addService(process.env.CURRENTLINK_BSH_SERVICENAME, process.env.CURRENTLINK_BSH_ADDRESS)) + } catch (error) { + console.log(error) + } finally { + callback() + } +} diff --git a/solidity/bmc/scripts/add_link_set_link_add_relay.js b/solidity/bmc/scripts/add_link_set_link_add_relay.js new file mode 100644 index 00000000..67ec5aec --- /dev/null +++ b/solidity/bmc/scripts/add_link_set_link_add_relay.js @@ -0,0 +1,19 @@ +const BMCManagement = artifacts.require('BMCManagement'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + console.log(await bmcManagement.addLink(process.env.NEXTLINK_BTP_ADDRESS)); + console.log(await bmcManagement.setLink( + process.env.NEXTLINK_BTP_ADDRESS, + parseInt(process.env.NEXTLINK_BLOCK_INTERVAL), + parseInt(process.env.NEXTLINK_ROTATION_MAX_AGGERATION), + parseInt(process.env.NEXTLINK_ROTATION_DELAY_LIMIT), + )) + console.log(await bmcManagement.addRelay(process.env.NEXTLINK_BTP_ADDRESS, process.env.RELAY_ADDRESSES.split(','))) + } catch (error) { + console.log(error) + } finally { + callback() + } +} diff --git a/solidity/bmc/scripts/add_verifier.js b/solidity/bmc/scripts/add_verifier.js new file mode 100644 index 00000000..0ca95bb1 --- /dev/null +++ b/solidity/bmc/scripts/add_verifier.js @@ -0,0 +1,12 @@ +const BMCManagement = artifacts.require('BMCManagement'); + +module.exports = async function (callback) { + try { + const bmcManagement = await BMCManagement.deployed(); + console.log(await bmcManagement.addVerifier(process.env.NEXTLINK_BTP_NET, process.env.CURRENTLINK_BMV_ADDRESS)); + } catch (error) { + console.log(error) + } finally { + callback() + } +} diff --git a/solidity/bmc/scripts/get_bmc_status_link.js b/solidity/bmc/scripts/get_bmc_status_link.js new file mode 100644 index 00000000..a1c91d9b --- /dev/null +++ b/solidity/bmc/scripts/get_bmc_status_link.js @@ -0,0 +1,12 @@ +const BMCPeriphery = artifacts.require('BMCPeriphery'); + +module.exports = async function (callback) { + try { + const bmcPeriphery = await BMCPeriphery.deployed(); + console.log(await bmcPeriphery.getStatus(process.env.NEXTLINK_BTP_ADDRESS)) + } catch (error) { + console.log(error) + } finally { + callback() + } +} diff --git a/solidity/bmc/test/integration/BMC.integration.test.js b/solidity/bmc/test/integration/BMC.integration.test.js new file mode 100644 index 00000000..749c0f7b --- /dev/null +++ b/solidity/bmc/test/integration/BMC.integration.test.js @@ -0,0 +1,993 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const BMCPeriphery = artifacts.require('BMCPeriphery'); +const MockBMCManagement = artifacts.require('MockBMCManagement'); +const MockBMV = artifacts.require('MockBMV'); +const MockBSH = artifacts.require('MockBSH'); +const BMCManagementV2 = artifacts.require('BMCManagementV2'); +const BMCPeripheryV2 = artifacts.require('BMCPeripheryV2'); + +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const URLSafeBase64 = require('urlsafe-base64'); +const rlp = require('rlp'); + +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +contract('BMC tests', (accounts) => { + describe('BMC ingration tests', () => { + let bmcManagement, bmcPeriphery, bmv1, bmv2, bmv3, bmv4; + + before(async () => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['0x1281.moonriver', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv1 = await MockBMV.new(); + bmv2 = await MockBMV.new(); + bmv3 = await MockBMV.new(); + bmv4 = await MockBMV.new(); + }); + + /*************************************************************************************** + Add/Remove Service Integration Tests + ***************************************************************************************/ + it('Service management - Scenario 1: Add service successfully if service is not registered', async () => { + let service = 'Coin/WrappedCoin'; + + await bmcManagement.addService(service, accounts[5]); + + let output = await bmcManagement.getServices(); + assert( + output[0].svc === service, output[0].addr === accounts[5], + ); + }); + + it('Service management - Scenario 2: Fail to add service if service is registered', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, accounts[4]), + 'BMCRevertAlreadyExistsBSH' + ); + }); + + it('Service management - Scenario 3: Fail to add service if caller is not contract owner', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, accounts[4], { from: accounts[6] }), + 'BMCRevertUnauthorized' + ); + }); + + it('Service management - Scenario 4: Fail to add service if service\'scontract address is invalid', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, '0x0000000000000000000000000000000000000000'), + 'BMCRevertInvalidAddress' + ); + }); + + it('Service management - Scenario 5: Fail to remove service if caller is not contract owner', async () => { + let service = 'Coin/WrappedCoin'; + await truffleAssert.reverts( + bmcManagement.removeService.call(service, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Service management - Scenario 6: Fail to remove service if service does not exist', async () => { + let service = 'Token A'; + await truffleAssert.reverts( + bmcManagement.removeService.call(service), + 'BMCRevertNotExistsBSH' + ); + }); + + it('Service management - Scenario 7: Remove service successfully if service does exist and caller is contract owner', async () => { + let service = 'Token'; + await bmcManagement.addService(service, accounts[4]); + await bmcManagement.removeService(service); + let output = await bmcManagement.getServices(); + assert( + output.length === 1, + output[0].svc === 'Coin/WrappedCoin', + output[0].addr == accounts[5] + ); + }); + + /*************************************************************************************** + Add/Remove Verifier Integration Tests + ***************************************************************************************/ + it('Verifier Management - Scenario 1: Add verifier successfully if caller is contract owner', async () => { + let network = '0x03.icon'; + await bmcManagement.addVerifier(network, bmv1.address); + let output = await bmcManagement.getVerifiers(); + assert( + output[0].net === network, output[0].addr === bmv1.address, + ); + }); + + it('Verifier Management - Scenario 2: Fail to add verifier if caller is not contract owner', async () => { + let network = '0x03.icon'; + await truffleAssert.reverts( + bmcManagement.addVerifier.call(network, bmv1.address, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Verifier Management - Scenario 3: Fail to add verifier if verifier is already registered', async () => { + let network = '0x03.icon'; + await truffleAssert.reverts( + bmcManagement.addVerifier.call(network, bmv1.address), + 'BMCRevertAlreadyExistsBMV' + ); + }); + + it('Verifier Management - Scenario 4: Fail to remove verifier if caller is not contract owner', async () => { + let network = '0x03.icon'; + await truffleAssert.reverts( + bmcManagement.removeVerifier.call(network, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Verifier Management - Scenario 5: Fail to remove verifer if verifier is not registered', async () => { + let network = '0x04.pra'; + await truffleAssert.reverts( + bmcManagement.removeVerifier.call(network), + 'BMCRevertNotExistsBMV' + ); + }); + + it('Verifier Management - Scenario 6: Remove verifier successfully if verifier is registered and caller is contract owner', async () => { + let network1 = '0x27.pra'; + let network2 = '0x1387.eos'; + let network3 = '0x01.eth'; + await bmcManagement.addVerifier(network1, bmv2.address); + await bmcManagement.addVerifier(network2, bmv3.address); + await bmcManagement.addVerifier(network3, bmv4.address); + await bmcManagement.removeVerifier('0x03.icon'); + let output = await bmcManagement.getVerifiers(); + assert( + output[0].net == network3, output[0].addr == bmv4.address, + output[1].net == network1, output[0].addr == bmv2.address, + output[2].net == network2, output[0].addr == bmv3.address, + ); + }); + + /*************************************************************************************** + Add/Remove Link Integration Tests + ***************************************************************************************/ + it('Link management - Scenario 1: Add link successfully if caller is contract owner', async () => { + let link = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await bmcManagement.addLink(link); + let output = await bmcManagement.getLinks(); + assert( + output[0] === link + ); + }); + + it('Link management - Scenario 2: Fail to add link if caller is not contract owner', async () => { + let link = 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + // BMC adds supporting link, but network has not yet registered any BMV + it('Link management - Scenario 3: Fail to add link if verifier used for this link does not exist', async () => { + let link = 'btp://0x62.sol/0x954024575b469e6c0cd1bc39d489dd25185ec728'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + 'BMCRevertNotExistsBMV' + ); + }); + + it('Link management - Scenario 4: Fail to add link if input invalid BTP address', async () => { + let link = 'btp://1234.eos:0x954024575b469e6c0cd1bc39d489dd25185ec728'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + '' + ); + }); + + it('Link management - Scenario 5: Fail to add link if link is already added', async () => { + let link = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + 'BMCRevertAlreadyExistsLink' + ); + }); + + it('Link management - Scenario 6: Fail to remove link if caller is not contract owner', async () => { + let link = 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await truffleAssert.reverts( + bmcManagement.removeLink.call(link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Link management - Scenario 7: Fail to remove link if link does not exist', async () => { + let link = 'btp://0x62.sol/0x954024575b469e6c0cd1bc39d489dd25185ec728'; + await truffleAssert.reverts( + bmcManagement.removeLink.call(link), + 'BMCRevertNotExistsLink' + ); + }); + + it('Link management - Scenario 8: Remove link successfully if link does exist and caller is contract owner', async () => { + let link1 = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let link2 = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + await bmcManagement.addLink(link1); + await bmcManagement.addLink(link2); + await bmcManagement.removeLink('btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + let output = await bmcManagement.getLinks(); + assert( + output[0] === link2, + output[1] === link1, + ); + }); + + /*************************************************************************************** + Set Link Stats Integration Tests + ***************************************************************************************/ + it('Link configuration - Scenario 1: Fail to set link config if caller is not contract owner', async () => { + let link = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let blockInterval = 15000; + let maxAggregation = 5; + let delayLimit = 4; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Link configuration - Scenario 2: Fail to set link config if link does not exist ', async () => { + let link = 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + let blockInterval = 6000; + let maxAggregation = 7; + let delayLimit = 3; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertNotExistsLink' + ); + }); + + it('Link configuration - Scenario 3: Fail to set link config if max aggregation is invalid', async () => { + let link = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let blockInterval = 6000; + let maxAggregation = 0; + let delayLimit = 3; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertInvalidParam' + ); + }); + + it('Link configuration - Scenario 4: Fail to set link config if delay limit is invalid', async () => { + let link = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let blockInterval = 6000; + let maxAggregation = 5; + let delayLimit = 0; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertInvalidParam' + ); + }); + + it('Link configuration - Scenario 5: Set link config successfully if setting is valid and caller is contract owner', async () => { + let link = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let blockInterval = 15000; + let maxAggregation = 5; + let delayLimit = 4; + let height = 1500; + let offset = 1400; + let lastHeight = 1450; + await bmv4.setStatus(height, offset, lastHeight); + await bmcManagement.setLink(link, blockInterval, maxAggregation, delayLimit); + let status = await bmcPeriphery.getStatus(link); + assert.equal(status.delayLimit, delayLimit, 'invalid delay limit'); + assert.equal(status.maxAggregation, maxAggregation, 'invalid max aggregation'); + assert.equal(status.blockIntervalDst, blockInterval, 'invalid block interval'); + }); + + /*************************************************************************************** + Add/Remove Route Integration Tests + ***************************************************************************************/ + it('Route management - Scenario 1: Add route successfully if caller is contract owner', async () => { + let dst = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + await bmcManagement.addRoute(dst, link); + let output = await bmcManagement.getRoutes(); + assert( + output[0].dst === dst, output[0].next === link, + ); + }); + + it('Route management - Scenario 2: Fail to add route if caller is not contract owner', async () => { + let dst = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Route management - Scenario 3: Fail to add route if route already exists', async () => { + let dst = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link), + 'BTPRevertAlreadyExistRoute' + ); + }); + + it('Route management - Scenario 4: Fail to add route if BTP addresses of links are invalid', async () => { + let dst = 'btp://0x1387.eos:0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let link = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link), + '' + ); + }); + + it('Route management - Scenario 5: Fail to remove route if caller is not contract owner', async () => { + let dst = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + await truffleAssert.reverts( + bmcManagement.removeRoute.call(dst, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Route management - Scenario 6: Fail to remove route if route does not exist', async () => { + let dst = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await truffleAssert.reverts( + bmcManagement.removeRoute.call(dst), + 'BTPRevertNotExistRoute' + ); + }); + + it('Route management - Scenario 7: Remove route successfully if route exists and caller is contract owner', async () => { + let dst1 = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + let link1 = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let dst2 = 'btp://0x02.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let link2 = 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + await bmcManagement.addRoute(dst1, link1); + await bmcManagement.addRoute(dst2, link2); + await bmcManagement.removeRoute('btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'); + let output = await bmcManagement.getRoutes(); + assert( + output[0].dst === dst2, output[0].next === link1, + output[1].dst === dst1, output[0].addr === link1, + ); + }); + + /*************************************************************************************** + Add/Remove Relays Integration Tests + ***************************************************************************************/ + + it('Relay management - Scenario 1: Fail to add relays if caller is not contract owner', async () => { + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await truffleAssert.reverts( + bmcManagement.addRelay.call(link, relays, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Relay management - Scenario 2: Fail to add relays if link does not exist', async () => { + let link = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await truffleAssert.reverts( + bmcManagement.addRelay.call(link, relays), + 'BMCRevertNotExistsLink' + ); + }); + + it('Relay management - Scenario 3: Add relays successfully if caller is contract owner', async () => { + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await bmcManagement.addRelay(link, relays); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[3], result[2] === accounts[4] + ); + }); + + it('Relay management - Scenario 4: Overwrite existing relays successfully ', async () => { + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let relays = [accounts[2], accounts[4], accounts[6], accounts[7]]; + await bmcManagement.addRelay(link, relays); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[4], + result[2] === accounts[6], result[3] === accounts[7] + ); + }); + + it('Relay management - Scenario 5: Fail to remove relay if caller is not contract owner', async () => { + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let relay = accounts[2]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Relay management - Scenario 6: Fail to remove relay if link does not exist', async () => { + let link = 'btp://0x27.pra/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + let relay = accounts[2]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay), + 'BMCRevertUnauthorized' + ); + }); + + it('Relay management - Scenario 7: Fail to remove relay if relays does not exist', async () => { + let link = 'btp://0x01.eth/0x55c45fa2bf3a31d6eedb63400d0805208aa556d5'; + let relay = accounts[8]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay), + 'BMCRevertUnauthorized' + ); + }); + + it('Relay management - Scenario 8: Remove relay successfully if relay/link does exist and caller is contract owner', async () => { + let link = 'btp://0x1387.eos/0x2d4504a081f342e5f31a2f710c1e2c5a2e94d79e'; + let relay = accounts[6]; + await bmcManagement.removeRelay(link, relay); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[4], + result[2] === accounts[7] + ); + }); + + /*************************************************************************************** + Owner Tests + ***************************************************************************************/ + it('Owners management - Scenario 1: Add a new owner successfully if caller is contract owner', async () => { + await bmcManagement.addOwner(accounts[1]); + const res = await bmcManagement.isOwner(accounts[1]); + assert.isTrue(res); + }); + + it('Owners management - Scenario 2: Fail to add a new owner if caller is not contract owner', async () => { + await truffleAssert.reverts( + bmcManagement.addOwner.call(accounts[1], { from: accounts[2] }), + 'BMCRevertUnauthorized' + ); + }); + + it('Owners management - Scenario 3: Fail to remove an owner if number of owners is 1', async () => { + await bmcManagement.removeOwner(accounts[1]); + await truffleAssert.reverts( + bmcManagement.removeOwner.call(accounts[0]), + 'BMCRevertLastOwner' + ); + }); + + it('Owners management - Scenario 4: Fail to remove an owner if caller is not contract owner', async () => { + await bmcManagement.addOwner(accounts[1]); + await truffleAssert.reverts( + bmcManagement.removeOwner.call(accounts[1], { from: accounts[2] }), + 'BMCRevertUnauthorized' + ); + }); + + it('Owners management - Scenario 5: Remove an owner successfully', async () => { + await bmcManagement.removeOwner(accounts[1]); + const res = await bmcManagement.isOwner(accounts[1]); + assert.isFalse(res); + }); + }); + + describe('Handle relay message tests', () => { + let bmcManagement, bmcPeriphery, bmv, bsh; + let network = '0x03.icon'; + let link = 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'; + let height = 0; + let offset = 0; + let lastHeight = 0; + let blockInterval = 3000; + let maxAggregation = 5; + let delayLimit = 3; + let relays; + + beforeEach(async () => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['0x27.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + bsh = await MockBSH.new(); + await bmcManagement.addService('Token', bsh.address); + await bmcManagement.addVerifier(network, bmv.address); + await bmcManagement.addLink(link); + relays = [accounts[2], accounts[3], accounts[4], accounts[5]]; + await bmcManagement.addRelay(link, relays); + await bmv.setStatus(height, offset, lastHeight); + await bmcManagement.setLink(link, blockInterval, maxAggregation, delayLimit); + }); + + it('Scenario 1: Revert if relay is invalid', async() => { + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x01', // rlp encode of signed int + 'message' + ]); + + await truffleAssert.reverts(bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString()), 'BMCRevertUnauthorized: invalid relay'); + + let bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 0, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'failed to update txSeq'); + }); + + it('Scenario 2: Revert if relay is not registered', async() => { + await bmcManagement.removeLink(link); + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + 'message' + ]); + + await truffleAssert.reverts(bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString()), 'BMCRevertUnauthorized: not registered relay'); + + let bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 0, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 0, 'failed to update txSeq'); + }); + + it('Scenario 3: Dispatch service message to BSH', async() => { + const transferCoin = [ + '0xaaa', + 'btp://0x27.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + rlp.encode(transferCoin) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + let bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + }); + + it('Scenario 4: Init link via BTP messages', async() => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['0x27.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + await bmcManagement.addVerifier(network, bmv.address); + + let tx = await bmcManagement.addLink(link); + let events = await bmcPeriphery.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + assert.equal(events[0].event, 'Message'); + + let eventData = events[0].returnValues; + assert.equal(eventData._next, link); + assert.equal(eventData._seq, 1); + + const bmcBtpAddr = await bmcPeriphery.getBmcBtpAddress(); + + const encodedSendingBtpMsg = '0x' + rlp.encode([ + bmcBtpAddr, + link, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]).toString('hex'); + assert.equal(eventData._msg, encodedSendingBtpMsg); + + const encodedReceivedBtpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]); + + await bmcManagement.addRelay(link, relays); + await bmcPeriphery.handleRelayMessage(link, encodedReceivedBtpMsg, {from: accounts[2]}); + + const linkInfo = await bmcManagement.getLink(link); + assert.isEmpty(linkInfo.reachable); + }); + + it('Scenario 5: Process LINK and UNLINK via BTP messages', async() => { + const bmcBtpAddr = await bmcPeriphery.getBmcBtpAddress(); + + const encodedReceivedBtpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + + const linkInfo = await bmcManagement.getLink(link); + assert.isEmpty(linkInfo.reachable); + + const btpAddress = 'btp://0x01.eth/' + web3.utils.randomHex(20); + + btpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x01', + rlp.encode([ + 'Link', + rlp.encode([ + btpAddress, + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[4]}); + + let bmcLink = await bmcManagement.getLink(link); + assert.equal(bmcLink.reachable[0], btpAddress, 'invalid reachable btp address'); + assert.equal(bmcLink.rxSeq, 2, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + + btpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x02', + rlp.encode([ + 'Unlink', + rlp.encode([ + btpAddress, + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[5]}); + + bmcLink = await bmcManagement.getLink(link); + assert.isEmpty(bmcLink.reachable, 'failed to remove link in reachable'); + assert.equal(bmcLink.rxSeq, 3, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + }); + + it('Scenario 6: Revert if internal handler does not exist', async () => { + const btpAddress = 'btp://0x01.eth/' + web3.utils.randomHex(20); + let eventMsg = [ + 'Unknown', + [ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + btpAddress, + ] + ]; + + let btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'bmc', + '0x00', + rlp.encode(eventMsg) + ]); + + await truffleAssert.reverts( + bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString(), {from: accounts[3]}), + 'BMCRevert: not exists internal handler' + ); + }); + + it('Scenario 7: Emit event to send BTP error response if routes are failed to resolve', async() => { + const btpAddress = 'btp://0x01.eth/' + web3.utils.randomHex(20); + let eventMsg = [ + 'Link', + [ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + btpAddress, + ] + ]; + + let btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://1234.solana/' + bmcPeriphery.address, + '_event', + '0x02', + rlp.encode(eventMsg) + ]); + + const tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://0x27.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), '_event'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement form of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertUnreachable: 1234.solana is unreachable'); + + bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('Scenario 8: Emit event to send BTP message to next BMC if routes are succeeded to resolve', async() => { + const destBtpAddress = 'btp://0x27.pra/' + bmcPeriphery.address; + const routeBtpAddress = 'btp://0x1028.solana/' + web3.utils.randomHex(20); + + await bmcManagement.addRoute( + routeBtpAddress, + destBtpAddress + ); + + await bmcManagement.addVerifier('0x27.pra', web3.utils.randomHex(20)); + await bmcManagement.addLink(destBtpAddress); + + const transferCoin = [ + '0xaaa', + 'btp://0x27.pra/0xbbb', + 'ICX', + 12 + ]; + + let btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + routeBtpAddress, + 'Token', + '0x02', + rlp.encode(transferCoin) + ]); + + const tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[4]}); + + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + return ev._next === destBtpAddress && ev._seq.toNumber() === 2 && ev._msg.toString() === '0x' + btpMsg.toString('hex'); + }); + + let bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'invalid txSeq'); + + bmcLink = await bmcManagement.getLink(destBtpAddress); + assert.equal(bmcLink.txSeq, 2, 'invalid txSeq'); + }); + + it('Scenario 9: Emit event to send BTP response message if destined BMC in BTP message is current BMC address (msg.sn >= 0)', async() => { + const transferCoin = [ + '0xaaa', + 'btp://0x27.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + rlp.encode(transferCoin) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + }); + + it('Scenario 10: Emit event to send BTP error response if destined BMC in BTP message is current BMC address (msg.sn >= 0) in case that BSH gets errors', async() => { + const transferCoin = [ + '0xaaa', + 'btp://0x27.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x03e8', // two complement of 1000 + rlp.encode(transferCoin) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://0x27.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'Token'); + assert.equal(bmcMessage[3].toString('hex'), 'fc18'); // two complement of -1000 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 40); // BSH_ERR = 40 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'Mocking error message on handleBTPMessage'); + + bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('Scenario 11: BSH handle BTP error successfully if destied BMC in BTP message is current BMC address (msg.sn < 0)', async() => { + const errResponse = [ + 0, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0xf6', // two complement of -10 + rlp.encode(errResponse) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + }); + + it('Scenario 12: Emit error event if destied BMC in BTP message is current BMC address (msg.sn < 0) and BSH reverts', async() => { + const errResponse = [ + 0, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0xfc18', // two complement of -1000 + rlp.encode(errResponse) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + await truffleAssert.eventEmitted(tx, 'ErrorOnBTPError', (ev) => { + return ev[0] === 'Token' && ev[1].toNumber() === 1000 && + ev[2].toNumber() === 0 && ev[3].toString() === 'Invalid service' && + ev[4].toNumber() === 40 && ev[5].toString() === 'Mocking error message on handleBTPError'; + }); + }); + + it('Scenario 13: Emit error event if destied BMC in BTP message is current BMC address (msg.sn < 0) and BSH reverts by low level error', async() => { + const errResponse = [ + 12, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'Token', + '0x9c', // two complement of -100 + rlp.encode(errResponse) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + await truffleAssert.eventEmitted(tx, 'ErrorOnBTPError', (ev) => { + return ev[0] === 'Token' && ev[1].toNumber() === 100 && + ev[2].toNumber() === 12 && ev[3].toString() === 'Invalid service' && + ev[4].toNumber() === 0 && ev[5].toString() === ''; + }); + }); + + it('Scenario 14: Emit event to send BTP error response if fee gathering message is failed to decode', async() => { + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + 'invalid rlp of service message' + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://0x27.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'bmc'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertParseFailure'); + + bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('Scenario 15: Emit event to send BTP error response if fee gathering message is failed to decode', async() => { + const serviceMsg = [ + 'FeeGathering', + 'invalid rlp of fee gather messgage', + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + rlp.encode(serviceMsg) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://0x27.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'bmc'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertParseFailure'); + + bmcLink = await bmcManagement.getLink('btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('Scenario 16: Dispatch gather fee message to BSH services', async() => { + const gatherFeeMsg = [ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a35678', + ['service1', 'service2', 'service3'] + ] + + const serviceMsg = [ + 'FeeGathering', + rlp.encode(gatherFeeMsg), + ]; + + const btpMsg = rlp.encode([ + 'btp://0x03.icon/cx10c8c08724e7a95c84829c07239ae2b839a262a3', + 'btp://0x27.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + rlp.encode(serviceMsg) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + assert.isNotEmpty(tx); + }); + }); +}); diff --git a/solidity/bmc/test/unit/BMC.unit.test.js b/solidity/bmc/test/unit/BMC.unit.test.js new file mode 100644 index 00000000..006f7936 --- /dev/null +++ b/solidity/bmc/test/unit/BMC.unit.test.js @@ -0,0 +1,1166 @@ +const BMCManagement = artifacts.require('BMCManagement'); +const BMCPeriphery = artifacts.require('BMCPeriphery'); +const MockBMCManagement = artifacts.require('MockBMCManagement'); +const MockBMV = artifacts.require('MockBMV'); +const MockBSH = artifacts.require('MockBSH'); +const BMCManagementV2 = artifacts.require('BMCManagementV2'); +const BMCPeripheryV2 = artifacts.require('BMCPeripheryV2'); + +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const URLSafeBase64 = require('urlsafe-base64'); +const rlp = require('rlp'); + +const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +let convertEthRlpToIconRlp = (buff, prefix=true) => { + return ((prefix) ? '0x': '') + buff.toString('hex').replace(/c1c0/g, 'f800'); +}; + +contract('BMC tests', (accounts) => { + describe('BMC Basic Unit Tests', () => { + let bmcManagement, bmcPeriphery, bmv1, bmv2, bmv3, bmv4; + + before(async () => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['0x1234.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv1 = await MockBMV.new(); + bmv2 = await MockBMV.new(); + bmv3 = await MockBMV.new(); + bmv4 = await MockBMV.new(); + }); + + /*************************************************************************************** + Set BMC Periphery Unit Tests + ***************************************************************************************/ + it('Set BMC Periphery - should revert if param input is address(0)', async () => { + await truffleAssert.reverts( + bmcManagement.setBMCPeriphery.call('0x0000000000000000000000000000000000000000'), + 'BMCRevertInvalidAddress' + ); + }); + + it('Set BMC Periphery - should revert if param input is duplicate address', async () => { + await truffleAssert.reverts( + bmcManagement.setBMCPeriphery.call(bmcPeriphery.address), + 'BMCRevertAlreadyExistsBMCPeriphery' + ); + }); + + it('Set BMC Periphery successfully', async () => { + await bmcManagement.setBMCPeriphery('0x0000000000000000000000000000000000000001'); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + }); + + /*************************************************************************************** + Add/Remove Service Unit Tests + ***************************************************************************************/ + it('Add Service - Service not requested nor registered - Success', async () => { + let service = 'Coin/WrappedCoin'; + + await bmcManagement.addService(service, accounts[5]); + + let output = await bmcManagement.getServices(); + assert( + output[0].svc === service, output[0].addr === accounts[5], + ); + }); + + it('Add Service - Service is registered - Failure', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, accounts[4]), + 'BMCRevertAlreadyExistsBSH' + ); + }); + + it('Add Service - Without Permission - Failure', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, accounts[4], { from: accounts[6] }), + 'BMCRevertUnauthorized' + ); + }); + + it('Add Service - Service contract address is invalid - Failure', async () => { + let service = 'Coin/WrappedCoin'; + + await truffleAssert.reverts( + bmcManagement.addService.call(service, '0x0000000000000000000000000000000000000000'), + 'BMCRevertInvalidAddress' + ); + }); + + it('Remove Service - Without Permission - Failure', async () => { + let service = 'Coin/WrappedCoin'; + await truffleAssert.reverts( + bmcManagement.removeService.call(service, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Service - Service Not Existed - Failure', async () => { + let service = 'Token A'; + await truffleAssert.reverts( + bmcManagement.removeService.call(service), + 'BMCRevertNotExistsBSH' + ); + }); + + it('Remove Service - Service Existed and With Permission - Success', async () => { + let service = 'Token'; + await bmcManagement.addService(service, accounts[4]); + await bmcManagement.removeService(service); + let output = await bmcManagement.getServices(); + assert( + output.length === 1, + output[0].svc === 'Coin/WrappedCoin', + output[0].addr == accounts[5] + ); + }); + + /*************************************************************************************** + Add/Remove Verifier Unit Tests + ***************************************************************************************/ + it('Add Verifier - With Permission - Success', async () => { + let network = '1234.iconee'; + await bmcManagement.addVerifier(network, bmv1.address); + let output = await bmcManagement.getVerifiers(); + assert( + output[0].net === network, output[0].addr === bmv1.address, + ); + }); + + it('Add Verifier - Without Permission - Failure', async () => { + let network = '1234.iconee'; + await truffleAssert.reverts( + bmcManagement.addVerifier.call(network, bmv1.address, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Add Verifier - Service Existed - Failure', async () => { + let network = '1234.iconee'; + await truffleAssert.reverts( + bmcManagement.addVerifier.call(network, bmv1.address), + 'BMCRevertAlreadyExistsBMV' + ); + }); + + it('Remove Verifier - Without Permission - Failure', async () => { + let network = '1234.iconee'; + await truffleAssert.reverts( + bmcManagement.removeVerifier.call(network, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Verifier - Service Not Existed - Failure', async () => { + let network = '1234.pra'; + await truffleAssert.reverts( + bmcManagement.removeVerifier.call(network), + 'BMCRevertNotExistsBMV' + ); + }); + + it('Remove Verifier - Verifier Existed and With Permission - Success', async () => { + let network1 = '1234.pra'; + let network2 = '1234.eos'; + let network3 = '1234.eth'; + await bmcManagement.addVerifier(network1, bmv2.address); + await bmcManagement.addVerifier(network2, bmv3.address); + await bmcManagement.addVerifier(network3, bmv4.address); + await bmcManagement.removeVerifier('1234.iconee'); + let output = await bmcManagement.getVerifiers(); + assert( + output[0].net == network3, output[0].addr == bmv4.address, + output[1].net == network1, output[0].addr == bmv2.address, + output[2].net == network2, output[0].addr == bmv3.address, + ); + }); + + /*************************************************************************************** + Add/Remove Link Unit Tests + ***************************************************************************************/ + it('Add Link - With Permission - Success', async () => { + let link = 'btp://1234.pra/0x1234'; + await bmcManagement.addLink(link); + let output = await bmcManagement.getLinks(); + assert( + output[0] === link + ); + }); + + it('Add Link - Without Permission - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + // BMC adds supporting link, but network has not yet registered any BMV + it('Add Link - Verifier Not Existed - Failure', async () => { + let link = 'btp://1234.btc/0x1234'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + 'BMCRevertNotExistsBMV' + ); + }); + + it('Add Link - Invalid BTP Address Format - Failure', async () => { + let link = 'btp://1234.eos:0x1234'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + '' + ); + }); + + it('Add Link - Link Existed - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.addLink.call(link), + 'BMCRevertAlreadyExistsLink' + ); + }); + + it('Remove Link - Without Permission - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.removeLink.call(link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Link - Link Not Existed - Failure', async () => { + let link = 'btp://1234.btc/0x1234'; + await truffleAssert.reverts( + bmcManagement.removeLink.call(link), + 'BMCRevertNotExistsLink' + ); + }); + + it('Remove Link - Link Existed and With Permission - Success', async () => { + let link1 = 'btp://1234.eos/0x1234'; + let link2 = 'btp://1234.eth/0x1234'; + await bmcManagement.addLink(link1); + await bmcManagement.addLink(link2); + await bmcManagement.removeLink('btp://1234.pra/0x1234'); + let output = await bmcManagement.getLinks(); + assert( + output[0] === link2, + output[1] === link1, + ); + }); + + /*************************************************************************************** + Set Link Stats Unit Tests + ***************************************************************************************/ + it('Set Link - Without Permission - Failure', async () => { + let link = 'btp://1234.eth/0x1234'; + let blockInterval = 15000; + let maxAggregation = 5; + let delayLimit = 4; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Set Link - Link not Existed - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + let blockInterval = 6000; + let maxAggregation = 7; + let delayLimit = 3; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertNotExistsLink' + ); + }); + + it('Set Link - Invalid Max Aggregation - Failure', async () => { + let link = 'btp://1234.eth/0x1234'; + let blockInterval = 6000; + let maxAggregation = 0; + let delayLimit = 3; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertInvalidParam' + ); + }); + + it('Set Link - Invalid Delay Limit - Failure', async () => { + let link = 'btp://1234.eth/0x1234'; + let blockInterval = 6000; + let maxAggregation = 5; + let delayLimit = 0; + await truffleAssert.reverts( + bmcManagement.setLink.call(link, blockInterval, maxAggregation, delayLimit), + 'BMCRevertInvalidParam' + ); + }); + + it('Set Link - With Permission and Valid Settings - Success', async () => { + let link = 'btp://1234.eth/0x1234'; + let blockInterval = 15000; + let maxAggregation = 5; + let delayLimit = 4; + let height = 1500; + let offset = 1400; + let lastHeight = 1450; + await bmv4.setStatus(height, offset, lastHeight); + await bmcManagement.setLink(link, blockInterval, maxAggregation, delayLimit); + let status = await bmcPeriphery.getStatus(link); + assert.equal(status.delayLimit, delayLimit, 'invalid delay limit'); + assert.equal(status.maxAggregation, maxAggregation, 'invalid max aggregation'); + assert.equal(status.blockIntervalDst, blockInterval, 'invalid block interval'); + }); + + /*************************************************************************************** + Add/Remove Route Unit Tests + ***************************************************************************************/ + it('Add Route - With Permission - Success', async () => { + let dst = 'btp://1234.iconee/0x1234'; + let link = 'btp://1234.pra/0x1234'; + await bmcManagement.addRoute(dst, link); + let output = await bmcManagement.getRoutes(); + assert( + output[0].dst === dst, output[0].next === link, + ); + }); + + it('Add Route - Without Permission - Failure', async () => { + let dst = 'btp://1234.iconee/0x1234'; + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Add Route - Route Existed - Failure', async () => { + let dst = 'btp://1234.iconee/0x1234'; + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link), + 'BTPRevertAlreadyExistRoute' + ); + }); + + it('Add Route - Destination/Link is invalid BTP Format Address - Failure', async () => { + let dst = 'btp://1234.iconee:0x1234'; + let link = 'btp://1234.pra/0x1234'; + await truffleAssert.reverts( + bmcManagement.addRoute.call(dst, link), + '' + ); + }); + + it('Remove Route - Without Permission - Failure', async () => { + let dst = 'btp://1234.iconee/0x1234'; + await truffleAssert.reverts( + bmcManagement.removeRoute.call(dst, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Route - Route Not Existed - Failure', async () => { + let dst = 'btp://1234.eos/0x1234'; + await truffleAssert.reverts( + bmcManagement.removeRoute.call(dst), + 'BTPRevertNotExistRoute' + ); + }); + + it('Remove Route - Route Existed and With Permission - Success', async () => { + let dst1 = 'btp://1234.pra/0x1234'; + let link1 = 'btp://1234.eos/0x1234'; + let dst2 = 'btp://1234.eth/0x1234'; + let link2 = 'btp://1234.iconee/0x1234'; + await bmcManagement.addRoute(dst1, link1); + await bmcManagement.addRoute(dst2, link2); + await bmcManagement.removeRoute('btp://1234.iconee/0x1234'); + let output = await bmcManagement.getRoutes(); + assert( + output[0].dst === dst2, output[0].next === link1, + output[1].dst === dst1, output[0].addr === link1, + ); + }); + + /*************************************************************************************** + Add/Remove Relays Unit Tests + ***************************************************************************************/ + + it('Add Relays - Without Permission - Failure', async () => { + let link = 'btp://1234.eos/0x1234'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await truffleAssert.reverts( + bmcManagement.addRelay.call(link, relays, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Add Relays - Link not existed - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await truffleAssert.reverts( + bmcManagement.addRelay.call(link, relays), + 'BMCRevertNotExistsLink' + ); + }); + + it('Add Relays - With Permission - Success', async () => { + let link = 'btp://1234.eos/0x1234'; + let relays = [accounts[2], accounts[3], accounts[4]]; + await bmcManagement.addRelay(link, relays); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[3], result[2] === accounts[4] + ); + }); + + it('Add Relays - Update Relays on existing one (Overwrite) - Success', async () => { + let link = 'btp://1234.eos/0x1234'; + let relays = [accounts[2], accounts[4], accounts[6], accounts[7]]; + await bmcManagement.addRelay(link, relays); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[4], + result[2] === accounts[6], result[3] === accounts[7] + ); + }); + + it('Remove Relay - Without Permission - Failure', async () => { + let link = 'btp://1234.eos/0x1234'; + let relay = accounts[2]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay, {from: accounts[1]}), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Relay - Link Not Existed - Failure', async () => { + let link = 'btp://1234.pra/0x1234'; + let relay = accounts[2]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Relay - None of any relays - Failure', async () => { + let link = 'btp://1234.eth/0x1234'; + let relay = accounts[2]; + await truffleAssert.reverts( + bmcManagement.removeRelay.call(link, relay), + 'BMCRevertUnauthorized' + ); + }); + + it('Remove Relay - Link/Relays existed and With permission - Success', async () => { + let link = 'btp://1234.eos/0x1234'; + let relay = accounts[6]; + await bmcManagement.removeRelay(link, relay); + let result = await bmcManagement.getRelays(link); + assert( + result[0] === accounts[2], result[1] === accounts[4], + result[2] === accounts[7] + ); + }); + }); + + describe('BMC Relay Rotation Unit Test - Rotate_term = 0', () => { + let bmcManagement, bmcPeriphery, bmv; + let network = '1234.iconee'; + let link = 'btp://1234.iconee/0x1234'; + let height = 0; + let offset = 0; + let lastHeight = 0; + let relays; + before(async () => { + bmcManagement = await deployProxy(MockBMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['1234.iconee', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + await bmcManagement.addVerifier(network, bmv.address); + await bmcManagement.addLink(link); + relays = [accounts[2], accounts[3], accounts[4], accounts[5]]; + await bmcManagement.addRelay(link, relays); + await bmv.setStatus(height, offset, lastHeight); + }); + + it('Link not set - Return address(0)', async () => { + await bmcManagement.relayRotation(link, 26, false); + let relay_info = await bmcManagement.getRelay(); + assert(relay_info.r === '0x0000000000000000000000000000000000000000'); + }); + }); + + describe('BMC Relay Rotation Unit Test - Rotate_term != 0', () => { + let bmcManagement, bmcPeriphery, bmv; + let network = '1234.iconee'; + let link = 'btp://1234.iconee/0x1234'; + let height = 0; + let offset = 0; + let lastHeight = 0; + let blockInterval = 3000; + let maxAggregation = 5; + let delayLimit = 3; + let relays; + let current; + before(async () => { + bmcManagement = await deployProxy(MockBMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['1234.iconee', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + await bmcManagement.addVerifier(network, bmv.address); + await bmcManagement.addLink(link); + relays = [accounts[2], accounts[3], accounts[4], accounts[5]]; + await bmcManagement.addRelay(link, relays); + await bmv.setStatus(height, offset, lastHeight); + await bmcManagement.setLink(link, blockInterval, maxAggregation, delayLimit); + }); + + // [block(t+1), block(t+15)] => Relay 1 + it('Relay Rotation - Base Message (hasMsg = false) - Relay 1 Allowable', async () => { + // After setting a link, consider block_time = block(t+1) + await bmcManagement.mineOneBlock(); // block(t+2) + await bmcManagement.relayRotation(link, 0, false); // block(t+3) + let relay_info = await bmcManagement.getRelay(); + assert(relay_info.r == accounts[2]); + }); + + // [block(t+1), block(t+15)] => Relay 1 + it('Relay Rotation - Base Message (hasMsg = false) - Relay 1 Still Allowable', async () => { + for (i = 0; i < 10; i++) { + await bmcManagement.mineOneBlock(); + } + // After a loop, block_time = block(t+13) + await bmcManagement.relayRotation(link, 0, false); // block(t+14) + let relay_info = await bmcManagement.getRelay(); + assert(relay_info.r == accounts[2]); + }); + + // [block(t+16), block(t+30)] => Relay 2 + it('Relay Rotation - Base Message (hasMsg = false) - Next Relay = Relay 2 Allowable', async () => { + for (i = 0; i < 10; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+24) + await bmcManagement.relayRotation(link, 0, false); // block(t+25) + let relay_info = await bmcManagement.getRelay(); + assert(relay_info.r == accounts[3]); + }); + + // [block(t+31), block(t+45)] => Relay 3 + it('Relay Rotation - Base Message (hasMsg = false) - Next Relay = Relay 3 Allowable', async () => { + for (i = 0; i < 10; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+35) + await bmcManagement.relayRotation(link, 0, false); // block(t+36) + let relay_info = await bmcManagement.getRelay(); + current = relay_info.cb; + assert(relay_info.r == accounts[4]); + }); + + // [block(t+31), block(t+45)] => Relay 3 + it('Relay Rotation - BTP Message (hasMsg = true) - Relay 3 Still Allowable', async () => { + for (i = 0; i < 9; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+45) + let linkStat = await bmcPeriphery.getStatus(link); + let scale = parseInt(linkStat.blockIntervalSrc) / parseInt(linkStat.blockIntervalDst); + let block = Math.floor((parseInt(current)+9)*scale); + // At the time calling relayRotation() + // block_time = block(t+45). Thus, Relay 3 is still allowable + await bmcManagement.relayRotation(link, block, true); // block(t+46) + let relay_info = await bmcManagement.getRelay(); + current = relay_info.cb; + assert(relay_info.r == accounts[4]); + }); + + // [block(t+46), block(t+61)] => Relay 4 + it('Relay Rotation - BTP Message (hasMsg = true) - Relay 4 Allowable', async () => { + for (i = 0; i < 5; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+51) + let linkStat = await bmcPeriphery.getStatus(link); + let scale = parseInt(linkStat.blockIntervalSrc) / parseInt(linkStat.blockIntervalDst); + let block = Math.floor((parseInt(current)+5)*scale); + await bmcManagement.relayRotation(link, block, true); // block(t+52) + let relay_info = await bmcManagement.getRelay(); + current = relay_info.cb; + assert(relay_info.r == accounts[5]); + }); + + // [block(t+46), block(t+60)] => Relay 4 + // However, Relay 4 not relay BTP Message on time 'delay_limit' + // Move to next + it('Relay Rotation - BTP Message (hasMsg = true) - Relay 4 Overtime, Relay 1 Allowable', async () => { + for (i = 0; i < 2; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+54) + let linkStat = await bmcPeriphery.getStatus(link); + let scale = parseInt(linkStat.blockIntervalSrc) / parseInt(linkStat.blockIntervalDst); + let block = Math.floor((parseInt(current)+2)*scale); + // If relayRotation() were called now, there would have been a regular case + // Relay 4 were still allowable thereafter + // However, Relay 4 passes 'delay_limit' to relay Message + // to simulate such thing, call mineOneBlock() 4 times + // Even though, block_height = 59 that is less than 60 + // but Relay 4 is skipped and move to a next Relay + + for (i = 0; i < 4; i++) { + await bmcManagement.mineOneBlock(); + } + // After loop, block_time = block(t+58) + await bmcManagement.relayRotation(link, block, true); // block(t+59) + let relay_info = await bmcManagement.getRelay(); + assert(relay_info.r == accounts[2]); + }); + }); + + describe('Handle relay message tests', () => { + let bmcManagement, bmcPeriphery, bmv, bsh; + let network = '1234.iconee'; + let link = 'btp://1234.iconee/0x1234'; + let height = 0; + let offset = 0; + let lastHeight = 0; + let blockInterval = 3000; + let maxAggregation = 5; + let delayLimit = 3; + let relays; + + beforeEach(async () => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['1234.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + bsh = await MockBSH.new(); + await bmcManagement.addService('Token', bsh.address); + await bmcManagement.addVerifier(network, bmv.address); + await bmcManagement.addLink(link); + relays = [accounts[2], accounts[3], accounts[4], accounts[5]]; + await bmcManagement.addRelay(link, relays); + await bmv.setStatus(height, offset, lastHeight); + await bmcManagement.setLink(link, blockInterval, maxAggregation, delayLimit); + }); + + it('should revert if relay is invalid', async() => { + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x01', // rlp encode of signed int + 'message' + ]); + + + await truffleAssert.reverts(bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString()), 'BMCRevertUnauthorized: invalid relay'); + + let bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 0, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'failed to update txSeq'); + }); + + it('should revert if relay is not registered', async() => { + await bmcManagement.removeLink(link); + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + 'message' + ]); + + await truffleAssert.reverts(bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString()), 'BMCRevertUnauthorized: not registered relay'); + + let bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 0, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 0, 'failed to update txSeq'); + }); + + it('should dispatch btp message to BSH', async() => { + const transferCoin = [ + '0xaaa', + 'btp://1234.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + rlp.encode(transferCoin) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + let bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + }); + + it('should init link via BTP messages', async() => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['1234.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + bmv = await MockBMV.new(); + await bmcManagement.addVerifier(network, bmv.address); + + let tx = await bmcManagement.addLink(link); + let events = await bmcPeriphery.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + assert.equal(events[0].event, 'Message'); + + let eventData = events[0].returnValues; + assert.equal(eventData._next, link); + assert.equal(eventData._seq, 1); + + const bmcBtpAddr = await bmcPeriphery.getBmcBtpAddress(); + + const encodedSendingBtpMsg = rlp.encode([ + bmcBtpAddr, + link, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]).toString('hex'); + assert.equal(eventData._msg, '0x' + encodedSendingBtpMsg); + + const encodedReceivedBtpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]); + + await bmcManagement.addRelay(link, relays); + await bmcPeriphery.handleRelayMessage(link, encodedReceivedBtpMsg.toString(), {from: accounts[2]}); + + const linkInfo = await bmcManagement.getLink(link); + assert.isEmpty(linkInfo.reachable); + }); + + it('should process LINK and UNLINK via BTP messages', async() => { + const bmcBtpAddr = await bmcPeriphery.getBmcBtpAddress(); + + const encodedReceivedBtpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x00', + rlp.encode([ + 'Init', + rlp.encode([ + [] + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, encodedReceivedBtpMsg.toString(), {from: accounts[3]}); + + const linkInfo = await bmcManagement.getLink(link); + assert.isEmpty(linkInfo.reachable); + + const btpAddress = 'btp://1234.eth/' + web3.utils.randomHex(20); + + btpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x01', + rlp.encode([ + 'Link', + rlp.encode([ + btpAddress, + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[4]}); + + let bmcLink = await bmcManagement.getLink(link); + assert.equal(bmcLink.reachable[0], btpAddress, 'invalid reachable btp address'); + assert.equal(bmcLink.rxSeq, 2, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + + btpMsg = rlp.encode([ + link, + bmcBtpAddr, + 'bmc', + '0x02', + rlp.encode([ + 'Unlink', + rlp.encode([ + btpAddress, + ]) + ]) + ]); + + await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[5]}); + + bmcLink = await bmcManagement.getLink(link); + assert.isEmpty(bmcLink.reachable, 'failed to remove link'); + assert.equal(bmcLink.rxSeq, 3, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 1, 'invalid txSeq'); + }); + + it('should revert if internal handler does not exist', async () => { + const btpAddress = 'btp://0x01.eth/' + web3.utils.randomHex(20); + let internalMsg = [ + 'Unknown', + rlp.encode([ + 'btp://1234.iconee/0x1234', + btpAddress + ]) + ]; + + btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'bmc', + '0x01', + rlp.encode(internalMsg) + ]); + + await truffleAssert.reverts( + bmcPeriphery.handleRelayMessage.call(link, btpMsg.toString(), {from: accounts[3]}), + 'BMCRevert: not exists internal handler' + ); + }); + + it('should emit event if routes are failed to resolve', async() => { + const btpAddress = 'btp://1234.eth/' + web3.utils.randomHex(20); + let internalMsg = [ + 0, // LINK, + [ + 'btp://1234.iconee/0x1234', + btpAddress, + ] + ]; + + let btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.solana/' + bmcPeriphery.address, + '_event', + '0x02', + rlp.encode(internalMsg) + ]); + + const tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://1234.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://1234.iconee/0x1234'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), '_event'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement form of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertUnreachable: 1234.solana is unreachable'); + + bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('should emit event if routes are succeeded to resolve', async() => { + const destBtpAddress = 'btp://1234.pra/' + bmcPeriphery.address; + const routeBtpAddress = 'btp://1234.solana/' + web3.utils.randomHex(20); + + await bmcManagement.addRoute( + routeBtpAddress, + destBtpAddress + ); + + await bmcManagement.addVerifier('1234.pra', web3.utils.randomHex(20)); + await bmcManagement.addLink(destBtpAddress); + + const transferCoin = [ + '0xaaa', + 'btp://1234.pra/0xbbb', + 'ICX', + 12 + ]; + + let btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + routeBtpAddress, + 'Token', + '0x02', + rlp.encode(transferCoin) + ]); + + const tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[4]}); + + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + return ev._next === destBtpAddress && ev._seq.toNumber() === 2 && ev._msg.toString() === '0x' + btpMsg.toString('hex'); + }); + + let bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'invalid txSeq'); + + bmcLink = await bmcManagement.getLink(destBtpAddress); + assert.equal(bmcLink.txSeq, 2, 'invalid txSeq'); + }); + + it('should emit message if dest in msg is same as bmc address (msg.sn >= 0)', async() => { + const transferCoin = [ + '0xaaa', + 'btp://1234.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x01', + rlp.encode(transferCoin) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + }); + + it('should emit error message if dest in received msg is same as bmc address (msg.sn >= 0) and bsh gets errors', async() => { + const transferCoin = [ + '0xaaa', + 'btp://1234.pra/0xbbb', + 'ICX', + 12 + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x03e8', // two complement of 1000 + rlp.encode(transferCoin) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://1234.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://1234.iconee/0x1234'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'Token'); + assert.equal(bmcMessage[3].toString('hex'), 'fc18'); // two complement of -1000 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 40); // BSH_ERR = 40 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'Mocking error message on handleBTPMessage'); + + bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('should emit message if dest in received msg is same as bmc address (msg.sn < 0)', async() => { + const errResponse = [ + 0, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0xf6', // two complement of -10 + rlp.encode(errResponse) + ]); + + let res = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + assert.isNotEmpty(res); + }); + + it('should emit message if dest in received msg is same as bmc address (msg.sn < 0) and bsh gets errors', async() => { + const errResponse = [ + 0, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0xfc18', // two complement of -1000 + rlp.encode(errResponse) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + await truffleAssert.eventEmitted(tx, 'ErrorOnBTPError', (ev) => { + return ev[0] === 'Token' && ev[1].toNumber() === 1000 && + ev[2].toNumber() === 0 && ev[3].toString() === 'Invalid service' && + ev[4].toNumber() === 40 && ev[5].toString() === 'Mocking error message on handleBTPError'; + }); + }); + + it('should emit message if dest in received msg is same as bmc address (msg.sn < 0) and bsh gets low level errors', async() => { + const errResponse = [ + 12, + 'Invalid service', + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'Token', + '0x9c', // two complement of -100 + rlp.encode(errResponse) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), {from: accounts[3]}); + await truffleAssert.eventEmitted(tx, 'ErrorOnBTPError', (ev) => { + return ev[0] === 'Token' && ev[1].toNumber() === 100 && + ev[2].toNumber() === 12 && ev[3].toString() === 'Invalid service' && + ev[4].toNumber() === 0 && ev[5].toString() === ''; + }); + }); + + it('should emit error message if fee service message is failed to decode', async() => { + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + 'invalid rlp of service message' + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://1234.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://1234.iconee/0x1234'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'bmc'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertParseFailure'); + + bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('should emit error message if fee gathering message is failed to decode', async() => { + const serviceMsg = [ + 'FeeGathering', + 'invalid rlp of fee gather messgage', + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + rlp.encode(serviceMsg) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + let bmcMessage; + await truffleAssert.eventEmitted(tx, 'Message', (ev) => { + bmcMessage = rlp.decode(ev._msg); + return ev._next === link && ev._seq.toNumber() === 2; + }); + + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[0].toString('hex')), 'btp://1234.pra/' + bmcPeriphery.address); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[1].toString('hex')), 'btp://1234.iconee/0x1234'); + assert.equal(web3.utils.hexToUtf8('0x' + bmcMessage[2].toString('hex')), 'bmc'); + assert.equal(bmcMessage[3].toString('hex'), 'fe'); // two complement of -2 + const errResponse = rlp.decode(bmcMessage[4]); + assert.equal(web3.utils.hexToNumber('0x' + errResponse[0].toString('hex')), 10); // BMC_ERR = 10 + assert.equal(web3.utils.hexToUtf8('0x' + errResponse[1].toString('hex')), 'BMCRevertParseFailure'); + + bmcLink = await bmcManagement.getLink('btp://1234.iconee/0x1234'); + assert.equal(bmcLink.rxSeq, 1, 'failed to update rxSeq'); + assert.equal(bmcLink.txSeq, 2, 'failed to update txSeq'); + }); + + it('should dispatch gather fee message to bsh services', async() => { + const gatherFeeMsg = [ + 'btp://1234.iconee/0x12345678', + ['service1', 'service2', 'service3'] + ] + + const serviceMsg = [ + 'FeeGathering', + rlp.encode(gatherFeeMsg), + ]; + + const btpMsg = rlp.encode([ + 'btp://1234.iconee/0x1234', + 'btp://1234.pra/' + bmcPeriphery.address, + 'bmc', + '0x02', + rlp.encode(serviceMsg) + ]); + + let tx = await bmcPeriphery.handleRelayMessage(link, btpMsg.toString(), { from: accounts[3] }); + assert.isNotEmpty(tx); + }); + }); + + describe('Test upgradable contracts', () => { + let bmcManagement, bmcPeriphery; + + before(async () => { + bmcManagement = await deployProxy(BMCManagement); + bmcPeriphery = await deployProxy(BMCPeriphery, ['0x1234.pra', bmcManagement.address]); + await bmcManagement.setBMCPeriphery(bmcPeriphery.address); + }); + + it('should upgrade BMC Management', async() => { + let bsh = await MockBSH.new(); + await bmcManagement.addService('Token', bsh.address); + let address = await bmcManagement.getBshServiceByName('Token'); + assert.equal(address, bsh.address); + + const upgradeBMCManagement = await upgradeProxy(bmcManagement.address, BMCManagementV2); + address = await upgradeBMCManagement.getBshServiceByName('Token'); + assert.equal(address, '0x0000000000000000000000000000000000000000'); + }); + + it('should upgrade BMC Periphery', async() => { + const upgradeBMCPeriphery = await upgradeProxy(bmcPeriphery.address, BMCPeripheryV2); + await truffleAssert.reverts( + upgradeBMCPeriphery.sendMessage.call('_to', '_svc', 0, '0x02'), + 'Upgrade successfully' + ); + }); + }); +}); diff --git a/solidity/bmc/test/unit/LibRLP.unit.test.js b/solidity/bmc/test/unit/LibRLP.unit.test.js new file mode 100644 index 00000000..91910818 --- /dev/null +++ b/solidity/bmc/test/unit/LibRLP.unit.test.js @@ -0,0 +1,192 @@ +const { assert } = require('chai'); +const testLibRLP = artifacts.require('TestLibRLP'); +const truffleAssert = require('truffle-assertions'); + +contract('RLP library unit tests', () => { + let libRLP; + + before(async () => { + libRLP = await testLibRLP.new(); + }); + + describe('RLP encode unit tests', () => { + it('should encode bytes', async () => { + const hexBytes = '0x4e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'; + const res = await libRLP.encodeBytes(hexBytes); + assert.equal(res, '0xa04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'); + }); + + it('should encode string', async () => { + const input = "correctly computes keccak256 hash of the item payload (nested list)"; + const res = await libRLP.encodeString(input); + assert.equal(res, '0xb843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c69737429'); + }); + + it('should encode address', async () => { + const address = '0xF2CD2AA0c7926743B1D4310b2BC984a0a453c3d4'; + const res = await libRLP.encodeAddress(address); + assert.equal(res, '0x94f2cd2aa0c7926743b1d4310b2bc984a0a453c3d4'); + }); + + it('should encode unsigned integer', async () => { + let res = await libRLP.encodeUint(54994); + assert.equal(res, '0x8300d6d2'); + + res = await libRLP.encodeUint(549943); + assert.equal(res, '0x83086437'); + + res = await libRLP.encodeUint(549945499454999); + assert.equal(res, '0x8701f42c2a240e17'); + + res = await libRLP.encodeUint(web3.utils.toBN('34028236692093846346337460743176821155')); + assert.equal(res, '0x90199999999999999999999999999999a3'); + }); + + it('should revert if unsigned integer is out of bounds', async () => { + await truffleAssert.reverts( + libRLP.encodeUint.call(web3.utils.toBN('999999999999999999999999999999999999999999999999999999999999')), + 'outOfBounds: [0, 2^128]' + ); + }); + + + it('should encode signed integer', async () => { + let res = await libRLP.encodeInt(3278); + assert.equal(res, '0x820cce'); + + res = await libRLP.encodeInt(-3278); + assert.equal(res, '0x82f332'); + + res = await libRLP.encodeInt(888889); + assert.equal(res, '0x830d9039'); + + res = await libRLP.encodeInt(-888889); + assert.equal(res, '0x83f26fc7'); + + res = await libRLP.encodeInt(web3.utils.toBN('1329227995784915872903807060280344571')); + assert.equal(res, '0x9000fffffffffffffffffffffffffffffb'); + + res = await libRLP.encodeInt(web3.utils.toBN('-1329227995784915872903807060280344571')); + assert.equal(res, '0x90ff000000000000000000000000000005'); + }); + + it('should revert if signed integer is out of bounds', async () => { + await truffleAssert.reverts( + libRLP.encodeInt.call(web3.utils.toBN('999999999999999999999999999999999999999999999999999999999999')), + 'outOfBounds: [-2^128-1, 2^128]' + ); + + await truffleAssert.reverts( + libRLP.encodeInt.call(web3.utils.toBN('-999999999999999999999999999999999999999999999999999999999999')), + 'outOfBounds: [-2^128-1, 2^128]' + ); + }); + + it('should encode boolean', async () => { + let res = await libRLP.encodeBool(true); + assert.equal(res, '0x01'); + + res = await libRLP.encodeBool(false); + assert.equal(res, '0x00'); + }); + + it('should encode list of rlp bytes', async () => { + const encodedBytes = '0xa04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'; + const encodedString = '0xb843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c69737429'; + const encodedAddress = '0x94f2cd2aa0c7926743b1d4310b2bc984a0a453c3d4'; + const encodedUint = '0x8701f42c2a240e17'; + const encodedInt = '0x83f26fc7'; + const encodeBoolean = '0x00'; + const list = [ + encodedBytes, + encodedString, + encodedAddress, + encodedUint, + encodedInt, + encodeBoolean + ] + + const res = await libRLP.encodeList(list); + assert.equal(res, '0xf888a04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330b843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c6973742994f2cd2aa0c7926743b1d4310b2bc984a0a453c3d48701f42c2a240e1783f26fc700'); + }); + }); + + describe('RLP decode unit tests', () => { + it('should decode rlp bytes to bytes', async () => { + const res = await libRLP.decodeBytes('0xa04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'); + assert.equal(res, '0x4e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'); + }); + + it('should decode rlp bytes to string', async () => { + const res = await libRLP.decodeString('0xb843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c69737429'); + assert.equal(res, 'correctly computes keccak256 hash of the item payload (nested list)'); + }); + + it('should decode rlp bytes to address', async () => { + const res = await libRLP.decodeAddress('0x94f2cd2aa0c7926743b1d4310b2bc984a0a453c3d4'); + assert.equal(res, '0xF2CD2AA0c7926743B1D4310b2BC984a0a453c3d4'); + }); + + it('should decode rlp bytes to unsiged integer', async () => { + let res = await libRLP.decodeUint('0x8300d6d2'); + assert.equal(res, 54994); + + res = await libRLP.decodeUint('0x83086437'); + assert.equal(res, 549943); + + res = await libRLP.decodeUint('0x8701f42c2a240e17'); + assert.equal(res, 549945499454999); + + res = await libRLP.decodeUint('0x90199999999999999999999999999999a3'); + assert.equal(res.toString(), web3.utils.toBN('34028236692093846346337460743176821155')); + }); + + it('should decode rlp bytes to signed integer', async () => { + let res = await libRLP.decodeInt('0x820cce'); + assert.equal(res, 3278); + + res = await libRLP.decodeInt('0x82f332'); + assert.equal(res, -3278); + + res = await libRLP.decodeInt('0x830d9039'); + assert.equal(res, 888889); + + res = await libRLP.decodeInt('0x83f26fc7'); + assert.equal(res, -888889); + + res = await libRLP.decodeInt('0x9000fffffffffffffffffffffffffffffb'); + assert.equal(res.toString(), web3.utils.toBN('1329227995784915872903807060280344571')); + + res = await libRLP.decodeInt('0x90ff000000000000000000000000000005'); + assert.equal(res.toString(), web3.utils.toBN('-1329227995784915872903807060280344571')); + }); + + it('should decode rlp bytes to boolean', async () => { + let res = await libRLP.decodeBool('0x01'); + assert.isTrue(res); + + res = await libRLP.decodeBool('0x00'); + assert.isFalse(res); + }); + + it('should decode rlp bytes to list of items', async () => { + const encodedBytes = '0xa04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330'; + const encodedString = '0xb843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c69737429'; + const encodedAddress = '0x94f2cd2aa0c7926743b1d4310b2bc984a0a453c3d4'; + const encodedUint = '0x8701f42c2a240e17'; + const encodedInt = '0x83f26fc7'; + const encodeBoolean = '0x00'; + const list = [ + encodedBytes, + encodedString, + encodedAddress, + encodedUint, + encodedInt, + encodeBoolean + ] + + const res = await libRLP.decodeList('0xf888a04e454b49dc8a2e2a229e0ce911e9fd4d2aa647de4cf6e0df40cf71bff7283330b843636f72726563746c7920636f6d7075746573206b656363616b3235362068617368206f6620746865206974656d207061796c6f616420286e6573746564206c6973742994f2cd2aa0c7926743b1d4310b2bc984a0a453c3d48701f42c2a240e1783f26fc700'); + assert.deepEqual(res, list); + }); + }); +}); \ No newline at end of file diff --git a/solidity/bmc/truffle-config.js b/solidity/bmc/truffle-config.js new file mode 100644 index 00000000..ecfc3c39 --- /dev/null +++ b/solidity/bmc/truffle-config.js @@ -0,0 +1,77 @@ +const HDWalletProvider = require('@truffle/hdwallet-provider'); +const web3 = require("web3") + +const privKeys = (process.env.PRIVATE_KEYS) ? process.env.PRIVATE_KEYS.split(',') : + [ + '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // Alith + '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // Baltathar + '0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b', // Charleth + '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68', // Dorothy + '0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4', // Ethan + '0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df', // Faith + '0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18', // Goliath + '0x0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab', // Heath + '0x4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8', // Ida + '0x94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b' // Judith + ]; + +module.exports = { + networks: { + development: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbeamlocal: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbase: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.testnet.moonbeam.network", + }), + network_id: 1287, + networkCheckTimeout: 100000, + // Make deploy faster for deployment + gasPrice: web3.utils.toWei("2", "Gwei"), + }, + moonriver: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.moonriver.moonbeam.network", + }), + network_id: 1285, + networkCheckTimeout: 100000, + }, + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.7.6", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 10 + }, + evmVersion: "petersburg" + } + } + }, + + db: { + enabled: false + } +}; diff --git a/solidity/bmc/yarn.lock b/solidity/bmc/yarn.lock new file mode 100644 index 00000000..be192a9c --- /dev/null +++ b/solidity/bmc/yarn.lock @@ -0,0 +1,5143 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/code-frame@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" + integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.5": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" + integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== + +"@babel/generator@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785" + integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA== + dependencies: + "@babel/types" "^7.14.5" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.13.0": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf" + integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw== + dependencies: + "@babel/compat-data" "^7.14.5" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-define-polyfill-provider@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" + integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-function-name@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4" + integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ== + dependencies: + "@babel/helper-get-function-arity" "^7.14.5" + "@babel/template" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/helper-get-function-arity@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" + integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-hoist-variables@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d" + integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3" + integrity sha512-SwrNHu5QWS84XlHwGYPDtCxcA0hrSlL2yhWYLgeOc0w7ccOl2qv4s/nARI0aYZW+bSwAL5CukeXA47B/1NKcnQ== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-split-export-declaration@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" + integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA== + dependencies: + "@babel/types" "^7.14.5" + +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + +"@babel/helper-validator-identifier@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.5.tgz#d0f0e277c512e0c938277faa85a3968c9a44c0e8" + integrity sha512-5lsetuxCLilmVGyiLEfoHBRX8UCFD+1m2x3Rj97WrW3V7H3u4RWRXA4evMjImCsin2J2YT0QaVDGf+z8ondbAg== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/highlight@^7.12.13": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.14.5", "@babel/parser@^7.14.7": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595" + integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA== + +"@babel/plugin-transform-runtime@^7.5.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.5.tgz#30491dad49c6059f8f8fa5ee8896a0089e987523" + integrity sha512-fPMBhh1AV8ZyneiCIA+wYYUH1arzlXR1UMcApjvchDhfKxhy2r2lReJv8uHEyihi4IFIGlr1Pdx7S5fkESDQsg== + dependencies: + "@babel/helper-module-imports" "^7.14.5" + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-polyfill-corejs2 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.2" + babel-plugin-polyfill-regenerator "^0.2.2" + semver "^6.3.0" + +"@babel/runtime@^7.5.5": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4" + integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.14.5" + "@babel/types" "^7.14.5" + +"@babel/traverse@^7.13.0": + version "7.14.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753" + integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.14.5" + "@babel/helper-function-name" "^7.14.5" + "@babel/helper-hoist-variables" "^7.14.5" + "@babel/helper-split-export-declaration" "^7.14.5" + "@babel/parser" "^7.14.7" + "@babel/types" "^7.14.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff" + integrity sha512-M/NzBpEL95I5Hh4dwhin5JlE7EzO5PHMAuzjxss3tiOBD46KfQvVedN/3jEPZvdRvtsK2222XfdHogNIttFgcg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + to-fast-properties "^2.0.0" + +"@cto.af/textdecoder@^0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@cto.af/textdecoder/-/textdecoder-0.0.0.tgz#e1e8d84c936c30a0f4619971f19ca41941af9fdc" + integrity sha512-sJpx3F5xcVV/9jNYJQtvimo4Vfld/nD3ph+ZWtQzZ03Zo8rJC7QKQTRcIGS13Rcz80DwFNthCWMrd58vpY4ZAQ== + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.3.0.tgz#f4c0ae4a4cef9f204d7781de805fd44b72756c81" + integrity sha512-1+MLhGP1GwxBDBNwMWVmhCsvKwh4gK7oIfOrmlmePNeskg1NhIrYssraJBieaFNHUYfKEd/1DjiVZMw8Qu5Cxw== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/networks" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/transactions" "^5.3.0" + "@ethersproject/web" "^5.3.0" + +"@ethersproject/abstract-signer@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.3.0.tgz#05172b653e15b535ed5854ef5f6a72f4b441052d" + integrity sha512-w8IFwOYqiPrtvosPuArZ3+QPR2nmdVTRrVY8uJYL3NNfMmQfTy3V3l2wbzX47UUlNbPJY+gKvzJAyvK1onZxJg== + dependencies: + "@ethersproject/abstract-provider" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + +"@ethersproject/address@^5.0.4", "@ethersproject/address@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.3.0.tgz#e53b69eacebf332e8175de814c5e6507d6932518" + integrity sha512-29TgjzEBK+gUEUAOfWCG7s9IxLNLCqvr+oDSk6L9TXD0VLvZJKhJV479tKQqheVA81OeGxfpdxYtUVH8hqlCvA== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/rlp" "^5.3.0" + +"@ethersproject/base64@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.3.0.tgz#b831fb35418b42ad24d943c557259062b8640824" + integrity sha512-JIqgtOmgKcbc2sjGWTXyXktqUhvFUDte8fPVsAaOrcPiJf6YotNF+nsrOYGC9pbHBEGSuSBp3QR0varkO8JHEw== + dependencies: + "@ethersproject/bytes" "^5.3.0" + +"@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.3.0.tgz#74ab2ec9c3bda4e344920565720a6ee9c794e9db" + integrity sha512-5xguJ+Q1/zRMgHgDCaqAexx/8DwDVLRemw2i6uR8KyGjwGdXI8f32QZZ1cKGucBN6ekJvpUpHy6XAuQnTv0mPA== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.3.0.tgz#473e0da7f831d535b2002be05e6f4ca3729a1bc9" + integrity sha512-rqLJjdVqCcn7glPer7Fxh87PRqlnRScVAoxcIP3PmOUNApMWJ6yRdOFfo2KvPAdO7Le3yEI1o0YW+Yvr7XCYvw== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.3.0.tgz#a5d6d86c0eec2c64c3024479609493b9afb3fc77" + integrity sha512-4y1feNOwEpgjAfiCFWOHznvv6qUF/H6uI0UKp8xdhftb+H+FbKflXg1pOgH5qs4Sr7EYBL+zPyPb+YD5g1aEyw== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + +"@ethersproject/hash@^5.0.4": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.3.0.tgz#f65e3bf3db3282df4da676db6cfa049535dd3643" + integrity sha512-gAFZSjUPQ32CIfoKSMtMEQ+IO0kQxqhwz9fCIFt2DtAq2u4pWt8mL9Z5P0r6KkLcQU8LE9FmuPPyd+JvBzmr1w== + dependencies: + "@ethersproject/abstract-signer" "^5.3.0" + "@ethersproject/address" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/strings" "^5.3.0" + +"@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.3.0.tgz#fb5cd36bdfd6fa02e2ea84964078a9fc6bd731be" + integrity sha512-Gv2YqgIUmRbYVNIibafT0qGaeGYLIA/EdWHJ7JcVxVSs2vyxafGxOJ5VpSBHWeOIsE6OOaCelYowhuuTicgdFQ== + dependencies: + "@ethersproject/bytes" "^5.3.0" + js-sha3 "0.5.7" + +"@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.3.0.tgz#7a69fa1d4ca0d4b7138da1627eb152f763d84dd0" + integrity sha512-8bwJ2gxJGkZZnpQSq5uSiZSJjyVTWmlGft4oH8vxHdvO1Asy4TwVepAhPgxIQIMxXZFUNMych1YjIV4oQ4I7dA== + +"@ethersproject/networks@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.3.0.tgz#d8ad06eb107c69fb8651f4c81ddd0e88944fdfea" + integrity sha512-XGbD9MMgqrR7SYz8o6xVgdG+25v7YT5vQG8ZdlcLj2I7elOBM7VNeQrnxfSN7rWQNcqu2z80OM29gGbQz+4Low== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.3.0.tgz#feef4c4babeb7c10a6b3449575016f4ad2c092b2" + integrity sha512-PaHxJyM5/bfusk6vr3yP//JMnm4UEojpzuWGTmtL5X4uNhNnFNvlYilZLyDr4I9cTkIbipCMsAuIcXWsmdRnEw== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/rlp@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.3.0.tgz#7cb93a7b5dfa69163894153c9d4b0d936f333188" + integrity sha512-oI0joYpsRanl9guDubaW+1NbcpK0vJ3F/6Wpcanzcnqq+oaW9O5E98liwkEDPcb16BUTLIJ+ZF8GPIHYxJ/5Pw== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/signing-key@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.3.0.tgz#a96c88f8173e1abedfa35de32d3e5db7c48e5259" + integrity sha512-+DX/GwHAd0ok1bgedV1cKO0zfK7P/9aEyNoaYiRsGHpCecN7mhLqcdoUiUzE7Uz86LBsxm5ssK0qA1kBB47fbQ== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.3.0.tgz#a6b640aab56a18e0909f657da798eef890968ff0" + integrity sha512-j/AzIGZ503cvhuF2ldRSjB0BrKzpsBMtCieDtn4TYMMZMQ9zScJn9wLzTQl/bRNvJbBE6TOspK0r8/Ngae/f2Q== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/constants" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.3.0.tgz#49b86f2bafa4d0bdf8e596578fc795ee47c50458" + integrity sha512-cdfK8VVyW2oEBCXhURG0WQ6AICL/r6Gmjh0e4Bvbv6MCn/GBd8FeBH3rtl7ho+AW50csMKeGv3m3K1HSHB2jMQ== + dependencies: + "@ethersproject/address" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/constants" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/rlp" "^5.3.0" + "@ethersproject/signing-key" "^5.3.0" + +"@ethersproject/web@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.3.0.tgz#7959c403f6476c61515008d8f92da51c553a8ee1" + integrity sha512-Ni6/DHnY6k/TD41LEkv0RQDx4jqWz5e/RZvrSecsxGYycF+MFy2z++T/yGc2peRunLOTIFwEksgEGGlbwfYmhQ== + dependencies: + "@ethersproject/base64" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/strings" "^5.3.0" + +"@openzeppelin/contracts-upgradeable@3.4.1-solc-0.7-2": + version "3.4.1-solc-0.7-2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1-solc-0.7-2.tgz#8d46f2310560d3756bd5235e20f4e50caa947e92" + integrity sha512-hGbNTTlkcsRhMdJ+IMAWKn5uI1IK9yvJamZpQou1aOjgr+VOFo7eqdiqs+Dwv8fGzN7L/Wdg4XqiW3vGqTHk3g== + +"@openzeppelin/truffle-upgrades@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.7.0.tgz#cbc0d3a2f32d1acb8a31bcc9b8c8f0f7eb01f510" + integrity sha512-0Bb7Qr6ULUYKB//TJF7s0++z4QKONR9b5/UCnqb+po1TYqxEBwftjebVR4rj7C1tWH8cj+igEMkyQb0ep9r9Lw== + dependencies: + "@openzeppelin/upgrades-core" "^1.7.0" + "@truffle/contract" "^4.2.12" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.7.0": + version "1.7.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.7.6.tgz#2c5e89b272aaf2164f51ca99167ffeecdab13749" + integrity sha512-xXoUJGWI2MHLtMwS2GN3PiymZ9WIj+uRAMrm8HIz0genGSIO6PwPMlmaUyq3O8/JzTMgpVV92aMcPjOkvI5SmQ== + dependencies: + bn.js "^5.1.2" + cbor "^7.0.0" + chalk "^4.1.0" + compare-versions "^3.6.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@solidity-parser/parser@^0.12.0", "@solidity-parser/parser@^0.12.1": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.2.tgz#1afad367cb29a2ed8cdd4a3a62701c2821fb578f" + integrity sha512-d7VS7PxgMosm5NyaiyDJRNID5pK4AWj1l64Dbz0147hJgy5k2C0/ZiKK/9u5c5K+HRUVHmp+RMvGEjGh84oA5Q== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/blockchain-utils@^0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.30.tgz#1fafbd8e8694d79280177b5eff167b0690838855" + integrity sha512-3hkHSHxVavoALcxpBqD4YwHuCmkBrvjq6PAGw93i6WCB+pnejBD5sFjVCiZZKCogh4kGObxxcwu53+3dyT/6IQ== + +"@truffle/codec@^0.10.9": + version "0.10.9" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.9.tgz#9c6f6a57b12894ad44fc37f41ddce18ebfc7b7e5" + integrity sha512-+xBcn1mTAqBhVaFULkMC+pJnUp3prL9QZtE5I4XhlCar3QLkSGR9Oy+Bm5qZwH72rctBRD/lGp2ezUo/oFc2MQ== + dependencies: + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + utf8 "^3.0.0" + web3-utils "1.3.6" + +"@truffle/contract-schema@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.1.tgz#13b404383d438b48960862022a20102970323666" + integrity sha512-2gvu6gxJtbbI67H2Bwh2rBuej+1uCV3z4zKFzQZP00hjNoL+QfybrmBcOVB88PflBeEB+oUXuwQfDoKX3TXlnQ== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.3.1" + +"@truffle/contract@^4.2.12": + version "4.3.19" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.19.tgz#cbd6b4f08b7d3bb5e7cdaba205f17c80cc3f7b60" + integrity sha512-asBwoxUePLwNAuYfHO/SvyZyqFQ9QMeUieJ6PXD9DBrUdNpjgJvk+WHYnXH4bWHgHakbTpL1MEBa1qmcAL+LMw== + dependencies: + "@truffle/blockchain-utils" "^0.0.30" + "@truffle/contract-schema" "^3.4.1" + "@truffle/debug-utils" "^5.0.19" + "@truffle/error" "^0.0.14" + "@truffle/interface-adapter" "^0.5.0" + bignumber.js "^7.2.1" + ethereum-ens "^0.8.0" + ethers "^4.0.32" + web3 "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +"@truffle/debug-utils@^5.0.19": + version "5.0.19" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.19.tgz#d23a4bfd9a8cacd922e0d5763aaee63836ca9553" + integrity sha512-wCF5fwyJTHGBwQD+m8npRCrUIgSPw9jRiZlvyuE+TDcVNBpV4A1NcdsTJR/E5lAViLDTp9lpelsgA/Mw65kU+w== + dependencies: + "@truffle/codec" "^0.10.9" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlight.js "^10.4.0" + highlightjs-solidity "^1.1.0" + +"@truffle/error@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.14.tgz#59683b5407bede7bddf16d80dc5592f9c5e5fa05" + integrity sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA== + +"@truffle/hdwallet-provider@^1.4.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.4.1.tgz#112fba16c703a32c03e1cf9fb9ebd458b66c28b0" + integrity sha512-mcImWO3oyFu+4Sxrvrer0ZghjiJyDpOzG+vWUYCeV6Nl1Q3sqxNDvJKyTFkXQzHeNOPTVCOenqxUAGWF8XwlCA== + dependencies: + "@trufflesuite/web3-provider-engine" "15.0.13-1" + any-promise "^1.3.0" + bindings "^1.5.0" + ethereum-cryptography "^0.1.3" + ethereum-protocol "^1.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^1.0.1" + +"@truffle/interface-adapter@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.0.tgz#00c9e16fe0edafbfbf4b331fb4db178c9148c8fc" + integrity sha512-0MRt9orgQqo0knyKDy0fGRqnI+alkuK0BUAvHB1/VUJgCKyWBNAUUZO5gPjuj75qCjV4Rw+W6SKDQpn2xOWsXw== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + web3 "1.3.6" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@trufflesuite/eth-json-rpc-filters@^4.1.2-1": + version "4.1.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz#61ab78c52e98a883e5cf086925b34a30297b1824" + integrity sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-0" + await-semaphore "^0.1.3" + eth-query "^2.1.2" + json-rpc-engine "^5.1.3" + lodash.flatmap "^4.5.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-json-rpc-infura@^4.0.3-0": + version "4.0.3-0" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz#6d22122937cf60ec9d21a02351c101fdc608c4fe" + integrity sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + cross-fetch "^2.1.1" + eth-json-rpc-errors "^1.0.1" + json-rpc-engine "^5.1.3" + +"@trufflesuite/eth-json-rpc-middleware@^4.4.2-0", "@trufflesuite/eth-json-rpc-middleware@^4.4.2-1": + version "4.4.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz#8c3638ed8a7ed89a1e5e71407de068a65bef0df2" + integrity sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA== + dependencies: + "@trufflesuite/eth-sig-util" "^1.4.2" + btoa "^1.2.1" + clone "^2.1.1" + eth-json-rpc-errors "^1.0.1" + eth-query "^2.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.6.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^5.1.3" + json-stable-stringify "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-sig-util@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz#b529e2f38ac08e652116f48981132a26242a4f08" + integrity sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + +"@trufflesuite/web3-provider-engine@15.0.13-1": + version "15.0.13-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.13-1.tgz#f6a7f7131a2fdc4ab53976318ed13ce83e8e4bcb" + integrity sha512-6u3x/iIN5fyj8pib5QTUDmIOUiwAGhaqdSTXdqCu6v9zo2BEwdCqgEJd1uXDh3DBmPRDfiZ/ge8oUPy7LerpHg== + dependencies: + "@trufflesuite/eth-json-rpc-filters" "^4.1.2-1" + "@trufflesuite/eth-json-rpc-infura" "^4.0.3-0" + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + "@trufflesuite/eth-sig-util" "^1.4.2" + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^4.4.2" + eth-json-rpc-errors "^2.0.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "15.12.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.1.tgz#9b60797dee1895383a725f828a869c86c6caa5c2" + integrity sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw== + +"@types/node@^12.12.6": + version "12.20.14" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.14.tgz#9caf7eea0df08b406829889cc015256a6d81ab10" + integrity sha512-iFJOS5Q470FF+r4Ol2pSley7/wCNVqf+jgjhtxLLaJcDs+To2iCxlXIkJXrGLD9w9G/oJ9ibySu7z92DCwr7Pg== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.2.tgz#20c29a87149d980f64464e56539bf4810fdb5d1d" + integrity sha512-QMg+9v0bbNJ2peLuHRWxzmy0HRJIG6gFZNhaRSp7S3ggSbCCxiqQB2/ybvhXyhHOCequpNkrx7OavNhrWOsW0A== + dependencies: + "@types/node" "*" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +available-typed-arrays@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" + integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== + +await-semaphore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/await-semaphore/-/await-semaphore-0.1.3.tgz#2b88018cc8c28e06167ae1cdff02504f1f9688d3" + integrity sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-plugin-polyfill-corejs2@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" + integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.2" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.3.tgz#72add68cf08a8bf139ba6e6dfc0b1d504098e57b" + integrity sha512-rCOFzEIJpJEAU14XCcV/erIf/wZQMmMT5l5vXOpL5uoznyOGfDIjPj6FVytMvtzaKSTSVKouOCTPJ5OMUZH30g== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + core-js-compat "^3.14.0" + +babel-plugin-polyfill-regenerator@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" + integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bluebird@^3.4.7, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +caniuse-lite@^1.0.30001219: + version "1.0.30001241" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001241.tgz#cd3fae47eb3d7691692b406568d7a3e5b23c7598" + integrity sha512-1uoSZ1Pq1VpH0WerIMqwptXHNNGfdl7d1cJUFs80CwQ/lVzdhTvsFZCeNFslze7AjsQnb4C85tzclPa1VShbeQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +cbor@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f" + integrity sha512-0aaAPgW92lLmypb9iCd22k7tSD1FbF6dps8VQzmIBKY6ych2gO09b2vo/SbaLTmezJuB8Kh88Rvpl/Uq52mNZg== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + nofilter "^2.0.3" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +cheerio-select@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9" + integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew== + dependencies: + css-select "^4.1.2" + css-what "^5.0.0" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.6.0" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.9" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f" + integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng== + dependencies: + cheerio-select "^1.4.0" + dom-serializer "^1.3.1" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-js-compat@^3.14.0: + version "3.15.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.15.2.tgz#47272fbb479880de14b4e6081f71f3492f5bd3cb" + integrity sha512-Wp+BJVvwopjI+A1EFqm2dwUmWYXrvucmtIB2LgXn/Rb+gWPKYxtmb4GKHGKG/KGF1eK9jfjzT38DITbTOCX/SQ== + dependencies: + browserslist "^4.16.6" + semver "7.0.0" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +css-select@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286" + integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-to-object@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-to-object/-/dir-to-object-2.0.0.tgz#29723e9bd1c3e58e4f307bd04ff634c0370c8f8a" + integrity sha512-sXs0JKIhymON7T1UZuO2Ud6VTNAx/VTBXIl4+3mjb2RgfOpt+hectX0x04YqPOPdkeOAKoJuKqwqnXXURNPNEA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1, dom-serializer@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7" + integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.723: + version "1.3.763" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.763.tgz#93f6f02506d099941f557b9db9ba50b30215bf15" + integrity sha512-UyvEPae0wvzsyNJhVfGeFSOlUkHEze8xSIiExO5tZQ8QTr7obFiJWGk3U4e7afFOJMQJDszqU/3Pk5jtKiaSEg== + +elliptic@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^4.4.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz#766a0a0eb4a52c867a28328e9ae21353812cf626" + integrity sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw== + dependencies: + "@babel/plugin-transform-runtime" "^7.5.5" + "@babel/runtime" "^7.5.5" + eth-query "^2.1.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-errors@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" + integrity sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + +ethereum-bloom-filters@^1.0.6: + version "1.0.9" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.9.tgz#4a59dead803af0c9e33834170bd7695df67061ec" + integrity sha512-GiK/RQkAkcVaEdxKVkPcG07PQ5vD7v2MFSHgZmBJSfMzNRHimntdBithsHAT89tAXnIpzVDWt8iaCD1DvkaxGg== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-ens@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" + integrity sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg== + dependencies: + bluebird "^3.4.7" + eth-ens-namehash "^2.0.0" + js-sha3 "^0.5.7" + pako "^1.0.4" + underscore "^1.8.3" + web3 "^1.0.0-beta.34" + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.1.tgz#664a4bcacfc1291ca2703de066df1178938dba1c" + integrity sha512-3Z5g1hG1das0JWU6cQ9HWWTY2nt9nXCcwj7eXVNAHKbo00XAZO8+NHlwdgXDWrL0SXVQMvTWN8Q/82DRH/JhPw== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.0.2" + randombytes "^2.0.6" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^4.0.32: + version "4.0.48" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" + integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@^7.1.2, glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +highlight.js@^10.4.0, highlight.js@^10.4.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-solidity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.1.0.tgz#bdf0adba6deffdb1651f8fa63bee4dacc3dc4e00" + integrity sha512-LtH7uuoe+FOmtQd41ozAZKLJC2chqdqs461FJcWAx00R3VcBhSQTRFfzRGtTQqu2wsVIEdHiyynrPrEDDWyIMw== + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +husky@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + +is-boolean-object@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + dependencies: + call-bind "^1.0.2" + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-stream@^1.0.0, is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-rpc-engine@^5.1.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +node-releases@^1.1.71: + version "1.1.73" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.73.tgz#dd4e81ddd5277ff846b80b52bb40c49edf7a7b20" + integrity sha512-uW7fodD6pyW2FZNZnp/Z3hvWKeEW1Y8R1+1CnErE8cXFXzl5blBOoVB41CvMer6P6Q0S5FXDwcHgFd1Wj0U9zg== + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +nofilter@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-2.0.3.tgz#f5460f3cb33147005883e3f5d4476239501fa187" + integrity sha512-FbuXC+lK+GU2+63D1kC1ETiZo+Z7SIi7B+mxKTCH1byrh6WFvfBCN/wpherFz0a0bjGd7EKTst/cz0yLeNngug== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-beta.7: + version "1.0.0-beta.10" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.10.tgz#f2a249002733826b08d981b599335ddb7e93af8d" + integrity sha512-55UsEbeJfqYKB3RFR7Nvpi+ApEoUfgdKHVg2ZybrbOkRW4RTblyONLL3mEr8Vrxpo7wBbObVLbWodGg4YXIQ7g== + dependencies: + "@solidity-parser/parser" "^0.12.1" + dir-to-object "^2.0.0" + emoji-regex "^9.2.2" + escape-string-regexp "^4.0.0" + prettier "^2.2.1" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.2" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" + integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.2.9: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.4.tgz#81770c60eeb027e6e447cb91ed599baf5e888e09" + integrity sha512-AEyjshF/PC6kox1c1l79Pji+DK9WVuk5u2WEh6bBKt188gWa63NBOAgYg0fBRr5CTUmsuGc1sGH7dgUVs83mKw== + dependencies: + "@solidity-parser/parser" "^0.12.0" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-ast@^0.4.15: + version "0.4.25" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.25.tgz#1e0dc3d6024e69c29698c49d74e5ee9421fe426d" + integrity sha512-8IpweS/vgHEpKvY4l0sfr3EsHk+JFIzRWqq/0JefRjzP/Wyi2xZYfx8aHlJ9kcEn2M2RCQK9PexuZ+scaa83OQ== + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +truffle-assertions@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/truffle-assertions/-/truffle-assertions-0.9.2.tgz#0f8360f53ad92b6d8fdb8ceb5dce54c1fc392e23" + integrity sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA== + dependencies: + assertion-error "^1.1.0" + lodash.isequal "^4.5.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + +underscore@^1.8.3: + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +urlsafe-base64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz#23f89069a6c62f46cf3a1d3b00169cefb90be0c6" + integrity sha1-I/iQaabGL0bPOh07ABac77kL4MY= + +utf-8-validate@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1" + integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +web3-bzz@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.6.tgz#95f370aecc3ff6ad07f057e6c0c916ef09b04dde" + integrity sha512-ibHdx1wkseujFejrtY7ZyC0QxQ4ATXjzcNUpaLrvM6AEae8prUiyT/OloG9FWDgFD2CPLwzKwfSQezYQlANNlw== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.12.1" + +web3-core-helpers@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz#c478246a9abe4e5456acf42657dac2f7c330be74" + integrity sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA== + dependencies: + underscore "1.12.1" + web3-eth-iban "1.3.6" + web3-utils "1.3.6" + +web3-core-method@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.6.tgz#4b0334edd94b03dfec729d113c69a4eb6ebc68ae" + integrity sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.12.1" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-utils "1.3.6" + +web3-core-promievent@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz#6c27dc79de8f71b74f5d17acaf9aaf593d3cb0c9" + integrity sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz#4fea269fe913fd4fca464b4f7c65cb94857b5b2a" + integrity sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA== + dependencies: + underscore "1.12.1" + util "^0.12.0" + web3-core-helpers "1.3.6" + web3-providers-http "1.3.6" + web3-providers-ipc "1.3.6" + web3-providers-ws "1.3.6" + +web3-core-subscriptions@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz#ee24e7974d1d72ff6c992c599deba4ef9b308415" + integrity sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-core@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.6.tgz#a6a761d1ff2f3ee462b8dab679229d2f8e267504" + integrity sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-requestmanager "1.3.6" + web3-utils "1.3.6" + +web3-eth-abi@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz#4272ca48d817aa651bbf97b269f5ff10abc2b8a9" + integrity sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ== + dependencies: + "@ethersproject/abi" "5.0.7" + underscore "1.12.1" + web3-utils "1.3.6" + +web3-eth-accounts@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.6.tgz#f9fcb50b28ee58090ab292a10d996155caa2b474" + integrity sha512-Ilr0hG6ONbCdSlVKffasCmNwftD5HsNpwyQASevocIQwHdTlvlwO0tb3oGYuajbKOaDzNTwXfz25bttAEoFCGA== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.12.1" + uuid "3.3.2" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-eth-contract@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz#cccf4d32dc56917fb6923e778498a9ba2a5ba866" + integrity sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +web3-eth-ens@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.6.tgz#0d28c5d4ea7b4462ef6c077545a77956a6cdf175" + integrity sha512-n27HNj7lpSkRxTgSx+Zo7cmKAgyg2ElFilaFlUu/X2CNH23lXfcPm2bWssivH9z0ndhg0OyR4AYFZqPaqDHkJA== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-contract "1.3.6" + web3-utils "1.3.6" + +web3-eth-iban@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz#0d6ba21fe78f190af8919e9cd5453882457209e0" + integrity sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ== + dependencies: + bn.js "^4.11.9" + web3-utils "1.3.6" + +web3-eth-personal@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.6.tgz#226137916754c498f0284f22c55924c87a2efcf0" + integrity sha512-pOHU0+/h1RFRYoh1ehYBehRbcKWP4OSzd4F7mDljhHngv6W8ewMHrAN8O1ol9uysN2MuCdRE19qkRg5eNgvzFQ== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-eth@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.6.tgz#2c650893d540a7a0eb1365dd5b2dca24ac919b7c" + integrity sha512-9+rnywRRpyX3C4hfsAQXPQh6vHh9XzQkgLxo3gyeXfbhbShUoq2gFVuy42vsRs//6JlsKdyZS7Z3hHPHz2wreA== + dependencies: + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-accounts "1.3.6" + web3-eth-contract "1.3.6" + web3-eth-ens "1.3.6" + web3-eth-iban "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-net@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.6.tgz#a56492e2227475e38db29394f8bac305a2446e41" + integrity sha512-KhzU3wMQY/YYjyMiQzbaLPt2kut88Ncx2iqjy3nw28vRux3gVX0WOCk9EL/KVJBiAA/fK7VklTXvgy9dZnnipw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-providers-http@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.6.tgz#36e8724a7424d52827819d53fd75dbf31f5422c2" + integrity sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q== + dependencies: + web3-core-helpers "1.3.6" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz#cef8d12c1ebb47adce5ebf597f553c623362cb4a" + integrity sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA== + dependencies: + oboe "2.1.5" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-providers-ws@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz#e1df617bc89d66165abdf2191da0014c505bfaac" + integrity sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + websocket "^1.0.32" + +web3-shh@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.6.tgz#4e3486c7eca5cbdb87f88910948223a5b7ea6c20" + integrity sha512-9zRo415O0iBslxBnmu9OzYjNErzLnzOsy+IOvSpIreLYbbAw0XkDWxv3SfcpKnTIWIACBR4AYMIxmmyi5iB3jw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-net "1.3.6" + +web3-utils@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.6.tgz#390bc9fa3a7179746963cfaca55bb80ac4d8dc10" + integrity sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.12.1" + utf8 "3.0.0" + +web3@1.3.6, web3@^1.0.0-beta.34: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.6.tgz#599425461c3f9a8cbbefa70616438995f4a064cc" + integrity sha512-jEpPhnL6GDteifdVh7ulzlPrtVQeA30V9vnki9liYlUvLV82ZM7BNOQJiuzlDePuE+jZETZSP/0G/JlUVt6pOA== + dependencies: + web3-bzz "1.3.6" + web3-core "1.3.6" + web3-eth "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-shh "1.3.6" + web3-utils "1.3.6" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" + integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== + dependencies: + async-limiter "~1.0.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/solidity/bmv/.prettierrc b/solidity/bmv/.prettierrc new file mode 100644 index 00000000..76536f79 --- /dev/null +++ b/solidity/bmv/.prettierrc @@ -0,0 +1,15 @@ +{ + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false, + "explicitTypes": "always" + } + } + ] +} \ No newline at end of file diff --git a/solidity/bmv/.solhint.json b/solidity/bmv/.solhint.json new file mode 100644 index 00000000..e90e710c --- /dev/null +++ b/solidity/bmv/.solhint.json @@ -0,0 +1,15 @@ +{ + "extends": ["solhint:recommended"], + "rules": { + "compiler-version": ["error", ">=0.5.0 <0.8.0"], + "prettier/prettier": "error", + "avoid-throw": "off", + "avoid-suicide": "error", + "avoid-sha3": "warn", + "no-inline-assembly": "off", + "func-visibility": ["warn", {"ignoreConstructors": true}], + "no-empty-blocks": "off", + "reason-string": "off" + }, + "plugins": ["prettier"] +} \ No newline at end of file diff --git a/solidity/bmv/README.md b/solidity/bmv/README.md new file mode 100644 index 00000000..91cc0fa2 --- /dev/null +++ b/solidity/bmv/README.md @@ -0,0 +1,33 @@ +## Set up +Node >= 10.x +``` +$ node --version +v15.12.0 +``` +Install tools +``` +$ npm install --global yarn truffle@5.3.0 +``` +Install dependencies +``` +$ yarn +``` + +## Test +1. Run in a background process or seperate terminal window +``` +$ docker run --rm -p 9933:9933 -p 9944:9944 purestake/moonbeam:v0.9.2 --dev --ws-external --rpc-external +``` +2. Compile contracts +``` +$ yarn contract:compile +``` +3. Run unit and integration test +``` +$ yarn test +``` +- Run specific test +``` +$ yarn test:unit +$ yarn test:integration +``` \ No newline at end of file diff --git a/solidity/bmv/contracts/BMV.sol b/solidity/bmv/contracts/BMV.sol new file mode 100644 index 00000000..de2534b3 --- /dev/null +++ b/solidity/bmv/contracts/BMV.sol @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./interfaces/IBMV.sol"; +import "./interfaces/IDataValidator.sol"; + +import "./libraries/Base64.sol"; +import "./libraries/MerkleTreeAccumulator.sol"; +import "./libraries/String.sol"; +import "./libraries/Types.sol"; +import "./libraries/MessageDecoder.sol"; +import "./libraries/Verifier.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMV is IBMV, Initializable { + using MerkleTreeAccumulator for MerkleTreeAccumulator.MTA; + using MerkleTreeAccumulator for bytes; + using String for string; + using String for address; + using Base64 for bytes; + using Base64 for string; + using MessageDecoder for bytes; + using MessageDecoder for Types.Validators; + using Verifier for Types.BlockUpdate; + using Verifier for Types.BlockProof; + + address private bmcAddr; + address private subBmvAddr; + string private netAddr; + uint256 private lastBlockHeight; + bytes32 internal lastBlockHash; + Types.Validators private validators; + MerkleTreeAccumulator.MTA internal mta; + + function initialize( + address _bmcAddr, + address _subBmvAddr, + string memory _netAddr, + bytes memory _rlpValidators, + uint256 _offset, + uint256 _rootsSize, + uint256 _cacheSize, + bytes32 _lastBlockHash + ) public initializer { + bmcAddr = _bmcAddr; + subBmvAddr = _subBmvAddr; + netAddr = _netAddr; + validators.decodeValidators(_rlpValidators); + mta.setOffset(_offset); + mta.rootsSize = _rootsSize; + mta.cacheSize = _cacheSize; + mta.isAllowNewerWitness = true; + lastBlockHeight = _offset; + lastBlockHash = _lastBlockHash; + } + + /** + @return Base64 encode of Merkle Tree + */ + function getMTA() external view override returns (string memory) { + return mta.toBytes().encode(); + } + + /** + @return connected BMC address + */ + function getConnectedBMC() external view override returns (address) { + return bmcAddr; + } + + /** + @return network address of the blockchain + */ + function getNetAddress() external view override returns (string memory) { + return netAddr; + } + + /** + @return hash of RLP encode from given list of validators + @return list of validators' addresses + */ + function getValidators() + external + view + override + returns (bytes32, address[] memory) + { + return (validators.validatorsHash, validators.validatorAddrs); + } + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + override + returns ( + uint256, + uint256, + uint256 + ) + { + return (mta.height, mta.offset, lastBlockHeight); + } + + function getLastReceiptHash(Types.RelayMessage memory relayMsg) + internal + returns (bytes32 receiptHash, uint256 lastHeight) + { + for (uint256 i = 0; i < relayMsg.blockUpdates.length; i++) { + // verify height + require( + relayMsg.blockUpdates[i].blockHeader.height <= mta.height + 1, + "BMVRevertInvalidBlockUpdateHigher" + ); + + require( + relayMsg.blockUpdates[i].blockHeader.height == mta.height + 1, + "BMVRevertInvalidBlockUpdateLower" + ); + + // verify prev block hash + if (i == 0) + require( + relayMsg.blockUpdates[i].blockHeader.prevHash == + lastBlockHash, + "BMVRevertInvalidBlockUpdate: Invalid block hash" + ); + else { + require( + relayMsg.blockUpdates[i].blockHeader.prevHash == + relayMsg.blockUpdates[i - 1].blockHeader.blockHash, + "BMVRevertInvalidBlockUpdate: Invalid block hash" + ); + } + + if (i == relayMsg.blockUpdates.length - 1) { + receiptHash = relayMsg.blockUpdates[i] + .blockHeader + .result + .receiptHash; + lastHeight = relayMsg.blockUpdates[i].blockHeader.height; + lastBlockHash = relayMsg.blockUpdates[i].blockHeader.blockHash; + } + + if ( + validators.validatorsHash != + relayMsg.blockUpdates[i].nextValidatorsHash || + i == relayMsg.blockUpdates.length - 1 + ) { + if (relayMsg.blockUpdates[i].verifyValidators(validators)) { + delete validators; + validators.decodeValidators( + relayMsg.blockUpdates[i].nextValidatorsRlp + ); + } + } + + mta.add(relayMsg.blockUpdates[i].blockHeader.blockHash); + } + + if (!relayMsg.isBPEmpty) { + relayMsg.blockProof.verifyMTAProof(mta); + receiptHash = relayMsg.blockProof.blockHeader.result.receiptHash; + lastHeight = relayMsg.blockProof.blockHeader.height; + } + + return (receiptHash, lastHeight); + } + + function checkAccessible( + string memory _currentAddr, + string memory _fromAddr + ) internal view { + (string memory _net, ) = _fromAddr.splitBTPAddress(); + require(netAddr.compareTo(_net), "BMVRevert: Invalid previous BMC"); + require(msg.sender == bmcAddr, "BMVRevert: Invalid BMC"); + (, string memory _contractAddr) = _currentAddr.splitBTPAddress(); + require( + _contractAddr.parseAddress() == bmcAddr, + "BMVRevert: Invalid BMC" + ); + } + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + + function handleRelayMessage( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes memory _msg + ) external override returns (bytes[] memory) { + checkAccessible(_bmc, _prev); + Types.RelayMessage memory relayMsg = _msg.decodeRelayMessage(); + require( + relayMsg.blockUpdates.length != 0 || !relayMsg.isBPEmpty, + "BMVRevert: Invalid relay message" + ); + + (bytes32 _receiptHash, uint256 _lastHeight) = + getLastReceiptHash(relayMsg); + + bytes[] memory msgs = + IDataValidator(subBmvAddr).validateReceipt( + _bmc, + _prev, + _seq, + _msg, + _receiptHash + ); + + if (msgs.length > 0) lastBlockHeight = _lastHeight; + return msgs; + } +} diff --git a/solidity/bmv/contracts/DataValidator.sol b/solidity/bmv/contracts/DataValidator.sol new file mode 100644 index 00000000..fa3b4b95 --- /dev/null +++ b/solidity/bmv/contracts/DataValidator.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./interfaces/IDataValidator.sol"; + +import "./libraries/Types.sol"; +import "./libraries/String.sol"; +import "./libraries/MessageDecoder.sol"; +import "./libraries/Verifier.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract DataValidator is IDataValidator, Initializable { + using String for string; + using MessageDecoder for bytes; + using MessageDecoder for string; + using MessageDecoder for Types.EventLog; + using Verifier for Types.ReceiptProof; + + bytes[] internal msgs; + + function initialize() public initializer {} + + function validateReceipt( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes memory _serializedMsg, + bytes32 _receiptHash + ) external virtual override returns (bytes[] memory) { + uint256 nextSeq = _seq + 1; + Types.Receipt memory receipt; + Types.MessageEvent memory messageEvent; + + Types.ReceiptProof[] memory receiptProofs = + _serializedMsg.decodeReceiptProofs(); + + (, string memory contractAddr) = _prev.splitBTPAddress(); + if (msgs.length > 0) delete msgs; + for (uint256 i = 0; i < receiptProofs.length; i++) { + receipt = receiptProofs[i].verifyMPTProof(_receiptHash); + for (uint256 j = 0; j < receipt.eventLogs.length; j++) { + if (!receipt.eventLogs[j].addr.compareTo(contractAddr)) + continue; + messageEvent = receipt.eventLogs[j].toMessageEvent(); + if (bytes(messageEvent.nextBmc).length != 0) { + if (messageEvent.seq > nextSeq) + revert("BMVRevertInvalidSequenceHigher"); + else if (messageEvent.seq < nextSeq) + revert("BMVRevertInvalidSequence"); + else if (messageEvent.nextBmc.compareTo(_bmc)) { + msgs.push(messageEvent.message); + nextSeq += 1; + } + } + } + } + return msgs; + } +} diff --git a/solidity/bmv/contracts/interfaces/IBMV.sol b/solidity/bmv/contracts/interfaces/IBMV.sol new file mode 100644 index 00000000..f71d841e --- /dev/null +++ b/solidity/bmv/contracts/interfaces/IBMV.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBMV { + /** + @return base64EncodedMTA Base64 encode of Merkle Tree + */ + function getMTA() external view returns (string memory); + + /** + @return addr connected BMC address + */ + function getConnectedBMC() external view returns (address); + + /** + @return net network address of the blockchain + */ + function getNetAddress() external view returns (string memory); + + /** + @return serializedHash hash of RLP encode from given list of validators + @return addresses list of validators' addresses + */ + function getValidators() external view returns (bytes32, address[] memory); + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + returns ( + uint256 height, + uint256 offset, + uint256 lastHeight + ); + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes memory _msg + ) external returns (bytes[] memory); +} diff --git a/solidity/bmv/contracts/interfaces/IDataValidator.sol b/solidity/bmv/contracts/interfaces/IDataValidator.sol new file mode 100644 index 00000000..53424633 --- /dev/null +++ b/solidity/bmv/contracts/interfaces/IDataValidator.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IDataValidator { + /** + @notice validate receipt proofs and return btp messages + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _serializedMsg serialized bytes of Relay Message + @param _receiptHash receipt root hash of MPT + @return serializedMessages List of serialized bytes of a BTP Message + */ + function validateReceipt( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes memory _serializedMsg, + bytes32 _receiptHash + ) external returns (bytes[] memory); +} diff --git a/solidity/bmv/contracts/libraries/Base64.sol b/solidity/bmv/contracts/libraries/Base64.sol new file mode 100644 index 00000000..0417b1f1 --- /dev/null +++ b/solidity/bmv/contracts/libraries/Base64.sol @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Base64 { + bytes private constant BASE64URLCHARS = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + function decode(string memory _str) internal pure returns (bytes memory) { + bytes memory _bs = bytes(_str); + uint256 remove = 0; + if (_bs[_bs.length - 1] == "=" && _bs[_bs.length - 2] == "=") { + remove += 2; + } else if (_bs[_bs.length - 1] == "=") { + remove++; + } + uint256 resultLength = (_bs.length / 4) * 3 - remove; + bytes memory result = new bytes(resultLength); + + uint256 i = 0; + uint256 j = 0; + for (; i + 4 < _bs.length; i += 4) { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[i]), + mapBase64Char(_bs[i + 1]), + mapBase64Char(_bs[i + 2]), + mapBase64Char(_bs[i + 3]) + ); + j += 3; + } + if (remove == 1) { + (result[j], result[j + 1], ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + 0 + ); + } else if (remove == 2) { + (result[j], , ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + 0, + 0 + ); + } else { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + mapBase64Char(_bs[_bs.length - 1]) + ); + } + return result; + } + + //solhint-disable-next-line + function mapBase64Char(bytes1 _char) private pure returns (uint8 res) { + //solhint-disable-next-line var-name-mixedcase + uint8 A = 0; + uint8 a = 26; + uint8 zero = 52; + if (uint8(_char) == 45) { + res = 62; + } else if (uint8(_char) == 95) { + res = 63; + } else if (uint8(_char) >= 48 && uint8(_char) <= 57) { + res = zero + (uint8(_char) - 48); + } else if (uint8(_char) >= 65 && uint8(_char) <= 90) { + res = A + (uint8(_char) - 65); + } else if (uint8(_char) >= 97 && uint8(_char) <= 122) { + res = a + (uint8(_char) - 97); + } + } + + function decode4( + uint256 a0, + uint256 a1, + uint256 a2, + uint256 a3 + ) + private + pure + returns ( + bytes1, + bytes1, + bytes1 + ) + { + uint256 n = + ((a0 & 63) << 18) | + ((a1 & 63) << 12) | + ((a2 & 63) << 6) | + (a3 & 63); + uint256 b0 = (n >> 16) & 255; + uint256 b1 = (n >> 8) & 255; + uint256 b2 = (n) & 255; + return (bytes1(uint8(b0)), bytes1(uint8(b1)), bytes1(uint8(b2))); + } + + function encode(bytes memory _bs) internal pure returns (string memory) { + uint256 rem = _bs.length % 3; + uint256 resLength; + if (_bs.length % 3 != 0) { + resLength = (_bs.length / 3) * 4 + 4; + } else { + resLength = (_bs.length / 3) * 4; + } + + bytes memory res = new bytes(resLength); + uint256 i = 0; + uint256 j = 0; + + for (; i + 3 <= _bs.length; i += 3) { + (res[j], res[j + 1], res[j + 2], res[j + 3]) = encode3( + uint8(_bs[i]), + uint8(_bs[i + 1]), + uint8(_bs[i + 2]) + ); + j += 4; + } + + if (rem != 0) { + uint8 la0 = uint8(_bs[_bs.length - rem]); + uint8 la1 = 0; + if (rem == 2) { + la1 = uint8(_bs[_bs.length - 1]); + } + (bytes1 b0, bytes1 b1, bytes1 b2, ) = encode3(la0, la1, 0); + res[j] = b0; + res[j + 1] = b1; + if (rem == 1) { + res[j + 2] = "="; + res[j + 3] = "="; + } else if (rem == 2) { + res[j + 2] = b2; + res[j + 3] = "="; + } + } + return string(res); + } + + function encode3( + uint256 a0, + uint256 a1, + uint256 a2 + ) + private + pure + returns ( + bytes1 b0, + bytes1 b1, + bytes1 b2, + bytes1 b3 + ) + { + uint256 n = (a0 << 16) | (a1 << 8) | a2; + uint256 c0 = (n >> 18) & 63; + uint256 c1 = (n >> 12) & 63; + uint256 c2 = (n >> 6) & 63; + uint256 c3 = (n) & 63; + b0 = BASE64URLCHARS[c0]; + b1 = BASE64URLCHARS[c1]; + b2 = BASE64URLCHARS[c2]; + b3 = BASE64URLCHARS[c3]; + } +} diff --git a/solidity/bmv/contracts/libraries/Bytes.sol b/solidity/bmv/contracts/libraries/Bytes.sol new file mode 100644 index 00000000..0f7cc72a --- /dev/null +++ b/solidity/bmv/contracts/libraries/Bytes.sol @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Bytes { + using Bytes for bytes; + + function bytesToBytes32(bytes memory b) internal pure returns (bytes32) { + bytes32 out; + if (b.length != 0) + for (uint256 i = 0; i < 32; i++) { + out |= bytes32(b[i] & 0xFF) >> (i * 8); + } + + return out; + } + + function bytesToAddress(bytes memory b) + internal + pure + returns (address addr) + { + assembly { + addr := mload(add(b, 20)) + } + } + + /// @dev Returns a slices from a byte array. + /// @param b The byte array to take a slice from. + /// @param from The starting index for the slice (inclusive). + /// @param to The final index for the slice (exclusive). + /// @return result The slice containing bytes at indices [from, to) + function slice( + bytes memory b, + uint256 from, + uint256 to + ) internal pure returns (bytes memory result) { + // Ensure that the from and to positions are valid positions for a slice within + // the byte array that is being used. + require(from <= to); + require(to <= b.length); + + // Create a new bytes structure and copy contents + result = new bytes(to - from); + memCopy( + result.contentAddress(), + b.contentAddress() + from, + result.length + ); + return result; + } + + /// @dev Copies `length` bytes from memory location `source` to `dest`. + /// @param dest memory address to copy bytes to. + /// @param source memory address to copy bytes from. + /// @param length number of bytes to copy. + function memCopy( + uint256 dest, + uint256 source, + uint256 length + ) internal pure { + if (length < 32) { + // Handle a partial word by reading destination and masking + // off the bits we are interested in. + // This correctly handles overlap, zero lengths and source == dest + assembly { + let mask := sub(exp(256, sub(32, length)), 1) + let s := and(mload(source), not(mask)) + let d := and(mload(dest), mask) + mstore(dest, or(s, d)) + } + } else { + // Skip the O(length) loop when source == dest. + if (source == dest) { + return; + } + + // For large copies we copy whole words at a time. The final + // word is aligned to the end of the range (instead of after the + // previous) to handle partial words. So a copy will look like this: + // + // #### + // #### + // #### + // #### + // + // We handle overlap in the source and destination range by + // changing the copying direction. This prevents us from + // overwriting parts of source that we still need to copy. + // + // This correctly handles source == dest + // + if (source > dest) { + assembly { + // We subtract 32 from `sEnd` and `dEnd` because it + // is easier to compare with in the loop, and these + // are also the addresses we need for copying the + // last bytes. + length := sub(length, 32) + let sEnd := add(source, length) + let dEnd := add(dest, length) + + // Remember the last 32 bytes of source + // This needs to be done here and not after the loop + // because we may have overwritten the last bytes in + // source already due to overlap. + let last := mload(sEnd) + + // Copy whole words front to back + // Note: the first check is always true, + // this could have been a do-while loop. + // solhint-disable-next-line no-empty-blocks + for { + + } lt(source, sEnd) { + + } { + mstore(dest, mload(source)) + source := add(source, 32) + dest := add(dest, 32) + } + + // Write the last 32 bytes + mstore(dEnd, last) + } + } else { + assembly { + // We subtract 32 from `sEnd` and `dEnd` because those + // are the starting points when copying a word at the end. + length := sub(length, 32) + let sEnd := add(source, length) + let dEnd := add(dest, length) + + // Remember the first 32 bytes of source + // This needs to be done here and not after the loop + // because we may have overwritten the first bytes in + // source already due to overlap. + let first := mload(source) + + // Copy whole words back to front + // We use a signed comparisson here to allow dEnd to become + // negative (happens when source and dest < 32). Valid + // addresses in local memory will never be larger than + // 2**255, so they can be safely re-interpreted as signed. + // Note: the first check is always true, + // this could have been a do-while loop. + // solhint-disable-next-line no-empty-blocks + for { + + } slt(dest, dEnd) { + + } { + mstore(dEnd, mload(sEnd)) + sEnd := sub(sEnd, 32) + dEnd := sub(dEnd, 32) + } + + // Write the first 32 bytes + mstore(dest, first) + } + } + } + } + + /// @dev Gets the memory address for the contents of a byte array. + /// @param input Byte array to lookup. + /// @return memoryAddress Memory address of the contents of the byte array. + function contentAddress(bytes memory input) + internal + pure + returns (uint256 memoryAddress) + { + assembly { + memoryAddress := add(input, 32) + } + return memoryAddress; + } +} diff --git a/solidity/bmv/contracts/libraries/Hash.sol b/solidity/bmv/contracts/libraries/Hash.sol new file mode 100644 index 00000000..ff4e24be --- /dev/null +++ b/solidity/bmv/contracts/libraries/Hash.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +import "./Bytes.sol"; + +/** + @notice pre-compiles contract address https://github.com/PureStake/moonbeam/blob/master/runtime/moonbeam/src/precompiles.rs + */ +library Hash { + using Bytes for bytes; + + function sha3FIPS256(bytes memory _input) internal returns (bytes32) { + address sha3FIPS256Address = + address(0x0000000000000000000000000000000000000400); // hash(1024) + (, bytes memory _returnData) = sha3FIPS256Address.call(_input); + return _returnData.bytesToBytes32(); + } + + function ecRecoverPublicKey( + bytes32 _messageHash, + bytes memory _voteSignature + ) internal returns (bytes memory) { + bytes memory _r = _voteSignature.slice(0, 32); + bytes memory _s = _voteSignature.slice(32, 64); + bytes memory _v = _voteSignature.slice(64, 65); + bytes memory _prefix = new bytes(31); + bytes memory _input = + abi.encodePacked(_messageHash, _prefix, _v, _r, _s); + address ecRecoverAddress = + address(0x0000000000000000000000000000000000000402); // hash(1026) + (, bytes memory _returnData) = ecRecoverAddress.call(_input); + return _returnData; + } +} diff --git a/solidity/bmv/contracts/libraries/MerklePatriciaTrie.sol b/solidity/bmv/contracts/libraries/MerklePatriciaTrie.sol new file mode 100644 index 00000000..97c729a2 --- /dev/null +++ b/solidity/bmv/contracts/libraries/MerklePatriciaTrie.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +import "./RLPDecode.sol"; +import "./Bytes.sol"; +import "./Hash.sol"; + +library MerklePatriciaTrie { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for bytes; + using Bytes for bytes; + using Hash for bytes; + using MerklePatriciaTrie for MerklePatriciaTrie.MPT; + + struct MPT { + bytes32 hash; + bytes serialized; + MPT[] children; + bytes nibbles; + bytes1 prefix; + bytes data; + } + + function init(bytes32 _hash, bytes memory serialized) + internal + returns (MPT memory node) + { + node.hash = _hash; + if (serialized.length != 0) { + node.serialized = serialized; + RLPDecode.RLPItem[] memory ls = serialized.toRlpItem().toList(); + if (ls.length == 2) { + bytes memory header = ls[0].toBytes(); + node.prefix = header[0] & 0xF0; + bytes memory nibbles; + + if ((node.prefix & 0x10) != 0) + nibbles = abi.encodePacked(header[0] & 0x0F); + + node.nibbles = bytesToNibbles( + header.slice(1, header.length), + nibbles + ); + + if ((node.prefix & 0x20) != 0) node.data = ls[1].toBytes(); + else { + node.children = new MPT[](1); + node.children[0] = init( + ls[1].toBytes().bytesToBytes32(), + "" + ); + } + } else if (ls.length == 17) { + MPT memory bNode; + node.children = new MPT[](16); + for (uint256 i = 0; i < 16; i++) { + if (ls[i].toBytes().length == 0) continue; + if (ls[i].toRlpBytes()[0] >= 0xC0) + bNode = init("", ls[i].toBytes()); + else bNode = init(ls[i].toBytes().bytesToBytes32(), ""); + + node.children[i] = bNode; + delete bNode; + } + node.data = ls[16].toBytes(); + } else revert("MPTException: Invalid list length"); + } + } + + function bytesToNibbles(bytes memory data, bytes memory nibbles) + internal + pure + returns (bytes memory) + { + bytes memory expanded = new bytes(data.length * 2); + for (uint256 i = 0; i < data.length; i++) { + expanded[i * 2] = (data[i] >> 4) & 0x0f; + expanded[i * 2 + 1] = data[i] & 0x0f; + } + return abi.encodePacked(nibbles, expanded); + } + + function matchNibbles(bytes memory src, bytes memory dst) + internal + pure + returns (uint256) + { + uint256 len = (dst.length < src.length) ? dst.length : src.length; + for (uint256 i = 0; i < len; i++) if (src[i] != dst[i]) return i; + return len; + } + + function prove( + MPT memory mpt, + bytes memory nibbles, + bytes[] memory proofs + ) internal returns (bytes memory) { + // check if node is a hash + if (mpt.hash.length > 0 && mpt.serialized.length == 0) { + bytes memory serialized = proofs[0]; + bytes32 _hash = serialized.sha3FIPS256(); + if (proofs.length > 1) { + bytes[] memory temp = new bytes[](proofs.length - 1); + for (uint256 i = 0; i < temp.length; i++) + temp[i] = proofs[i + 1]; + delete proofs; + proofs = temp; + } else if (proofs.length == 1) delete proofs; + + require(mpt.hash == _hash, "MPTException: Mismatch hash"); + MPT memory node = init(_hash, serialized); + return node.prove(nibbles, proofs); + // check if node is extension + } else if (mpt.children.length == 1) { + uint256 sharedLen = matchNibbles(mpt.nibbles, nibbles); + require( + sharedLen >= mpt.nibbles.length, + "MPTException: Mismatch nibbles on extension" + ); + return + mpt.children[0].prove( + nibbles.slice(sharedLen, nibbles.length), + proofs + ); + // check if node is branch + } else if (mpt.children.length == 16) { + if (nibbles.length == 0) return mpt.data; + else { + MPT memory node = mpt.children[uint8(nibbles[0])]; + return node.prove(nibbles.slice(1, nibbles.length), proofs); + } + } else { + uint256 sharedLen = matchNibbles(mpt.nibbles, nibbles); + require( + sharedLen >= nibbles.length, + "MPTException: Mismatch nibbles on leaf" + ); + return mpt.data; + } + } + + function prove( + bytes32 root, + bytes memory key, + bytes[] memory proofs + ) internal returns (bytes memory) { + bytes memory nibbles = bytesToNibbles(key, ""); + MPT memory mpt = init(root, ""); + return mpt.prove(nibbles, proofs); + } +} diff --git a/solidity/bmv/contracts/libraries/MerkleTreeAccumulator.sol b/solidity/bmv/contracts/libraries/MerkleTreeAccumulator.sol new file mode 100644 index 00000000..147f8a1f --- /dev/null +++ b/solidity/bmv/contracts/libraries/MerkleTreeAccumulator.sol @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./RLPEncode.sol"; +import "./Bytes.sol"; +import "./Hash.sol"; + +library MerkleTreeAccumulator { + using RLPEncode for bytes; + using RLPEncode for uint256; + using RLPEncode for bool; + + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for bytes; + + using Bytes for bytes; + + using Hash for bytes; + + struct MTA { + uint256 height; + bytes32[] roots; + uint256 offset; + uint256 rootsSize; + uint256 cacheSize; + bytes32[] cache; + bool isAllowNewerWitness; + } + + function initFromSerialized(MTA storage mta, bytes memory rlpBytes) + internal + { + RLPDecode.RLPItem[] memory unpacked; + RLPDecode.RLPItem[] memory serializedRoots; + + if (rlpBytes.length != 0) { + unpacked = rlpBytes.toRlpItem().toList(); + } + + if (unpacked.length > 0) mta.height = unpacked[0].toUint(); + + if (unpacked.length > 1) { + serializedRoots = unpacked[1].toList(); + for (uint256 i = 0; i < serializedRoots.length; i++) { + mta.roots.push(serializedRoots[i].toBytes().bytesToBytes32()); + } + } + + if (unpacked.length > 2) mta.offset = unpacked[2].toUint(); + + if (unpacked.length > 3) mta.rootsSize = unpacked[3].toUint(); + + if (unpacked.length > 4) mta.cacheSize = unpacked[4].toUint(); + + if (unpacked.length > 5) { + delete serializedRoots; + serializedRoots = unpacked[5].toList(); + for (uint256 i = 0; i < serializedRoots.length; i++) { + mta.cache.push(serializedRoots[i].toBytes().bytesToBytes32()); + } + } + + if (unpacked.length > 6) + mta.isAllowNewerWitness = unpacked[6].toBoolean(); + + if (mta.height == 0 && mta.offset == 0) mta.height = mta.offset; + } + + function setOffset(MTA storage mta, uint256 offset) internal { + mta.offset = offset; + if (mta.height == 0 && mta.offset > 0) mta.height = mta.offset; + } + + function getRoot(MTA storage mta, uint256 idx) + internal + view + returns (bytes32 root) + { + require( + 0 <= idx && idx < mta.roots.length, + "BMVRevertInvalidBlockWitness: root index is out of range" + ); + return mta.roots[idx]; + } + + function doesIncludeCache(MTA storage mta, bytes32 _hash) + internal + view + returns (bool) + { + if (_hash == 0 && _hash.length == 0) return false; + for (uint256 i = 0; i < mta.cache.length; i++) + if (mta.cache[i] == _hash) return true; + return false; + } + + function putCache(MTA storage mta, bytes32 _hash) internal { + if (mta.cacheSize > 0) mta.cache.push(_hash); + if (mta.cache.length > mta.cacheSize) { + bytes32[] memory newCache = new bytes32[](mta.cacheSize); + for (uint256 i = 0; i < mta.cacheSize; i++) + newCache[i] = mta.cache[i + mta.cache.length - mta.cacheSize]; + delete mta.cache; + mta.cache = newCache; + } + } + + function add(MTA storage mta, bytes32 _hash) internal { + putCache(mta, _hash); + if (mta.height == 0) mta.roots.push(_hash); + else if (mta.roots.length == 0) mta.roots.push(_hash); + else { + bytes32 root; + for (uint256 i = 0; i < mta.roots.length; i++) { + if (mta.roots[i] == 0) { + root = _hash; + mta.roots[i] = root; + break; + } else { + if (0 < mta.rootsSize && mta.rootsSize <= i + 1) { + root = _hash; + mta.roots[i] = root; + mta.offset += 2**i; + break; + } else { + _hash = abi + .encodePacked(mta.roots[i], _hash) + .sha3FIPS256(); + delete mta.roots[i]; + } + } + } + + if (root == 0) mta.roots.push(_hash); + } + mta.height += 1; + } + + function getRootIndexByHeight(MTA storage mta, uint256 height) + internal + view + returns (uint256) + { + uint256 idx = height - 1 - mta.offset; + uint256 rootIdx = 0; + uint256 i = mta.roots.length; + uint256 bitFlag; + while (i > 0) { + require( + idx >= 0, + "BMVRevertInvalidBlockWitness: given height is out of range" + ); + i -= 1; + if (mta.roots[i] == 0) continue; + bitFlag = 1 << i; + if (idx < bitFlag) { + rootIdx = i; + break; + } + idx -= bitFlag; + } + return rootIdx; + } + + function verify( + bytes32[] memory witness, + bytes32 root, + bytes32 leaf, + uint256 index + ) internal { + bytes32 hash = leaf; + for (uint256 i = 0; i < witness.length; i++) { + if (index % 2 == 0) { + hash = abi.encodePacked(hash, witness[i]).sha3FIPS256(); + } else { + hash = abi.encodePacked(witness[i], hash).sha3FIPS256(); + } + + index = index / 2; + } + + require(hash == root, "BMVRevertInvalidBlockWitness: invalid witness"); + } + + function verify( + MTA storage mta, + bytes32[] memory proof, + bytes32 leaf, + uint256 height, + uint256 at + ) internal { + bytes32 root; + uint256 rootIdx; + + if (mta.height == at) { + root = getRoot(mta, proof.length); + verify(proof, root, leaf, height - 1 - mta.offset); + } else if (mta.height < at) { + require( + mta.isAllowNewerWitness, + "BMVRevertInvalidBlockWitness: not allowed newer witness" + ); + require( + mta.height >= height, + "BMVRevertInvalidBlockWitness: given witness for newer node" + ); + rootIdx = getRootIndexByHeight(mta, height); + root = getRoot(mta, rootIdx); + bytes32[] memory sliceRoots = new bytes32[](rootIdx); + for (uint256 i = 0; i < rootIdx; i++) sliceRoots[i] = proof[i]; + verify(sliceRoots, root, leaf, height - 1 - mta.offset); + } else { + if (int256(mta.height - height - 1) < int256(mta.cacheSize)) + require( + doesIncludeCache(mta, leaf), + "BMVRevertInvalidBlockWitness: invalid old witness" + ); + else { + revert("BMVRevertInvalidBlockWitnessOld"); + } + } + } + + function toBytes(MTA storage mta) internal view returns (bytes memory) { + bytes memory rlpBytes; + bytes memory rlpTemp; + + // RLP encode roots[] + if (mta.roots.length == 0) + rlpTemp = abi.encodePacked(RLPEncode.LIST_SHORT_START); + else { + for (uint256 i = 0; i < mta.roots.length; i++) { + rlpTemp = abi.encodePacked( + rlpTemp, + abi.encodePacked(mta.roots[i]).encodeBytes() + ); + } + rlpTemp = abi.encodePacked( + rlpTemp.length.addLength(false), + rlpTemp + ); + } + + // RLP encode height, roots[], offset, rootsSize, cacheSize + rlpBytes = abi.encodePacked( + rlpBytes, + mta.height.encodeUint(), + rlpTemp, + mta.offset.encodeUint(), + mta.rootsSize.encodeUint(), + mta.cacheSize.encodeUint() + ); + delete rlpTemp; + + // RLP encode cache + if (mta.cache.length == 0) + rlpTemp = abi.encodePacked(RLPEncode.LIST_SHORT_START); + else { + for (uint256 i = 0; i < mta.cache.length; i++) { + rlpTemp = abi.encodePacked( + rlpTemp, + abi.encodePacked(mta.cache[i]).encodeBytes() + ); + } + rlpTemp = abi.encodePacked( + rlpTemp.length.addLength(false), + rlpTemp + ); + } + rlpBytes = abi.encodePacked(rlpBytes, rlpTemp); + + // RLP encode isAllowNewerWitness + rlpBytes = abi.encodePacked( + rlpBytes, + mta.isAllowNewerWitness.encodeBool() + ); + + // Add rlp bytes length header + rlpBytes = abi.encodePacked(rlpBytes.length.addLength(false), rlpBytes); + return rlpBytes; + } +} diff --git a/solidity/bmv/contracts/libraries/MessageDecoder.sol b/solidity/bmv/contracts/libraries/MessageDecoder.sol new file mode 100644 index 00000000..85e25b4b --- /dev/null +++ b/solidity/bmv/contracts/libraries/MessageDecoder.sol @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <=0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./Types.sol"; +import "./String.sol"; +import "./Bytes.sol"; +import "./Hash.sol"; + +library MessageDecoder { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for bytes; + using String for string; + using String for address; + using Bytes for bytes; + using Bytes for bytes; + using MessageDecoder for bytes; + using MessageDecoder for RLPDecode.RLPItem; + using Hash for bytes; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function decodeBlockHeader(bytes memory _rlp) + internal + returns (Types.BlockHeader memory) + { + // Decode RLP bytes into a list of items + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bool isResultEmpty = true; + if (ls[10].toBytes().length == 0) { + return + Types.BlockHeader( + _rlp.sha3FIPS256(), + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes().bytesToBytes32(), + ls[5].toBytes().bytesToBytes32(), + ls[6].toBytes().bytesToBytes32(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.Result("", "", "", ""), + isResultEmpty + ); + } + RLPDecode.RLPItem[] memory subList = + ls[10].toBytes().toRlpItem().toList(); + isResultEmpty = false; + return + Types.BlockHeader( + _rlp.sha3FIPS256(), + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes().bytesToBytes32(), + ls[5].toBytes().bytesToBytes32(), + ls[6].toBytes().bytesToBytes32(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.Result( + subList[0].toBytes().bytesToBytes32(), + subList[1].toBytes().bytesToBytes32(), + subList[2].toBytes().bytesToBytes32(), + subList.length == 4 ? subList[3].toBytes() : bytes("") + ), + isResultEmpty + ); + } + + // Votes item consists of: + // round as integer + // blockPartSetID is a list that consists of two items - integer and bytes + // and TS[] ts_list (an array of list) + function decodeVotes(bytes memory _rlp) + internal + pure + returns (Types.Votes memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.TS memory item; + Types.TS[] memory tsList; + Types.Votes memory votes; + if (ls.length > 0) { + tsList = new Types.TS[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpTs = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + item = Types.TS( + rlpTs[i].toList()[0].toUint(), + rlpTs[i].toList()[1].toBytes() + ); + tsList[i] = item; + } + + votes = Types.Votes( + ls[0].toUint(), + Types.BPSI( + ls[1].toList()[0].toUint(), + ls[1].toList()[1].toBytes() + ), + tsList + ); + } + return votes; + } + + function decodeBlockWitness(RLPDecode.RLPItem memory _rlpItem) + internal + pure + returns (Types.BlockWitness memory) + { + RLPDecode.RLPItem[] memory ls = _rlpItem.toList(); + + bytes32[] memory witnesses = new bytes32[](ls[1].toList().length); + + for (uint256 i = 0; i < ls[1].toList().length; i++) { + witnesses[i] = ls[1].toList()[i].toBytes().bytesToBytes32(); + } + return Types.BlockWitness(ls[0].toUint(), witnesses); + } + + function decodeEventProof(bytes memory _rlp) + internal + pure + returns (Types.EventProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory data = ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory mptProofs = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + mptProofs[i] = data[i].toBytes(); + } + return Types.EventProof(ls[0].toUint(), ls[0].toRlpBytes(), mptProofs); + } + + function decodeBlockUpdate(bytes memory _rlp) + internal + returns (Types.BlockUpdate memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.Votes memory _v = ls[1].toBytes().decodeVotes(); + // Types.Votes memory _v; + + // BlockUpdate may or may not include the RLP of addresses of validators + // In that case, RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START == 0xF800 + // Thus, length of data will be 0. Therein, loop will be skipped + // and the _validators[] will be empty + // Otherwise, executing normally to read and assign value into the array _validators[] + address[] memory _validators; + bytes32 _validatorsHash; + bytes memory _rlpBytes; + if (ls[2].toBytes().length != 0) { + RLPDecode.RLPItem[] memory _rlpItems = + ls[2].toBytes().toRlpItem().toList(); + _rlpBytes = ls[2].toBytes(); + _validatorsHash = _rlpBytes.sha3FIPS256(); + _validators = new address[](_rlpItems.length); + for (uint256 i = 0; i < _rlpItems.length; i++) { + _validators[i] = _rlpItems[i] + .toBytes() + .slice(1, 21) + .bytesToAddress(); + } + } + return + Types.BlockUpdate(_bh, _v, _validators, _rlpBytes, _validatorsHash); + } + + function decodeReceiptProof(bytes memory _rlp) + internal + pure + returns (Types.ReceiptProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory serializedMptProofs = + ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory mptProofs = new bytes[](serializedMptProofs.length); + for (uint256 i = 0; i < serializedMptProofs.length; i++) { + mptProofs[i] = serializedMptProofs[i].toBytes(); + } + + Types.EventProof[] memory eventProofs = + new Types.EventProof[](ls[2].toList().length); + + for (uint256 i = 0; i < ls[2].toList().length; i++) { + eventProofs[i] = ls[2].toList()[i].toRlpBytes().decodeEventProof(); + } + + return + Types.ReceiptProof( + ls[0].toUint(), + ls[0].toRlpBytes(), + mptProofs, + eventProofs + ); + } + + function decodeEventLog(bytes memory _rlp) + internal + pure + returns (Types.EventLog memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + string memory addr = + ls[0].toBytes().slice(1, 21).bytesToAddress().addressToString(true); + + RLPDecode.RLPItem[] memory temp = ls[1].toList(); + bytes[] memory idxed = new bytes[](temp.length); + for (uint256 i = 0; i < ls.length; i++) { + idxed[i] = temp[i].toBytes(); + } + delete temp; + + temp = ls[2].toList(); + bytes[] memory data = new bytes[](temp.length); + for (uint256 i = 0; i < data.length; i++) { + data[i] = temp[i].toBytes(); + } + return Types.EventLog(addr, idxed, data); + } + + function decodeBlockProof(bytes memory _rlp) + internal + returns (Types.BlockProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.BlockWitness memory _bw = ls[1].decodeBlockWitness(); + + return Types.BlockProof(_bh, _bw); + } + + function decodeRelayMessage(bytes memory _rlp) + internal + returns (Types.RelayMessage memory) + { + // _rlp.toRlpItem() removes the LIST_HEAD_START of RelayMessage + // then .toList() to itemize all fields in the RelayMessage + // which are [RLP_ENCODE(BlockUpdate)], RLP_ENCODE(BlockProof), and + // the RLP_ENCODE(ReceiptProof) + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + // return ls[0].toList()[0].toBytes().toRlpItem().toList()[0].toBytes().toRlpItem().toList()[8].toBytes(); + + // If [RLP_ENCODE(BlockUpdate)] was empty, it should be started by 0xF800 + // therein, ls[0].toBytes() will be null (length = 0) + // Otherwise, create an array of BlockUpdate struct to decode + Types.BlockUpdate[] memory _buArray; + if (ls[0].toBytes().length != 0) { + _buArray = new Types.BlockUpdate[](ls[0].toList().length); + for (uint256 i = 0; i < ls[0].toList().length; i++) { + // Each of items inside an array [RLP_ENCODE(BlockUpdate)] + // is a string which defines RLP_ENCODE(BlockUpdate) + // that contains a LIST_HEAD_START and multiple RLP of data + // ls[0].toList()[i].toBytes() returns bytes presentation of + // RLP_ENCODE(BlockUpdate) + _buArray[i] = ls[0].toList()[i].toBytes().decodeBlockUpdate(); + } + } + + bool isBPEmpty = true; + Types.BlockProof memory _bp; + // If RLP_ENCODE(BlockProof) is omitted, + // ls[1].toBytes() should be null (length = 0) + if (ls[1].toBytes().length != 0) { + _bp = ls[1].toBytes().decodeBlockProof(); + isBPEmpty = false; // add this field into RelayMessage + // to specify whether BlockProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + + return Types.RelayMessage(_buArray, _bp, isBPEmpty); + } + + function decodeReceiptProofs(bytes memory _rlp) + internal + pure + returns (Types.ReceiptProof[] memory _rp) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + if (ls[2].toBytes().length != 0) { + _rp = new Types.ReceiptProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _rp[i] = ls[2].toList()[i].toBytes().decodeReceiptProof(); + } + } + } + + function decodeValidators( + Types.Validators storage validators, + bytes memory _rlp + ) internal { + validators.serializedBytes = _rlp; + validators.validatorsHash = validators.serializedBytes.sha3FIPS256(); + + RLPDecode.RLPItem[] memory _rlpItems = + validators.serializedBytes.toRlpItem().toList(); + address[] memory newVals = new address[](_rlpItems.length); + for (uint256 i = 0; i < _rlpItems.length; i++) { + newVals[i] = _rlpItems[i].toBytes().slice(1, 21).bytesToAddress(); + validators.containedValidators[newVals[i]] = true; + } + validators.validatorAddrs = newVals; + } + + function toMessageEvent(Types.EventLog memory eventLog) + internal + pure + returns (Types.MessageEvent memory) + { + string memory method = string(eventLog.idx[0]).split("(")[0]; + if (method.compareTo("Message")) { + return + Types.MessageEvent( + string(eventLog.idx[1]), + eventLog.idx[2].toRlpItem().toUint(), + eventLog.data[0] + ); + } + return Types.MessageEvent("", 0, ""); + } +} diff --git a/solidity/bmv/contracts/libraries/RLPDecode.sol b/solidity/bmv/contracts/libraries/RLPDecode.sol new file mode 100644 index 00000000..3fff3f07 --- /dev/null +++ b/solidity/bmv/contracts/libraries/RLPDecode.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +library RLPDecode { + uint8 public constant STRING_SHORT_START = 0x80; + uint8 public constant STRING_LONG_START = 0xb8; + uint8 public constant LIST_SHORT_START = 0xc0; + uint8 public constant LIST_LONG_START = 0xf8; + uint8 public constant WORD_SIZE = 32; + + struct RLPItem { + uint256 len; + uint256 memPtr; + } + + /* + * @param item RLP encoded bytes + */ + function toRlpItem(bytes memory item) + internal + pure + returns (RLPItem memory) + { + uint256 memPtr; + assembly { + memPtr := add(item, 0x20) + } + + return RLPItem(item.length, memPtr); + } + + /* + * @param item RLP encoded bytes + */ + function rlpLen(RLPItem memory item) internal pure returns (uint256) { + return item.len; + } + + /* + * @param item RLP encoded list in bytes + */ + function toList(RLPItem memory item) + internal + pure + returns (RLPItem[] memory) + { + // require(isList(item), "RLPDecoder iterator is not a list"); + require(isList(item)); + + uint256 items = numItems(item); + RLPItem[] memory result = new RLPItem[](items); + + uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 dataLen; + for (uint256 i = 0; i < items; i++) { + dataLen = _itemLength(memPtr); + result[i] = RLPItem(dataLen, memPtr); + memPtr = memPtr + dataLen; + } + + return result; + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.len == 0) return false; + + uint8 byte0; + uint256 memPtr = item.memPtr; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /** RLPItem conversions into data types **/ + + // @returns raw rlp encoding in bytes + function toRlpBytes(RLPItem memory item) + internal + pure + returns (bytes memory) + { + bytes memory result = new bytes(item.len); + if (result.length == 0) return result; + + uint256 ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.memPtr, ptr, item.len); + return result; + } + + // any non-zero byte except "0x80" is considered true + function toBoolean(RLPItem memory item) internal pure returns (bool) { + // require(item.len == 1, "RLPDecoder toBoolean invalid length"); + require(item.len == 1); + uint256 result; + uint256 memPtr = item.memPtr; + assembly { + result := byte(0, mload(memPtr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function toAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + // require(item.len == 21, "RLPDecoder toAddress invalid length"); + require(item.len == 21); + return address(toUint(item)); + } + + function toUint(RLPItem memory item) internal pure returns (uint256) { + // require( + // item.len > 0 && item.len <= 33, + // "RLPDecoder toUint invalid length" + // ); + require(item.len > 0 && item.len <= 33); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; + + uint256 result; + uint256 memPtr = item.memPtr + offset; + assembly { + result := mload(memPtr) + + // shfit to the correct location if neccesary + if lt(len, 32) { + result := div(result, exp(256, sub(32, len))) + } + } + + return result; + } + + function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + // require(item.len > 0, "RLPDecoder toBytes invalid length"); + require(item.len > 0); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; // data length + bytes memory result = new bytes(len); + + uint256 destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(item.memPtr + offset, destPtr, len); + return result; + } + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) internal pure returns (uint256) { + if (item.len == 0) return 0; + + uint256 count = 0; + uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 endPtr = item.memPtr + item.len; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint256 memPtr) private pure returns (uint256) { + uint256 itemLen; + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) itemLen = 1; + else if (byte0 < STRING_LONG_START) + itemLen = byte0 - STRING_SHORT_START + 1; + else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + memPtr := add(memPtr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + memPtr := add(memPtr, 1) + + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint256 memPtr) private pure returns (uint256) { + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) return 0; + else if ( + byte0 < STRING_LONG_START || + (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START) + ) return 1; + else if (byte0 < LIST_SHORT_START) + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + else return byte0 - (LIST_LONG_START - 1) + 1; + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param len Amount of memory to copy from the source + */ + function copy( + uint256 src, + uint256 dest, + uint256 len + ) private pure { + if (len == 0) return; + + // copy as many word sizes as possible + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + // left over bytes. Mask is used to remove unwanted bytes from the word + uint256 mask = 256**(WORD_SIZE - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } +} diff --git a/solidity/bmv/contracts/libraries/RLPEncode.sol b/solidity/bmv/contracts/libraries/RLPEncode.sol new file mode 100644 index 00000000..e1787ff2 --- /dev/null +++ b/solidity/bmv/contracts/libraries/RLPEncode.sol @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title RLPEncode + * @dev A simple RLP encoding library. + * @author Bakaoh + * The original code was modified. For more info, please check the link: + * https://github.com/bakaoh/solidity-rlp-encode.git + */ +library RLPEncode { + uint8 public constant LIST_SHORT_START = 0xc0; + uint8 public constant LIST_LONG_START = 0xf7; + + uint8 internal constant MAX_UINT8 = type(uint8).max; + uint16 internal constant MAX_UINT16 = type(uint16).max; + uint24 internal constant MAX_UINT24 = type(uint24).max; + uint32 internal constant MAX_UINT32 = type(uint32).max; + uint40 internal constant MAX_UINT40 = type(uint40).max; + uint48 internal constant MAX_UINT48 = type(uint48).max; + uint56 internal constant MAX_UINT56 = type(uint56).max; + uint64 internal constant MAX_UINT64 = type(uint64).max; + uint72 internal constant MAX_UINT72 = type(uint72).max; + uint80 internal constant MAX_UINT80 = type(uint80).max; + uint88 internal constant MAX_UINT88 = type(uint88).max; + uint96 internal constant MAX_UINT96 = type(uint96).max; + uint104 internal constant MAX_UINT104 = type(uint104).max; + uint112 internal constant MAX_UINT112 = type(uint112).max; + uint120 internal constant MAX_UINT120 = type(uint120).max; + uint128 internal constant MAX_UINT128 = type(uint128).max; + + /* + * Internal functions + */ + + /** + * @dev RLP encodes a byte string. + * @param self The byte string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeBytes(bytes memory self) + internal + pure + returns (bytes memory) + { + bytes memory encoded; + if (self.length == 1 && uint8(self[0]) <= 128) { + encoded = self; + } else { + encoded = concat(encodeLength(self.length, 128), self); + } + return encoded; + } + + /** + * @dev RLP encodes a list of RLP encoded byte byte strings. + * @param self The list of RLP encoded byte strings. + * @return The RLP encoded list of items in bytes. + */ + function encodeList(bytes[] memory self) + internal + pure + returns (bytes memory) + { + bytes memory list = flatten(self); + return concat(encodeLength(list.length, 192), list); + } + + /** + * @dev RLP encodes a uint. + * @param self The uint to encode. + * @return The RLP encoded uint in bytes. + */ + function encodeUint(uint256 self) internal pure returns (bytes memory) { + uint256 nBytes = bitLength(self) / 8 + 1; + bytes memory uintBytes = encodeUintByLength(self); + if (nBytes - uintBytes.length > 0) { + uintBytes = abi.encodePacked(bytes1(0), uintBytes); + } + return encodeBytes(uintBytes); + } + + /** + * @dev RLP encodes an int. + * @param self The int to encode. + * @return The RLP encoded int in bytes. + */ + function encodeInt(int256 self) internal pure returns (bytes memory) { + return encodeUint(uint256(self)); + } + + /** + * @dev RLP encodes a bool. + * @param self The bool to encode. + * @return The RLP encoded bool in bytes. + */ + function encodeBool(bool self) internal pure returns (bytes memory) { + bytes memory encoded = new bytes(1); + encoded[0] = (self ? bytes1(0x01) : bytes1(0x00)); + return encoded; + } + + /* + * Private functions + */ + + /** + * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param len The length of the string or the payload. + * @param offset 128 if item is string, 192 if item is list. + * @return RLP encoded bytes. + */ + function encodeLength(uint256 len, uint256 offset) + private + pure + returns (bytes memory) + { + bytes memory encoded; + if (len < 56) { + encoded = new bytes(1); + encoded[0] = bytes32(len + offset)[31]; + } else { + uint256 lenLen; + uint256 i = 1; + while (len / i != 0) { + lenLen++; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes32(lenLen + offset + 55)[31]; + for (i = 1; i <= lenLen; i++) { + encoded[i] = bytes32((len / (256**(lenLen - i))) % 256)[31]; + } + } + return encoded; + } + + /** + * @dev Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function toBinary(uint256 _x) private pure returns (bytes memory) { + // Modify library to make it work properly when _x = 0 + if (_x == 0) { + return abi.encodePacked(uint8(_x)); + } + bytes memory b = new bytes(32); + assembly { + mstore(add(b, 32), _x) + } + uint256 i; + for (i = 0; i < 32; i++) { + if (b[i] != 0) { + break; + } + } + bytes memory res = new bytes(32 - i); + for (uint256 j = 0; j < res.length; j++) { + res[j] = b[i++]; + } + return res; + } + + /** + * @dev Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function memcpy( + uint256 _dest, + uint256 _src, + uint256 _len + ) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask = 256**(32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * @dev Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return The flattened byte string. + */ + function flatten(bytes[] memory _list) private pure returns (bytes memory) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i; + for (i = 0; i < _list.length; i++) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; i++) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } + + /** + * @dev Concatenates two bytes. + * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. + * @param _preBytes First byte string. + * @param _postBytes Second byte string. + * @return Both byte string combined. + */ + function concat(bytes memory _preBytes, bytes memory _postBytes) + private + pure + returns (bytes memory) + { + bytes memory tempBytes; + + assembly { + tempBytes := mload(0x40) + + let length := mload(_preBytes) + mstore(tempBytes, length) + + let mc := add(tempBytes, 0x20) + let end := add(mc, length) + + for { + let cc := add(_preBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + length := mload(_postBytes) + mstore(tempBytes, add(length, mload(tempBytes))) + + mc := end + end := add(mc, length) + + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore( + 0x40, + and( + add(add(end, iszero(add(length, mload(_preBytes)))), 31), + not(31) + ) + ) + } + + return tempBytes; + } + + function addLength(uint256 length, bool isLongList) + internal + pure + returns (bytes memory) + { + if (length > 55 && !isLongList) { + bytes memory payLoadSize = encodeUintByLength(length); + uint256 lengthSize = payLoadSize.length; + bytes memory listHeadStart = addLength(lengthSize, true); + return abi.encodePacked(listHeadStart, payLoadSize); + } else if (length <= 55 && !isLongList) { + return abi.encodePacked(uint8(LIST_SHORT_START + length)); + } + return abi.encodePacked(uint8(LIST_LONG_START + length)); + } + + /** + * @dev convert uint to strict bytes. + * @notice only handle to uint128 due to contract code size limit + * @param length The uint to convert. + * @return The uint in strict bytes without padding. + */ + function encodeUintByLength(uint256 length) + internal + pure + returns (bytes memory) + { + if (length < MAX_UINT8) { + return abi.encodePacked(uint8(length)); + } else if (length >= MAX_UINT8 && length < MAX_UINT16) { + return abi.encodePacked(uint16(length)); + } else if (length >= MAX_UINT16 && length < MAX_UINT24) { + return abi.encodePacked(uint24(length)); + } else if (length >= MAX_UINT24 && length < MAX_UINT32) { + return abi.encodePacked(uint32(length)); + } else if (length >= MAX_UINT32 && length < MAX_UINT40) { + return abi.encodePacked(uint40(length)); + } else if (length >= MAX_UINT40 && length < MAX_UINT48) { + return abi.encodePacked(uint48(length)); + } else if (length >= MAX_UINT48 && length < MAX_UINT56) { + return abi.encodePacked(uint56(length)); + } else if (length >= MAX_UINT56 && length < MAX_UINT64) { + return abi.encodePacked(uint64(length)); + } else if (length >= MAX_UINT64 && length < MAX_UINT72) { + return abi.encodePacked(uint72(length)); + } else if (length >= MAX_UINT72 && length < MAX_UINT80) { + return abi.encodePacked(uint80(length)); + } else if (length >= MAX_UINT80 && length < MAX_UINT88) { + return abi.encodePacked(uint88(length)); + } else if (length >= MAX_UINT88 && length < MAX_UINT96) { + return abi.encodePacked(uint96(length)); + } else if (length >= MAX_UINT96 && length < MAX_UINT104) { + return abi.encodePacked(uint104(length)); + } else if (length >= MAX_UINT104 && length < MAX_UINT112) { + return abi.encodePacked(uint112(length)); + } else if (length >= MAX_UINT112 && length < MAX_UINT120) { + return abi.encodePacked(uint120(length)); + } + require( + length >= MAX_UINT120 && length < MAX_UINT128, + "outOfBounds: [0, 2^128]" + ); + return abi.encodePacked(uint128(length)); + } + + function bitLength(uint256 n) internal pure returns (uint256) { + uint256 count; + while (n != 0) { + count += 1; + n >>= 1; + } + return count; + } +} diff --git a/solidity/bmv/contracts/libraries/String.sol b/solidity/bmv/contracts/libraries/String.sol new file mode 100644 index 00000000..3c4e7eb6 --- /dev/null +++ b/solidity/bmv/contracts/libraries/String.sol @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * Strings Library + * + * This is a simple library of string functions which try to simplify + * string operations in solidity. + * + * Please be aware some of these functions can be quite gas heavy so use them only when necessary + * + * The original library was modified. If you want to know more about the original version + * please check this link: https://github.com/willitscale/solidity-util.git + */ +library String { + /** + * splitBTPAddress + * + * Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + * into Network_address (1234.iconee) and Server_address (0x123456789) + * + * @param _base String base BTP Address format to be split + * @dev _base must follow a BTP Address format + * + * @return string, string The resulting strings of Network_address and Server_address + */ + function splitBTPAddress(string memory _base) + internal + pure + returns (string memory, string memory) + { + string[] memory temp = split(_base, "/"); + return (temp[2], temp[3]); + } + + /** + * Concat + * + * Appends two strings together and returns a new value + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string which will be the concatenated + * prefix + * @param _value The value to be the concatenated suffix + * @return string The resulting string from combinging the base and value + */ + function concat(string memory _base, string memory _value) + internal + pure + returns (string memory) + { + return string(abi.encodePacked(_base, _value)); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string starting + * from a defined offset + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @param _offset The starting point to start searching from which can start + * from 0, but must not exceed the length of the string + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function _indexOf( + string memory _base, + string memory _value, + uint256 _offset + ) internal pure returns (int256) { + bytes memory _baseBytes = bytes(_base); + bytes memory _valueBytes = bytes(_value); + + assert(_valueBytes.length == 1); + + for (uint256 i = _offset; i < _baseBytes.length; i++) { + if (_baseBytes[i] == _valueBytes[0]) { + return int256(i); + } + } + + return -1; + } + + /* + * String Split (Very high gas cost) + * + * Splits a string into an array of strings based off the delimiter value. + * Please note this can be quite a gas expensive function due to the use of + * storage so only use if really required. + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string value to be split. + * @param _value The delimiter to split the string on which must be a single + * character + * @return string[] An array of values split based off the delimiter, but + * do not container the delimiter. + */ + function split(string memory _base, string memory _value) + internal + pure + returns (string[] memory splitArr) + { + bytes memory _baseBytes = bytes(_base); + + uint256 _offset = 0; + uint256 _splitsCount = 1; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) break; + else { + _splitsCount++; + _offset = uint256(_limit) + 1; + } + } + + splitArr = new string[](_splitsCount); + + _offset = 0; + _splitsCount = 0; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) { + _limit = int256(_baseBytes.length); + } + + string memory _tmp = new string(uint256(_limit) - _offset); + bytes memory _tmpBytes = bytes(_tmp); + + uint256 j = 0; + for (uint256 i = _offset; i < uint256(_limit); i++) { + _tmpBytes[j++] = _baseBytes[i]; + } + _offset = uint256(_limit) + 1; + splitArr[_splitsCount++] = string(_tmpBytes); + } + return splitArr; + } + + /** + * Compare To + * + * Compares the characters of two strings, to ensure that they have an + * identical footprint + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string base to compare against + * @param _value The string the base is being compared to + * @return bool Simply notates if the two string have an equivalent + */ + function compareTo(string memory _base, string memory _value) + internal + pure + returns (bool) + { + if ( + keccak256(abi.encodePacked(_base)) == + keccak256(abi.encodePacked(_value)) + ) { + return true; + } + return false; + } + + function addressToString(address _address, bool isIconContract) + internal + pure + returns (string memory) + { + bytes32 _bytes = bytes32(uint256(_address)); + //solhint-disable-next-line + bytes memory HEX = "0123456789abcdef"; + bytes memory _string = new bytes(42); + if (isIconContract) _string[0] = "c"; + else _string[0] = "0"; + _string[1] = "x"; + for (uint256 i = 0; i < 20; i++) { + _string[2 + i * 2] = HEX[uint8(_bytes[i + 12] >> 4)]; + _string[3 + i * 2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; + } + return string(_string); + } + + function parseAddress(string memory _a) internal pure returns (address) { + bytes memory tmp = bytes(_a); + uint160 iaddr = 0; + uint160 b1; + uint160 b2; + for (uint256 i = 2; i < 2 + 2 * 20; i += 2) { + iaddr *= 256; + b1 = uint160(uint8(tmp[i])); + b2 = uint160(uint8(tmp[i + 1])); + if ((b1 >= 97) && (b1 <= 102)) { + b1 -= 87; + } else if ((b1 >= 65) && (b1 <= 70)) { + b1 -= 55; + } else if ((b1 >= 48) && (b1 <= 57)) { + b1 -= 48; + } + if ((b2 >= 97) && (b2 <= 102)) { + b2 -= 87; + } else if ((b2 >= 65) && (b2 <= 70)) { + b2 -= 55; + } else if ((b2 >= 48) && (b2 <= 57)) { + b2 -= 48; + } + iaddr += (b1 * 16 + b2); + } + return address(iaddr); + } +} diff --git a/solidity/bmv/contracts/libraries/Types.sol b/solidity/bmv/contracts/libraries/Types.sol new file mode 100644 index 00000000..c80ea1d2 --- /dev/null +++ b/solidity/bmv/contracts/libraries/Types.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Types { + /** + * @Notice List of ALL Struct being used to Encode and Decode RLP Messages + */ + + // Result = State Hash + Patch Receipt Hash + Receipt Hash + Extension Data + struct Result { + bytes32 stateHash; + bytes32 patchReceiptHash; + bytes32 receiptHash; + bytes extensionData; + } + + struct BlockHeader { + bytes32 blockHash; + uint256 version; + uint256 height; + uint256 timestamp; + bytes proposer; + bytes32 prevHash; + bytes32 voteHash; + bytes32 nextValidatorsHash; + bytes patchTxHash; + bytes txHash; + bytes logsBloom; + Result result; + bool isResultEmpty; // add to check whether SPR is an empty struct + // It will not be included in serializing thereafter + } + + // TS = Timestamp + Signature + struct TS { + uint256 timestamp; + bytes signature; + } + + // BPSI = blockPartSetID + struct BPSI { + uint256 n; + bytes b; + } + + struct Validators { + bytes serializedBytes; + bytes32 validatorsHash; + address[] validatorAddrs; + mapping(address => bool) containedValidators; + mapping(address => bool) checkDuplicateVotes; + } + + struct Votes { + uint256 round; + BPSI blockPartSetID; + TS[] ts; + } + + struct BlockWitness { + uint256 height; + bytes32[] witnesses; + } + + struct EventProof { + uint256 index; + bytes mptKey; + bytes[] mptProofs; + } + + struct BlockUpdate { + BlockHeader blockHeader; + Votes votes; + address[] nextValidators; + bytes nextValidatorsRlp; + bytes32 nextValidatorsHash; + } + + struct Receipt { + EventLog[] eventLogs; + bytes32 eventLogHash; + } + + struct EventLog { + string addr; + bytes[] idx; + bytes[] data; + } + + struct MessageEvent { + string nextBmc; + uint256 seq; + bytes message; + } + + struct ReceiptProof { + uint256 index; + bytes mptKey; + bytes[] mptProofs; + EventProof[] eventProofs; + } + + struct BlockProof { + BlockHeader blockHeader; + BlockWitness blockWitness; + } + + struct RelayMessage { + BlockUpdate[] blockUpdates; + BlockProof blockProof; + bool isBPEmpty; // add to check in a case BlockProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + } +} diff --git a/solidity/bmv/contracts/libraries/Verifier.sol b/solidity/bmv/contracts/libraries/Verifier.sol new file mode 100644 index 00000000..8f51edab --- /dev/null +++ b/solidity/bmv/contracts/libraries/Verifier.sol @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <=0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./Types.sol"; +import "./MessageDecoder.sol"; +import "./RLPEncode.sol"; +import "./MerkleTreeAccumulator.sol"; +import "./MerklePatriciaTrie.sol"; +import "./Bytes.sol"; +import "./Hash.sol"; + +library Verifier { + using RLPDecode for bytes; + using RLPDecode for RLPDecode.RLPItem; + using RLPEncode for bytes; + using RLPEncode for uint256; + using MerkleTreeAccumulator for uint256; + using MerkleTreeAccumulator for bytes32; + using MerkleTreeAccumulator for MerkleTreeAccumulator.MTA; + using MerklePatriciaTrie for bytes32; + using MessageDecoder for bytes; + using Bytes for bytes; + using Hash for bytes; + using Hash for bytes32; + + function verifyMTAProof( + Types.BlockProof memory _blockProof, + MerkleTreeAccumulator.MTA storage mta + ) internal { + require( + _blockProof.blockWitness.witnesses.length != 0, + "BMVRevertInvalidBlockWitness" + ); + + require( + mta.height >= _blockProof.blockHeader.height, + "BMVRevertInvalidBlockProofHigher" + ); + + mta.verify( + _blockProof.blockWitness.witnesses, + _blockProof.blockHeader.blockHash, + _blockProof.blockHeader.height, + _blockProof.blockWitness.height + ); + } + + function verifyValidators( + Types.BlockUpdate memory _blockUpdate, + Types.Validators storage validators + ) internal returns (bool) { + require( + _blockUpdate.votes.ts.length != 0, + "BMVRevertInvalidBlockUpdate: Not exists votes" + ); + + verifyVotes( + _blockUpdate.votes, + _blockUpdate.blockHeader.height, + _blockUpdate.blockHeader.blockHash, + validators, + _blockUpdate.blockHeader.nextValidatorsHash != + validators.validatorsHash + ); + + if ( + _blockUpdate.blockHeader.nextValidatorsHash != + validators.validatorsHash + ) { + if (_blockUpdate.nextValidators.length == 0) { + revert( + "BMVRevertInvalidBlockUpdate: Not exists next validators" + ); + } else if ( + _blockUpdate.nextValidatorsHash == + _blockUpdate.blockHeader.nextValidatorsHash + ) { + return true; + } else + revert( + "BMVRevertInvalidBlockUpdate: Invalid next validator hash" + ); + } + return false; + } + + function verifyVotes( + Types.Votes memory _votes, + uint256 _blockHeight, + bytes32 _blockHash, + Types.Validators storage validators, + bool _isNextValidatorsUpdated + ) internal { + // Calculate RLP of vote item [block height, vote.round, vote_type precommit = 1, block hash, vote.bpsi] + bytes memory serializedVoteMsg; + bytes[] memory _blockPartSetId = new bytes[](2); + _blockPartSetId[0] = RLPEncode.encodeUint(_votes.blockPartSetID.n); + _blockPartSetId[1] = RLPEncode.encodeBytes(_votes.blockPartSetID.b); + serializedVoteMsg = abi.encodePacked( + RLPEncode.encodeUint(_blockHeight), + RLPEncode.encodeUint(_votes.round), + RLPEncode.encodeUint(1), + RLPEncode.encodeBytes(abi.encodePacked(_blockHash)), + RLPEncode.encodeList(_blockPartSetId) + ); + + bytes32 _msgHash; + bytes memory _encodedVoteMsg; + for (uint256 i = 0; i < _votes.ts.length; i++) { + _encodedVoteMsg = abi.encodePacked( + serializedVoteMsg, + RLPEncode.encodeUint(_votes.ts[i].timestamp) + ); + _encodedVoteMsg = abi.encodePacked( + _encodedVoteMsg.length.addLength(false), + _encodedVoteMsg + ); + + _msgHash = _encodedVoteMsg.sha3FIPS256(); + bytes memory _publicKey = + _msgHash.ecRecoverPublicKey(_votes.ts[i].signature); + bytes memory _hashKey = abi.encodePacked(_publicKey.sha3FIPS256()); + address _address = _hashKey.slice(12, 32).bytesToAddress(); + + require( + validators.containedValidators[_address], + "BMVRevertInvalidVotes: Invalid signature" + ); + + require( + !validators.checkDuplicateVotes[_address], + "BMVRevertInvalidVotes: Duplicated votes" + ); + validators.checkDuplicateVotes[_address] = true; + } + + require( + _votes.ts.length > (validators.validatorAddrs.length * 2) / 3, + "BMVRevertInvalidVotes: Require votes > 2/3" + ); + + for (uint256 i = 0; i < validators.validatorAddrs.length; i++) { + delete validators.checkDuplicateVotes[validators.validatorAddrs[i]]; + if (_isNextValidatorsUpdated) + delete validators.containedValidators[ + validators.validatorAddrs[i] + ]; + } + } + + function verifyMPTProof( + Types.ReceiptProof memory _receiptProof, + bytes32 _receiptHash + ) internal returns (Types.Receipt memory) { + bytes memory leaf = + _receiptHash.prove(_receiptProof.mptKey, _receiptProof.mptProofs); + + Types.Receipt memory receipt; + receipt.eventLogHash = leaf.toRlpItem().toList()[8] + .toBytes() + .bytesToBytes32(); + + receipt.eventLogs = new Types.EventLog[]( + _receiptProof.eventProofs.length + ); + bytes[] memory serializedEventlLogs = + new bytes[](_receiptProof.eventProofs.length); + for (uint256 i = 0; i < _receiptProof.eventProofs.length; i++) { + serializedEventlLogs[i] = receipt.eventLogHash.prove( + _receiptProof.eventProofs[i].mptKey, + _receiptProof.eventProofs[i].mptProofs + ); + receipt.eventLogs[i] = serializedEventlLogs[i].decodeEventLog(); + } + return receipt; + } +} diff --git a/solidity/bmv/contracts/test/MockBMC.sol b/solidity/bmv/contracts/test/MockBMC.sol new file mode 100644 index 00000000..c2a9af60 --- /dev/null +++ b/solidity/bmv/contracts/test/MockBMC.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBMV.sol"; +import "../libraries/String.sol"; + +contract MockBMC { + using String for string; + using String for address; + + string public btpAddr; + IBMV public bmv; + + constructor(string memory _net) { + btpAddr = string( + abi.encodePacked( + "btp://", + _net, + "/", + address(this).addressToString(false) + ) + ); + } + + function addVerifier(string calldata _net, address _addr) external {} + + function testHandleRelayMessage( + address _bmvAddr, + string calldata _prev, + uint256 _seq, + string memory _msg + ) public returns (bytes[] memory) { + bmv = IBMV(_bmvAddr); + return bmv.handleRelayMessage(btpAddr, _prev, _seq, fromHex(_msg)); + } + + // Convert an hexadecimal character to their value + function fromHexChar(uint8 c) private pure returns (uint8) { + if (bytes1(c) >= bytes1("0") && bytes1(c) <= bytes1("9")) { + return c - uint8(bytes1("0")); + } + if (bytes1(c) >= bytes1("a") && bytes1(c) <= bytes1("f")) { + return 10 + c - uint8(bytes1("a")); + } + if (bytes1(c) >= bytes1("A") && bytes1(c) <= bytes1("F")) { + return 10 + c - uint8(bytes1("A")); + } + } + + // Convert an hexadecimal string to raw bytes + function fromHex(string memory s) private pure returns (bytes memory) { + bytes memory ss = bytes(s); + require(ss.length % 2 == 0); // length must be even + bytes memory r = new bytes(ss.length / 2); + for (uint256 i = 0; i < ss.length / 2; ++i) { + r[i] = bytes1( + fromHexChar(uint8(ss[2 * i])) * + 16 + + fromHexChar(uint8(ss[2 * i + 1])) + ); + } + return r; + } +} diff --git a/solidity/bmv/contracts/test/MockBMV.sol b/solidity/bmv/contracts/test/MockBMV.sol new file mode 100644 index 00000000..6301b8d2 --- /dev/null +++ b/solidity/bmv/contracts/test/MockBMV.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../BMV.sol"; +import "../libraries/MerkleTreeAccumulator.sol"; + +contract MockBMV is BMV { + using MerkleTreeAccumulator for MerkleTreeAccumulator.MTA; + + function addRootToMTA(bytes32 _blockHash) external { + mta.add(_blockHash); + } + + function setMTAHeight(uint256 _height) external { + mta.height = mta.offset = _height; + } +} diff --git a/solidity/bmv/contracts/test/MockDataValidator.sol b/solidity/bmv/contracts/test/MockDataValidator.sol new file mode 100644 index 00000000..c18bb6a6 --- /dev/null +++ b/solidity/bmv/contracts/test/MockDataValidator.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../DataValidator.sol"; +import "../libraries/String.sol"; +import "../libraries/MessageDecoder.sol"; +import "../libraries/Verifier.sol"; + +contract MockDataValidator is DataValidator { + using String for string; + using MessageDecoder for bytes; + using MessageDecoder for string; + using MessageDecoder for Types.EventLog; + using Verifier for Types.ReceiptProof; + + function validateReceipt( + string memory, /* _bmc */ + string memory _prev, + uint256 _seq, + bytes memory _serializedMsg, + bytes32 _receiptHash + ) external override returns (bytes[] memory) { + uint256 nextSeq = _seq + 1; + Types.Receipt memory receipt; + Types.MessageEvent memory messageEvent; + + Types.ReceiptProof[] memory receiptProofs = + _serializedMsg.decodeReceiptProofs(); + + (, string memory contractAddr) = _prev.splitBTPAddress(); + if (msgs.length > 0) delete msgs; + for (uint256 i = 0; i < receiptProofs.length; i++) { + receipt = receiptProofs[i].verifyMPTProof(_receiptHash); + for (uint256 j = 0; j < receipt.eventLogs.length; j++) { + if (!receipt.eventLogs[j].addr.compareTo(contractAddr)) + continue; + messageEvent = receipt.eventLogs[j].toMessageEvent(); + if (bytes(messageEvent.nextBmc).length != 0) { + if (messageEvent.seq > nextSeq) + revert("BMVRevertInvalidSequenceHigher"); + else if (messageEvent.seq < nextSeq) + revert("BMVRevertInvalidSequence"); + else if ( + // @dev mock implementation for testing + // messageEvent.nextBmc.compareTo(_bmc) + bytes(messageEvent.nextBmc).length > 0 + ) { + msgs.push(messageEvent.message); + nextSeq += 1; + } + } + } + } + return msgs; + } +} diff --git a/solidity/bmv/contracts/test/TestLibMPT.sol b/solidity/bmv/contracts/test/TestLibMPT.sol new file mode 100644 index 00000000..7bdd081f --- /dev/null +++ b/solidity/bmv/contracts/test/TestLibMPT.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/MerklePatriciaTrie.sol"; + +contract TestLibMPT { + using MerklePatriciaTrie for MerklePatriciaTrie.MPT; + using MerklePatriciaTrie for bytes32; + using MerklePatriciaTrie for bytes; + + function bytesToNibbles(bytes memory data, bytes memory nibbles) + public + pure + returns (bytes memory) + { + return data.bytesToNibbles(nibbles); + } + + function matchNibbles(bytes memory src, bytes memory dst) + public + pure + returns (uint256) + { + return src.matchNibbles(dst); + } + + function prove( + bytes32 root, + bytes memory key, + bytes[] memory proofs + ) public returns (bytes memory) { + return root.prove(key, proofs); + } +} diff --git a/solidity/bmv/contracts/test/TestLibMTA.sol b/solidity/bmv/contracts/test/TestLibMTA.sol new file mode 100644 index 00000000..73e4a889 --- /dev/null +++ b/solidity/bmv/contracts/test/TestLibMTA.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/MerkleTreeAccumulator.sol"; + +contract TestLibMTA { + using MerkleTreeAccumulator for MerkleTreeAccumulator.MTA; + using MerkleTreeAccumulator for bytes; + + MerkleTreeAccumulator.MTA public mta; + + constructor() {} + + function initFromSerialized(bytes memory serializedData) external { + mta.initFromSerialized(serializedData); + } + + function getMTA() public view returns (MerkleTreeAccumulator.MTA memory) { + return mta; + } + + function setOffset(uint256 offset) public { + mta.setOffset(offset); + } + + function getRoot(uint256 idx) public view returns (bytes32) { + return mta.getRoot(idx); + } + + function doesIncludeCache(bytes32 _hash) public view returns (bool) { + return mta.doesIncludeCache(_hash); + } + + function putCache(bytes32 _hash) public { + mta.putCache(_hash); + } + + function add(bytes32 _hash) public { + mta.add(_hash); + } + + function getRootIndexByHeight(uint256 height) + public + view + returns (uint256) + { + return mta.getRootIndexByHeight(height); + } + + function verify( + bytes32[] calldata proof, + bytes32 leaf, + uint256 height, + uint256 at + ) public { + mta.verify(proof, leaf, height, at); + } + + function toRlpBytes() public view returns (bytes memory) { + return mta.toBytes(); + } +} diff --git a/solidity/bmv/contracts/test/UpgradeBMV.sol b/solidity/bmv/contracts/test/UpgradeBMV.sol new file mode 100644 index 00000000..ec678ec0 --- /dev/null +++ b/solidity/bmv/contracts/test/UpgradeBMV.sol @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IBMV.sol"; +import "../interfaces/IDataValidator.sol"; + +import "../libraries/Base64.sol"; +import "../libraries/MerkleTreeAccumulator.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; +import "../libraries/MessageDecoder.sol"; +import "../libraries/Verifier.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract BMVV2 is IBMV, Initializable { + using MerkleTreeAccumulator for MerkleTreeAccumulator.MTA; + using MerkleTreeAccumulator for bytes; + using String for string; + using String for address; + using Base64 for bytes; + using Base64 for string; + using MessageDecoder for bytes; + using MessageDecoder for Types.Validators; + using Verifier for Types.BlockUpdate; + using Verifier for Types.BlockProof; + + address private bmcAddr; + address private subBmvAddr; + string private netAddr; + uint256 private lastBlockHeight; + bytes32 private lastBlockHash; + Types.Validators private validators; + MerkleTreeAccumulator.MTA private mta; + + function initialize( + address _bmcAddr, + address _subBmvAddr, + string memory _netAddr, + bytes memory _rlpValidators, + uint256 _offset, + bytes32 _lastBlockHash + ) public initializer { + bmcAddr = _bmcAddr; + subBmvAddr = _subBmvAddr; + netAddr = _netAddr; + validators.decodeValidators(_rlpValidators); + mta.setOffset(_offset); + lastBlockHash = _lastBlockHash; + } + + /** + @return Base64 encode of Merkle Tree + */ + function getMTA() external view override returns (string memory) { + return mta.toBytes().encode(); + } + + /** + @return connected BMC address + */ + function getConnectedBMC() external view override returns (address) { + return bmcAddr; + } + + /** + @return network address of the blockchain + */ + function getNetAddress() external view override returns (string memory) { + return netAddr; + } + + /** + @return hash of RLP encode from given list of validators + @return list of validators' addresses + */ + function getValidators() + external + view + override + returns (bytes32, address[] memory) + { + return (validators.validatorsHash, validators.validatorAddrs); + } + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + override + returns ( + uint256, + uint256, + uint256 + ) + { + return (mta.height, mta.offset, lastBlockHeight); + } + + function getLastReceiptHash(Types.RelayMessage memory relayMsg) + internal + returns (bytes32 receiptHash, uint256 lastHeight) + { + for (uint256 i = 0; i < relayMsg.blockUpdates.length; i++) { + if (i == relayMsg.blockUpdates.length - 1) { + receiptHash = relayMsg.blockUpdates[i] + .blockHeader + .result + .receiptHash; + lastHeight = relayMsg.blockUpdates[i].blockHeader.height; + lastBlockHash = relayMsg.blockUpdates[i].blockHeader.blockHash; + } + + if (relayMsg.blockUpdates[i].nextValidators.length > 0) { + delete validators; + validators.decodeValidators( + relayMsg.blockUpdates[i].nextValidatorsRlp + ); + } + + mta.add(relayMsg.blockUpdates[i].blockHeader.blockHash); + } + + if (relayMsg.blockProof.blockWitness.witnesses.length != 0) { + relayMsg.blockProof.verifyMTAProof(mta); + receiptHash = relayMsg.blockProof.blockHeader.result.receiptHash; + lastHeight = relayMsg.blockProof.blockHeader.height; + } + + return (receiptHash, lastHeight); + } + + function checkAccessible(string memory currentAddr, string memory fromAddr) + internal + view + { + string memory net; + string memory contractAddr; + (net, ) = fromAddr.splitBTPAddress(); + require(netAddr.compareTo(net), "BMVRevert: Invalid previous BMC"); + require(msg.sender == bmcAddr, "BMVRevert: Invalid BMC"); + (, contractAddr) = currentAddr.splitBTPAddress(); + require( + contractAddr.compareTo(bmcAddr.addressToString(false)), + "BMVRevert: Invalid BMC" + ); + } + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string memory _bmc, + string memory _prev, + uint256 _seq, + bytes memory /* _msg */ + ) external override returns (bytes[] memory) { + bytes[] memory msgs = new bytes[](2); + msgs[0] = IDataValidator(subBmvAddr).validateReceipt( + _bmc, + _prev, + _seq, + bytes("test upgradable BMV"), + keccak256("test root hash") + )[0]; + msgs[1] = bytes("Succeed to upgrade BMV contract"); + return msgs; + } +} diff --git a/solidity/bmv/contracts/test/UpgradeDataValidator.sol b/solidity/bmv/contracts/test/UpgradeDataValidator.sol new file mode 100644 index 00000000..5226dce5 --- /dev/null +++ b/solidity/bmv/contracts/test/UpgradeDataValidator.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../interfaces/IDataValidator.sol"; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +contract DataValidatorV2 is IDataValidator, Initializable { + bytes[] private msgs; + + function initialize() public initializer {} + + function validateReceipt( + string memory, /* _bmc */ + string memory, /* _prev */ + uint256, /* _seq */ + bytes memory, /* _serializedMsg */ + bytes32 /* _receiptHash */ + ) external override returns (bytes[] memory) { + msgs.push(bytes("Succeed to upgrade Data Validator contract")); + return msgs; + } +} diff --git a/solidity/bmv/migrations/1_deploy_bmv.js b/solidity/bmv/migrations/1_deploy_bmv.js new file mode 100644 index 00000000..f227ad96 --- /dev/null +++ b/solidity/bmv/migrations/1_deploy_bmv.js @@ -0,0 +1,23 @@ +const BMV = artifacts.require("BMV"); +const SUB_BMV = artifacts.require("DataValidator"); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +module.exports = async function (deployer, network) { + if (network !== "development") { + await deployProxy(SUB_BMV, { deployer }); + await deployProxy( + BMV, + [ + process.env.BMC_PERIPHERY_ADDRESS, + SUB_BMV.address, + process.env.BMV_ICON_NET, + process.env.BMV_ICON_ENCODED_VALIDATORS, + parseInt(process.env.BMV_ICON_INIT_OFFSET), + parseInt(process.env.BMV_ICON_INIT_ROOTSSIZE), + parseInt(process.env.BMV_ICON_INIT_CACHESIZE), + process.env.BMV_ICON_LASTBLOCK_HASH, + ], + { deployer } + ); + } +}; \ No newline at end of file diff --git a/solidity/bmv/package.json b/solidity/bmv/package.json new file mode 100644 index 00000000..f11718f7 --- /dev/null +++ b/solidity/bmv/package.json @@ -0,0 +1,36 @@ +{ + "name": "pra-bmv", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@openzeppelin/contracts-upgradeable": "3.4.1-solc-0.7-2", + "@openzeppelin/truffle-upgrades": "^1.7.0", + "@truffle/hdwallet-provider": "^1.4.0" + }, + "devDependencies": { + "chai": "^4.3.4", + "husky": "^6.0.0", + "js-sha3": "^0.8.0", + "lodash": "^4.17.21", + "prettier": "^2.2.1", + "prettier-plugin-solidity": "^1.0.0-beta.6", + "rlp": "^2.2.6", + "solhint": "^3.3.4", + "solhint-plugin-prettier": "^0.0.5", + "truffle-assertions": "^0.9.2", + "urlsafe-base64": "^1.0.0" + }, + "scripts": { + "linter": "./node_modules/.bin/solhint -f table ./contracts/**/*.sol -f table ./contracts/*.sol", + "prettier": "./node_modules/.bin/prettier --write ./contracts -l", + "contract:compile": "truffle compile --all", + "test": "yarn test:unit && yarn test:integration", + "test:unit": "rm -rf .openzeppelin && truffle test test/unit/*.js", + "test:integration" : "rm -rf .openzeppelin && truffle test test/integration/*.js" + }, + "husky": { + "hooks": { + "pre-push": "yarn linter && yarn prettier" + } + } +} diff --git a/solidity/bmv/test/integration/BMV.integration.test.js b/solidity/bmv/test/integration/BMV.integration.test.js new file mode 100644 index 00000000..fdd6dbcd --- /dev/null +++ b/solidity/bmv/test/integration/BMV.integration.test.js @@ -0,0 +1,1871 @@ +const rlp = require('rlp'); +const { sha3_256 } = require('js-sha3') +const assert = require('chai').assert; +const urlSafeBase64 = require('urlsafe-base64'); +const _ = require('lodash'); +const truffleAssert = require('truffle-assertions'); + +const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +const BMV = artifacts.require('BMV'); +const DataValidator = artifacts.require('DataValidator'); + +const MockBMC = artifacts.require('MockBMC'); +const MockBMV = artifacts.require('MockBMV'); +const MockDataValidator = artifacts.require('MockDataValidator'); + +const BMVV2 = artifacts.require('BMVV2'); +const DataValidatorV2 = artifacts.require('DataValidatorV2'); + +let sha3FIPS256 = (input) => { + return '0x' + sha3_256.update(input).hex(); +} + +/* + Since ICON RLP library is not supported in Javascript, ETH RLP library is used instead. + ETH ELP encode of `null` is `80` but the result ICON RLP encode is `f800`. + So `[[]]` is encoded by ETH RLP encode, resulted into `c1c0` then replace it by `f800`. + It means ICON RLP encode (`None`) = ETH ELP encode (`[[]]`). + */ +let convertEthRlpToIconRlp = (buff, prefix=true) => { + return ((prefix) ? '0x': '') + buff.toString('hex').replace(/c1c0/g, 'f800'); +}; + +contract('BMV integration tests', async () => { + const iconNet = '0x3.icon'; + const praNet = '0x8.pra'; + const prevBtpAddr = 'btp://0x3.icon/cx7a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a' + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00'))); + + const lastBlockHash = '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'; + const btpMsgs = ['0xf8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4']; + const initOffset = 21171; + const initRootSize = 8; + const initCacheSize = 8; + + let bmv, dataValidator, bmc; + + beforeEach(async () => { + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + }); + + it('Get BMV info - Scenario 1: Get connected BMC address', async () => { + const addr = await bmv.getConnectedBMC(); + assert.equal(addr, bmc.address, 'incorrect bmc address'); + }); + + it('Get BMV info - Scenario 2: Get network address', async () => { + const btpAddr = await bmv.getNetAddress(); + assert.equal(btpAddr, iconNet, 'incorrect btp network address'); + }); + + it('Get BMV info - Scenario 3: Get list of validators and hash of RLP encode from given list', async () => { + const res = await bmv.getValidators(); + const hash = sha3FIPS256(rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00')))); + assert.equal(res[0], hash, 'incorrect validators\' hash'); + for (let i = 0; i < res[1].length; i++) { + assert.deepEqual(res[1][i], web3.utils.toChecksumAddress(validatorsList[i].replace('hx', '0x')), 'incorrect validator address'); + } + }); + + it('Get BMV info - Scenario 4: Get BMV status', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + const status = await bmv.getStatus(); + assert.isNotEmpty(status, 'invalid status'); + assert.equal(status[0], initOffset + 1, 'incorrect current MTA height'); + assert.equal(status[1], initOffset, 'incorrect offset'); + assert.equal(status[2], initOffset + 1, 'incorrect last block height'); + }); + + it('Handle relay message - Scenario 1: Revert if previous BMC is invalid', async () => { + const invalidBtpAddr = 'btp://0x4.icon/cx7a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a' + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, invalidBtpAddr, 0, '0x'), + 'BMVRevert: Invalid previous BMC' + ); + }); + + it('Handle relay message - Scenario 2: Revert if BMV receives Relay Message not from authorized BMC', async () => { + const bmcBtpAddr = await bmc.btpAddr(); + await truffleAssert.reverts( + bmv.handleRelayMessage.call(bmcBtpAddr, prevBtpAddr, 0, '0x'), + 'BMVRevert: Invalid BMC' + ); + }); + + it('Handle relay message - Scenario 3: Revert if BMC’s address, retrieved from provided BTP address, does not match an address of connected BMC', async () => { + const bmc2 = await MockBMC.new(praNet); + await truffleAssert.reverts( + bmc2.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, '0x'), + 'BMVRevert: Invalid BMC' + ); + }); + + it('Handle relay message - Scenario 4: Verify and decode relay message', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + const res = await bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg); + for (let i = 0; i < btpMsgs.length; i++) + assert.equal(btpMsgs[i], res[i], 'incorrect service messages'); + }); + + it('Handle relay message - Scenario 5: Revert if both block updates and block proofs are empty', async () => { + const blockUpdates = []; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode([]); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevert: Invalid relay message' + ); + }); + + it('Handle relay message - Scenario 6: Revert if prev hash in block update doesn\'t match last block hash in MTA', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0x0000000000000000000000000000000000000000000000000000000000000001', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Invalid block hash' + ); + }); + + it('Handle relay message - Scenario 7: Revert if height in block update is higher than MTA current height', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21175, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdateHigher' + ); + }); + + it('Handle relay message - Scenario 8: Revert if height in block update is lower than MTA current height', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21170, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdateLower' + ); + }); + + it('Handle relay message - Scenario 9: Verify and decode relay message with block proof', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // block hash #21171 + ] + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + const res = await bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg); + + for (let i = 0; i < btpMsgs.length; i++) + assert.equal(btpMsgs[i], res[i], 'incorrect service messages'); + }); + + it('Handle relay message - Scenario 10: Revert if block witness is empty', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ ] // empty block witness + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockWitness' + ); + }); + + it('Handle relay message - Scenario 11: Revert if block header height in block proof is higher than MTA height', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 30000, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb' ] + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockProofHigher' + ); + }); + + it('Handle relay message - Scenario 12: Revert if root hash in MPT receipt/event proofs is invalid', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf90146822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f81fa', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'MPTException: Mismatch hash' + ); + }); + + it('Handle relay message - Scenario 13: Revert if nibbles on extension/leaf in MPT receipt/event proofs are invalid', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x0234', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'MPTException: Mismatch nibbles on leaf' + ); + }); + + it('Handle relay message - Scenario 14: Revert if next validators does not exist', async () => { + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x01'))); + + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x0234', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Not exists next validators' + ); + }); + + it('Handle relay message - Scenario 15: Revert if next validator hash is invalid', async () => { + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x01'))); + + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x01b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x0234', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Invalid next validator hash' + ); + }); + + it('Handle relay message - Scenario 16: Revert if votes does not exist', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Not exists votes' + ); + }); + + it('Handle relay message - Scenario 17: Revert if votes contain invalid signatures', async () => { + const validatorsList = [ + 'hx5a05b58a25a1e5ea0f1d5715e1f655dffc1fb30a' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00'))); + + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidVotes: Invalid signature' + ); + }); + + it('Handle relay message - Scenario 18: Revert if votes are duplicate', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ], + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidVotes: Duplicated votes' + ); + }); + + it('Handle relay message - Scenario 19: Revert if number of votes is less than 2/3', async () => { + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd', + 'hx5a05b58a25a1e5ea0f1d5715e1f655dffc1fb30a' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00'))); + + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidVotes: Require votes > 2/3' + ); + }); + + it('Handle relay message - Scenario 20: Revert if sequence number of BTP message is higher or lower than expected', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = '0x' + convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 3, baseMsg), + 'BMVRevertInvalidSequence' + ); + }); +}); diff --git a/solidity/bmv/test/unit/BMV.unit.test.js b/solidity/bmv/test/unit/BMV.unit.test.js new file mode 100644 index 00000000..be140180 --- /dev/null +++ b/solidity/bmv/test/unit/BMV.unit.test.js @@ -0,0 +1,1323 @@ +const rlp = require('rlp'); +const { sha3_256 } = require('js-sha3') +const assert = require('chai').assert; +const urlSafeBase64 = require('urlsafe-base64'); +const _ = require('lodash'); +const truffleAssert = require('truffle-assertions'); + +const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +const BMV = artifacts.require('BMV'); +const DataValidator = artifacts.require('DataValidator'); + +const MockBMC = artifacts.require('MockBMC'); +const MockBMV = artifacts.require('MockBMV'); +const MockDataValidator = artifacts.require('MockDataValidator'); + +const BMVV2 = artifacts.require('BMVV2'); +const DataValidatorV2 = artifacts.require('DataValidatorV2'); + +let sha3FIPS256 = (input) => { + return '0x' + sha3_256.update(input).hex(); +} + +/* + Since ICON RLP library is not supported in Javascript, ETH RLP library is used instead. + ETH ELP encode of `null` is `80` but the result ICON RLP encode is `f800`. + So `[[]]` is encoded by ETH RLP encode, resulted into `c1c0` then replace it by `f800`. + It means ICON RLP encode (`None`) = ETH ELP encode (`[[]]`). + */ +let convertEthRlpToIconRlp = (buff, prefix=true) => { + return ((prefix) ? '0x': '') + buff.toString('hex').replace(/c1c0/g, 'f800'); +}; + +contract('BMV unit tests', async () => { + const iconNet = '0x3.icon'; + const praNet = '0x8.pra'; + const prevBtpAddr = 'btp://0x3.icon/cx7a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a' + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]; + const encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00'))); + + const lastBlockHash = '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'; + const btpMsgs = ['0xf8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4']; + const initOffset = 21171; + const initRootSize = 8; + const initCacheSize = 8; + + let bmv, dataValidator, bmc; + + beforeEach(async () => { + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + }); + + it('check contract code size', async () => { + console.log('- BMV1 : ', BMV.deployedBytecode.length / 2 - 1); + assert.isBelow(BMV.deployedBytecode.length / 2 - 1, 24576, 'contract size is restricted to 24KB'); + console.log('- BMV2 : ', DataValidator.deployedBytecode.length / 2 - 1); + assert.isBelow(DataValidator.deployedBytecode.length / 2 - 1, 24576, 'contract size is restricted to 24KB'); + }); + + it('should get connected BMC\'s address', async () => { + const addr = await bmv.getConnectedBMC(); + assert.equal(addr, bmc.address, 'incorrect bmc address'); + }); + + it('should get network address', async () => { + const btpAddr = await bmv.getNetAddress(); + assert.equal(btpAddr, iconNet, 'incorrect btp network address'); + }); + + it('should get list of validators and hash of RLP encode from given list', async () => { + const res = await bmv.getValidators(); + const hash = sha3FIPS256(rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x00')))); + assert.equal(res[0], hash, 'incorrect validators\' hash'); + for (let i = 0; i < res[1].length; i++) { + assert.deepEqual(res[1][i], web3.utils.toChecksumAddress(validatorsList[i].replace('hx', '0x')), 'incorrect validator address'); + } + }); + + it('should get BMV status', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + const status = await bmv.getStatus(); + assert.isNotEmpty(status, 'invalid status'); + assert.equal(status[0], initOffset + 1, 'incorrect current MTA height'); + assert.equal(status[1], initOffset, 'incorrect offset'); + assert.equal(status[2], initOffset + 1, 'incorrect last block height'); + }); + + it('should revert if previous BMC is invalid', async () => { + const invalidBtpAddr = 'btp://0x4.icon/cx7a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a' + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, invalidBtpAddr, 0, '0x'), + 'BMVRevert: Invalid previous BMC' + ); + }); + + it('should revert if BMV receives Relay Message not from authorized BMC', async () => { + const bmcBtpAddr = await bmc.btpAddr(); + await truffleAssert.reverts( + bmv.handleRelayMessage.call(bmcBtpAddr, prevBtpAddr, 0, '0x'), + 'BMVRevert: Invalid BMC' + ); + }); + + it('should revert if BMC’s address, retrieved from provided BTP address, does not match an address of connected BMC', async () => { + const bmc2 = await MockBMC.new(praNet); + await truffleAssert.reverts( + bmc2.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, '0x'), + 'BMVRevert: Invalid BMC' + ); + }); + + it('should verify relay message', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + const res = await bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg); + for (let i = 0; i < btpMsgs.length; i++) + assert.equal(btpMsgs[i], res[i], 'incorrect service messages'); + }); + + it('should revert if both block updates and block proofs are empty', async () => { + const blockUpdates = []; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode([]); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevert: Invalid relay message' + ); + }); + + + it('should revert if prev hash in block update does\'t match last block hash in MTA', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0x0000000000000000000000000000000000000000000000000000000000000001', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Invalid block hash' + ); + }); + + it('should revert if height in block update is higher than MTA\'s current height', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21175, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdateHigher' + ); + }); + + it('should revert if height in block update is lower than MTA\'s current height', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21170, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdateLower' + ); + }); + + it('should verify relay message with block proof', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // block hash #21171 + ] + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + const res = await bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg); + + for (let i = 0; i < btpMsgs.length; i++) + assert.equal(btpMsgs[i], res[i], 'incorrect service messages'); + }); + + it('should revert if block witness is empty', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ ] // empty block witness + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockWitness' + ); + }); + + it('should revert if block header height in block proof is higher than MTA\'s height', async () => { + // add block #21171 to MTA + await bmv.setMTAHeight(initOffset - 1); + await bmv.addRootToMTA('0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb'); + + let blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + [[]] + ]; + + let blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + let blockProof = [[]]; // block proof is empty + + let receiptProofs = []; // receipt proofs is empty + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + // add block #21172 to MTA + await bmc.testHandleRelayMessage(bmv.address, prevBtpAddr, 0, baseMsg); + + // verify missing event from the old block added to MTA + let oldBlockHeader = convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 30000, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ); + + blockUpdates = []; // block updates is empty + + blockProof = rlp.encode( + [ + oldBlockHeader, + [ + 21171, // witness height in mta + [ '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb' ] + ] + ] + ); + + receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + receiptProofs = [ + receiptProof + ]; + + baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockProofHigher' + ); + }); + + it('should upgrade BMV and DataValidator', async () => { + const upgradeDataValidator = await upgradeProxy(dataValidator.address, DataValidatorV2); + const upgradeBMV = await upgradeProxy(bmv.address, BMVV2); + + let msgs = await upgradeDataValidator.validateReceipt.call( + 'param1', + 'param2', + 100, + web3.utils.randomHex(20), + web3.utils.randomHex(32) + ); + + assert.equal(web3.utils.hexToAscii(msgs[0]), 'Succeed to upgrade Data Validator contract'); + + msgs = await upgradeBMV.handleRelayMessage.call( + 'param1', + 'param2', + 200, + '0x' + ); + + assert.equal(web3.utils.hexToAscii(msgs[0]), 'Succeed to upgrade Data Validator contract'); + assert.equal(web3.utils.hexToAscii(msgs[1]), 'Succeed to upgrade BMV contract'); + }); + + it('should revert if next validators are invalid', async () => { + const validatorsList = [ + 'hxb6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]; + let encodedValidators = rlp.encode(validatorsList.map(validator => validator.replace('hx', '0x01'))); + + bmc = await MockBMC.new(praNet); + dataValidator = await deployProxy(MockDataValidator); + bmv = await deployProxy( + MockBMV, + [ + bmc.address, + dataValidator.address, + iconNet, + encodedValidators, + initOffset, + initRootSize, + initCacheSize, + lastBlockHash + ] + ); + + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x0234', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidBlockUpdate: Not exists next validators' + ); + }); + + it('should revert if votes are invalid', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ], + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 0, baseMsg), + 'BMVRevertInvalidVotes: Duplicated votes' + ); + }); + + it('should revert if sequence number of BTP message is higher or lower than expected', async () => { + const blockUpdate = [ + convertEthRlpToIconRlp( + rlp.encode( + [ // Block header + 2, // version + 21172, // height + 1624383652806088, // timestamp + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd', // proposer + '0xe2ea2b413a46c6f35165faad7d570899d4deeb3658c19a04596d42d5473456eb', // previous hash + '0xb3531419f3bf6d9f624f8bad9541ffbeac2e0883484087801e08e7c855bbd339', // vote hash + '0xed9e644e59b2ff65446f5f3d7d77c27858facf8aeb3b969470d7499c79f9757c', // next validatos hash + [[]], // patch tx hash + [[]], // tx hash + '0x00802070482c1a0f0884c2a060285c0c21041000221032043801128bc0e331a854363b2090c88000691c0c112684c040', // logs bloom + rlp.encode( + [ // result + '0x5962b2791303f11a7e663bfba365162408ed03935e472f0ea3e668c268992a9b', // state hash + [[]], // patch receipt hash + '0x05649c24151f44c4a9dd10b652cd20ffbe2203b1ebba0d388342f999619d2af7', // receipt hash + '0xf867a04f820eefa94c3e731d177461f260b90c6f7c71f78170fad578c136a12033b423a0c37eaafab80062deb7eafa82ffc42719604138eef0234da69f789956f7949d1da0bb87db4b20e1d46a2d8f0e6aea6e32fb0451c474a905942086e8e33b3e2b1ab8f800f800' // extension data + ] + ) + ] + ) + ), + convertEthRlpToIconRlp( + rlp.encode( + [ // Votes + '0x00', // round + [ // block part set id + 1, + '0x323afffff77a4432b5e0fef58fa27ec793514ce6bae42020c369f28c12cfed10' + ], + [ // vote items + [ + 1624383654796604, // time stamp + '0x8ac623fb4054e748d5e212ec01c0c263256017b3635ddc412f033a54056cf8306d03173e616605c8717ea5e7a4429cc1a53a0ae1e85c1e70c754d9a02b2bd13800' // signature + ] + ] + ] + ) + ), + // Next validators + rlp.encode([ + '0x00b6b5791be0b5ef67063b3c10b840fb81514db2fd' + ]) + ]; + + const blockUpdates = [ + convertEthRlpToIconRlp(rlp.encode(blockUpdate)) + ]; + + const blockProof = [[]]; // block proofs is empty + + const receiptProof = rlp.encode( + [ + '0x00', // tx index + '0xf9014ab90147f90144822000b9013ef9013b0095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198a8303e5508303e5508502e90edd00b8ef0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000010000000200010000000400000000000000000000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000f800f800a0377898d2f5bbfe29c8601e5ee0a3a5d53568bfbe77e2b726c98cf1242bfcfe6f', + [ + [ + '0x00', // event index + '0xf9016fb9016cf90169822000b90163f9016095017a0c2dd9751e592ac4fbd6c70bd5ec574ebf198af852964d657373616765287374722c696e742c627974657329b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303401f8f4b8f2f8f0b8396274703a2f2f3078332e69636f6e2f637837613063326464393735316535393261633466626436633730626435656335373465626631393861b8386274703a2f2f3078382e7072612f30784630653436383437633862464431323243346235454545314434343934464637433546433531303490436f696e2f57726170706564436f696e01b867f86500b862f860aa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307836353144353230353463386163346231393139316437343237433236333337393735363744333963c9c88449434f4e8200d4' + ] + ] + ] + ); + + const receiptProofs = [ + receiptProof + ]; + + let baseMsg = convertEthRlpToIconRlp( + rlp.encode( + [ + blockUpdates, + blockProof, + receiptProofs + ] + ), + false + ); + await truffleAssert.reverts( + bmc.testHandleRelayMessage.call(bmv.address, prevBtpAddr, 3, baseMsg), + 'BMVRevertInvalidSequence' + ); + }); +}); diff --git a/solidity/bmv/test/unit/LibMPT.unit.test.js b/solidity/bmv/test/unit/LibMPT.unit.test.js new file mode 100644 index 00000000..6be2df2a --- /dev/null +++ b/solidity/bmv/test/unit/LibMPT.unit.test.js @@ -0,0 +1,127 @@ +const assert = require('chai').assert; +const truffleAssert = require('truffle-assertions'); + +let testMpt = artifacts.require('TestLibMPT'); + +contract('MPT library unit tests', async () => { + let testLibMpt; + + beforeEach(async () => { + testLibMpt = await testMpt.new(); + }); + + it('should convert bytes to nibbles', async () => { + let nibbles = await testLibMpt.bytesToNibbles('0x00', '0x'); + assert.equal(nibbles, '0x0000'); + + nibbles = await testLibMpt.bytesToNibbles('0x10', '0x00'); + assert.equal(nibbles, '0x000100'); + + nibbles = await testLibMpt.bytesToNibbles('0x112345', '0x00'); + assert.equal(nibbles, '0x00010102030405'); + + nibbles = await testLibMpt.bytesToNibbles('0x00012345', '0x'); + assert.equal(nibbles, '0x0000000102030405'); + + nibbles = await testLibMpt.bytesToNibbles('0x200f1cb8', '0x01'); + assert.equal(nibbles, '0x010200000f010c0b08'); + + nibbles = await testLibMpt.bytesToNibbles('0x3f1cb8', '0x0102'); + assert.equal(nibbles, '0x0102030f010c0b08'); + }); + + it('should get shared nibbles length from 2 bytes', async () => { + let sharedLength = await testLibMpt.matchNibbles(Buffer.from(''), Buffer.from('a')); + assert.equal(sharedLength, 0); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('a'), Buffer.from('')); + assert.equal(sharedLength, 0); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('a'), Buffer.from('a')); + assert.equal(sharedLength, 1); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('aaac'), Buffer.from('aaab')); + assert.equal(sharedLength, 3); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('abcd'), Buffer.from('ab')); + assert.equal(sharedLength, 2); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('abcd'), Buffer.from('abcdef')); + assert.equal(sharedLength, 4); + + sharedLength = await testLibMpt.matchNibbles(Buffer.from('fedcba'), Buffer.from('abcdef')); + assert.equal(sharedLength, 0); + }); + + it('should validate MPT proof and return the value of leaf node', async () => { + let rootHash = '0x2d98966d69f4ada0703e8c02a1dbe5386762cc3b90fb9e94d7fe4cf81da744ea'; + let key = '0x00'; + let proofs = [ + '0xf90195822000b9018ff9018c9501f41a446a295d02e1a9d6ea341de9efb4e39910cdf858964d657373616765287374722c696e742c627974657329b83e6274703a2f2f30783563643235662e69636f6e2f63786331663639336331333639666161616335666532663465303435303234633066303330333564366601f90119b90116f90113b83e6274703a2f2f30783465353931382e69636f6e2f637866343161343436613239356430326531613964366561333431646539656662346533393931306364b83e6274703a2f2f30783563643235662e69636f6e2f637863316636393363313336396661616163356665326634653034353032346330663033303335643666865f6576656e7400b889f887844c696e6bf880b83e6274703a2f2f30783465353931382e69636f6e2f637866343161343436613239356430326531613964366561333431646539656662346533393931306364b83e6274703a2f2f30783563643235662e69636f6e2f637863316636393363313336396661616163356665326634653034353032346330663033303335643666' + ]; + let res = await testLibMpt.prove.call(rootHash, key, proofs); + assert.equal(res, '0xf9018c9501f41a446a295d02e1a9d6ea341de9efb4e39910cdf858964d657373616765287374722c696e742c627974657329b83e6274703a2f2f30783563643235662e69636f6e2f63786331663639336331333639666161616335666532663465303435303234633066303330333564366601f90119b90116f90113b83e6274703a2f2f30783465353931382e69636f6e2f637866343161343436613239356430326531613964366561333431646539656662346533393931306364b83e6274703a2f2f30783563643235662e69636f6e2f637863316636393363313336396661616163356665326634653034353032346330663033303335643666865f6576656e7400b889f887844c696e6bf880b83e6274703a2f2f30783465353931382e69636f6e2f637866343161343436613239356430326531613964366561333431646539656662346533393931306364b83e6274703a2f2f30783563643235662e69636f6e2f637863316636393363313336396661616163356665326634653034353032346330663033303335643666'); + + rootHash = '0x1d854f17d88a1be09b146325fe67fb6baaf5ab192f40fe07ecfa0f84d73179f7'; + key = '0x02'; + proofs = [ + '0xe210a0649ceaf94e739f89bbbd83ee6fdae6b979c401a6a6f13c3c598f72f6cdae799a', + '0xf871a03820879dad43db74eb87a07da4ab0a5141c1379f79d98b29cb80f5a297ba60bfa01d13281e65471f09e46ac284af3e5f67715db5c73341c3391eb3a4bb6cc888ffa0baff8756de1ba52f32ca341fe264b3081a148b2629436252e713139c36568d928080808080808080808080808080', + '0xef20adec0095002f071e05f858af2332c7c4ac7244319d14c233b2830493e0830186a08502e90edd0080f800f800f800' + ] + res = await testLibMpt.prove.call(rootHash, key, proofs); + assert.equal(res, '0xec0095002f071e05f858af2332c7c4ac7244319d14c233b2830493e0830186a08502e90edd0080f800f800f800') + + rootHash = '0xd6c78aa41d9ba127796aa9bab44e1b9ea9118e8f102b5b1d19ff9c3524fc8698'; + key = '0x00'; + proofs = [ + '0xe210a02e86a8f91ce74713198526c327a863b83d30672345e750ed6b65c2af393b92e7', + '0xf851a0c95a885ab20c18b3af8bd81a25804369c5ebdd5082a47981933949c4c9f99849a010b969d5c75d29cc1710e8aa62d01d5a27a93925289616a58f16568b83fabf3a808080808080808080808080808080', + '0xf9016420b90160f9015d950113f39ffc77aff9907521862b9e12276f8b6464b1f854964d657373616765287374722c696e742c627974657329b83a6274703a2f2f30783530312e7072612f30783735424164373363324436393641336636336631653844366364643364663037393764363737383002f8efb8edf8ebb8396274703a2f2f3078332e69636f6e2f637831336633396666633737616666393930373532313836326239653132323736663862363436346231b83a6274703a2f2f30783530312e7072612f3078373542416437336332443639364133663633663165384436636464336466303739376436373738308a6e6174697665636f696e01b866f86400b861f85faa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307846663634643346366566453233313745453238303764323233613042646334633063343964664442c8c7834943588203e8' + ] + res = await testLibMpt.prove.call(rootHash, key, proofs); + assert.equal(res, '0xf9015d950113f39ffc77aff9907521862b9e12276f8b6464b1f854964d657373616765287374722c696e742c627974657329b83a6274703a2f2f30783530312e7072612f30783735424164373363324436393641336636336631653844366364643364663037393764363737383002f8efb8edf8ebb8396274703a2f2f3078332e69636f6e2f637831336633396666633737616666393930373532313836326239653132323736663862363436346231b83a6274703a2f2f30783530312e7072612f3078373542416437336332443639364133663633663165384436636464336466303739376436373738308a6e6174697665636f696e01b866f86400b861f85faa687862366235373931626530623565663637303633623363313062383430666238313531346462326664aa307846663634643346366566453233313745453238303764323233613042646334633063343964664442c8c7834943588203e8') + }); + + it('should revert if given serialized proofs length is invalid', async () => { + let rootHash = '0xcaa6f66e5523800ef5b38afed1f69cae3d8dd634b8ad2a2c6719905bfdefd86d'; + let key = '0x01'; + let proofs = [ + '0xf884a1a03820879dad43db74eb87a07da4ab0a5141c1379f79d98b29cb80f5a297ba60bfa1a01d13281e65471f09e46ac284af3e5f67715db5c73341c3391eb3a4bb6cc888ffa1a0baff8756de1ba52f32ca341fe264b3081a148b2629436252e713139c36568d92818081808180818081808180818081808180818081808180818081808180', + ] + await truffleAssert.reverts(testLibMpt.prove.call(rootHash, key, proofs), 'MPTException: Invalid list length'); + }); + + it('should revert if root hash in proof is invalid', async () => { + let rootHash = '0x2d854f17d88a1be09b146325fe67fb6baaf5ab192f40fe07ecfa0f84d73179f7'; + let key = '0x02'; + let proofs = [ + '0xe210a0649ceaf94e739f89bbbd83ee6fdae6b979c401a6a6f13c3c598f72f6cdae799a11', + '0xf871a03820879dad43db74eb87a07da4ab0a5141c1379f79d98b29cb80f5a297ba60bfa01d13281e65471f09e46ac284af3e5f67715db5c73341c3391eb3a4bb6cc888ffa0baff8756de1ba52f32ca341fe264b3081a148b2629436252e713139c36568d928080808080808080808080808080', + '0xef20adec0095002f071e05f858af2332c7c4ac7244319d14c233b2830493e0830186a08502e90edd0080f800f800f800' + ] + await truffleAssert.reverts(testLibMpt.prove.call(rootHash, key, proofs), 'MPTException: Mismatch hash'); + }); + + it('should revert if nibbles on extension are invalid', async () => { + let rootHash = '0x1d854f17d88a1be09b146325fe67fb6baaf5ab192f40fe07ecfa0f84d73179f7'; + let key = '0x14'; + let proofs = [ + '0xe210a0649ceaf94e739f89bbbd83ee6fdae6b979c401a6a6f13c3c598f72f6cdae799a', + '0xf871a03820879dad43db74eb87a07da4ab0a5141c1379f79d98b29cb80f5a297ba60bfa01d13281e65471f09e46ac284af3e5f67715db5c73341c3391eb3a4bb6cc888ffa0baff8756de1ba52f32ca341fe264b3081a148b2629436252e713139c36568d928080808080808080808080808080', + '0xef20adec0095002f071e05f858af2332c7c4ac7244319d14c233b2830493e0830186a08502e90edd0080f800f800f800' + ] + await truffleAssert.reverts(testLibMpt.prove.call(rootHash, key, proofs), 'MPTException: Mismatch nibbles on extension'); + }); + + it('should revert if nibbles on leaf are invalid', async () => { + let rootHash = '0x1d854f17d88a1be09b146325fe67fb6baaf5ab192f40fe07ecfa0f84d73179f7'; + let key = '0x0234'; + let proofs = [ + '0xe210a0649ceaf94e739f89bbbd83ee6fdae6b979c401a6a6f13c3c598f72f6cdae799a', + '0xf871a03820879dad43db74eb87a07da4ab0a5141c1379f79d98b29cb80f5a297ba60bfa01d13281e65471f09e46ac284af3e5f67715db5c73341c3391eb3a4bb6cc888ffa0baff8756de1ba52f32ca341fe264b3081a148b2629436252e713139c36568d928080808080808080808080808080', + '0xef20adec0095002f071e05f858af2332c7c4ac7244319d14c233b2830493e0830186a08502e90edd0080f800f800f800' + ] + await truffleAssert.reverts(testLibMpt.prove.call(rootHash, key, proofs), 'MPTException: Mismatch nibbles on leaf'); + }); +}); \ No newline at end of file diff --git a/solidity/bmv/test/unit/LibMTA.unit.test.js b/solidity/bmv/test/unit/LibMTA.unit.test.js new file mode 100644 index 00000000..f28fee1d --- /dev/null +++ b/solidity/bmv/test/unit/LibMTA.unit.test.js @@ -0,0 +1,725 @@ +const rlp = require('rlp'); +const _ = require('lodash'); +const assert = require('chai').assert; +const truffleAssert = require('truffle-assertions'); +const { sha3_256 } = require('js-sha3') + +let testMta = artifacts.require('TestLibMTA'); + +const nullHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; + +let sha3FIPS256 = (input) => { + return '0x' + sha3_256.update(input).hex(); +} + +let sha3FIPS256Packed = (inputA, inputB) => { + return '0x' + sha3_256.update(Buffer.from(inputA.slice(2), 'hex')).update(Buffer.from(inputB.slice(2), 'hex')).toString(); +} + +let toHex = (buf) => { + buf = buf.toString('hex'); + if (buf.substring(0, 2) == '0x') + return buf; + return '0x' + buf.toString('hex'); +}; + +let toRLPBytes = (data) => { + return rlp.encode([ + data.height, + data.roots, + data.offset, + data.rootsSize, + data.cacheSize, + data.cache, + data.isAllowNewerWitness + ]); +}; + +let rootHashes = [ + sha3FIPS256('root 1'), + sha3FIPS256('root 2'), + sha3FIPS256('root 3'), + sha3FIPS256('root 4') +]; + +let rootCaches = [ + sha3FIPS256('cache 1'), + sha3FIPS256('cache 2'), + sha3FIPS256('cache 3'), + sha3FIPS256('cache 4'), + sha3FIPS256('cache 5') +]; + +let data = { + height: 10, + roots: rootHashes, + offset: 10, + rootsSize: 4, + cacheSize: 5, + cache: rootCaches, + isAllowNewerWitness: 0 +}; + +let initData = { + height: 2, + roots: [], + offset: 2, + rootsSize: 4, + cacheSize: 5, + cache: [], + isAllowNewerWitness: 1 +}; + +let expected = { + height: data.height.toString(), + roots: data.roots, + offset: data.offset.toString(), + rootsSize: data.rootsSize.toString(), + cacheSize: data.cacheSize.toString(), + cache: data.cache, + isAllowNewerWitness: (data.isAllowNewerWitness === 1) ? true : false +} + +contract('MTA library unit tests', async () => { + let testLibMta; + + beforeEach(async () => { + testLibMta = await testMta.new(); + }); + + it('should initialize Merkel Tree Accumulator with given RLP bytes', async () => { + await testLibMta.initFromSerialized(toRLPBytes(data)); + + const mta = await testLibMta.getMTA(); + assert.deepEqual(mta, Object.values(expected), 'mta data should match initial data'); + }); + + it('should update offset of MTA', async () => { + await testLibMta.setOffset(4); + + const mta = await testLibMta.getMTA(); + assert.equal(mta.offset, 4, 'failed to update offset in MTA'); + }); + + it('should get root hash by index', async () => { + await testLibMta.initFromSerialized(toRLPBytes(data)); + + const rootHash = await testLibMta.getRoot(1); + assert.equal(rootHash, rootHashes[1], 'return incorrect root hash'); + + await truffleAssert.reverts(testLibMta.getRoot(4), 'root index is out of range'); + }); + + it('should check whether a root is in cache', async () => { + await testLibMta.initFromSerialized(toRLPBytes(data)); + + let checkCache = await testLibMta.doesIncludeCache('0x'); + assert.equal(checkCache, false, 'invalid empty hash param'); + + checkCache = await testLibMta.doesIncludeCache(rootCaches[4]); + assert.equal(checkCache, true, 'root is not in cache'); + + checkCache = await testLibMta.doesIncludeCache('0x12345'); + assert.equal(checkCache, false, 'root is in cache'); + }); + + it('should push a cache into caches list in MTA', async () => { + await testLibMta.initFromSerialized(toRLPBytes(data)); + + const newCacheRoot = sha3FIPS256('new cache'); + await testLibMta.putCache(newCacheRoot); + + const mta = await testLibMta.getMTA(); + + let expected = _.drop([...rootCaches, newCacheRoot], 1); + + assert.include(mta.cache, newCacheRoot, 'failed to add cache'); + assert.deepEqual(mta.cache, expected, 'failed to update cache list in MTA'); + }); + + it('should add hash roots to MTA', async () => { + await testLibMta.initFromSerialized(toRLPBytes(initData)); // rootsSize = 4, cacheSize = 5 + + /* add first item 'dog' + * root [ hash(dog) ] + * data [ dog ] + * cache [ dog ] + */ + const root1 = sha3FIPS256('dog'); + await testLibMta.add(root1); + + let mta = await testLibMta.getMTA(); + let h = mta[0]; // height + let r = mta[1]; // roots + let c = mta[5]; // cache + + assert.equal(h, initData.height + 1); + assert.equal(r[0], root1); + assert.deepEqual(c, [root1]); + + /* add second item 'dog' + * root (higher item first) [ 0x0 hash(dog, cat) ] + * data [ dog cat ] + * cache [ dog cat ] + */ + const root2 = sha3FIPS256('cat'); + const root12 = sha3FIPS256Packed(root1, root2); + await testLibMta.add(root2); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 2); + assert.equal(r[0], nullHash); + assert.equal(r[1], root12); + assert.deepEqual(c, [root1, root2]); + + /* add third item 'snake' + * root [ hash(snake) hash(dog, cat) ] + * data [ dog cat snake ] + * cache [ dog cat snake ] + */ + const root3 = sha3FIPS256('snake'); + await testLibMta.add(root3); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 3); + assert.equal(r[0], root3); + assert.equal(r[1], root12); + assert.deepEqual(c, [root1, root2, root3]); + + /* add 4th item 'pig' + * root [ 0x0 0x0 hash(hash(dog, cat), hash(snake, pig))] + * data [ dog cat snake pig ] + * cache [ dog cat snake pig ] + */ + const root4 = sha3FIPS256('pig'); + const root34 = sha3FIPS256Packed(root3, root4); + const root1234 = sha3FIPS256Packed(root12, root34); + await testLibMta.add(root4); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 4); + assert.equal(r[0], nullHash); + assert.equal(r[1], nullHash); + assert.equal(r[2], root1234); + assert.deepEqual(c, [root1, root2, root3, root4]); + + /* add 5th item 'chicken' + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * cache [ dog cat snake pig chicken ] + */ + const root5 = sha3FIPS256('chicken'); + await testLibMta.add(root5); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 5); + assert.equal(r[0], root5); + assert.equal(r[1], nullHash); + assert.equal(r[2], root1234); + assert.deepEqual(c, [root1, root2, root3, root4, root5]); + + /* add 6th item 'cow' + * root [ 0x0 hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow ] + * cache [ cat snake pig chicken cow ] + */ + const root6 = sha3FIPS256('chicken'); + const root56 = sha3FIPS256Packed(root5, root6); + await testLibMta.add(root6); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 6); + assert.equal(r[0], nullHash); + assert.equal(r[1], root56); + assert.equal(r[2], root1234); + assert.deepEqual(c, [root2, root3, root4, root5, root6]); + + /* add 7th item 'fish' + * root [ hash(fish) hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * cache [ snake pig chicken cow fish ] + */ + const root7 = sha3FIPS256('fish'); + await testLibMta.add(root7); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 7); + assert.equal(r[0], root7); + assert.equal(r[1], root56); + assert.equal(r[2], root1234); + assert.deepEqual(c, [root3, root4, root5, root6, root7]); + + /* add 8th item 'wolf' + * root [ 0x0 0x0 0x0 hash(hash(hash(dog, cat), hash(snake, pig)),hash(hash(chicken, cow), hash(fish, wolf)) ] + * data [ dog cat snake pig chicken cow fish wolf ] + * cache [ pig chicken cow fish wolf ] + */ + const root8 = sha3FIPS256('wolf'); + const root78 = sha3FIPS256Packed(root7, root8); + const root5678 = sha3FIPS256Packed(root56, root78); + const root12345678 = sha3FIPS256Packed(root1234, root5678); + await testLibMta.add(root8); + + mta = await testLibMta.getMTA(); + h = mta[0]; // height + r = mta[1]; // roots + c = mta[5]; // cache + + assert.equal(h, initData.height + 8); + assert.equal(r[0], nullHash); + assert.equal(r[1], nullHash); + assert.equal(r[2], nullHash); + assert.equal(r[3], root12345678); + assert.deepEqual(c, [root4, root5, root6, root7, root8]); + }); + + it('should get root index by block height', async () => { + /* + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + + // find item with height = 3 (snake) + let idx = await testLibMta.getRootIndexByHeight(5); + assert.equal(idx, 2); + + // find item with height = 5 (chicken) + idx = await testLibMta.getRootIndexByHeight(7); + assert.equal(idx, 0); + }); + + it('should verify leaf if height of BMV\'s MTA is equal to relay ones', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + const root34 = sha3FIPS256Packed(root3, root4); + const witness = [root6, root34]; + + // prove item 5 (snake) + await testLibMta.verify(witness, root5, 5, 7); + }); + + it('should verify leaf if height of BMV\'s MTA is less than relay ones', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 3 4 5 6 7 8 9 ] + * offset = 2 + */ + + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + const root34 = sha3FIPS256Packed(root3, root4); + const witness = [root6, root34]; + + // prove item 5 (snake) + await testLibMta.verify(witness, root5, 5, 9); + }); + + it('should verify leaf if height of BMV\'s MTA is less than relay ones with different offset', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(fish) hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 3 4 5 6 7 8 9 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(lion) 0x0 hash(hash(chicken, cow), hash(fish, wolf)) ] + * data [ chicken cow fish wolf lion ] + * height [ 7 8 9 10 11 ] + * offset = 6 + */ + + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + const root8 = sha3FIPS256('cow'); + const root9 = sha3FIPS256('fish'); + const root10 = sha3FIPS256('wolf'); + + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + await testLibMta.add(root8); + await testLibMta.add(root9); + + const root78 = sha3FIPS256Packed(root7, root8); + const witness = [root10, root78]; + + // prove item 9 (fish) + await testLibMta.verify(witness, root9, 9, 11); + }); + + it('should fail to verify leaf if proofs (witness) are modified and revert', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + const root34 = sha3FIPS256Packed(root3, root4); + const witness = [root6, root34]; + + // modify witness + witness[1] = sha3FIPS256('fake pig'); + + await truffleAssert.reverts(testLibMta.verify.call(witness, root5, 5, 7), 'BMVRevertInvalidBlockWitness: invalid witness'); + }); + + it('should fail to verify leaf if newer witnesses are not allowed ', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 3 4 5 6 7 8 9 ] + * offset = 2 + */ + + await testLibMta.initFromSerialized(toRLPBytes({ ...initData, isAllowNewerWitness: 0 })); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + const root34 = sha3FIPS256Packed(root3, root4); + const witness = [root6, root34]; + + await truffleAssert.reverts(testLibMta.verify.call(witness, root5, 5, 9), 'BMVRevertInvalidBlockWitness: not allowed newer witness'); + }); + + it('should fail to verify leaf that haven\'t synced yet in BMV MTA', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + */ + + /* Relay's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 3 4 5 6 7 8 9 ] + * offset = 2 + */ + + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + const root8 = sha3FIPS256('cow'); + const root9 = sha3FIPS256('fish'); + const root10 = sha3FIPS256('wolf'); + + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + const root78 = sha3FIPS256Packed(root7, root8); + const witness = [root10, root78]; + + await truffleAssert.reverts(testLibMta.verify.call(witness, root9, 9, 9), 'BMVRevertInvalidBlockWitness: given witness for newer node'); + }); + + it('should verify by cache', async () => { + /* BMV's MTA + * root idx [ 0 1 2 ] + * root [ hash(bird) hash(tiger, lion) hash(hash(chicken, cow), hash(fish, wolf)) ] + * data [ chicken cow fish wolf tiger lion bird ] + * cache [ cow fish wolf tiger lion bird ] + * height [ 10 11 12 13 14 15 16 ] + * offset = 9 + */ + + /* Relay's MTA + * root idx [ 0 1 2 ] + * root [ hash(fish) hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 6 7 8 9 10 11 12 ] + * offset = 5 + */ + await testLibMta.initFromSerialized(toRLPBytes({ ...initData, height: 9, offset: 9, cacheSize: 6 })); + + const root10 = sha3FIPS256('chicken'); + const root11 = sha3FIPS256('cow'); + const root12 = sha3FIPS256('fish'); + const root13 = sha3FIPS256('wolf'); + const root14 = sha3FIPS256('tiger'); + const root15 = sha3FIPS256('lion'); + const root16 = sha3FIPS256('bird'); + + await testLibMta.add(root10); + await testLibMta.add(root11); + await testLibMta.add(root12); + await testLibMta.add(root13); + await testLibMta.add(root14); + await testLibMta.add(root15); + await testLibMta.add(root16); + + const witness = [root10]; + + // prove item 11 (cow) + await testLibMta.verify(witness, root11, 11, 12); + }); + + it('should fail to verify by cache and revert', async () => { + /* BMV's MTA + * root idx [ 0 1 2 ] + * root [ hash(bird) hash(tiger, lion) hash(hash(chicken, cow), hash(fish, wolf)) ] + * data [ chicken cow fish wolf tiger lion bird ] + * cache [ fish wolf tiger lion bird ] + * height [ 10 11 12 13 14 15 16 ] + * offset = 9 + */ + + /* Relay's MTA + * root idx [ 0 1 2 ] + * root [ hash(fish) hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 6 7 8 9 10 11 12 ] + * offset = 5 + */ + await testLibMta.initFromSerialized(toRLPBytes({ ...initData, height: 9, offset: 9 })); + + const root10 = sha3FIPS256('chicken'); + const root11 = sha3FIPS256('cow'); + const root12 = sha3FIPS256('fish'); + const root13 = sha3FIPS256('wolf'); + const root14 = sha3FIPS256('tiger'); + const root15 = sha3FIPS256('lion'); + const root16 = sha3FIPS256('bird'); + + await testLibMta.add(root10); + await testLibMta.add(root11); + await testLibMta.add(root12); + await testLibMta.add(root13); + await testLibMta.add(root14); + await testLibMta.add(root15); + await testLibMta.add(root16); + + const witness = [root10]; + + // prove item 11 (cow) + await truffleAssert.reverts(testLibMta.verify.call(witness, root11, 11, 12), 'BMVRevertInvalidBlockWitness: invalid old witness'); + }); + + it('should fail to verify by old witness cache', async () => { + /* BMV's MTA + * root idx [ 0 1 2 ] + * root [ hash(bird) hash(tiger, lion) hash(hash(chicken, cow), hash(fish, wolf)) ] + * data [ chicken cow fish wolf tiger lion bird ] + * cache [ fish wolf tiger lion bird ] + * height [ 10 11 12 13 14 15 16 ] + * offset = 9 + */ + + /* Relay's MTA + * root idx [ 0 1 2 ] + * root [ hash(fish) hash(chicken, cow) hash(hash(dog, cat), hash(snake, pig)) ] + * data [ dog cat snake pig chicken cow fish ] + * height [ 6 7 8 9 10 11 12 ] + * offset = 5 + */ + await testLibMta.initFromSerialized(toRLPBytes({ ...initData, height: 9, offset: 9 })); + + const root10 = sha3FIPS256('chicken'); + const root11 = sha3FIPS256('cow'); + const root12 = sha3FIPS256('fish'); + const root13 = sha3FIPS256('wolf'); + const root14 = sha3FIPS256('tiger'); + const root15 = sha3FIPS256('lion'); + const root16 = sha3FIPS256('bird'); + + await testLibMta.add(root10); + await testLibMta.add(root11); + await testLibMta.add(root12); + await testLibMta.add(root13); + await testLibMta.add(root14); + await testLibMta.add(root15); + await testLibMta.add(root16); + + const witness = [root11]; + + // prove item 10 (cow) + await truffleAssert.reverts(testLibMta.verify.call(witness, root11, 10, 12), 'BMVRevertInvalidBlockWitnessOld'); + }); + + it('should get bytes encoding of MTA', async () => { + /* BMV's MTA + * rootIdx [ 0 1 2 ] + * root [ hash(chicken) 0x0 hash(hash(dog, cat), hash(snake, pig)) ] + * cache [ dog cat snake pig chicken ] + * height [ 3 4 5 6 7 ] + * offset = 2 + * isAllowNewerWitness = true + */ + await testLibMta.initFromSerialized(toRLPBytes(initData)); + const root3 = sha3FIPS256('dog'); + const root4 = sha3FIPS256('cat'); + const root5 = sha3FIPS256('snake'); + const root6 = sha3FIPS256('pig'); + const root7 = sha3FIPS256('chicken'); + const root3456 = sha3FIPS256Packed( + sha3FIPS256Packed(root3, root4), + sha3FIPS256Packed(root5, root6) + ); + + await testLibMta.add(root3); + await testLibMta.add(root4); + await testLibMta.add(root5); + await testLibMta.add(root6); + await testLibMta.add(root7); + + let expected = rlp.encode([ + initData.height + 5, + [root7, nullHash, root3456], + initData.offset, + initData.rootsSize, + initData.cacheSize, + [root3, root4, root5, root6, root7], + initData.isAllowNewerWitness + ]); + + const rlpBytes = await testLibMta.toRlpBytes(); + assert.equal(rlpBytes, toHex(expected)); + }); +}); \ No newline at end of file diff --git a/solidity/bmv/truffle-config.js b/solidity/bmv/truffle-config.js new file mode 100644 index 00000000..ecfc3c39 --- /dev/null +++ b/solidity/bmv/truffle-config.js @@ -0,0 +1,77 @@ +const HDWalletProvider = require('@truffle/hdwallet-provider'); +const web3 = require("web3") + +const privKeys = (process.env.PRIVATE_KEYS) ? process.env.PRIVATE_KEYS.split(',') : + [ + '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // Alith + '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // Baltathar + '0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b', // Charleth + '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68', // Dorothy + '0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4', // Ethan + '0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df', // Faith + '0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18', // Goliath + '0x0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab', // Heath + '0x4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8', // Ida + '0x94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b' // Judith + ]; + +module.exports = { + networks: { + development: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbeamlocal: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbase: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.testnet.moonbeam.network", + }), + network_id: 1287, + networkCheckTimeout: 100000, + // Make deploy faster for deployment + gasPrice: web3.utils.toWei("2", "Gwei"), + }, + moonriver: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.moonriver.moonbeam.network", + }), + network_id: 1285, + networkCheckTimeout: 100000, + }, + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.7.6", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 10 + }, + evmVersion: "petersburg" + } + } + }, + + db: { + enabled: false + } +}; diff --git a/solidity/bmv/yarn.lock b/solidity/bmv/yarn.lock new file mode 100644 index 00000000..f38aefae --- /dev/null +++ b/solidity/bmv/yarn.lock @@ -0,0 +1,5120 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.4.tgz#45720fe0cecf3fd42019e1d12cc3d27fadc98d58" + integrity sha512-i2wXrWQNkH6JplJQGn3Rd2I4Pij8GdHkXwHMxm+zV5YG/Jci+bCNrWZEWC4o+umiDkRrRs4dVzH3X4GP7vyjQQ== + +"@babel/generator@^7.14.2": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91" + integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA== + dependencies: + "@babel/types" "^7.14.2" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.13.0": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.4.tgz#33ebd0ffc34248051ee2089350a929ab02f2a516" + integrity sha512-JgdzOYZ/qGaKTVkn5qEDV/SXAh8KcyUVkCoSWGN8T3bwrgd6m+/dJa2kVGi6RJYJgEYPBdZ84BZp9dUjNWkBaA== + dependencies: + "@babel/compat-data" "^7.14.4" + "@babel/helper-validator-option" "^7.12.17" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-define-polyfill-provider@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" + integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-function-name@^7.14.2": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2" + integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ== + dependencies: + "@babel/helper-get-function-arity" "^7.12.13" + "@babel/template" "^7.12.13" + "@babel/types" "^7.14.2" + +"@babel/helper-get-function-arity@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" + integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12": + version "7.13.12" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977" + integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA== + dependencies: + "@babel/types" "^7.13.12" + +"@babel/helper-plugin-utils@^7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" + integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== + +"@babel/helper-split-export-declaration@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" + integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== + dependencies: + "@babel/types" "^7.12.13" + +"@babel/helper-validator-identifier@^7.14.0": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" + integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== + +"@babel/helper-validator-option@^7.12.17": + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" + integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== + +"@babel/highlight@^7.12.13": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" + integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.12.13", "@babel/parser@^7.14.2": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18" + integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA== + +"@babel/plugin-transform-runtime@^7.5.5": + version "7.14.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.14.3.tgz#1fd885a2d0de1d3c223795a4e9be72c2db4515cf" + integrity sha512-t960xbi8wpTFE623ef7sd+UpEC5T6EEguQlTBJDEO05+XwnIWVfuqLw/vdLWY6IdFmtZE+65CZAfByT39zRpkg== + dependencies: + "@babel/helper-module-imports" "^7.13.12" + "@babel/helper-plugin-utils" "^7.13.0" + babel-plugin-polyfill-corejs2 "^0.2.0" + babel-plugin-polyfill-corejs3 "^0.2.0" + babel-plugin-polyfill-regenerator "^0.2.0" + semver "^6.3.0" + +"@babel/runtime@^7.5.5": + version "7.14.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" + integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" + integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/parser" "^7.12.13" + "@babel/types" "^7.12.13" + +"@babel/traverse@^7.13.0": + version "7.14.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b" + integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.14.2" + "@babel/helper-function-name" "^7.14.2" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.14.2" + "@babel/types" "^7.14.2" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.12.13", "@babel/types@^7.13.12", "@babel/types@^7.14.2": + version "7.14.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0" + integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw== + dependencies: + "@babel/helper-validator-identifier" "^7.14.0" + to-fast-properties "^2.0.0" + +"@cto.af/textdecoder@^0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@cto.af/textdecoder/-/textdecoder-0.0.0.tgz#e1e8d84c936c30a0f4619971f19ca41941af9fdc" + integrity sha512-sJpx3F5xcVV/9jNYJQtvimo4Vfld/nD3ph+ZWtQzZ03Zo8rJC7QKQTRcIGS13Rcz80DwFNthCWMrd58vpY4ZAQ== + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.2.0.tgz#b5c24b162f119b5d241738ded9555186013aa77d" + integrity sha512-Xi7Pt+CulRijc/vskBGIaYMEhafKjoNx8y4RNj/dnSpXHXScOJUSTtypqGBUngZddRbcwZGbHwEr6DZoKZwIZA== + dependencies: + "@ethersproject/bignumber" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/networks" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + "@ethersproject/transactions" "^5.2.0" + "@ethersproject/web" "^5.2.0" + +"@ethersproject/abstract-signer@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.2.0.tgz#8e291fb6558b4190fb3e2fe440a9ffd092a2f459" + integrity sha512-JTXzLUrtoxpOEq1ecH86U7tstkEa9POKAGbGBb+gicbjGgzYYkLR4/LD83SX2/JNWvtYyY8t5errt5ehiy1gxQ== + dependencies: + "@ethersproject/abstract-provider" "^5.2.0" + "@ethersproject/bignumber" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + +"@ethersproject/address@^5.0.4", "@ethersproject/address@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.2.0.tgz#afcfa92db84582f54a60a9da361cea4aae450a69" + integrity sha512-2YfZlalWefOEfnr/CdqKRrgMgbKidYc+zG4/ilxSdcryZSux3eBU5/5btAT/hSiaHipUjd8UrWK8esCBHU6QNQ== + dependencies: + "@ethersproject/bignumber" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/keccak256" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/rlp" "^5.2.0" + +"@ethersproject/base64@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.2.0.tgz#e01066d25e5b4e8a051545163bee5def47bd9534" + integrity sha512-D9wOvRE90QBI+yFsKMv0hnANiMzf40Xicq9JZbV9XYzh7srImmwmMcReU2wHjOs9FtEgSJo51Tt+sI1dKPYKDg== + dependencies: + "@ethersproject/bytes" "^5.2.0" + +"@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.2.0.tgz#03f91ea740c5adb6f8c6a2e91bb4ee5ffaff5503" + integrity sha512-+MNQTxwV7GEiA4NH/i51UqQ+lY36O0rxPdV+0qzjFSySiyBlJpLk6aaa4UTvKmYWlI7YKZm6vuyCENeYn7qAOw== + dependencies: + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + bn.js "^4.4.0" + +"@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.2.0.tgz#327917d5a1600f92fd2a9da4052fa6d974583132" + integrity sha512-O1CRpvJDnRTB47vvW8vyqojUZxVookb4LJv/s06TotriU3Xje5WFvlvXJu1yTchtxTz9BbvJw0lFXKpyO6Dn7w== + dependencies: + "@ethersproject/logger" "^5.2.0" + +"@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.2.0.tgz#ccea78ce325f78abfe7358397c03eec570518d92" + integrity sha512-p+34YG0KbHS20NGdE+Ic0M6egzd7cDvcfoO9RpaAgyAYm3V5gJVqL7UynS87yCt6O6Nlx6wRFboPiM5ctAr+jA== + dependencies: + "@ethersproject/bignumber" "^5.2.0" + +"@ethersproject/hash@^5.0.4": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.2.0.tgz#2d21901eafc5bdb738b4ad96bee364d371ec724b" + integrity sha512-wEGry2HFFSssFiNEkFWMzj1vpdFv4rQlkBp41UfL6J58zKGNycoAWimokITDMk8p7548MKr27h48QfERnNKkRw== + dependencies: + "@ethersproject/abstract-signer" "^5.2.0" + "@ethersproject/address" "^5.2.0" + "@ethersproject/bignumber" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/keccak256" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + "@ethersproject/strings" "^5.2.0" + +"@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.2.0.tgz#15257862807c23f24a3209d1016d322dca85a464" + integrity sha512-LqyxTwVANga5Y3L1yo184czW6b3PibabN8xyE/eOulQLLfXNrHHhwrOTpOhoVRWCICVCD/5SjQfwqTrczjS7jQ== + dependencies: + "@ethersproject/bytes" "^5.2.0" + js-sha3 "0.5.7" + +"@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.2.0.tgz#accf5348251f78b6c8891af67f42490a4ea4e5ae" + integrity sha512-dPZ6/E3YiArgG8dI/spGkaRDry7YZpCntf4gm/c6SI8Mbqiihd7q3nuLN5VvDap/0K3xm3RE1AIUOcUwwh2ezQ== + +"@ethersproject/networks@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.2.0.tgz#66c23c6ac477dd703645b2c971ac842d8b8aa524" + integrity sha512-q+htMgq7wQoEnjlkdHM6t1sktKxNbEB/F6DQBPNwru7KpQ1R0n0UTIXJB8Rb7lSnvjqcAQ40X3iVqm94NJfYDw== + dependencies: + "@ethersproject/logger" "^5.2.0" + +"@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.2.0.tgz#8fadf367f7ac7357019d0224aa579b234c545ac1" + integrity sha512-oNFkzcoGwXXV+/Yp/MLcDLrL/2i360XIy2YN9yRZJPnIbLwjroFNLiRzLs6PyPw1D09Xs8OcPR1/nHv6xDKE2A== + dependencies: + "@ethersproject/logger" "^5.2.0" + +"@ethersproject/rlp@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.2.0.tgz#bbf605183818a9d96bdc40323d734c79e26cfaca" + integrity sha512-RqGsELtPWxcFhOOhSr0lQ2hBNT9tBE08WK0tb6VQbCk97EpqkbgP8yXED9PZlWMiRGchJTw6S+ExzK62XMX/fw== + dependencies: + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + +"@ethersproject/signing-key@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.2.0.tgz#e8eb10d3c0f4a575479db8d70c62aaf93cd384d1" + integrity sha512-9A+dVSkrVAPuhJnWqLWV/NkKi/KB4iagTKEuojfuApUfeIHEhpwQ0Jx3cBimk7qWISSSKdgiAmIqpvVtZ5FEkg== + dependencies: + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + bn.js "^4.4.0" + elliptic "6.5.4" + +"@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.2.0.tgz#e93d989859587191c3f64bda124d9dedbc3f5a97" + integrity sha512-RmjX800wRYKgrzo2ZCSlA8OCQYyq4+M46VgjSVDVyYkLZctBXC3epqlppDA24R7eo856KNbXqezZsMnHT+sSuA== + dependencies: + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/constants" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + +"@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.2.0.tgz#052e2ef8f8adf7037ebe4cc47aad2a61950e6491" + integrity sha512-QrGbhGYsouNNclUp3tWMbckMsuXJTOsA56kT3BuRrLlXJcUH7myIihajXdSfKcyJsvHJPrGZP+U3TKh+sLzZtg== + dependencies: + "@ethersproject/address" "^5.2.0" + "@ethersproject/bignumber" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/constants" "^5.2.0" + "@ethersproject/keccak256" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + "@ethersproject/rlp" "^5.2.0" + "@ethersproject/signing-key" "^5.2.0" + +"@ethersproject/web@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.2.0.tgz#47d8e152e7fcc07ba0aff4f99fde268fde79dd7a" + integrity sha512-mYb9qxGlOBFR2pR6t1CZczuqqX6r8RQGn7MtwrBciMex3cvA/qs+wbmcDgl+/OZY0Pco/ih6WHQRnVi+4sBeCQ== + dependencies: + "@ethersproject/base64" "^5.2.0" + "@ethersproject/bytes" "^5.2.0" + "@ethersproject/logger" "^5.2.0" + "@ethersproject/properties" "^5.2.0" + "@ethersproject/strings" "^5.2.0" + +"@openzeppelin/contracts-upgradeable@3.4.1-solc-0.7-2": + version "3.4.1-solc-0.7-2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1-solc-0.7-2.tgz#8d46f2310560d3756bd5235e20f4e50caa947e92" + integrity sha512-hGbNTTlkcsRhMdJ+IMAWKn5uI1IK9yvJamZpQou1aOjgr+VOFo7eqdiqs+Dwv8fGzN7L/Wdg4XqiW3vGqTHk3g== + +"@openzeppelin/truffle-upgrades@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.7.0.tgz#cbc0d3a2f32d1acb8a31bcc9b8c8f0f7eb01f510" + integrity sha512-0Bb7Qr6ULUYKB//TJF7s0++z4QKONR9b5/UCnqb+po1TYqxEBwftjebVR4rj7C1tWH8cj+igEMkyQb0ep9r9Lw== + dependencies: + "@openzeppelin/upgrades-core" "^1.7.0" + "@truffle/contract" "^4.2.12" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.7.0": + version "1.7.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.7.6.tgz#2c5e89b272aaf2164f51ca99167ffeecdab13749" + integrity sha512-xXoUJGWI2MHLtMwS2GN3PiymZ9WIj+uRAMrm8HIz0genGSIO6PwPMlmaUyq3O8/JzTMgpVV92aMcPjOkvI5SmQ== + dependencies: + bn.js "^5.1.2" + cbor "^7.0.0" + chalk "^4.1.0" + compare-versions "^3.6.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@solidity-parser/parser@^0.13.0", "@solidity-parser/parser@^0.13.2": + version "0.13.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.13.2.tgz#b6c71d8ca0b382d90a7bbed241f9bc110af65cbe" + integrity sha512-RwHnpRnfrnD2MSPveYoPh8nhofEvX7fgjHk1Oq+NNvCcLx4r1js91CO9o+F/F3fBzOCyvm8kKRTriFICX/odWw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/blockchain-utils@^0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.30.tgz#1fafbd8e8694d79280177b5eff167b0690838855" + integrity sha512-3hkHSHxVavoALcxpBqD4YwHuCmkBrvjq6PAGw93i6WCB+pnejBD5sFjVCiZZKCogh4kGObxxcwu53+3dyT/6IQ== + +"@truffle/codec@^0.10.8": + version "0.10.8" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.8.tgz#d6555a153ef4be27e1109320de37b10150124cda" + integrity sha512-S4fxInoPH+gTF5zTawiqgFqU2tOIfob2dk0Zc/wqATH8hf1AmRjoYSL6hazk9mdynvmsvNM/QjO6XKCMX3pYYw== + dependencies: + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + utf8 "^3.0.0" + web3-utils "1.3.6" + +"@truffle/contract-schema@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.1.tgz#13b404383d438b48960862022a20102970323666" + integrity sha512-2gvu6gxJtbbI67H2Bwh2rBuej+1uCV3z4zKFzQZP00hjNoL+QfybrmBcOVB88PflBeEB+oUXuwQfDoKX3TXlnQ== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.3.1" + +"@truffle/contract@^4.2.12": + version "4.3.18" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.18.tgz#15c60ff97480ca4bc4831e4b9c659305acc358ac" + integrity sha512-CvP6iTC/sz3rAfy3awFdDsvE6Q8xfjfW/7oM3OMD49nZnBNHKGQUG5CVKeAa180IVxbA8MGFr3KDCPtN2p0zMw== + dependencies: + "@truffle/blockchain-utils" "^0.0.30" + "@truffle/contract-schema" "^3.4.1" + "@truffle/debug-utils" "^5.0.18" + "@truffle/error" "^0.0.14" + "@truffle/interface-adapter" "^0.5.0" + bignumber.js "^7.2.1" + ethereum-ens "^0.8.0" + ethers "^4.0.32" + web3 "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +"@truffle/debug-utils@^5.0.18": + version "5.0.18" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.18.tgz#ceb12c263e8f29fdacec6a5727dcca3ca7d678a8" + integrity sha512-uBPjp6w3LQK8bfSKU1vw1JjNeDn/DX2SnDH0bLNMZg224bhdcDOBhKQBPx3PhrYS01Yhktfu5kDhxgeGQzQfWg== + dependencies: + "@truffle/codec" "^0.10.8" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlight.js "^10.4.0" + highlightjs-solidity "^1.1.0" + +"@truffle/error@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.14.tgz#59683b5407bede7bddf16d80dc5592f9c5e5fa05" + integrity sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA== + +"@truffle/hdwallet-provider@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.4.0.tgz#8b937d0688eebc049db4ab0e9ed895598b9b4d48" + integrity sha512-zCc4IWEtfAuKWWktZOaYFoHCvameQIz5Bk+S2ahCPxYMlWECWrbCMjoqqLojB1Hyz+IsvU1cspmr3E6PoO4ZmQ== + dependencies: + "@trufflesuite/web3-provider-engine" "15.0.13-1" + any-promise "^1.3.0" + bindings "^1.5.0" + ethereum-cryptography "^0.1.3" + ethereum-protocol "^1.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^1.0.1" + +"@truffle/interface-adapter@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.0.tgz#00c9e16fe0edafbfbf4b331fb4db178c9148c8fc" + integrity sha512-0MRt9orgQqo0knyKDy0fGRqnI+alkuK0BUAvHB1/VUJgCKyWBNAUUZO5gPjuj75qCjV4Rw+W6SKDQpn2xOWsXw== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + web3 "1.3.6" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@trufflesuite/eth-json-rpc-filters@^4.1.2-1": + version "4.1.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz#61ab78c52e98a883e5cf086925b34a30297b1824" + integrity sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-0" + await-semaphore "^0.1.3" + eth-query "^2.1.2" + json-rpc-engine "^5.1.3" + lodash.flatmap "^4.5.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-json-rpc-infura@^4.0.3-0": + version "4.0.3-0" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz#6d22122937cf60ec9d21a02351c101fdc608c4fe" + integrity sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + cross-fetch "^2.1.1" + eth-json-rpc-errors "^1.0.1" + json-rpc-engine "^5.1.3" + +"@trufflesuite/eth-json-rpc-middleware@^4.4.2-0", "@trufflesuite/eth-json-rpc-middleware@^4.4.2-1": + version "4.4.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz#8c3638ed8a7ed89a1e5e71407de068a65bef0df2" + integrity sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA== + dependencies: + "@trufflesuite/eth-sig-util" "^1.4.2" + btoa "^1.2.1" + clone "^2.1.1" + eth-json-rpc-errors "^1.0.1" + eth-query "^2.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.6.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^5.1.3" + json-stable-stringify "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-sig-util@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz#b529e2f38ac08e652116f48981132a26242a4f08" + integrity sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + +"@trufflesuite/web3-provider-engine@15.0.13-1": + version "15.0.13-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.13-1.tgz#f6a7f7131a2fdc4ab53976318ed13ce83e8e4bcb" + integrity sha512-6u3x/iIN5fyj8pib5QTUDmIOUiwAGhaqdSTXdqCu6v9zo2BEwdCqgEJd1uXDh3DBmPRDfiZ/ge8oUPy7LerpHg== + dependencies: + "@trufflesuite/eth-json-rpc-filters" "^4.1.2-1" + "@trufflesuite/eth-json-rpc-infura" "^4.0.3-0" + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + "@trufflesuite/eth-sig-util" "^1.4.2" + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^4.4.2" + eth-json-rpc-errors "^2.0.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "15.6.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08" + integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA== + +"@types/node@^12.12.6": + version "12.20.13" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.13.tgz#e743bae112bd779ac9650f907197dd2caa7f0364" + integrity sha512-1x8W5OpxPq+T85OUsHRP6BqXeosKmeXRtjoF39STcdf/UWLqUsoehstZKOi0CunhVqHG17AyZgpj20eRVooK6A== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.2.tgz#20c29a87149d980f64464e56539bf4810fdb5d1d" + integrity sha512-QMg+9v0bbNJ2peLuHRWxzmy0HRJIG6gFZNhaRSp7S3ggSbCCxiqQB2/ybvhXyhHOCequpNkrx7OavNhrWOsW0A== + dependencies: + "@types/node" "*" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-jsx@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +any-promise@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +available-typed-arrays@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" + integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== + +await-semaphore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/await-semaphore/-/await-semaphore-0.1.3.tgz#2b88018cc8c28e06167ae1cdff02504f1f9688d3" + integrity sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-plugin-polyfill-corejs2@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" + integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.2" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.2.tgz#7424a1682ee44baec817327710b1b094e5f8f7f5" + integrity sha512-l1Cf8PKk12eEk5QP/NQ6TH8A1pee6wWDJ96WjxrMXFLHLOBFzYM4moG80HFgduVhTqAFez4alnZKEhP/bYHg0A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + core-js-compat "^3.9.1" + +babel-plugin-polyfill-regenerator@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" + integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bluebird@^3.4.7, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserslist@^4.16.6: + version "4.16.6" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" + integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== + dependencies: + caniuse-lite "^1.0.30001219" + colorette "^1.2.2" + electron-to-chromium "^1.3.723" + escalade "^3.1.1" + node-releases "^1.1.71" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +caniuse-lite@^1.0.30001219: + version "1.0.30001235" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001235.tgz#ad5ca75bc5a1f7b12df79ad806d715a43a5ac4ed" + integrity sha512-zWEwIVqnzPkSAXOUlQnPW2oKoYb2aLQ4Q5ejdjBcnH63rfypaW34CxaeBn1VMya2XaEU3P/R2qHpWyj+l0BT1A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +cbor@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f" + integrity sha512-0aaAPgW92lLmypb9iCd22k7tSD1FbF6dps8VQzmIBKY6ych2gO09b2vo/SbaLTmezJuB8Kh88Rvpl/Uq52mNZg== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + nofilter "^2.0.3" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +cheerio-select@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9" + integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew== + dependencies: + css-select "^4.1.2" + css-what "^5.0.0" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.6.0" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.9" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f" + integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng== + dependencies: + cheerio-select "^1.4.0" + dom-serializer "^1.3.1" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" + integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-js-compat@^3.9.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.14.0.tgz#b574dabf29184681d5b16357bd33d104df3d29a5" + integrity sha512-R4NS2eupxtiJU+VwgkF9WTpnSfZW4pogwKHd8bclWU2sp93Pr5S1uYJI84cMOubJRou7bcfL0vmwtLslWN5p3A== + dependencies: + browserslist "^4.16.6" + semver "7.0.0" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.3.tgz#e8a0b3c54598136e037f8650f8e823ccdfac198e" + integrity sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw== + dependencies: + node-fetch "2.1.2" + whatwg-fetch "2.0.4" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +css-select@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.2.tgz#8b52b6714ed3a80d8221ec971c543f3b12653286" + integrity sha512-nu5ye2Hg/4ISq4XqdLY2bEatAcLIdt3OYGFc9Tm9n7VSlFBcfRv0gBNksHRgSdUDQGtN3XrZ94ztW+NfzkFSUw== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-to-object@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-to-object/-/dir-to-object-2.0.0.tgz#29723e9bd1c3e58e4f307bd04ff634c0370c8f8a" + integrity sha512-sXs0JKIhymON7T1UZuO2Ud6VTNAx/VTBXIl4+3mjb2RgfOpt+hectX0x04YqPOPdkeOAKoJuKqwqnXXURNPNEA== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1, dom-serializer@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7" + integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.723: + version "1.3.749" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.749.tgz#0ecebc529ceb49dd2a7c838ae425236644c3439a" + integrity sha512-F+v2zxZgw/fMwPz/VUGIggG4ZndDsYy0vlpthi3tjmDZlcfbhN5mYW0evXUsBr2sUtuDANFtle410A9u/sd/4A== + +elliptic@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^4.4.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz#766a0a0eb4a52c867a28328e9ae21353812cf626" + integrity sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw== + dependencies: + "@babel/plugin-transform-runtime" "^7.5.5" + "@babel/runtime" "^7.5.5" + eth-query "^2.1.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-errors@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" + integrity sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + +ethereum-bloom-filters@^1.0.6: + version "1.0.9" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.9.tgz#4a59dead803af0c9e33834170bd7695df67061ec" + integrity sha512-GiK/RQkAkcVaEdxKVkPcG07PQ5vD7v2MFSHgZmBJSfMzNRHimntdBithsHAT89tAXnIpzVDWt8iaCD1DvkaxGg== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-ens@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" + integrity sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg== + dependencies: + bluebird "^3.4.7" + eth-ens-namehash "^2.0.0" + js-sha3 "^0.5.7" + pako "^1.0.4" + underscore "^1.8.3" + web3 "^1.0.0-beta.34" + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.1.tgz#664a4bcacfc1291ca2703de066df1178938dba1c" + integrity sha512-3Z5g1hG1das0JWU6cQ9HWWTY2nt9nXCcwj7eXVNAHKbo00XAZO8+NHlwdgXDWrL0SXVQMvTWN8Q/82DRH/JhPw== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.0.2" + randombytes "^2.0.6" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^4.0.32: + version "4.0.48" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" + integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" + integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@^7.1.2, glob@^7.1.3: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +highlight.js@^10.4.0, highlight.js@^10.4.1: + version "10.7.2" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" + integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== + +highlightjs-solidity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.1.0.tgz#bdf0adba6deffdb1651f8fa63bee4dacc3dc4e00" + integrity sha512-LtH7uuoe+FOmtQd41ozAZKLJC2chqdqs461FJcWAx00R3VcBhSQTRFfzRGtTQqu2wsVIEdHiyynrPrEDDWyIMw== + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +husky@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + +is-boolean-object@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + dependencies: + call-bind "^1.0.2" + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-core-module@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.4.0.tgz#8e9fc8e15027b011418026e98f0e6f4d86305cc1" + integrity sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-stream@^1.0.0, is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-rpc-engine@^5.1.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.47.0: + version "1.47.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c" + integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.30" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d" + integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg== + dependencies: + mime-db "1.47.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.1.2.tgz#ab884e8e7e57e38a944753cec706f788d1768bb5" + integrity sha1-q4hOjn5X44qUR1POxwb3iNF2i7U= + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +node-releases@^1.1.71: + version "1.1.72" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" + integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +nofilter@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-2.0.3.tgz#f5460f3cb33147005883e3f5d4476239501fa187" + integrity sha512-FbuXC+lK+GU2+63D1kC1ETiZo+Z7SIi7B+mxKTCH1byrh6WFvfBCN/wpherFz0a0bjGd7EKTst/cz0yLeNngug== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-beta.6: + version "1.0.0-beta.11" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.11.tgz#580a16cab6517ab20cd08e65afd0300caa32125d" + integrity sha512-b3lh/9tn9BC3I723Aa91zebw2NDlbbBroKg7izP+jU0QfrLO6ldi0jY0XrqX3+zGqAI2aLcP8Jr1eGdymK5CgQ== + dependencies: + "@solidity-parser/parser" "^0.13.0" + dir-to-object "^2.0.0" + emoji-regex "^9.2.2" + escape-string-regexp "^4.0.0" + prettier "^2.2.1" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.2" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-addr@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" + integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.2.9: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +regenerator-runtime@^0.13.4: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.6.tgz#abe9af185a9a7defefba480047b3e42cbe9a1210" + integrity sha512-HWUxTAv2h7hx3s3hAab3ifnlwb02ZWhwFU/wSudUHqteMS3ll9c+m1FlGn9V8ztE2rf3Z82fQZA005Wv7KpcFA== + dependencies: + "@solidity-parser/parser" "^0.13.2" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-ast@^0.4.15: + version "0.4.23" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.23.tgz#1e567ac0b69db5d0c9ba7eb7e3ba20546dc7e951" + integrity sha512-j1658JXgq+v3z5I6quPSbQdu54pq9/bC2QItB6yOKgQigWNXV8S4eZ4TMs7zmMuy4zKd8HrtphfOusXTKsvBdw== + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +source-map@^0.5.0: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" + integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +truffle-assertions@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/truffle-assertions/-/truffle-assertions-0.9.2.tgz#0f8360f53ad92b6d8fdb8ceb5dce54c1fc392e23" + integrity sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA== + dependencies: + assertion-error "^1.1.0" + lodash.isequal "^4.5.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + +underscore@^1.8.3: + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +urlsafe-base64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz#23f89069a6c62f46cf3a1d3b00169cefb90be0c6" + integrity sha1-I/iQaabGL0bPOh07ABac77kL4MY= + +utf-8-validate@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1" + integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +web3-bzz@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.6.tgz#95f370aecc3ff6ad07f057e6c0c916ef09b04dde" + integrity sha512-ibHdx1wkseujFejrtY7ZyC0QxQ4ATXjzcNUpaLrvM6AEae8prUiyT/OloG9FWDgFD2CPLwzKwfSQezYQlANNlw== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.12.1" + +web3-core-helpers@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz#c478246a9abe4e5456acf42657dac2f7c330be74" + integrity sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA== + dependencies: + underscore "1.12.1" + web3-eth-iban "1.3.6" + web3-utils "1.3.6" + +web3-core-method@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.6.tgz#4b0334edd94b03dfec729d113c69a4eb6ebc68ae" + integrity sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.12.1" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-utils "1.3.6" + +web3-core-promievent@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz#6c27dc79de8f71b74f5d17acaf9aaf593d3cb0c9" + integrity sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz#4fea269fe913fd4fca464b4f7c65cb94857b5b2a" + integrity sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA== + dependencies: + underscore "1.12.1" + util "^0.12.0" + web3-core-helpers "1.3.6" + web3-providers-http "1.3.6" + web3-providers-ipc "1.3.6" + web3-providers-ws "1.3.6" + +web3-core-subscriptions@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz#ee24e7974d1d72ff6c992c599deba4ef9b308415" + integrity sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-core@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.6.tgz#a6a761d1ff2f3ee462b8dab679229d2f8e267504" + integrity sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-requestmanager "1.3.6" + web3-utils "1.3.6" + +web3-eth-abi@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz#4272ca48d817aa651bbf97b269f5ff10abc2b8a9" + integrity sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ== + dependencies: + "@ethersproject/abi" "5.0.7" + underscore "1.12.1" + web3-utils "1.3.6" + +web3-eth-accounts@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.6.tgz#f9fcb50b28ee58090ab292a10d996155caa2b474" + integrity sha512-Ilr0hG6ONbCdSlVKffasCmNwftD5HsNpwyQASevocIQwHdTlvlwO0tb3oGYuajbKOaDzNTwXfz25bttAEoFCGA== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.12.1" + uuid "3.3.2" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-eth-contract@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz#cccf4d32dc56917fb6923e778498a9ba2a5ba866" + integrity sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +web3-eth-ens@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.6.tgz#0d28c5d4ea7b4462ef6c077545a77956a6cdf175" + integrity sha512-n27HNj7lpSkRxTgSx+Zo7cmKAgyg2ElFilaFlUu/X2CNH23lXfcPm2bWssivH9z0ndhg0OyR4AYFZqPaqDHkJA== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-contract "1.3.6" + web3-utils "1.3.6" + +web3-eth-iban@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz#0d6ba21fe78f190af8919e9cd5453882457209e0" + integrity sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ== + dependencies: + bn.js "^4.11.9" + web3-utils "1.3.6" + +web3-eth-personal@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.6.tgz#226137916754c498f0284f22c55924c87a2efcf0" + integrity sha512-pOHU0+/h1RFRYoh1ehYBehRbcKWP4OSzd4F7mDljhHngv6W8ewMHrAN8O1ol9uysN2MuCdRE19qkRg5eNgvzFQ== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-eth@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.6.tgz#2c650893d540a7a0eb1365dd5b2dca24ac919b7c" + integrity sha512-9+rnywRRpyX3C4hfsAQXPQh6vHh9XzQkgLxo3gyeXfbhbShUoq2gFVuy42vsRs//6JlsKdyZS7Z3hHPHz2wreA== + dependencies: + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-accounts "1.3.6" + web3-eth-contract "1.3.6" + web3-eth-ens "1.3.6" + web3-eth-iban "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-net@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.6.tgz#a56492e2227475e38db29394f8bac305a2446e41" + integrity sha512-KhzU3wMQY/YYjyMiQzbaLPt2kut88Ncx2iqjy3nw28vRux3gVX0WOCk9EL/KVJBiAA/fK7VklTXvgy9dZnnipw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-providers-http@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.6.tgz#36e8724a7424d52827819d53fd75dbf31f5422c2" + integrity sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q== + dependencies: + web3-core-helpers "1.3.6" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz#cef8d12c1ebb47adce5ebf597f553c623362cb4a" + integrity sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA== + dependencies: + oboe "2.1.5" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-providers-ws@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz#e1df617bc89d66165abdf2191da0014c505bfaac" + integrity sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + websocket "^1.0.32" + +web3-shh@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.6.tgz#4e3486c7eca5cbdb87f88910948223a5b7ea6c20" + integrity sha512-9zRo415O0iBslxBnmu9OzYjNErzLnzOsy+IOvSpIreLYbbAw0XkDWxv3SfcpKnTIWIACBR4AYMIxmmyi5iB3jw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-net "1.3.6" + +web3-utils@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.6.tgz#390bc9fa3a7179746963cfaca55bb80ac4d8dc10" + integrity sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.12.1" + utf8 "3.0.0" + +web3@1.3.6, web3@^1.0.0-beta.34: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.6.tgz#599425461c3f9a8cbbefa70616438995f4a064cc" + integrity sha512-jEpPhnL6GDteifdVh7ulzlPrtVQeA30V9vnki9liYlUvLV82ZM7BNOQJiuzlDePuE+jZETZSP/0G/JlUVt6pOA== + dependencies: + web3-bzz "1.3.6" + web3-core "1.3.6" + web3-eth "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-shh "1.3.6" + web3-utils "1.3.6" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" + integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== + dependencies: + async-limiter "~1.0.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/solidity/bsh/.prettierrc b/solidity/bsh/.prettierrc new file mode 100644 index 00000000..e368cfec --- /dev/null +++ b/solidity/bsh/.prettierrc @@ -0,0 +1,15 @@ +{ + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false, + "explicitTypes": "always" + } + } + ] +} diff --git a/solidity/bsh/.solhint.json b/solidity/bsh/.solhint.json new file mode 100644 index 00000000..ec279d46 --- /dev/null +++ b/solidity/bsh/.solhint.json @@ -0,0 +1,16 @@ +{ + "extends": ["solhint:recommended"], + "rules": { + "compiler-version": ["error", ">=0.5.0 <0.8.0"], + "prettier/prettier": "error", + "avoid-throw": "off", + "avoid-suicide": "error", + "avoid-sha3": "warn", + "no-inline-assembly": "off", + "func-visibility": ["warn", {"ignoreConstructors": true}], + "no-empty-blocks": "off", + "reason-string": "off" + }, + "plugins": ["prettier"] + } + \ No newline at end of file diff --git a/solidity/bsh/README.md b/solidity/bsh/README.md new file mode 100644 index 00000000..dde536fa --- /dev/null +++ b/solidity/bsh/README.md @@ -0,0 +1,33 @@ +## Set up +Node >= 10.x +``` +$ node --version +v15.12.0 +``` +Install tools +``` +$ npm install --global yarn truffle@5.3.0 +``` +Install dependencies +``` +$ yarn +``` + +## Test +1. Run in a background process or seperate terminal window +``` +$ docker run --rm -d -p 9933:9933 -p 9944:9944 purestake/moonbeam:v0.9.2 --dev --ws-external --rpc-external +``` +2. Compile contracts +``` +$ yarn contract:compile +``` +3. Run unit and integration test +``` +$ yarn test +``` +- Run specific test +``` +$ yarn test:unit +$ yarn test:integration +``` diff --git a/solidity/bsh/contracts/BSHCore.sol b/solidity/bsh/contracts/BSHCore.sol new file mode 100644 index 00000000..9aa7fd4f --- /dev/null +++ b/solidity/bsh/contracts/BSHCore.sol @@ -0,0 +1,683 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./interfaces/IBSHPeriphery.sol"; +import "./interfaces/IBSHCore.sol"; +import "./libraries/String.sol"; +import "./libraries/Types.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155HolderUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; + +/** + @title BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following contract can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHCore is + Initializable, + IBSHCore, + ERC1155Upgradeable, + ERC1155HolderUpgradeable, + ReentrancyGuardUpgradeable +{ + using SafeMathUpgradeable for uint256; + using String for string; + event SetOwnership(address indexed promoter, address indexed newOwner); + event RemoveOwnership(address indexed remover, address indexed formerOwner); + + modifier onlyOwner { + require(owners[msg.sender] == true, "Unauthorized"); + _; + } + + modifier onlyBSHPeriphery { + require(msg.sender == address(bshPeriphery), "Unauthorized"); + _; + } + + uint256 private constant FEE_DENOMINATOR = 10**4; + uint256 public feeNumerator; + uint256 public fixedFee; + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + + IBSHPeriphery internal bshPeriphery; + + address[] private listOfOwners; + uint256[] private chargedAmounts; // a list of amounts have been charged so far (use this when Fee Gathering occurs) + string[] internal coinsName; // a string array stores names of supported coins + string[] private chargedCoins; // a list of coins' names have been charged so far (use this when Fee Gathering occurs) + + mapping(address => bool) private owners; + mapping(string => uint256) internal aggregationFee; // storing Aggregation Fee in state mapping variable. + mapping(address => mapping(string => Types.Balance)) internal balances; + mapping(string => uint256) private coins; // a list of all supported coins + + function initialize( + string calldata _uri, + string calldata _nativeCoinName, + uint256 _feeNumerator, + uint256 _fixedFee + ) public initializer { + __ERC1155_init(_uri); + __ERC1155Holder_init(); + + owners[msg.sender] = true; + listOfOwners.push(msg.sender); + emit SetOwnership(address(0), msg.sender); + + coins[_nativeCoinName] = 0; + feeNumerator = _feeNumerator; + fixedFee = _fixedFee; + coinsName.push(_nativeCoinName); + } + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override onlyOwner { + require(owners[_owner] == false, "ExistedOwner"); + owners[_owner] = true; + listOfOwners.push(_owner); + emit SetOwnership(msg.sender, _owner); + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override onlyOwner { + require(listOfOwners.length > 1, "Unable to remove last Owner"); + require(owners[_owner] == true, "Removing Owner not found"); + delete owners[_owner]; + _remove(_owner); + emit RemoveOwnership(msg.sender, _owner); + } + + function _remove(address _addr) internal { + for (uint256 i = 0; i < listOfOwners.length; i++) + if (listOfOwners[i] == _addr) { + listOfOwners[i] = listOfOwners[listOfOwners.length - 1]; + listOfOwners.pop(); + break; + } + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return owners[_owner]; + } + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + function getOwners() external view override returns (address[] memory) { + return listOfOwners; + } + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) + external + override + onlyOwner + { + require(_bshPeriphery != address(0), "InvalidSetting"); + if (address(bshPeriphery) != address(0)) { + require( + bshPeriphery.hasPendingRequest() == false, + "HasPendingRequest" + ); + } + bshPeriphery = IBSHPeriphery(_bshPeriphery); + } + + /** + @notice update base uri. + @dev Caller must be an Owner of this contract + the uri must be initilized in construction. + @param _newURI new uri + */ + function updateUri(string calldata _newURI) external override onlyOwner { + _setURI(_newURI); + } + + /** + @notice set fee ratio. + @dev Caller must be an Owner of this contract + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external override onlyOwner { + require(_feeNumerator <= FEE_DENOMINATOR, "InvalidSetting"); + feeNumerator = _feeNumerator; + } + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external override onlyOwner { + require(_fixedFee > 0, "InvalidSetting"); + fixedFee = _fixedFee; + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external override onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() + external + view + override + returns (string[] memory _names) + { + return coinsName; + } + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) + external + view + override + returns (uint256 _coinId) + { + return coins[_coinName]; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + override + returns (bool _valid) + { + return (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])); + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + override + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner, coins[_coinName]), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + override + returns ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ) + { + _usableBalances = new uint256[](_coinNames.length); + _lockedBalances = new uint256[](_coinNames.length); + _refundableBalances = new uint256[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + ( + _usableBalances[i], + _lockedBalances[i], + _refundableBalances[i] + ) = this.getBalanceOf(_owner, _coinNames[i]); + } + return (_usableBalances, _lockedBalances, _refundableBalances); + } + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + override + returns (Types.Asset[] memory _accumulatedFees) + { + _accumulatedFees = new Types.Asset[](coinsName.length); + for (uint256 i = 0; i < coinsName.length; i++) { + _accumulatedFees[i] = ( + Types.Asset(coinsName[i], aggregationFee[coinsName[i]]) + ); + } + return _accumulatedFees; + } + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable override { + // Aggregation Fee will be charged on BSH Contract + // A new charging fee has been proposed. `fixedFee` is introduced + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + // If msg.value less than _chargeAmt, it likely fails when calculating + // _amount = _value - _chargeAmt + uint256 _chargeAmt = + msg.value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // @dev msg.value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage( + msg.sender, + _to, + coinsName[0], + msg.value, + _chargeAmt + ); + } + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer from a Requester (include fee) + @param _to Target BTP address. + */ + function transfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external override { + require(coins[_coinName] != 0, "UnregisterCoin"); + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + // If _value less than _chargeAmt, it likely fails when calculating + // _amount = _value - _chargeAmt + uint256 _chargeAmt = + _value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // Transfer and Lock Token processes: + // BSHCore contract calls safeTransferFrom() to transfer the Token from Caller's account (msg.sender) + // Before that, Caller must approve (setApproveForAll) to accept + // token being transfer out by an Operator + // If this requirement is failed, a transaction is reverted. + // After transferring token, BSHCore contract updates Caller's locked balance + // as a record of pending transfer transaction + // When a transaction is completed without any error on another chain, + // Locked Token amount (bind to an address of caller) will be reset/subtract, + // then emit a successful TransferEnd event as a notification + // Otherwise, the locked amount will also be updated + // but BSHCore contract will issue a refund to Caller before emitting an error TransferEnd event + this.safeTransferFrom( + msg.sender, + address(this), + coins[_coinName], + _value, + "" + ); + // @dev _value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage(msg.sender, _to, _coinName, _value, _chargeAmt); + } + + /** + @notice This private function handles overlapping procedure before sending a service message to BSHPeriphery + @param _from An address of a Requester + @param _to BTP address of of Receiver on another chain + @param _coinName A given name of a requested coin + @param _value A requested amount to transfer from a Requester (include fee) + @param _chargeAmt An amount being charged for this request + */ + function _sendServiceMessage( + address _from, + string calldata _to, + string memory _coinName, + uint256 _value, + uint256 _chargeAmt + ) private { + // Lock this requested _value as a record of a pending transferring transaction + // @dev `_value` is a requested amount to transfer, from a Requester, including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[0] = _value.sub(_chargeAmt); + lockBalance(_from, _coinName, _value); + string[] memory _coins = new string[](1); + _coins[0] = _coinName; + uint256[] memory _amounts = new uint256[](1); + _amounts[0] = _value.sub(_chargeAmt); + uint256[] memory _fees = new uint256[](1); + _fees[0] = _chargeAmt; + + // @dev `_amounts` is a true amount to receive at a destination after deducting a charged fee + bshPeriphery.sendServiceMessage(_from, _to, _coins, _amounts, _fees); + } + + /** + @notice Allow users to transfer multiple coins/wrapped coins to another chain + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + In case of transferring a native coin, it also checks `msg.value` + The number of requested coins MUST be as the same as the number of requested values + The requested coins and values MUST be matched respectively + @param _coinNames A list of requested transferring coins/wrapped coins + @param _values A list of requested transferring values respectively with its coin name + @param _to Target BTP address. + */ + function transferBatch( + string[] calldata _coinNames, + uint256[] memory _values, + string calldata _to + ) external payable override { + require(_coinNames.length == _values.length, "InvalidRequest"); + uint256 size = + msg.value != 0 ? _coinNames.length.add(1) : _coinNames.length; + uint256[] memory _ids = new uint256[](_coinNames.length); + string[] memory _coins = new string[](size); + uint256[] memory _amounts = new uint256[](size); + uint256[] memory _chargeAmts = new uint256[](size); + + for (uint256 i = 0; i < _coinNames.length; i++) { + _ids[i] = coins[_coinNames[i]]; + // Does not need to check if _coinNames[i] == native_coin + // If _coinNames[i] is a native_coin, coins[_coinNames[i]] = 0 + require(_ids[i] != 0, "UnregisterCoin"); + + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[i] = _coinNames[i]; + _chargeAmts[i] = _values[i] + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[i] = _values[i].sub(_chargeAmts[i]); + + // Lock this requested _value as a record of a pending transferring transaction + // @dev Note that: _value is a requested amount to transfer from a Requester including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[i] = _values[i].sub(_chargeAmts[i]); + lockBalance(msg.sender, _coinNames[i], _values[i]); + } + + this.safeBatchTransferFrom( + msg.sender, + address(this), + _ids, + _values, + "" + ); + + if (msg.value != 0) { + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[size - 1] = coinsName[0]; // push native_coin at the end of request + _chargeAmts[size - 1] = msg + .value + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[size - 1] = msg.value.sub(_chargeAmts[size - 1]); + lockBalance(msg.sender, coinsName[0], msg.value); + } + + // @dev `_amounts` is true amounts to receive at a destination after deducting charged fees + bshPeriphery.sendServiceMessage( + msg.sender, + _to, + _coins, + _amounts, + _chargeAmts + ); + } + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) + external + override + nonReentrant + { + require( + balances[msg.sender][_coinName].refundableBalance >= _value, + "Imbalance" + ); + + balances[msg.sender][_coinName].refundableBalance = balances[ + msg.sender + ][_coinName] + .refundableBalance + .sub(_value); + + this.refund(msg.sender, _coinName, _value); + } + + // Solidity does not allow using try_catch with interal/private function + // Thus, this function would be set as 'external` + // But, it has restriction. It should be called by this contract only + // In addition, there are only two functions calling this refund() + // + handleRequestService(): this function only called by BSHPeriphery + // + reclaim(): this function can be called by ANY + // In case of reentrancy attacks, the chance happenning on BSHPeriphery + // since it requires a request from BMC which requires verification fron BMV + // reclaim() has higher chance to have reentrancy attacks. + // So, it must be prevented by adding 'nonReentrant' + function refund( + address _to, + string calldata _coinName, + uint256 _value + ) external { + require(msg.sender == address(this), "Unauthorized"); + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + this.safeTransferFrom(address(this), _to, _id, _value, ""); + } + } + + function paymentTransfer(address payable _to, uint256 _amount) private { + (bool sent, ) = _to.call{value: _amount}(""); + require(sent, "Payment transfer failed"); + } + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external override onlyBSHPeriphery { + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + _mint(_to, _id, _value, ""); + } + } + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external override onlyBSHPeriphery { + // Fee Gathering and Transfer Coin Request use the same method + // and both have the same response + // In case of Fee Gathering's response, `_requester` is this contract's address + // Thus, check that first + // -- If `_requester` is this contract's address, then check whethere response's code is RC_ERR + // In case of RC_ERR, adding back charged fees to `aggregationFee` state variable + // In case of RC_OK, ignore and return + // -- Otherwise, handle service's response as normal + if (_requester == address(this)) { + if (_rspCode == RC_ERR) { + aggregationFee[_coinName] = aggregationFee[_coinName].add( + _value + ); + } + return; + } + uint256 _amount = _value.add(_fee); + balances[_requester][_coinName].lockedBalance = balances[_requester][ + _coinName + ] + .lockedBalance + .sub(_amount); + + // A new implementation has been proposed to prevent spam attacks + // In receiving error response, BSHCore refunds `_value`, not including `_fee`, back to Requestor + if (_rspCode == RC_ERR) { + try this.refund(_requester, _coinName, _value) {} catch { + balances[_requester][_coinName].refundableBalance = balances[ + _requester + ][_coinName] + .refundableBalance + .add(_value); + } + } else if (_rspCode == RC_OK) { + uint256 _id = coins[_coinName]; + if (_id != 0) { + _burn(address(this), _id, _value); + } + } + aggregationFee[_coinName] = aggregationFee[_coinName].add(_fee); + } + + /** + @notice Handle a request of Fee Gathering + Usage: Copy all charged fees to an array + @dev Caller must be an BSHPeriphery contract + */ + function transferFees(string calldata _fa) + external + override + onlyBSHPeriphery + { + // @dev Due to uncertainty in identifying a size of returning memory array + // and Solidity does not allow to use 'push' with memory array (only storage) + // thus, must use 'temp' storage state variable + for (uint256 i = 0; i < coinsName.length; i++) { + if (aggregationFee[coinsName[i]] != 0) { + chargedCoins.push(coinsName[i]); + chargedAmounts.push(aggregationFee[coinsName[i]]); + delete aggregationFee[coinsName[i]]; + } + } + bshPeriphery.sendServiceMessage( + address(this), + _fa, + chargedCoins, + chargedAmounts, + new uint256[](chargedCoins.length) // chargedFees is an array of 0 since this is a fee gathering request + ); + delete chargedCoins; + delete chargedAmounts; + } + + function lockBalance( + address _to, + string memory _coinName, + uint256 _value + ) private { + balances[_to][_coinName].lockedBalance = balances[_to][_coinName] + .lockedBalance + .add(_value); + } +} diff --git a/solidity/bsh/contracts/BSHPeriphery.sol b/solidity/bsh/contracts/BSHPeriphery.sol new file mode 100644 index 00000000..136ab02b --- /dev/null +++ b/solidity/bsh/contracts/BSHPeriphery.sol @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./interfaces/IBSHPeriphery.sol"; +import "./interfaces/IBSHCore.sol"; +import "./interfaces/IBMCPeriphery.sol"; +import "./libraries/Types.sol"; +import "./libraries/RLPEncodeStruct.sol"; +import "./libraries/RLPDecodeStruct.sol"; +import "./libraries/ParseAddress.sol"; +import "./libraries/String.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +/** + @title BSHPeriphery contract + @dev This contract is used to handle communications among BMCService and BSHCore contract + @dev OwnerUpgradeable has been removed. This contract does not have its own Owners + Instead, BSHCore manages ownership roles. + Thus, BSHPeriphery should call bshCore.isOwner() and pass an address for verification + in case of implementing restrictions, if needed, in the future. +*/ +contract BSHPeriphery is Initializable, IBSHPeriphery { + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.Response; + using RLPDecodeStruct for bytes; + using SafeMathUpgradeable for uint256; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + using String for uint256; + + /** @notice Sends a receipt to user + The `_from` sender + The `_to` receiver. + The `_sn` sequence number of service message. + The `_assetDetails` a list of `_coinName` and `_value` + */ + event TransferStart( + address indexed _from, + string _to, + uint256 _sn, + Types.AssetTransferDetail[] _assetDetails + ); + + /** @notice Sends a final notification to a user + The `_from` sender + The `_sn` sequence number of service message. + The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 + The `_response` message of response if error + */ + event TransferEnd( + address indexed _from, + uint256 _sn, + uint256 _code, + string _response + ); + + /** @notice Notify that BSH contract has received unknown response + The `_from` sender + The `_sn` sequence number of service message + */ + event UnknownResponse(string _from, uint256 _sn); + + IBMCPeriphery private bmc; + IBSHCore internal bshCore; + mapping(uint256 => Types.PendingTransferCoin) public requests; // a list of transferring requests + string public serviceName; // BSH Service Name + + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + uint256 private serialNo; // a counter of sequence number of service message + uint256 private numOfPendingRequests; + + modifier onlyBMC { + require(msg.sender == address(bmc), "Unauthorized"); + _; + } + + modifier onlyBSHCore { + require(msg.sender == address(bshCore), "Unauthorized"); + _; + } + function initialize( + address _bmc, + address _bshCore, + string memory _serviceName + ) public initializer { + bmc = IBMCPeriphery(_bmc); + bshCore = IBSHCore(_bshCore); + serviceName = _serviceName; + } + + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view override returns (bool) { + return numOfPendingRequests != 0; + } + + function sendServiceMessage( + address _from, + string memory _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external override onlyBSHCore{ + // Send Service Message to BMC + // If '_to' address is an invalid BTP Address format + // VM throws an error and revert(). Thus, it does not need + // a try_catch at this point + (string memory _toNetwork, string memory _toAddress) = + _to.splitBTPAddress(); + Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); + Types.AssetTransferDetail[] memory _assetDetails = + new Types.AssetTransferDetail[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + _assets[i] = Types.Asset(_coinNames[i], _values[i]); + _assetDetails[i] = Types.AssetTransferDetail( + _coinNames[i], + _values[i], + _fees[i] + ); + } + + serialNo++; + + // Because `stack is too deep`, must create `_strFrom` to waive this error + // `_strFrom` is a string type of an address `_from` + string memory _strFrom = _from.toString(); + bmc.sendMessage( + _toNetwork, + serviceName, + serialNo, + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types + .TransferCoin(_strFrom, _toAddress, _assets) + .encodeTransferCoinMsg() + ) + .encodeServiceMessage() + ); + // Push pending tx into Record list + requests[serialNo] = Types.PendingTransferCoin( + _strFrom, + _to, + _coinNames, + _values, + _fees + ); + numOfPendingRequests++; + emit TransferStart(_from, _to, serialNo, _assetDetails); + } + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); + string memory errMsg; + + if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { + Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); + // checking receiving address whether is a valid address + // revert() if not a valid one + try this.checkParseAddress(_tc.to) { + try this.handleRequestService(_tc.to, _tc.assets) { + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + "", + RC_OK + ); + return; + } catch Error(string memory _err) { + errMsg = _err; + } + } catch { + errMsg = "InvalidAddress"; + } + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + errMsg, + RC_ERR + ); + } else if ( + _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE + ) { + // Check whether '_sn' is pending state + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + Types.Response memory response = _sm.data.decodeResponse(); + // @dev Not implement try_catch at this point + // + If RESPONSE_REQUEST_SERVICE: + // If RC_ERR, BSHCore proceeds a refund. If a refund is failed, BSHCore issues refundable Balance + // If RC_OK: + // - requested coin = native -> update aggregation fee (likely no issue) + // - requested coin = wrapped coin -> BSHCore calls itself to burn its tokens and update aggregation fee (likely no issue) + // The only issue, which might happen, is BSHCore's token balance lower than burning amount + // If so, there might be something went wrong before + // + If RESPONSE_FEE_GATHERING + // If RC_ERR, BSHCore saves charged fees back to `aggregationFee` state mapping variable + // If RC_OK: do nothing + handleResponseService(_sn, response.code, response.message); + } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { + emit UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + sendResponseMessage( + Types.ServiceType.UNKNOWN_TYPE, + _from, + _sn, + "Unknown", + RC_ERR + ); + } + } + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata, /* _src */ + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + string memory _emitMsg = + string("errCode: ") + .concat(_code.toString()) + .concat(", errMsg: ") + .concat(_msg); + handleResponseService(_sn, RC_ERR, _emitMsg); + } + + function handleResponseService( + uint256 _sn, + uint256 _code, + string memory _msg + ) private { + address _caller = requests[_sn].from.parseAddress(); + uint256 loop = requests[_sn].coinNames.length; + for (uint256 i = 0; i < loop; i++) { + bshCore.handleResponseService( + _caller, + requests[_sn].coinNames[i], + requests[_sn].amounts[i], + requests[_sn].fees[i], + _code + ); + } + delete requests[_sn]; + numOfPendingRequests--; + emit TransferEnd(_caller, _sn, _code, _msg); + } + + /** + @notice Handle a list of minting/transferring coins/tokens + @dev Caller must be BMC contract only + @param _to An address to receive coins/tokens + @param _assets A list of requested coin respectively with an amount + */ + function handleRequestService( + string memory _to, + Types.Asset[] memory _assets + ) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint256 i = 0; i < _assets.length; i++) { + require( + bshCore.isValidCoin(_assets[i].coinName) == true, + "UnregisteredCoin" + ); + // @dev There might be many errors generating by BSHCore contract + // which includes also low-level error + // Thus, must use try_catch at this point so that it can return an expected response + try + bshCore.mint( + _to.parseAddress(), + _assets[i].coinName, + _assets[i].value + ) + {} catch { + revert("TransferFailed"); + } + } + } + + function sendResponseMessage( + Types.ServiceType _serviceType, + string memory _to, + uint256 _sn, + string memory _msg, + uint256 _code + ) private { + bmc.sendMessage( + _to, + serviceName, + _sn, + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage() + ); + } + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override + onlyBMC + { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + // If adress of Fee Aggregator (_fa) is invalid BTP address format + // revert(). Then, BMC will catch this error + // @dev this part simply check whether `_fa` is splittable (`prefix` + `_net` + `dstAddr`) + // checking validity of `_net` and `dstAddr` does not belong to BSHPeriphery's scope + _fa.splitBTPAddress(); + bshCore.transferFees(_fa); + } + + // @dev Solidity does not allow to use try_catch with internal function + // Thus, this is a work-around solution + // Since this function is basically checking whether a string address + // can be parsed to address type. Hence, it would not have any restrictions + function checkParseAddress(string calldata _to) external pure { + _to.parseAddress(); + } +} diff --git a/solidity/bsh/contracts/interfaces/IBMCPeriphery.sol b/solidity/bsh/contracts/interfaces/IBMCPeriphery.sol new file mode 100644 index 00000000..78787903 --- /dev/null +++ b/solidity/bsh/contracts/interfaces/IBMCPeriphery.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; + +interface IBMCPeriphery { + /** + @notice Get BMC BTP address + */ + function getBmcBtpAddress() external view returns (string memory); + + /** + @notice Verify and decode RelayMessage with BMV, and dispatch BTP Messages to registered BSHs + @dev Caller must be a registered relayer. + @param _prev BTP Address of the BMC generates the message + @param _msg base64 encoded string of serialized bytes of Relay Message refer RelayMessage structure + */ + function handleRelayMessage(string calldata _prev, string calldata _msg) + external; + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string calldata _to, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC + @return _linkStats The link status + */ + function getStatus(string calldata _link) + external + view + returns (Types.LinkStats memory _linkStats); +} diff --git a/solidity/bsh/contracts/interfaces/IBMV.sol b/solidity/bsh/contracts/interfaces/IBMV.sol new file mode 100644 index 00000000..e370af4e --- /dev/null +++ b/solidity/bsh/contracts/interfaces/IBMV.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBMV { + /** + @return base64EncodedMTA Base64 encode of Merkle Tree + */ + function getMTA() external view returns (string memory base64EncodedMTA); + + /** + @return addr connected BMC address + */ + function getConnectedBMC() external view returns (address addr); + + /** + @return net network address of the blockchain + */ + function getNetAddress() external view returns (string memory net); + + /** + @return serializedHash hash of RLP encode from given list of validators + @return addresses list of validators' addresses + */ + function getValidators() + external + view + returns (bytes32 serializedHash, address[] memory addresses); + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + returns ( + uint256 height, + uint256 offset, + uint256 lastHeight + ); + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string calldata _bmc, + string calldata _prev, + uint256 _seq, + string calldata _msg + ) external returns (bytes[] memory serializedMessages); +} diff --git a/solidity/bsh/contracts/interfaces/IBSH.sol b/solidity/bsh/contracts/interfaces/IBSH.sol new file mode 100644 index 00000000..26769f6e --- /dev/null +++ b/solidity/bsh/contracts/interfaces/IBSH.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBSH { + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external; + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external; +} diff --git a/solidity/bsh/contracts/interfaces/IBSHCore.sol b/solidity/bsh/contracts/interfaces/IBSHCore.sol new file mode 100644 index 00000000..118ec994 --- /dev/null +++ b/solidity/bsh/contracts/interfaces/IBSHCore.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol"; + +/** + @title Interface of BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following interface can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +interface IBSHCore is IERC1155Upgradeable, IERC1155ReceiverUpgradeable { + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external; + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external; + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view returns (bool); + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + + function getOwners() external view returns (address[] memory); + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) external; + + /** + @notice update base uri. + @dev Caller must be an Owner of this contract + the uri must be initilized in construction. + @param _newURI new uri + */ + function updateUri(string calldata _newURI) external; + + /** + @notice set fee ratio. + @dev Caller must be an Owner of this contract + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external; + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external; + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external; + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() external view returns (string[] memory _names); + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) + external + view + returns (uint256 _coinId); + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + returns (bool _valid); + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ); + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + returns ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ); + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + returns (Types.Asset[] memory _accumulatedFees); + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable; + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer. + @param _to Target BTP address. + */ + function transfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external; + + /** + @notice Allow users to transfer multiple coins/wrapped coins to another chain + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + In case of transferring a native coin, it also checks `msg.value` with `_values[i]` + It MUST revert if `msg.value` is not equal to `_values[i]` + The number of requested coins MUST be as the same as the number of requested values + The requested coins and values MUST be matched respectively + @param _coinNames A list of requested transferring coins/wrapped coins + @param _values A list of requested transferring values respectively with its coin name + @param _to Target BTP address. + */ + function transferBatch( + string[] memory _coinNames, + uint256[] memory _values, + string calldata _to + ) external payable; + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) external; + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external; + + /** + @notice Handle a request of Fee Gathering + @dev Caller must be an BSHPeriphery contract + @param _fa BTP Address of Fee Aggregator + */ + function transferFees(string calldata _fa) external; + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external; +} diff --git a/solidity/bsh/contracts/interfaces/IBSHPeriphery.sol b/solidity/bsh/contracts/interfaces/IBSHPeriphery.sol new file mode 100644 index 00000000..7fbe4534 --- /dev/null +++ b/solidity/bsh/contracts/interfaces/IBSHPeriphery.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./IBSH.sol"; + +/** + @title Interface of BSHPeriphery contract + @dev This contract is used to handle communications among BMCService and BSHCore contract +*/ +interface IBSHPeriphery is IBSH { + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view returns (bool); + + /** + @notice Send Service Message from BSHCore contract to BMCService contract + @dev Caller must be BSHCore only + @param _to A network address of destination chain + @param _coinNames A list of coin name that are requested to transfer + @param _values A list of an amount to receive at destination chain respectively with its coin name + @param _fees A list of an amount of charging fee respectively with its coin name + */ + function sendServiceMessage( + address _from, + string calldata _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external; + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override; + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override; + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override; +} diff --git a/solidity/bsh/contracts/libraries/DecodeBase64.sol b/solidity/bsh/contracts/libraries/DecodeBase64.sol new file mode 100644 index 00000000..283540d5 --- /dev/null +++ b/solidity/bsh/contracts/libraries/DecodeBase64.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title DecodeBase64 + * @dev A simple Base64 decoding library. + * @author Quang Tran + */ + +library DecodeBase64 { + function decode(string memory _str) internal pure returns (bytes memory) { + bytes memory _bs = bytes(_str); + uint256 remove = 0; + if (_bs[_bs.length - 1] == "=" && _bs[_bs.length - 2] == "=") { + remove += 2; + } else if (_bs[_bs.length - 1] == "=") { + remove++; + } + uint256 resultLength = (_bs.length / 4) * 3 - remove; + bytes memory result = new bytes(resultLength); + + uint256 i = 0; + uint256 j = 0; + for (; i + 4 < _bs.length; i += 4) { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[i]), + mapBase64Char(_bs[i + 1]), + mapBase64Char(_bs[i + 2]), + mapBase64Char(_bs[i + 3]) + ); + j += 3; + } + if (remove == 1) { + (result[j], result[j + 1], ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + 0 + ); + } else if (remove == 2) { + (result[j], , ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + 0, + 0 + ); + } else { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + mapBase64Char(_bs[_bs.length - 1]) + ); + } + return result; + } + + function mapBase64Char(bytes1 _char) private pure returns (uint8) { + // solhint-disable-next-line + uint8 A = 0; + uint8 a = 26; + uint8 zero = 52; + if (uint8(_char) == 45) { + return 62; + } else if (uint8(_char) == 95) { + return 63; + } else if (uint8(_char) >= 48 && uint8(_char) <= 57) { + return zero + (uint8(_char) - 48); + } else if (uint8(_char) >= 65 && uint8(_char) <= 90) { + return A + (uint8(_char) - 65); + } else if (uint8(_char) >= 97 && uint8(_char) <= 122) { + return a + (uint8(_char) - 97); + } + return 0; + } + + function decode4( + uint256 a0, + uint256 a1, + uint256 a2, + uint256 a3 + ) + private + pure + returns ( + bytes1, + bytes1, + bytes1 + ) + { + uint256 n = + ((a0 & 63) << 18) | + ((a1 & 63) << 12) | + ((a2 & 63) << 6) | + (a3 & 63); + uint256 b0 = (n >> 16) & 255; + uint256 b1 = (n >> 8) & 255; + uint256 b2 = (n) & 255; + return (bytes1(uint8(b0)), bytes1(uint8(b1)), bytes1(uint8(b2))); + } +} diff --git a/solidity/bsh/contracts/libraries/EncodeBase64.sol b/solidity/bsh/contracts/libraries/EncodeBase64.sol new file mode 100644 index 00000000..5a09250f --- /dev/null +++ b/solidity/bsh/contracts/libraries/EncodeBase64.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title EncodeBase64 + * @dev A simple Base64 encoding library. + * The original library was modified to make it work for our case + * For more info, please check the link: https://github.com/OpenZeppelin/solidity-jwt.git + */ +library EncodeBase64 { + bytes private constant BASE64URLCHARS = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + function encode(bytes memory _bs) internal pure returns (string memory) { + // bytes memory _bs = bytes(_str); + uint256 rem = _bs.length % 3; + uint256 resLength; + if (_bs.length % 3 != 0) { + resLength = (_bs.length / 3) * 4 + 4; + } else { + resLength = (_bs.length / 3) * 4; + } + + // uint256 res_length = (_bs.length + 2) / 3 * 4 - ((3 - rem) % 3); + bytes memory res = new bytes(resLength); + uint256 i = 0; + uint256 j = 0; + + for (; i + 3 <= _bs.length; i += 3) { + (res[j], res[j + 1], res[j + 2], res[j + 3]) = encode3( + uint8(_bs[i]), + uint8(_bs[i + 1]), + uint8(_bs[i + 2]) + ); + j += 4; + } + + if (rem != 0) { + uint8 la0 = uint8(_bs[_bs.length - rem]); + uint8 la1 = 0; + if (rem == 2) { + la1 = uint8(_bs[_bs.length - 1]); + } + (bytes1 b0, bytes1 b1, bytes1 b2, ) = encode3(la0, la1, 0); + res[j] = b0; + res[j + 1] = b1; + if (rem == 1) { + res[j + 2] = "="; + res[j + 3] = "="; + } else if (rem == 2) { + res[j + 2] = b2; + res[j + 3] = "="; + } + } + return string(res); + } + + function encode3( + uint256 a0, + uint256 a1, + uint256 a2 + ) + private + pure + returns ( + bytes1 b0, + bytes1 b1, + bytes1 b2, + bytes1 b3 + ) + { + uint256 n = (a0 << 16) | (a1 << 8) | a2; + uint256 c0 = (n >> 18) & 63; + uint256 c1 = (n >> 12) & 63; + uint256 c2 = (n >> 6) & 63; + uint256 c3 = (n) & 63; + b0 = BASE64URLCHARS[c0]; + b1 = BASE64URLCHARS[c1]; + b2 = BASE64URLCHARS[c2]; + b3 = BASE64URLCHARS[c3]; + } +} diff --git a/solidity/bsh/contracts/libraries/ParseAddress.sol b/solidity/bsh/contracts/libraries/ParseAddress.sol new file mode 100644 index 00000000..7a9d9cd3 --- /dev/null +++ b/solidity/bsh/contracts/libraries/ParseAddress.sol @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Utility library of inline functions on addresses + */ +library ParseAddress { + function parseAddress(string memory account) + internal + pure + returns (address accountAddress) + { + bytes memory accountBytes = bytes(account); + require( + accountBytes.length == 42 && + accountBytes[0] == bytes1("0") && + accountBytes[1] == bytes1("x"), + "Invalid address format" + ); + + // create a new fixed-size byte array for the ascii bytes of the address. + bytes memory accountAddressBytes = new bytes(20); + + // declare variable types. + uint8 b; + uint8 nibble; + uint8 asciiOffset; + + for (uint256 i = 0; i < 40; i++) { + // get the byte in question. + b = uint8(accountBytes[i + 2]); + + bool isValidASCII = true; + // ensure that the byte is a valid ascii character (0-9, A-F, a-f) + if (b < 48) isValidASCII = false; + if (57 < b && b < 65) isValidASCII = false; + if (70 < b && b < 97) isValidASCII = false; + if (102 < b) isValidASCII = false; //bytes(hex""); + + // If string contains invalid ASCII characters, revert() + if (!isValidASCII) revert("Invalid address"); + + // find the offset from ascii encoding to the nibble representation. + if (b < 65) { + // 0-9 + asciiOffset = 48; + } else if (70 < b) { + // a-f + asciiOffset = 87; + } else { + // A-F + asciiOffset = 55; + } + + // store left nibble on even iterations, then store byte on odd ones. + if (i % 2 == 0) { + nibble = b - asciiOffset; + } else { + accountAddressBytes[(i - 1) / 2] = ( + byte(16 * nibble + (b - asciiOffset)) + ); + } + } + + // pack up the fixed-size byte array and cast it to accountAddress. + bytes memory packed = abi.encodePacked(accountAddressBytes); + assembly { + accountAddress := mload(add(packed, 20)) + } + + // return false in the event the account conversion returned null address. + if (accountAddress == address(0)) { + // ensure that provided address is not also the null address first. + for (uint256 i = 2; i < accountBytes.length; i++) + require(accountBytes[i] == hex"30", "Invalid address"); + } + + // get the capitalized characters in the actual checksum. + string memory actual = _toChecksumString(accountAddress); + + // compare provided string to actual checksum string to test for validity. + require( + keccak256(abi.encodePacked(actual)) == + keccak256(abi.encodePacked(account)), + "Invalid checksum" + ); + } + + /** + * @dev Get a checksummed string hex representation of an account address. + * @param account address The account to get the checksum for. + * @return The checksummed account string in ASCII format. Note that leading + * "0x" is not included. + */ + function toString(address account) internal pure returns (string memory) { + // call internal function for converting an account to a checksummed string. + return _toChecksumString(account); + } + + /** + * @dev Get a fixed-size array of whether or not each character in an account + * will be capitalized in the checksum. + * @param account address The account to get the checksum capitalization + * information for. + * @return A fixed-size array of booleans that signify if each character or + * "nibble" of the hex encoding of the address will be capitalized by the + * checksum. + */ + function getChecksumCapitalizedCharacters(address account) + internal + pure + returns (bool[40] memory) + { + // call internal function for computing characters capitalized in checksum. + return _toChecksumCapsFlags(account); + } + + /** + * @dev Determine whether a string hex representation of an account address + * matches the ERC-55 checksum of that address. + * @param accountChecksum string The checksummed account string in ASCII + * format. Note that a leading "0x" MUST NOT be included. + * @return A boolean signifying whether or not the checksum is valid. + */ + function isChecksumValid(string calldata accountChecksum) + internal + pure + returns (bool) + { + // call internal function for validating checksum strings. + return _isChecksumValid(accountChecksum); + } + + function _toChecksumString(address account) + internal + pure + returns (string memory asciiString) + { + // convert the account argument from address to bytes. + bytes20 data = bytes20(account); + + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + bool leftCaps; + bool rightCaps; + uint8 asciiOffset; + + // get the capitalized characters in the actual checksum. + bool[40] memory caps = _toChecksumCapsFlags(account); + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // locate and extract each capitalization status. + leftCaps = caps[2 * i]; + rightCaps = caps[2 * i + 1]; + + // get the offset from nibble value to ascii character for left nibble. + asciiOffset = _getAsciiOffset(leftNibble, leftCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i] = byte(leftNibble + asciiOffset); + + // get the offset from nibble value to ascii character for right nibble. + asciiOffset = _getAsciiOffset(rightNibble, rightCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i + 1] = byte(rightNibble + asciiOffset); + } + + return string(abi.encodePacked("0x", string(asciiBytes))); + } + + function _toChecksumCapsFlags(address account) + internal + pure + returns (bool[40] memory characterCapitalized) + { + // convert the address to bytes. + bytes20 a = bytes20(account); + + // hash the address (used to calculate checksum). + bytes32 b = keccak256(abi.encodePacked(_toAsciiString(a))); + + // declare variable types. + uint8 leftNibbleAddress; + uint8 rightNibbleAddress; + uint8 leftNibbleHash; + uint8 rightNibbleHash; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i; i < a.length; i++) { + // locate the byte and extract each nibble for the address and the hash. + rightNibbleAddress = uint8(a[i]) % 16; + leftNibbleAddress = (uint8(a[i]) - rightNibbleAddress) / 16; + rightNibbleHash = uint8(b[i]) % 16; + leftNibbleHash = (uint8(b[i]) - rightNibbleHash) / 16; + + characterCapitalized[2 * i] = (leftNibbleAddress > 9 && + leftNibbleHash > 7); + characterCapitalized[2 * i + 1] = (rightNibbleAddress > 9 && + rightNibbleHash > 7); + } + } + + function _isChecksumValid(string memory provided) + internal + pure + returns (bool ok) + { + // convert the provided string into account type. + address account = _toAddress(provided); + + // return false in the event the account conversion returned null address. + if (account == address(0)) { + // ensure that provided address is not also the null address first. + bytes memory b = bytes(provided); + for (uint256 i; i < b.length; i++) { + if (b[i] != hex"30") { + return false; + } + } + } + + // get the capitalized characters in the actual checksum. + string memory actual = _toChecksumString(account); + + // compare provided string to actual checksum string to test for validity. + return (keccak256(abi.encodePacked(actual)) == + keccak256(abi.encodePacked(provided))); + } + + function _getAsciiOffset(uint8 nibble, bool caps) + internal + pure + returns (uint8 offset) + { + // to convert to ascii characters, add 48 to 0-9, 55 to A-F, & 87 to a-f. + if (nibble < 10) { + offset = 48; + } else if (caps) { + offset = 55; + } else { + offset = 87; + } + } + + function _toAddress(string memory account) + internal + pure + returns (address accountAddress) + { + // convert the account argument from address to bytes. + bytes memory accountBytes = bytes(account); + + // create a new fixed-size byte array for the ascii bytes of the address. + bytes memory accountAddressBytes = new bytes(20); + + // declare variable types. + uint8 b; + uint8 nibble; + uint8 asciiOffset; + + // only proceed if the provided string has a length of 40. + if (accountBytes.length == 40) { + for (uint256 i; i < 40; i++) { + // get the byte in question. + b = uint8(accountBytes[i]); + + // ensure that the byte is a valid ascii character (0-9, A-F, a-f) + if (b < 48) return address(0); + if (57 < b && b < 65) return address(0); + if (70 < b && b < 97) return address(0); + if (102 < b) return address(0); //bytes(hex""); + + // find the offset from ascii encoding to the nibble representation. + if (b < 65) { + // 0-9 + asciiOffset = 48; + } else if (70 < b) { + // a-f + asciiOffset = 87; + } else { + // A-F + asciiOffset = 55; + } + + // store left nibble on even iterations, then store byte on odd ones. + if (i % 2 == 0) { + nibble = b - asciiOffset; + } else { + accountAddressBytes[(i - 1) / 2] = ( + byte(16 * nibble + (b - asciiOffset)) + ); + } + } + + // pack up the fixed-size byte array and cast it to accountAddress. + bytes memory packed = abi.encodePacked(accountAddressBytes); + assembly { + accountAddress := mload(add(packed, 20)) + } + } + } + + // based on https://ethereum.stackexchange.com/a/56499/48410 + function _toAsciiString(bytes20 data) + internal + pure + returns (string memory asciiString) + { + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // to convert to ascii characters, add 48 to 0-9 and 87 to a-f. + asciiBytes[2 * i] = byte(leftNibble + (leftNibble < 10 ? 48 : 87)); + asciiBytes[2 * i + 1] = byte( + rightNibble + (rightNibble < 10 ? 48 : 87) + ); + } + + return string(asciiBytes); + } +} diff --git a/solidity/bsh/contracts/libraries/RLPDecode.sol b/solidity/bsh/contracts/libraries/RLPDecode.sol new file mode 100644 index 00000000..a7764aa9 --- /dev/null +++ b/solidity/bsh/contracts/libraries/RLPDecode.sol @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Change supporting solidity compiler version + * The original code can be found via this link: https://github.com/hamdiallam/Solidity-RLP.git + */ + +library RLPDecode { + uint8 private constant STRING_SHORT_START = 0x80; + uint8 private constant STRING_LONG_START = 0xb8; + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf8; + uint8 private constant WORD_SIZE = 32; + + struct RLPItem { + uint256 len; + uint256 memPtr; + } + + struct Iterator { + RLPItem item; // Item that's being iterated over. + uint256 nextPtr; // Position of the next item in the list. + } + + /* + * @dev Returns the next element in the iteration. Reverts if it has not next element. + * @param self The iterator. + * @return The next element in the iteration. + */ + function next(Iterator memory self) internal pure returns (RLPItem memory) { + require(hasNext(self), "Must have next elements"); + + uint256 ptr = self.nextPtr; + uint256 itemLength = _itemLength(ptr); + self.nextPtr = ptr + itemLength; + + return RLPItem(itemLength, ptr); + } + + /* + * @dev Returns true if the iteration has more elements. + * @param self The iterator. + * @return true if the iteration has more elements. + */ + function hasNext(Iterator memory self) internal pure returns (bool) { + RLPItem memory item = self.item; + return self.nextPtr < item.memPtr + item.len; + } + + /* + * @param item RLP encoded bytes + */ + function toRlpItem(bytes memory item) + internal + pure + returns (RLPItem memory) + { + uint256 memPtr; + assembly { + memPtr := add(item, 0x20) + } + + return RLPItem(item.length, memPtr); + } + + /* + * @dev Create an iterator. Reverts if item is not a list. + * @param self The RLP item. + * @return An 'Iterator' over the item. + */ + function iterator(RLPItem memory self) + internal + pure + returns (Iterator memory) + { + require(isList(self), "Must be a list"); + + uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); + return Iterator(self, ptr); + } + + /* + * @param item RLP encoded bytes + */ + function rlpLen(RLPItem memory item) internal pure returns (uint256) { + return item.len; + } + + /* + * @param item RLP encoded bytes + */ + function payloadLen(RLPItem memory item) internal pure returns (uint256) { + return item.len - _payloadOffset(item.memPtr); + } + + /* + * @param item RLP encoded list in bytes + */ + function toList(RLPItem memory item) + internal + pure + returns (RLPItem[] memory) + { + require(isList(item), "Must be a list"); + + uint256 items = numItems(item); + RLPItem[] memory result = new RLPItem[](items); + + uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 dataLen; + for (uint256 i = 0; i < items; i++) { + dataLen = _itemLength(memPtr); + result[i] = RLPItem(dataLen, memPtr); + memPtr = memPtr + dataLen; + } + + return result; + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.len == 0) return false; + + uint8 byte0; + uint256 memPtr = item.memPtr; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /** RLPItem conversions into data types **/ + + // @returns raw rlp encoding in bytes + function toRlpBytes(RLPItem memory item) + internal + pure + returns (bytes memory) + { + bytes memory result = new bytes(item.len); + if (result.length == 0) return result; + + uint256 ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.memPtr, ptr, item.len); + return result; + } + + // any non-zero byte except "0x80" is considered true + function toBoolean(RLPItem memory item) internal pure returns (bool) { + require(item.len == 1, "Must have length 1"); + uint256 result; + uint256 memPtr = item.memPtr; + assembly { + result := byte(0, mload(memPtr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function toAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + require(item.len == 21, "Must have length 21"); + + return address(uint160(toUint(item))); + } + + function toUint(RLPItem memory item) internal pure returns (uint256) { + require(item.len > 0 && item.len <= 33, "Invalid uint number"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; + + uint256 result; + uint256 memPtr = item.memPtr + offset; + assembly { + result := mload(memPtr) + + // shfit to the correct location if neccesary + if lt(len, 32) { + result := div(result, exp(256, sub(32, len))) + } + } + + return result; + } + + function toInt(RLPItem memory item) internal pure returns (int256) { + if ((toBytes(item)[0] & 0x80) == 0x80) { + return int256(toUint(item) - 2**(toBytes(item).length * 8)); + } + + return int256(toUint(item)); + } + + // enforces 32 byte length + function toUintStrict(RLPItem memory item) internal pure returns (uint256) { + // one byte prefix + require(item.len == 33, "Must have length 33"); + + uint256 result; + uint256 memPtr = item.memPtr + 1; + assembly { + result := mload(memPtr) + } + + return result; + } + + function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + require(item.len > 0, "Invalid length"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; // data length + bytes memory result = new bytes(len); + + uint256 destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(item.memPtr + offset, destPtr, len); + return result; + } + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) private pure returns (uint256) { + if (item.len == 0) return 0; + + uint256 count = 0; + uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 endPtr = item.memPtr + item.len; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint256 memPtr) private pure returns (uint256) { + uint256 itemLen; + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) itemLen = 1; + else if (byte0 < STRING_LONG_START) + itemLen = byte0 - STRING_SHORT_START + 1; + else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + memPtr := add(memPtr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + memPtr := add(memPtr, 1) + + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint256 memPtr) private pure returns (uint256) { + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) return 0; + else if ( + byte0 < STRING_LONG_START || + (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START) + ) return 1; + else if (byte0 < LIST_SHORT_START) + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + else return byte0 - (LIST_LONG_START - 1) + 1; + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param len Amount of memory to copy from the source + */ + function copy( + uint256 src, + uint256 dest, + uint256 len + ) private pure { + if (len == 0) return; + + // copy as many word sizes as possible + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + // left over bytes. Mask is used to remove unwanted bytes from the word + uint256 mask = 256**(WORD_SIZE - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } +} diff --git a/solidity/bsh/contracts/libraries/RLPDecodeStruct.sol b/solidity/bsh/contracts/libraries/RLPDecodeStruct.sol new file mode 100644 index 00000000..4ddf2029 --- /dev/null +++ b/solidity/bsh/contracts/libraries/RLPDecodeStruct.sol @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./Types.sol"; + +//import "./RLPEncode.sol"; + +library RLPDecodeStruct { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using RLPDecode for bytes; + + using RLPDecodeStruct for bytes; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function decodeBMCService(bytes memory _rlp) + internal + pure + returns (Types.BMCService memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCService( + string(ls[0].toBytes()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeGatherFeeMessage(bytes memory _rlp) + internal + pure + returns (Types.GatherFeeMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory subList = ls[1].toList(); + string[] memory _svcs = new string[](subList.length); + for (uint256 i = 0; i < subList.length; i++) { + _svcs[i] = string(subList[i].toBytes()); + } + return Types.GatherFeeMessage(string(ls[0].toBytes()), _svcs); + } + + function decodeEventMessage(bytes memory _rlp) + internal + pure + returns (Types.EventMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.EventMessage( + string(ls[0].toBytes()), + Types.Connection( + string(ls[1].toList()[0].toBytes()), + string(ls[1].toList()[1].toBytes()) + ) + ); + } + + function decodeCoinRegister(bytes memory _rlp) + internal + pure + returns (string[] memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + string[] memory _coins = new string[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + _coins[i] = string(ls[i].toBytes()); + } + return _coins; + } + + function decodeBMCMessage(bytes memory _rlp) + internal + pure + returns (Types.BMCMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCMessage( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + string(ls[2].toBytes()), + ls[3].toInt(), + ls[4].toBytes() // bytes array of RLPEncode(ServiceMessage) + ); + } + + function decodeServiceMessage(bytes memory _rlp) + internal + pure + returns (Types.ServiceMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.ServiceMessage( + Types.ServiceType(ls[0].toUint()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeTransferCoinMsg(bytes memory _rlp) + internal + pure + returns (Types.TransferCoin memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + Types.Asset[] memory assets = new Types.Asset[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpAssets = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + assets[i] = Types.Asset( + string(rlpAssets[i].toList()[0].toBytes()), + rlpAssets[i].toList()[1].toUint() + ); + } + return + Types.TransferCoin( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + assets + ); + } + + function decodeResponse(bytes memory _rlp) + internal + pure + returns (Types.Response memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return Types.Response(ls[0].toUint(), string(ls[1].toBytes())); + } + + function decodeBlockHeader(bytes memory _rlp) + internal + pure + returns (Types.BlockHeader memory) + { + // Decode RLP bytes into a list of items + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bool isSPREmpty = true; + if (ls[10].toBytes().length == 0) { + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR("", "", ""), + isSPREmpty + ); + } + RLPDecode.RLPItem[] memory subList = + ls[10].toBytes().toRlpItem().toList(); + isSPREmpty = false; + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR( + subList[0].toBytes(), + subList[1].toBytes(), + subList[2].toBytes() + ), + isSPREmpty + ); + } + + // Votes item consists of: + // round as integer + // blockPartSetID is a list that consists of two items - integer and bytes + // and TS[] ts_list (an array of list) + function decodeVotes(bytes memory _rlp) + internal + pure + returns (Types.Votes memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.TS[] memory tsList = new Types.TS[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpTs = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + tsList[i] = Types.TS( + rlpTs[i].toList()[0].toUint(), + rlpTs[i].toList()[1].toBytes() + ); + } + return + Types.Votes( + ls[0].toUint(), + Types.BPSI( + ls[1].toList()[0].toUint(), + ls[1].toList()[1].toBytes() + ), + tsList + ); + } + + // Wait for confirmation + function decodeBlockWitness(bytes memory _rlp) + internal + pure + returns (Types.BlockWitness memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + bytes[] memory witnesses = new bytes[](ls[1].toList().length); + // witnesses is an array of hash of leaf node + // The array size may also vary, thus loop is needed therein + for (uint256 i = 0; i < ls[1].toList().length; i++) { + witnesses[i] = ls[1].toList()[i].toBytes(); + } + return Types.BlockWitness(ls[0].toUint(), witnesses); + } + + function decodeEventProof(bytes memory _rlp) + internal + pure + returns (Types.EventProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory data = ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory eventMptNode = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + eventMptNode[i] = data[i].toBytes(); + } + return Types.EventProof(ls[0].toUint(), eventMptNode); + } + + function decodeBlockUpdate(bytes memory _rlp) + internal + pure + returns (Types.BlockUpdate memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + // Types.BlockHeader memory _bh; + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.Votes memory _v = ls[1].toBytes().decodeVotes(); + // Types.Votes memory _v; + + // BlockUpdate may or may not include the RLP of addresses of validators + // In that case, RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START == 0xF800 + // Thus, length of data will be 0. Therein, loop will be skipped + // and the _validators[] will be empty + // Otherwise, executing normally to read and assign value into the array _validators[] + bytes[] memory _validators; + if (ls[2].toBytes().length != 0) { + _validators = new bytes[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _validators[i] = ls[2].toList()[i].toBytes(); + } + } + return Types.BlockUpdate(_bh, _v, _validators); + } + + function decodeReceiptProof(bytes memory _rlp) + internal + pure + returns (Types.ReceiptProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory receiptList = + ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory txReceipts = new bytes[](receiptList.length); + for (uint256 i = 0; i < receiptList.length; i++) { + txReceipts[i] = receiptList[i].toBytes(); + } + + Types.EventProof[] memory _ep = + new Types.EventProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _ep[i] = Types.EventProof( + ls[2].toList()[i].toList()[0].toUint(), + ls[2].toList()[i].toList()[1].toBytes().decodeEventLog() + ); + } + + return Types.ReceiptProof(ls[0].toUint(), txReceipts, _ep); + } + + function decodeEventLog(bytes memory _rlp) + internal + pure + returns (bytes[] memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bytes[] memory eventMptNode = new bytes[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + eventMptNode[i] = ls[i].toBytes(); + } + return eventMptNode; + } + + function decodeBlockProof(bytes memory _rlp) + internal + pure + returns (Types.BlockProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.BlockWitness memory _bw = ls[1].toBytes().decodeBlockWitness(); + + return Types.BlockProof(_bh, _bw); + } + + function decodeRelayMessage(bytes memory _rlp) + internal + pure + returns (Types.RelayMessage memory) + { + // _rlp.toRlpItem() removes the LIST_HEAD_START of RelayMessage + // then .toList() to itemize all fields in the RelayMessage + // which are [RLP_ENCODE(BlockUpdate)], RLP_ENCODE(BlockProof), and + // the RLP_ENCODE(ReceiptProof) + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + // return ( + // ls[0].toList()[0].toBytes().toRlpItem().toList()[1].toBytes().toRlpItem().toList()[2].toList()[0].toList()[1].toBytes() + // ); + + // If [RLP_ENCODE(BlockUpdate)] was empty, it should be started by 0xF800 + // therein, ls[0].toBytes() will be null (length = 0) + // Otherwise, create an array of BlockUpdate struct to decode + Types.BlockUpdate[] memory _buArray; + if (ls[0].toBytes().length != 0) { + _buArray = new Types.BlockUpdate[](ls[0].toList().length); + for (uint256 i = 0; i < ls[0].toList().length; i++) { + // Each of items inside an array [RLP_ENCODE(BlockUpdate)] + // is a string which defines RLP_ENCODE(BlockUpdate) + // that contains a LIST_HEAD_START and multiple RLP of data + // ls[0].toList()[i].toBytes() returns bytes presentation of + // RLP_ENCODE(BlockUpdate) + _buArray[i] = ls[0].toList()[i].toBytes().decodeBlockUpdate(); + } + } + bool isBPEmpty = true; + Types.BlockProof memory _bp; + // If RLP_ENCODE(BlockProof) is omitted, + // ls[1].toBytes() should be null (length = 0) + if (ls[1].toBytes().length != 0) { + _bp = ls[1].toBytes().decodeBlockProof(); + isBPEmpty = false; // add this field into RelayMessage + // to specify whether BlockProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + + bool isRPEmpty = true; + Types.ReceiptProof[] memory _rp; + // If [RLP_ENCODE(ReceiptProof)] is omitted, + // ls[2].toBytes() should be null (length = 0) + if (ls[2].toBytes().length != 0) { + _rp = new Types.ReceiptProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _rp[i] = ls[2].toList()[i].toBytes().decodeReceiptProof(); + } + isRPEmpty = false; // add this field into RelayMessage + // to specify whether ReceiptProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + return Types.RelayMessage(_buArray, _bp, isBPEmpty, _rp, isRPEmpty); + } +} diff --git a/solidity/bsh/contracts/libraries/RLPEncode.sol b/solidity/bsh/contracts/libraries/RLPEncode.sol new file mode 100644 index 00000000..a7bb08db --- /dev/null +++ b/solidity/bsh/contracts/libraries/RLPEncode.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title RLPEncode + * @dev A simple RLP encoding library. + * @author Bakaoh + * The original code was modified. For more info, please check the link: + * https://github.com/bakaoh/solidity-rlp-encode.git + */ +library RLPEncode { + int8 internal constant MAX_INT8 = type(int8).max; + int16 internal constant MAX_INT16 = type(int16).max; + int24 internal constant MAX_INT24 = type(int24).max; + int32 internal constant MAX_INT32 = type(int32).max; + int40 internal constant MAX_INT40 = type(int40).max; + int48 internal constant MAX_INT48 = type(int48).max; + int56 internal constant MAX_INT56 = type(int56).max; + int64 internal constant MAX_INT64 = type(int64).max; + int72 internal constant MAX_INT72 = type(int72).max; + int80 internal constant MAX_INT80 = type(int80).max; + int88 internal constant MAX_INT88 = type(int88).max; + int96 internal constant MAX_INT96 = type(int96).max; + int104 internal constant MAX_INT104 = type(int104).max; + int112 internal constant MAX_INT112 = type(int112).max; + int120 internal constant MAX_INT120 = type(int120).max; + int128 internal constant MAX_INT128 = type(int128).max; + + uint8 internal constant MAX_UINT8 = type(uint8).max; + uint16 internal constant MAX_UINT16 = type(uint16).max; + uint24 internal constant MAX_UINT24 = type(uint24).max; + uint32 internal constant MAX_UINT32 = type(uint32).max; + uint40 internal constant MAX_UINT40 = type(uint40).max; + uint48 internal constant MAX_UINT48 = type(uint48).max; + uint56 internal constant MAX_UINT56 = type(uint56).max; + uint64 internal constant MAX_UINT64 = type(uint64).max; + uint72 internal constant MAX_UINT72 = type(uint72).max; + uint80 internal constant MAX_UINT80 = type(uint80).max; + uint88 internal constant MAX_UINT88 = type(uint88).max; + uint96 internal constant MAX_UINT96 = type(uint96).max; + uint104 internal constant MAX_UINT104 = type(uint104).max; + uint112 internal constant MAX_UINT112 = type(uint112).max; + uint120 internal constant MAX_UINT120 = type(uint120).max; + uint128 internal constant MAX_UINT128 = type(uint128).max; + + /* + * Internal functions + */ + + /** + * @dev RLP encodes a byte string. + * @param self The byte string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeBytes(bytes memory self) + internal + pure + returns (bytes memory) + { + bytes memory encoded; + if (self.length == 1 && uint8(self[0]) <= 128) { + encoded = self; + } else { + encoded = concat(encodeLength(self.length, 128), self); + } + return encoded; + } + + /** + * @dev RLP encodes a list of RLP encoded byte byte strings. + * @param self The list of RLP encoded byte strings. + * @return The RLP encoded list of items in bytes. + */ + function encodeList(bytes[] memory self) + internal + pure + returns (bytes memory) + { + bytes memory list = flatten(self); + return concat(encodeLength(list.length, 192), list); + } + + /** + * @dev RLP encodes a string. + * @param self The string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeString(string memory self) + internal + pure + returns (bytes memory) + { + return encodeBytes(bytes(self)); + } + + /** + * @dev RLP encodes an address. + * @param self The address to encode. + * @return The RLP encoded address in bytes. + */ + function encodeAddress(address self) internal pure returns (bytes memory) { + bytes memory inputBytes; + assembly { + let m := mload(0x40) + mstore( + add(m, 20), + xor(0x140000000000000000000000000000000000000000, self) + ) + mstore(0x40, add(m, 52)) + inputBytes := m + } + return encodeBytes(inputBytes); + } + + /** + * @dev RLP encodes a uint. + * @param self The uint to encode. + * @return The RLP encoded uint in bytes. + */ + function encodeUint(uint256 self) internal pure returns (bytes memory) { + uint256 nBytes = bitLength(self) / 8 + 1; + bytes memory uintBytes = encodeUintByLength(self); + if (nBytes - uintBytes.length > 0) { + uintBytes = abi.encodePacked(bytes1(0), uintBytes); + } + return encodeBytes(uintBytes); + } + + /** + * @dev convert int to strict bytes. + * @notice only handle to int128 due to contract code size limit + * @param n The int to convert. + * @return The int in strict bytes without padding. + */ + function intToStrictBytes(int256 n) internal pure returns (bytes memory) { + if (-MAX_INT8 - 1 <= n && n <= MAX_INT8) { + return abi.encodePacked(int8(n)); + } else if (-MAX_INT16 - 1 <= n && n <= MAX_INT16) { + return abi.encodePacked(int16(n)); + } else if (-MAX_INT24 - 1 <= n && n <= MAX_INT24) { + return abi.encodePacked(int24(n)); + } else if (-MAX_INT32 - 1 <= n && n <= MAX_INT32) { + return abi.encodePacked(int32(n)); + } else if (-MAX_INT40 - 1 <= n && n <= MAX_INT40) { + return abi.encodePacked(int40(n)); + } else if (-MAX_INT48 - 1 <= n && n <= MAX_INT48) { + return abi.encodePacked(int48(n)); + } else if (-MAX_INT56 - 1 <= n && n <= MAX_INT56) { + return abi.encodePacked(int56(n)); + } else if (-MAX_INT64 - 1 <= n && n <= MAX_INT64) { + return abi.encodePacked(int64(n)); + } else if (-MAX_INT72 - 1 <= n && n <= MAX_INT72) { + return abi.encodePacked(int72(n)); + } else if (-MAX_INT80 - 1 <= n && n <= MAX_INT80) { + return abi.encodePacked(int80(n)); + } else if (-MAX_INT88 - 1 <= n && n <= MAX_INT88) { + return abi.encodePacked(int88(n)); + } else if (-MAX_INT96 - 1 <= n && n <= MAX_INT96) { + return abi.encodePacked(int96(n)); + } else if (-MAX_INT104 - 1 <= n && n <= MAX_INT104) { + return abi.encodePacked(int104(n)); + } else if (-MAX_INT112 - 1 <= n && n <= MAX_INT112) { + return abi.encodePacked(int112(n)); + } else if (-MAX_INT120 - 1 <= n && n <= MAX_INT120) { + return abi.encodePacked(int120(n)); + } + require( + -MAX_INT128 - 1 <= n && n <= MAX_INT128, + "outOfBounds: [-2^128-1, 2^128]" + ); + return abi.encodePacked(int128(n)); + } + + /** + * @dev RLP encodes an int. + * @param self The int to encode. + * @return The RLP encoded int in bytes. + */ + function encodeInt(int256 self) internal pure returns (bytes memory) { + return encodeBytes(intToStrictBytes(self)); + } + + /** + * @dev RLP encodes a bool. + * @param self The bool to encode. + * @return The RLP encoded bool in bytes. + */ + function encodeBool(bool self) internal pure returns (bytes memory) { + bytes memory encoded = new bytes(1); + encoded[0] = (self ? bytes1(0x01) : bytes1(0x00)); + return encoded; + } + + /* + * Private functions + */ + + /** + * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param len The length of the string or the payload. + * @param offset 128 if item is string, 192 if item is list. + * @return RLP encoded bytes. + */ + function encodeLength(uint256 len, uint256 offset) + private + pure + returns (bytes memory) + { + bytes memory encoded; + if (len < 56) { + encoded = new bytes(1); + encoded[0] = bytes32(len + offset)[31]; + } else { + uint256 lenLen; + uint256 i = 1; + while (len / i != 0) { + lenLen++; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes32(lenLen + offset + 55)[31]; + for (i = 1; i <= lenLen; i++) { + encoded[i] = bytes32((len / (256**(lenLen - i))) % 256)[31]; + } + } + return encoded; + } + + /** + * @dev Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function toBinary(uint256 _x) private pure returns (bytes memory) { + // Modify library to make it work properly when _x = 0 + if (_x == 0) { + return abi.encodePacked(uint8(_x)); + } + bytes memory b = new bytes(32); + assembly { + mstore(add(b, 32), _x) + } + uint256 i; + for (i = 0; i < 32; i++) { + if (b[i] != 0) { + break; + } + } + bytes memory res = new bytes(32 - i); + for (uint256 j = 0; j < res.length; j++) { + res[j] = b[i++]; + } + return res; + } + + /** + * @dev Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function memcpy( + uint256 _dest, + uint256 _src, + uint256 _len + ) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask = 256**(32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * @dev Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return The flattened byte string. + */ + function flatten(bytes[] memory _list) private pure returns (bytes memory) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i; + for (i = 0; i < _list.length; i++) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; i++) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } + + /** + * @dev Concatenates two bytes. + * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. + * @param _preBytes First byte string. + * @param _postBytes Second byte string. + * @return Both byte string combined. + */ + function concat(bytes memory _preBytes, bytes memory _postBytes) + private + pure + returns (bytes memory) + { + bytes memory tempBytes; + + assembly { + tempBytes := mload(0x40) + + let length := mload(_preBytes) + mstore(tempBytes, length) + + let mc := add(tempBytes, 0x20) + let end := add(mc, length) + + for { + let cc := add(_preBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + length := mload(_postBytes) + mstore(tempBytes, add(length, mload(tempBytes))) + + mc := end + end := add(mc, length) + + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore( + 0x40, + and( + add(add(end, iszero(add(length, mload(_preBytes)))), 31), + not(31) + ) + ) + } + + return tempBytes; + } + + /** + * @dev convert uint to strict bytes. + * @notice only handle to uint128 due to contract code size limit + * @param length The uint to convert. + * @return The uint in strict bytes without padding. + */ + function encodeUintByLength(uint256 length) + internal + pure + returns (bytes memory) + { + if (length < MAX_UINT8) { + return abi.encodePacked(uint8(length)); + } else if (length >= MAX_UINT8 && length < MAX_UINT16) { + return abi.encodePacked(uint16(length)); + } else if (length >= MAX_UINT16 && length < MAX_UINT24) { + return abi.encodePacked(uint24(length)); + } else if (length >= MAX_UINT24 && length < MAX_UINT32) { + return abi.encodePacked(uint32(length)); + } else if (length >= MAX_UINT32 && length < MAX_UINT40) { + return abi.encodePacked(uint40(length)); + } else if (length >= MAX_UINT40 && length < MAX_UINT48) { + return abi.encodePacked(uint48(length)); + } else if (length >= MAX_UINT48 && length < MAX_UINT56) { + return abi.encodePacked(uint56(length)); + } else if (length >= MAX_UINT56 && length < MAX_UINT64) { + return abi.encodePacked(uint64(length)); + } else if (length >= MAX_UINT64 && length < MAX_UINT72) { + return abi.encodePacked(uint72(length)); + } else if (length >= MAX_UINT72 && length < MAX_UINT80) { + return abi.encodePacked(uint80(length)); + } else if (length >= MAX_UINT80 && length < MAX_UINT88) { + return abi.encodePacked(uint88(length)); + } else if (length >= MAX_UINT88 && length < MAX_UINT96) { + return abi.encodePacked(uint96(length)); + } else if (length >= MAX_UINT96 && length < MAX_UINT104) { + return abi.encodePacked(uint104(length)); + } else if (length >= MAX_UINT104 && length < MAX_UINT112) { + return abi.encodePacked(uint112(length)); + } else if (length >= MAX_UINT112 && length < MAX_UINT120) { + return abi.encodePacked(uint120(length)); + } + require( + length >= MAX_UINT120 && length < MAX_UINT128, + "outOfBounds: [0, 2^128]" + ); + return abi.encodePacked(uint128(length)); + } + + function bitLength(uint256 n) internal pure returns (uint256) { + uint256 count; + while (n != 0) { + count += 1; + n >>= 1; + } + return count; + } +} diff --git a/solidity/bsh/contracts/libraries/RLPEncodeStruct.sol b/solidity/bsh/contracts/libraries/RLPEncodeStruct.sol new file mode 100644 index 00000000..d578afeb --- /dev/null +++ b/solidity/bsh/contracts/libraries/RLPEncodeStruct.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPEncode.sol"; +import "./Types.sol"; + +library RLPEncodeStruct { + using RLPEncode for bytes; + using RLPEncode for string; + using RLPEncode for uint256; + using RLPEncode for address; + using RLPEncode for int256; + + using RLPEncodeStruct for Types.BlockHeader; + using RLPEncodeStruct for Types.BlockWitness; + using RLPEncodeStruct for Types.BlockUpdate; + using RLPEncodeStruct for Types.BlockProof; + using RLPEncodeStruct for Types.EventProof; + using RLPEncodeStruct for Types.ReceiptProof; + using RLPEncodeStruct for Types.Votes; + using RLPEncodeStruct for Types.RelayMessage; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function encodeBMCService(Types.BMCService memory _bs) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bs.serviceType.encodeString(), + _bs.payload.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeGatherFeeMessage(Types.GatherFeeMessage memory _gfm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _gfm.svcs.length; i++) { + temp = abi.encodePacked(_gfm.svcs[i].encodeString()); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _gfm.fa.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeEventMessage(Types.EventMessage memory _em) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _em.conn.from.encodeString(), + _em.conn.to.encodeString() + ); + _rlp = abi.encodePacked( + _em.eventType.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeCoinRegister(string[] memory _coins) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _coins.length; i++) { + temp = abi.encodePacked(_coins[i].encodeString()); + _rlp = abi.encodePacked(_rlp, temp); + } + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBMCMessage(Types.BMCMessage memory _bm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bm.src.encodeString(), + _bm.dst.encodeString(), + _bm.svc.encodeString(), + _bm.sn.encodeInt(), + _bm.message.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeServiceMessage(Types.ServiceMessage memory _sm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + uint256(_sm.serviceType).encodeUint(), + _sm.data.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeTransferCoinMsg(Types.TransferCoin memory _data) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _data.assets.length; i++) { + temp = abi.encodePacked( + _data.assets[i].coinName.encodeString(), + _data.assets[i].value.encodeUint() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + _rlp = abi.encodePacked( + _data.from.encodeString(), + _data.to.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeResponse(Types.Response memory _res) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _res.code.encodeUint(), + _res.message.encodeString() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockHeader(Types.BlockHeader memory _bh) + internal + pure + returns (bytes memory) + { + // Serialize the first 10 items in the BlockHeader + // patchTxHash and txHash might be empty. + // In that case, encoding these two items gives the result as 0xF800 + // Similarly, logsBloom might be also empty + // But, encoding this item gives the result as 0x80 + bytes memory _rlp = + abi.encodePacked( + _bh.version.encodeUint(), + _bh.height.encodeUint(), + _bh.timestamp.encodeUint(), + _bh.proposer.encodeBytes(), + _bh.prevHash.encodeBytes(), + _bh.voteHash.encodeBytes(), + _bh.nextValidators.encodeBytes() + ); + bytes memory temp1; + if (_bh.patchTxHash.length != 0) { + temp1 = _bh.patchTxHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1); + + if (_bh.txHash.length != 0) { + temp1 = _bh.txHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1, _bh.logsBloom.encodeBytes()); + bytes memory temp2; + // SPR struct could be an empty struct + // In that case, serialize(SPR) = 0xF800 + if (_bh.isSPREmpty) { + temp2 = emptyListHeadStart(); + } else { + // patchReceiptHash and receiptHash might be empty + // In that case, encoding these two items gives the result as 0xF800 + if (_bh.spr.patchReceiptHash.length != 0) { + temp1 = _bh.spr.patchReceiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(_bh.spr.stateHash.encodeBytes(), temp1); + + if (_bh.spr.receiptHash.length != 0) { + temp1 = _bh.spr.receiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(temp2, temp1); + temp2 = abi + .encodePacked(addLength(temp2.length, false), temp2) + .encodeBytes(); + } + _rlp = abi.encodePacked(_rlp, temp2); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeVotes(Types.Votes memory _vote) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + + // First, serialize an array of TS + for (uint256 i = 0; i < _vote.ts.length; i++) { + temp = abi.encodePacked( + _vote.ts[i].timestamp.encodeUint(), + _vote.ts[i].signature.encodeBytes() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + + // Next, serialize the blockPartSetID + temp = abi.encodePacked( + _vote.blockPartSetID.n.encodeUint(), + _vote.blockPartSetID.b.encodeBytes() + ); + // Combine all of them + _rlp = abi.encodePacked( + _vote.round.encodeUint(), + addLength(temp.length, false), + temp, + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockWitness(Types.BlockWitness memory _bw) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _bw.witnesses.length; i++) { + temp = _bw.witnesses[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _bw.height.encodeUint(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeEventProof(Types.EventProof memory _ep) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _ep.eventMptNode.length; i++) { + temp = _ep.eventMptNode[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + _rlp = abi.encodePacked(_ep.index.encodeUint(), _rlp); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockUpdate(Types.BlockUpdate memory _bu) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // In the case that _validators[] is an empty array, loop will be skipped + // and RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START (0xF800) instead + if (_bu.validators.length != 0) { + for (uint256 i = 0; i < _bu.validators.length; i++) { + temp = _bu.validators[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + } else { + _rlp = emptyListHeadStart(); + } + + _rlp = abi.encodePacked( + _bu.bh.encodeBlockHeader().encodeBytes(), + _bu.votes.encodeVotes().encodeBytes(), + _rlp + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeReceiptProof(Types.ReceiptProof memory _rp) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // Serialize [bytes] which are transaction receipts + for (uint256 i = 0; i < _rp.txReceipts.length; i++) { + temp = _rp.txReceipts[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + bytes memory eventProof; + for (uint256 i = 0; i < _rp.ep.length; i++) { + temp = _rp.ep[i].encodeEventProof(); + eventProof = abi.encodePacked(eventProof, temp); + } + _rlp = abi.encodePacked( + _rp.index.encodeUint(), + _rlp, + addLength(eventProof.length, false), + eventProof + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockProof(Types.BlockProof memory _bp) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bp.bh.encodeBlockHeader().encodeBytes(), + _bp.bw.encodeBlockWitness().encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeRelayMessage(Types.RelayMessage memory _rm) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + if (_rm.buArray.length != 0) { + for (uint256 i = 0; i < _rm.buArray.length; i++) { + temp = _rm.buArray[i].encodeBlockUpdate().encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked(addLength(_rlp.length, false), _rlp); + } else { + _rlp = emptyListShortStart(); + } + + if (_rm.isBPEmpty == false) { + temp = _rm.bp.encodeBlockProof(); + } else { + temp = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp); + + bytes memory receiptProof; + if (_rm.isRPEmpty == false) { + for (uint256 i = 0; i < _rm.rp.length; i++) { + temp = _rm.rp[i].encodeReceiptProof().encodeBytes(); + receiptProof = abi.encodePacked(receiptProof, temp); + } + receiptProof = abi.encodePacked( + addLength(receiptProof.length, false), + receiptProof + ); + } else { + receiptProof = emptyListShortStart(); + } + _rlp = abi.encodePacked(_rlp, receiptProof); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + // Adding LIST_HEAD_START by length + // There are two cases: + // 1. List contains less than or equal 55 elements (total payload of the RLP) -> LIST_HEAD_START = LIST_SHORT_START + [0-55] = [0xC0 - 0xF7] + // 2. List contains more than 55 elements: + // - Total Payload = 512 elements = 0x0200 + // - Length of Total Payload = 2 + // => LIST_HEAD_START = \x (LIST_LONG_START + length of Total Payload) \x (Total Payload) = \x(F7 + 2) \x(0200) = \xF9 \x0200 = 0xF90200 + function addLength(uint256 length, bool isLongList) + internal + pure + returns (bytes memory) + { + if (length > 55 && !isLongList) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(length); + return + abi.encodePacked( + addLength(payLoadSize.length, true), + payLoadSize + ); + } else if (length <= 55 && !isLongList) { + return abi.encodePacked(uint8(LIST_SHORT_START + length)); + } + return abi.encodePacked(uint8(LIST_LONG_START + length)); + } + + function emptyListHeadStart() internal pure returns (bytes memory) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(0); + return + abi.encodePacked( + abi.encodePacked(uint8(LIST_LONG_START + payLoadSize.length)), + payLoadSize + ); + } + + function emptyListShortStart() internal pure returns (bytes memory) { + return abi.encodePacked(LIST_SHORT_START); + } +} diff --git a/solidity/bsh/contracts/libraries/String.sol b/solidity/bsh/contracts/libraries/String.sol new file mode 100644 index 00000000..55ce075c --- /dev/null +++ b/solidity/bsh/contracts/libraries/String.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * String Library + * + * This is a simple library of string functions which try to simplify + * string operations in solidity. + * + * Please be aware some of these functions can be quite gas heavy so use them only when necessary + * + * The original library was modified. If you want to know more about the original version + * please check this link: https://github.com/willitscale/solidity-util.git + */ +library String { + /** + * splitBTPAddress + * + * Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + * into Network_address (1234.iconee) and Server_address (0x123456789) + * + * @param _base String base BTP Address format to be split + * @dev _base must follow a BTP Address format + * + * @return string, string The resulting strings of Network_address and Server_address + */ + function splitBTPAddress(string memory _base) + internal + pure + returns (string memory, string memory) + { + string[] memory temp = split(_base, "/"); + return (temp[2], temp[3]); + } + + /** + * Concat + * + * Appends two strings together and returns a new value + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string which will be the concatenated + * prefix + * @param _value The value to be the concatenated suffix + * @return string The resulting string from combinging the base and value + */ + function concat(string memory _base, string memory _value) + internal + pure + returns (string memory) + { + return string(abi.encodePacked(_base, _value)); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function indexOf(string memory _base, string memory _value) + internal + pure + returns (int256) + { + return _indexOf(_base, _value, 0); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string starting + * from a defined offset + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @param _offset The starting point to start searching from which can start + * from 0, but must not exceed the length of the string + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function _indexOf( + string memory _base, + string memory _value, + uint256 _offset + ) internal pure returns (int256) { + bytes memory _baseBytes = bytes(_base); + bytes memory _valueBytes = bytes(_value); + + assert(_valueBytes.length == 1); + + for (uint256 i = _offset; i < _baseBytes.length; i++) { + if (_baseBytes[i] == _valueBytes[0]) { + return int256(i); + } + } + + return -1; + } + + /** + * Length + * + * Returns the length of the specified string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string to be measured + * @return uint The length of the passed string + */ + function length(string memory _base) internal pure returns (uint256) { + bytes memory _baseBytes = bytes(_base); + return _baseBytes.length; + } + + /* + * String Split (Very high gas cost) + * + * Splits a string into an array of strings based off the delimiter value. + * Please note this can be quite a gas expensive function due to the use of + * storage so only use if really required. + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string value to be split. + * @param _value The delimiter to split the string on which must be a single + * character + * @return string[] An array of values split based off the delimiter, but + * do not container the delimiter. + */ + function split(string memory _base, string memory _value) + internal + pure + returns (string[] memory splitArr) + { + bytes memory _baseBytes = bytes(_base); + + uint256 _offset = 0; + uint256 _splitsCount = 1; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) break; + else { + _splitsCount++; + _offset = uint256(_limit) + 1; + } + } + + splitArr = new string[](_splitsCount); + + _offset = 0; + _splitsCount = 0; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) { + _limit = int256(_baseBytes.length); + } + + string memory _tmp = new string(uint256(_limit) - _offset); + bytes memory _tmpBytes = bytes(_tmp); + + uint256 j = 0; + for (uint256 i = _offset; i < uint256(_limit); i++) { + _tmpBytes[j++] = _baseBytes[i]; + } + _offset = uint256(_limit) + 1; + splitArr[_splitsCount++] = string(_tmpBytes); + } + return splitArr; + } + + /** + * Compare To + * + * Compares the characters of two strings, to ensure that they have an + * identical footprint + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string base to compare against + * @param _value The string the base is being compared to + * @return bool Simply notates if the two string have an equivalent + */ + function compareTo(string memory _base, string memory _value) + internal + pure + returns (bool) + { + if ( + keccak256(abi.encodePacked(_base)) == + keccak256(abi.encodePacked(_value)) + ) { + return true; + } + return false; + } + + function toString(uint256 _i) internal pure returns (string memory) { + if (_i == 0) { + return "0"; + } + uint256 j = _i; + uint256 len; + while (j != 0) { + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint256 k = len - 1; + while (_i != 0) { + bstr[k--] = byte(uint8(48 + (_i % 10))); + _i /= 10; + } + return string(bstr); + } +} diff --git a/solidity/bsh/contracts/libraries/Types.sol b/solidity/bsh/contracts/libraries/Types.sol new file mode 100644 index 00000000..4e48e5f1 --- /dev/null +++ b/solidity/bsh/contracts/libraries/Types.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Types { + /** + * @Notice List of ALL Struct being used to Encode and Decode RLP Messages + */ + + // SPR = State Hash + Pathch Receipt Hash + Receipt Hash + struct SPR { + bytes stateHash; + bytes patchReceiptHash; + bytes receiptHash; + } + + struct BlockHeader { + uint256 version; + uint256 height; + uint256 timestamp; + bytes proposer; + bytes prevHash; + bytes voteHash; + bytes nextValidators; + bytes patchTxHash; + bytes txHash; + bytes logsBloom; + SPR spr; + bool isSPREmpty; // add to check whether SPR is an empty struct + // It will not be included in serializing thereafter + } + + // TS = Timestamp + Signature + struct TS { + uint256 timestamp; + bytes signature; + } + + // BPSI = blockPartSetID + struct BPSI { + uint256 n; + bytes b; + } + + struct Votes { + uint256 round; + BPSI blockPartSetID; + TS[] ts; + } + + struct BlockWitness { + uint256 height; + bytes[] witnesses; + } + + struct EventProof { + uint256 index; + bytes[] eventMptNode; + } + + struct BlockUpdate { + BlockHeader bh; + Votes votes; + bytes[] validators; + } + + struct ReceiptProof { + uint256 index; + bytes[] txReceipts; + EventProof[] ep; + } + + struct BlockProof { + BlockHeader bh; + BlockWitness bw; + } + + struct RelayMessage { + BlockUpdate[] buArray; + BlockProof bp; + bool isBPEmpty; // add to check in a case BlockProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + ReceiptProof[] rp; + bool isRPEmpty; // add to check in a case ReceiptProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + } + + /** + * @Notice List of ALL Structs being used by a BSH contract + */ + enum ServiceType { + REQUEST_COIN_TRANSFER, + REQUEST_COIN_REGISTER, + REPONSE_HANDLE_SERVICE, + UNKNOWN_TYPE + } + + struct PendingTransferCoin { + string from; + string to; + string[] coinNames; + uint256[] amounts; + uint256[] fees; + } + + struct TransferCoin { + string from; + string to; + Asset[] assets; + } + + struct Asset { + string coinName; + uint256 value; + } + + struct AssetTransferDetail { + string coinName; + uint256 value; + uint256 fee; + } + + struct Response { + uint256 code; + string message; + } + + struct ServiceMessage { + ServiceType serviceType; + bytes data; + } + + struct Coin { + uint256 id; + string symbol; + uint256 decimals; + } + + struct Balance { + uint256 lockedBalance; + uint256 refundableBalance; + } + + struct Request { + string serviceName; + address bsh; + } + + /** + * @Notice List of ALL Structs being used by a BMC contract + */ + + struct VerifierStats { + uint256 heightMTA; // MTA = Merkle Trie Accumulator + uint256 offsetMTA; + uint256 lastHeight; // Block height of last verified message which is BTP-Message contained + bytes extra; + } + + struct Service { + string svc; + address addr; + } + + struct Verifier { + string net; + address addr; + } + + struct Route { + string dst; // BTP Address of destination BMC + string next; // BTP Address of a BMC before reaching dst BMC + } + + struct Link { + address[] relays; // Address of multiple Relays handle for this link network + uint256 rxSeq; + uint256 txSeq; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 maxAggregation; + uint256 delayLimit; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rxHeight; + uint256 rxHeightSrc; + bool isConnected; + } + + struct LinkStats { + uint256 rxSeq; + uint256 txSeq; + VerifierStats verifier; + RelayStats[] relays; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rotateTerm; + uint256 delayLimit; + uint256 maxAggregation; + uint256 rxHeightSrc; + uint256 rxHeight; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 currentHeight; + } + + struct RelayStats { + address addr; + uint256 blockCount; + uint256 msgCount; + } + + struct BMCMessage { + string src; // an address of BMC (i.e. btp://1234.PARA/0x1234) + string dst; // an address of destination BMC + string svc; // service name of BSH + int256 sn; // sequence number of BMC + bytes message; // serializef Service Message from BSH + } + + struct Connection { + string from; + string to; + } + + struct EventMessage { + string eventType; + Connection conn; + } + + struct BMCService { + string serviceType; + bytes payload; + } + + struct GatherFeeMessage { + string fa; // BTP address of Fee Aggregator + string[] svcs; // a list of services + } +} diff --git a/solidity/bsh/contracts/test/AnotherHolder.sol b/solidity/bsh/contracts/test/AnotherHolder.sol new file mode 100644 index 00000000..499c60cb --- /dev/null +++ b/solidity/bsh/contracts/test/AnotherHolder.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol"; +import "./BSHPeripheryV1.sol"; +import "./BSHCoreV1.sol"; + +contract AnotherHolder is ERC1155Holder { + BSHPeripheryV1 private bshs; + BSHCoreV1 private bshc; + using String for string; + + function deposit() external payable {} + + function addBSHContract(address _bshs, address _bshc) external { + bshs = BSHPeripheryV1(_bshs); + bshc = BSHCoreV1(_bshc); + } + + function setApprove(address _operator) external { + bshc.setApprovalForAll(_operator, true); + } + + function callTransfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external { + bshc.transfer(_coinName, _value, _to); + } + + // function isSendingNative(string[] memory _coinNames) + // private + // pure + // returns (int256) + // { + // for (uint256 i = 0; i < _coinNames.length; i++) { + // if (_coinNames[i].compareTo("PARA")) { + // return int256(i); + // } + // } + // return -1; + // } + + function callTransferBatch( + address _bsh, + string[] memory _coinNames, + uint256[] memory _values, + string calldata _to, + uint256 _native + ) external { + // int256 pos = isSendingNative(_coinNames); + if (_native != 0) { + (bool success, bytes memory err) = + _bsh.call{value: _native}( + abi.encodeWithSignature( + "transferBatch(string[],uint256[],string)", + _coinNames, + _values, + _to + ) + ); + require(success, string(err)); + } else { + bshc.transferBatch(_coinNames, _values, _to); + } + } +} diff --git a/solidity/bsh/contracts/test/BMC.sol b/solidity/bsh/contracts/test/BMC.sol new file mode 100644 index 00000000..8f977373 --- /dev/null +++ b/solidity/bsh/contracts/test/BMC.sol @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSH.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../interfaces/IBMV.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/EncodeBase64.sol"; +import "../libraries/DecodeBase64.sol"; +import "../libraries/Types.sol"; + +contract BMC is IBMCPeriphery { + using ParseAddress for address; + using ParseAddress for string; + using RLPDecodeStruct for bytes; + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.EventMessage; + using RLPEncodeStruct for Types.Response; + using String for string; + + uint256 internal constant RC_OK = 0; + uint256 internal constant RC_ERR = 1; + + event Message( + string _next, // an address of the next BMC (it could be a destination BMC) + uint256 _seq, // a sequence number of BMC (NOT sequence number of BSH) + bytes _msg + ); + + // emit EVENT to announce link/unlink service + event Event(string _next, uint256 _seq, bytes _msg); + + event ErrorOnBTPError( + string _svc, + int256 _sn, + uint256 _code, + string _errMsg, + uint256 _svcErrCode, + string _svcErrMsg + ); + + mapping(address => bool) private _owners; + uint256 private numOfOwner; + + mapping(string => address) internal bshServices; + mapping(string => address) private bmvServices; + mapping(string => string) private connectedBMC; + mapping(address => Types.RelayStats) private relayStats; + mapping(string => string) private routes; + mapping(string => Types.Link) internal links; // should be private, temporarily set internal for testing + mapping(string => string[]) private reachable; + string[] private listBMVNames; + string[] private listBSHNames; + string[] private listRouteKeys; + string[] private listLinkNames; + address[] private addrs; + + string public bmcAddress; // a network address BMV, i.e. btp://1234.pra + uint256 private numOfBMVService; + uint256 private numOfBSHService; + uint256 private numOfLinks; + uint256 private numOfRoutes; + + uint256 private constant BLOCK_INTERVAL_MSEC = 1000; + uint256 internal constant UNKNOWN_ERR = 0; + uint256 internal constant BMC_ERR = 10; + uint256 internal constant BMV_ERR = 25; + uint256 internal constant BSH_ERR = 40; + uint256 private constant DECIMAL_PRECISION = 10**6; + + modifier owner { + require(_owners[msg.sender] == true, "BMCRevertUnauthorized"); + _; + } + + constructor(string memory _network) { + bmcAddress = string("btp://").concat(_network).concat("/").concat( + address(this).toString() + ); + _owners[msg.sender] = true; + numOfOwner++; + } + + /***************************************************************************************** + + *****************************************************************************************/ + function getBmcBtpAddress() external view override returns (string memory) { + return bmcAddress; + } + + function handleRelayMessage(string calldata _prev, string calldata _msg) + external + override + {} + + function handleMessage(string calldata _prev, Types.BMCMessage memory _msg) + internal + { + if (_msg.svc.compareTo("bmc")) { + Types.BMCService memory _sm; + try this.tryDecodeBMCService(_msg.message) returns ( + Types.BMCService memory ret + ) { + _sm = ret; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + } + if (_sm.serviceType.compareTo("FeeGathering")) { + Types.GatherFeeMessage memory _gatherFee; + try this.tryDecodeGatherFeeMessage(_sm.payload) returns ( + Types.GatherFeeMessage memory ret + ) { + _gatherFee = ret; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + } + for (uint256 i = 0; i < _gatherFee.svcs.length; i++) { + // If 'svc' not found, ignore + if (bshServices[_gatherFee.svcs[i]] != address(0)) { + try + IBSH(bshServices[_gatherFee.svcs[i]]) + .handleFeeGathering( + _gatherFee.fa, + _gatherFee.svcs[i] + ) + {} catch { + // If BSH contract throws a revert error, ignore and continue + } + } + } + } + } else { + if (bshServices[_msg.svc] == address(0)) { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertNotExistsBSH"); + return; + } + + if (_msg.sn >= 0) { + (string memory _net, ) = _msg.src.splitBTPAddress(); + try + IBSH(bshServices[_msg.svc]).handleBTPMessage( + _net, + _msg.svc, + uint256(_msg.sn), + _msg.message + ) + {} catch Error(string memory _error) { + _sendError(_prev, _msg, BSH_ERR, _error); + } + } else { + Types.Response memory _errMsg = _msg.message.decodeResponse(); + try + IBSH(bshServices[_msg.svc]).handleBTPError( + _msg.src, + _msg.svc, + uint256(int256(_msg.sn) * -1), + _errMsg.code, + _errMsg.message + ) + {} catch Error(string memory _error) { + emit ErrorOnBTPError( + _msg.svc, + int256(_msg.sn) * -1, + _errMsg.code, + _errMsg.message, + BSH_ERR, + _error + ); + } catch (bytes memory _error) { + emit ErrorOnBTPError( + _msg.svc, + int256(_msg.sn) * -1, + _errMsg.code, + _errMsg.message, + UNKNOWN_ERR, + string(_error) + ); + } + } + } + } + + // @dev Solidity does not allow using try_catch with internal function + // Thus, work-around solution is the followings + // If there is any error throwing, BMC contract can catch it, then reply back a RC_ERR Response + function tryDecodeBMCService(bytes calldata _msg) + external + pure + returns (Types.BMCService memory) + { + return _msg.decodeBMCService(); + } + + function tryDecodeGatherFeeMessage(bytes calldata _msg) + external + pure + returns (Types.GatherFeeMessage memory) + { + return _msg.decodeGatherFeeMessage(); + } + + function _sendMessage(string memory _to, bytes memory _serializedMsg) + internal + { + links[_to].txSeq += 1; + emit Message(_to, links[_to].txSeq, _serializedMsg); + } + + function _sendError( + string calldata _prev, + Types.BMCMessage memory _message, + uint256 _errCode, + string memory _errMsg + ) internal { + if (_message.sn > 0) { + bytes memory _serializedMsg = + Types + .BMCMessage( + bmcAddress, + _message + .src, + _message + .svc, + int256(_message.sn) * -1, + Types + .ServiceMessage( + Types + .ServiceType + .REPONSE_HANDLE_SERVICE, + Types.Response(_errCode, _errMsg).encodeResponse() + ) + .encodeServiceMessage() + ) + .encodeBMCMessage(); + _sendMessage(_prev, _serializedMsg); + } + } + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string memory _to, + string memory _svc, + uint256 _sn, + bytes memory _msg + ) external override { + require( + msg.sender == address(this) || bshServices[_svc] == msg.sender, + "BMCRevertUnauthorized" + ); + require(_sn >= 0, "BMCRevertInvalidSN"); + // In case BSH sends a REQUEST_COIN_TRANSFER, + // but '_to' is a network which is not supported by BMC + // revert() therein + if (bmvServices[_to] == address(0)) { + revert("BMCRevertNotExistsBMV"); + } + string memory _toBMC = connectedBMC[_to]; + bytes memory _rlp = + Types + .BMCMessage(bmcAddress, _toBMC, _svc, int256(_sn), _msg) + .encodeBMCMessage(); + if (_svc.compareTo("_EVENT")) { + links[_toBMC].txSeq += 1; + emit Event(_toBMC, links[_toBMC].txSeq, _rlp); + } else { + links[_toBMC].txSeq += 1; + emit Message(_toBMC, links[_toBMC].txSeq, _rlp); + } + } + + /** + @notice Add the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + @param _addr Service's contract address + */ + function addService(string memory _svc, address _addr) external owner { + require(_addr != address(0), "BMCRevertInvalidAddress"); + require(bshServices[_svc] == address(0), "BMCRevertAlreadyExistsBSH"); + bshServices[_svc] = _addr; + listBSHNames.push(_svc); + } + + /** + @notice Registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + @param _addr Address of BMV + */ + function addVerifier(string memory _net, address _addr) external owner { + require(bmvServices[_net] == address(0), "BMCRevertAlreadyExistsBMV"); + bmvServices[_net] = _addr; + listBMVNames.push(_net); + numOfBMVService++; + } + + /** + @notice Initializes status information for the link. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function addLink(string calldata _link) external owner { + string memory _net; + (_net, ) = _link.splitBTPAddress(); + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + require( + links[_link].isConnected == false, + "BMCRevertAlreadyExistsLink" + ); + links[_link] = Types.Link( + new address[](0), + 0, + 0, + BLOCK_INTERVAL_MSEC, + 0, + 10, + 3, + 0, + 0, + 0, + 0, + true + ); + listLinkNames.push(_link); + connectedBMC[_net] = _link; + numOfLinks++; + + // propagate an event "LINK" + propagateEvent("Link", _link); + } + + function propagateEvent(string memory _eventType, string calldata _link) + private + {} + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC. + @return tx_seq Next sequence number of the next sending message. + @return rx_seq Next sequence number of the message to receive. + @return verifier VerifierStatus Object contains status information of the BMV. + */ + function getStatus(string calldata _link) + external + view + override + returns (Types.LinkStats memory _linkStats) + { + _linkStats.txSeq = links[_link].txSeq; + } +} diff --git a/solidity/bsh/contracts/test/BSHCoreV1.sol b/solidity/bsh/contracts/test/BSHCoreV1.sol new file mode 100644 index 00000000..692a3536 --- /dev/null +++ b/solidity/bsh/contracts/test/BSHCoreV1.sol @@ -0,0 +1,679 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155HolderUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; + +/** + @title BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following contract can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHCoreV1 is + Initializable, + IBSHCore, + ERC1155Upgradeable, + ERC1155HolderUpgradeable, + ReentrancyGuardUpgradeable +{ + using SafeMathUpgradeable for uint256; + using String for string; + event SetOwnership(address indexed promoter, address indexed newOwner); + event RemoveOwnership(address indexed remover, address indexed formerOwner); + + modifier onlyOwner { + require(owners[msg.sender] == true, "Unauthorized"); + _; + } + + modifier onlyBSHPeriphery { + require(msg.sender == address(bshPeriphery), "Unauthorized"); + _; + } + + uint256 private constant FEE_DENOMINATOR = 10**4; + uint256 public feeNumerator; + uint256 public fixedFee; + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + + IBSHPeriphery internal bshPeriphery; + + address[] private listOfOwners; + uint256[] private chargedAmounts; // a list of amounts have been charged so far (use this when Fee Gathering occurs) + string[] internal coinsName; // a string array stores names of supported coins + string[] private chargedCoins; // a list of coins' names have been charged so far (use this when Fee Gathering occurs) + + mapping(address => bool) private owners; + mapping(string => uint256) internal aggregationFee; // storing Aggregation Fee in state mapping variable. + mapping(address => mapping(string => Types.Balance)) internal balances; + mapping(string => uint256) private coins; // a list of all supported coins + + function initialize( + string calldata _uri, + string calldata _nativeCoinName, + uint256 _feeNumerator, + uint256 _fixedFee + ) public initializer { + __ERC1155_init(_uri); + __ERC1155Holder_init(); + + owners[msg.sender] = true; + listOfOwners.push(msg.sender); + emit SetOwnership(address(0), msg.sender); + + coins[_nativeCoinName] = 0; + feeNumerator = _feeNumerator; + fixedFee = _fixedFee; + coinsName.push(_nativeCoinName); + } + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override onlyOwner { + require(owners[_owner] == false, "ExistedOwner"); + owners[_owner] = true; + listOfOwners.push(_owner); + emit SetOwnership(msg.sender, _owner); + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override onlyOwner { + require(listOfOwners.length > 1, "Unable to remove last Owner"); + require(owners[_owner] == true, "Removing Owner not found"); + delete owners[_owner]; + _remove(_owner); + emit RemoveOwnership(msg.sender, _owner); + } + + function _remove(address _addr) internal { + for (uint256 i = 0; i < listOfOwners.length; i++) + if (listOfOwners[i] == _addr) { + listOfOwners[i] = listOfOwners[listOfOwners.length - 1]; + listOfOwners.pop(); + break; + } + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return owners[_owner]; + } + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + function getOwners() external view override returns (address[] memory) { + return listOfOwners; + } + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) + external + override + onlyOwner + { + require(_bshPeriphery != address(0), "InvalidSetting"); + if (address(bshPeriphery) != address(0)) { + require( + bshPeriphery.hasPendingRequest() == false, + "HasPendingRequest" + ); + } + bshPeriphery = IBSHPeriphery(_bshPeriphery); + } + + /** + @notice update base uri. + @dev Caller must be an Owner of this contract + the uri must be initilized in construction. + @param _newURI new uri + */ + function updateUri(string calldata _newURI) external override onlyOwner { + _setURI(_newURI); + } + + /** + @notice set fee ratio. + @dev Caller must be an Owner of this contract + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external override onlyOwner { + require(_feeNumerator <= FEE_DENOMINATOR, "InvalidSetting"); + feeNumerator = _feeNumerator; + } + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external override onlyOwner { + require(_fixedFee > 0, "InvalidSetting"); + fixedFee = _fixedFee; + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external override onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() + external + view + override + returns (string[] memory _names) + { + return coinsName; + } + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) + external + view + override + returns (uint256 _coinId) + { + return coins[_coinName]; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + override + returns (bool _valid) + { + return (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])); + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + override + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner, coins[_coinName]), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + override + returns ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ) + { + _usableBalances = new uint256[](_coinNames.length); + _lockedBalances = new uint256[](_coinNames.length); + _refundableBalances = new uint256[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + ( + _usableBalances[i], + _lockedBalances[i], + _refundableBalances[i] + ) = this.getBalanceOf(_owner, _coinNames[i]); + } + return (_usableBalances, _lockedBalances, _refundableBalances); + } + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + override + returns (Types.Asset[] memory _accumulatedFees) + { + _accumulatedFees = new Types.Asset[](coinsName.length); + for (uint256 i = 0; i < coinsName.length; i++) { + _accumulatedFees[i] = ( + Types.Asset(coinsName[i], aggregationFee[coinsName[i]]) + ); + } + return _accumulatedFees; + } + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable override { + // Aggregation Fee will be charged on BSH Contract + // A new charging fee has been proposed. `fixedFee` is introduced + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + uint256 _chargeAmt = + msg.value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // @dev msg.value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage( + msg.sender, + _to, + coinsName[0], + msg.value, + _chargeAmt + ); + } + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer from a Requester (include fee) + @param _to Target BTP address. + */ + function transfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external override { + require(coins[_coinName] != 0, "UnregisterCoin"); + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + uint256 _chargeAmt = + _value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // Transfer and Lock Token processes: + // BSHCore contract calls safeTransferFrom() to transfer the Token from Caller's account (msg.sender) + // Before that, Caller must approve (setApproveForAll) to accept + // token being transfer out by an Operator + // If this requirement is failed, a transaction is reverted. + // After transferring token, BSHCore contract updates Caller's locked balance + // as a record of pending transfer transaction + // When a transaction is completed without any error on another chain, + // Locked Token amount (bind to an address of caller) will be reset/subtract, + // then emit a successful TransferEnd event as a notification + // Otherwise, the locked amount will also be updated + // but BSHCore contract will issue a refund to Caller before emitting an error TransferEnd event + this.safeTransferFrom( + msg.sender, + address(this), + coins[_coinName], + _value, + "" + ); + // @dev _value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage(msg.sender, _to, _coinName, _value, _chargeAmt); + } + + /** + @notice This private function handles overlapping procedure before sending a service message to BSHPeriphery + @param _from An address of a Requester + @param _to BTP address of of Receiver on another chain + @param _coinName A given name of a requested coin + @param _value A requested amount to transfer from a Requester (include fee) + @param _chargeAmt An amount being charged for this request + */ + function _sendServiceMessage( + address _from, + string calldata _to, + string memory _coinName, + uint256 _value, + uint256 _chargeAmt + ) private { + // Lock this requested _value as a record of a pending transferring transaction + // @dev `_value` is a requested amount to transfer, from a Requester, including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[0] = _value.sub(_chargeAmt); + lockBalance(_from, _coinName, _value); + string[] memory _coins = new string[](1); + _coins[0] = _coinName; + uint256[] memory _amounts = new uint256[](1); + _amounts[0] = _value.sub(_chargeAmt); + uint256[] memory _fees = new uint256[](1); + _fees[0] = _chargeAmt; + + // @dev `_amounts` is a true amount to receive at a destination after deducting a charged fee + bshPeriphery.sendServiceMessage(_from, _to, _coins, _amounts, _fees); + } + + /** + @notice Allow users to transfer multiple coins/wrapped coins to another chain + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + In case of transferring a native coin, it also checks `msg.value` + The number of requested coins MUST be as the same as the number of requested values + The requested coins and values MUST be matched respectively + @param _coinNames A list of requested transferring coins/wrapped coins + @param _values A list of requested transferring values respectively with its coin name + @param _to Target BTP address. + */ + function transferBatch( + string[] calldata _coinNames, + uint256[] memory _values, + string calldata _to + ) external payable override { + require(_coinNames.length == _values.length, "InvalidRequest"); + uint256 size = + msg.value != 0 ? _coinNames.length.add(1) : _coinNames.length; + uint256[] memory _ids = new uint256[](_coinNames.length); + string[] memory _coins = new string[](size); + uint256[] memory _amounts = new uint256[](size); + uint256[] memory _chargeAmts = new uint256[](size); + + for (uint256 i = 0; i < _coinNames.length; i++) { + _ids[i] = coins[_coinNames[i]]; + // Does not need to check if _coinNames[i] == native_coin + // If _coinNames[i] is a native_coin, coins[_coinNames[i]] = 0 + require(_ids[i] != 0, "UnregisterCoin"); + + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[i] = _coinNames[i]; + _chargeAmts[i] = _values[i] + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[i] = _values[i].sub(_chargeAmts[i]); + + // Lock this requested _value as a record of a pending transferring transaction + // @dev Note that: _value is a requested amount to transfer from a Requester including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[i] = _values[i].sub(_chargeAmts[i]); + lockBalance(msg.sender, _coinNames[i], _values[i]); + } + + this.safeBatchTransferFrom( + msg.sender, + address(this), + _ids, + _values, + "" + ); + + if (msg.value != 0) { + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[size - 1] = coinsName[0]; // push native_coin at the end of request + _chargeAmts[size - 1] = msg + .value + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[size - 1] = msg.value.sub(_chargeAmts[size - 1]); + lockBalance(msg.sender, coinsName[0], msg.value); + } + + // @dev `_amounts` is true amounts to receive at a destination after deducting charged fees + bshPeriphery.sendServiceMessage( + msg.sender, + _to, + _coins, + _amounts, + _chargeAmts + ); + } + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) + external + override + nonReentrant + { + require( + balances[msg.sender][_coinName].refundableBalance >= _value, + "Imbalance" + ); + + balances[msg.sender][_coinName].refundableBalance = balances[ + msg.sender + ][_coinName] + .refundableBalance + .sub(_value); + + this.refund(msg.sender, _coinName, _value); + } + + // Solidity does not allow using try_catch with interal/private function + // Thus, this function would be set as 'external` + // But, it has restriction. It should be called by this contract only + // In addition, there are only two functions calling this refund() + // + handleRequestService(): this function only called by BSHPeriphery + // + reclaim(): this function can be called by ANY + // In case of reentrancy attacks, the chance happenning on BSHPeriphery + // since it requires a request from BMC which requires verification fron BMV + // reclaim() has higher chance to have reentrancy attacks. + // So, it must be prevented by adding 'nonReentrant' + function refund( + address _to, + string calldata _coinName, + uint256 _value + ) external { + require(msg.sender == address(this), "Unauthorized"); + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + this.safeTransferFrom(address(this), _to, _id, _value, ""); + } + } + + function paymentTransfer(address payable _to, uint256 _amount) private { + (bool sent, ) = _to.call{value: _amount}(""); + require(sent, "Payment transfer failed"); + } + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external override onlyBSHPeriphery { + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + _mint(_to, _id, _value, ""); + } + } + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external override onlyBSHPeriphery { + // Fee Gathering and Transfer Coin Request use the same method + // and both have the same response + // In case of Fee Gathering's response, `_requester` is this contract's address + // Thus, check that first + // -- If `_requester` is this contract's address, then check whethere response's code is RC_ERR + // In case of RC_ERR, adding back charged fees to `aggregationFee` state variable + // In case of RC_OK, ignore and return + // -- Otherwise, handle service's response as normal + if (_requester == address(this)) { + if (_rspCode == RC_ERR) { + aggregationFee[_coinName] = aggregationFee[_coinName].add( + _value + ); + } + return; + } + uint256 _amount = _value.add(_fee); + balances[_requester][_coinName].lockedBalance = balances[_requester][ + _coinName + ] + .lockedBalance + .sub(_amount); + + // A new implementation has been proposed to prevent spam attacks + // In receiving error response, BSHCore refunds `_value`, not including `_fee`, back to Requestor + if (_rspCode == RC_ERR) { + try this.refund(_requester, _coinName, _value) {} catch { + balances[_requester][_coinName].refundableBalance = balances[ + _requester + ][_coinName] + .refundableBalance + .add(_value); + } + } else if (_rspCode == RC_OK) { + uint256 _id = coins[_coinName]; + if (_id != 0) { + _burn(address(this), _id, _value); + } + } + aggregationFee[_coinName] = aggregationFee[_coinName].add(_fee); + } + + /** + @notice Handle a request of Fee Gathering + Usage: Copy all charged fees to an array + @dev Caller must be an BSHPeriphery contract + */ + function transferFees(string calldata _fa) + external + override + onlyBSHPeriphery + { + // @dev Due to uncertainty in identifying a size of returning memory array + // and Solidity does not allow to use 'push' with memory array (only storage) + // thus, must use 'temp' storage state variable + for (uint256 i = 0; i < coinsName.length; i++) { + if (aggregationFee[coinsName[i]] != 0) { + chargedCoins.push(coinsName[i]); + chargedAmounts.push(aggregationFee[coinsName[i]]); + delete aggregationFee[coinsName[i]]; + } + } + bshPeriphery.sendServiceMessage( + address(this), + _fa, + chargedCoins, + chargedAmounts, + new uint256[](chargedCoins.length) // chargedFees is an array of 0 since this is a fee gathering request + ); + delete chargedCoins; + delete chargedAmounts; + } + + function lockBalance( + address _to, + string memory _coinName, + uint256 _value + ) private { + balances[_to][_coinName].lockedBalance = balances[_to][_coinName] + .lockedBalance + .add(_value); + } +} diff --git a/solidity/bsh/contracts/test/BSHCoreV2.sol b/solidity/bsh/contracts/test/BSHCoreV2.sol new file mode 100644 index 00000000..698022c3 --- /dev/null +++ b/solidity/bsh/contracts/test/BSHCoreV2.sol @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155HolderUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; + +/** + @title BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following contract can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHCoreV2 is + Initializable, + IBSHCore, + ERC1155Upgradeable, + ERC1155HolderUpgradeable, + ReentrancyGuardUpgradeable +{ + using SafeMathUpgradeable for uint256; + using String for string; + event SetOwnership(address indexed promoter, address indexed newOwner); + event RemoveOwnership(address indexed remover, address indexed formerOwner); + + modifier onlyOwner { + require(owners[msg.sender] == true, "Unauthorized"); + _; + } + + modifier onlyBSHPeriphery { + require(msg.sender == address(bshPeriphery), "Unauthorized"); + _; + } + + uint256 private constant FEE_DENOMINATOR = 10**4; + uint256 public feeNumerator; + uint256 public fixedFee; + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + + IBSHPeriphery internal bshPeriphery; + + address[] private listOfOwners; + uint256[] private chargedAmounts; // a list of amounts have been charged so far (use this when Fee Gathering occurs) + string[] internal coinsName; // a string array stores names of supported coins + string[] private chargedCoins; // a list of coins' names have been charged so far (use this when Fee Gathering occurs) + + mapping(address => bool) private owners; + mapping(string => uint256) internal aggregationFee; // storing Aggregation Fee in state mapping variable. + mapping(address => mapping(string => Types.Balance)) internal balances; + mapping(string => uint256) private coins; // a list of all supported coins + + // This is just an example to show how to add more state variable + mapping(address => mapping(string => uint256)) private stakes; + + // Since upgrading contract is not allowed to call initialize() again + // Thus, initialize() can be removed + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override onlyOwner { + owners[_owner] = true; + listOfOwners.push(_owner); + emit SetOwnership(_msgSender(), _owner); + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override onlyOwner { + require(listOfOwners.length > 1, "Unable to remove last Owner"); + delete owners[_owner]; + _remove(_owner); + emit RemoveOwnership(_msgSender(), _owner); + } + + function _remove(address _addr) internal { + for (uint256 i = 0; i < listOfOwners.length; i++) + if (listOfOwners[i] == _addr) { + listOfOwners[i] = listOfOwners[listOfOwners.length - 1]; + listOfOwners.pop(); + break; + } + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return owners[_owner]; + } + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + function getOwners() external view override returns (address[] memory) { + return listOfOwners; + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function addStake(string calldata _coinName, uint256 _value) + external + payable + { + if (_coinName.compareTo(coinsName[0])) { + require(msg.value == _value, "InvalidAmount"); + } else { + this.safeTransferFrom( + msg.sender, + address(this), + coins[_coinName], + _value, + "" + ); + } + stakes[msg.sender][_coinName] = stakes[msg.sender][_coinName].add( + _value + ); + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function mintMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _mint(_acc, _id, _value, ""); + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function burnMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _burn(_acc, _id, _value); + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function setAggregationFee(string calldata _coinName, uint256 _value) + external + { + aggregationFee[_coinName] += _value; + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function clearAggregationFee() external { + for (uint256 i = 0; i < coinsName.length; i++) { + delete aggregationFee[coinsName[i]]; + } + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function clearBSHPerifSetting() external { + bshPeriphery = IBSHPeriphery(address(0)); + } + + // @notice This is just an example to show how to add more function in upgrading a contract + function setRefundableBalance( + address _acc, + string calldata _coinName, + uint256 _value + ) external { + balances[_acc][_coinName].refundableBalance += _value; + } + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) + external + override + onlyOwner + { + require(_bshPeriphery != address(0), "InvalidSetting"); + if (address(bshPeriphery) != address(0)) { + require( + bshPeriphery.hasPendingRequest() == false, + "HasPendingRequest" + ); + } + bshPeriphery = IBSHPeriphery(_bshPeriphery); + } + + /** + @notice update base uri. + @dev Caller must be an operator of BTP network + the uri must be initilized in construction. + @param _newURI new uri + */ + function updateUri(string calldata _newURI) external override onlyOwner { + _setURI(_newURI); + } + + /** + @notice set fee ratio. + @dev Caller must be an operator of BTP network + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external override onlyOwner { + // Assuming, adding require() to check input _feeNumerator when upgrading a contract + require(_feeNumerator <= FEE_DENOMINATOR, "InvalidSetting"); + feeNumerator = _feeNumerator; + } + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external override onlyOwner { + require(_fixedFee > 0, "InvalidSetting"); + fixedFee = _fixedFee; + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external override onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() + external + view + override + returns (string[] memory _names) + { + return coinsName; + } + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) + external + view + override + returns (uint256 _coinId) + { + return coins[_coinName]; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + override + returns (bool _valid) + { + return (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])); + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + override + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner, coins[_coinName]), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + override + returns ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ) + { + _usableBalances = new uint256[](_coinNames.length); + _lockedBalances = new uint256[](_coinNames.length); + _refundableBalances = new uint256[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + ( + _usableBalances[i], + _lockedBalances[i], + _refundableBalances[i] + ) = this.getBalanceOf(_owner, _coinNames[i]); + } + return (_usableBalances, _lockedBalances, _refundableBalances); + } + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + override + returns (Types.Asset[] memory _accumulatedFees) + { + _accumulatedFees = new Types.Asset[](coinsName.length); + for (uint256 i = 0; i < coinsName.length; i++) { + _accumulatedFees[i] = ( + Types.Asset(coinsName[i], aggregationFee[coinsName[i]]) + ); + } + return _accumulatedFees; + } + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable override { + // Aggregation Fee will be charged on BSH Contract + // A new charging fee has been proposed. `fixedFee` is introduced + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + uint256 _chargeAmt = + msg.value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // @dev msg.value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage( + msg.sender, + _to, + coinsName[0], + msg.value, + _chargeAmt + ); + } + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer from a Requester (include fee) + @param _to Target BTP address. + */ + function transfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external override { + require(coins[_coinName] != 0, "UnregisterCoin"); + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + uint256 _chargeAmt = + _value.mul(feeNumerator).div(FEE_DENOMINATOR).add(fixedFee); + + // Transfer and Lock Token processes: + // BSHCore contract calls safeTransferFrom() to transfer the Token from Caller's account (msg.sender) + // Before that, Caller must approve (setApproveForAll) to accept + // token being transfer out by an Operator + // If this requirement is failed, a transaction is reverted. + // After transferring token, BSHCore contract updates Caller's locked balance + // as a record of pending transfer transaction + // When a transaction is completed without any error on another chain, + // Locked Token amount (bind to an address of caller) will be reset/subtract, + // then emit a successful TransferEnd event as a notification + // Otherwise, the locked amount will also be updated + // but BSHCore contract will issue a refund to Caller before emitting an error TransferEnd event + this.safeTransferFrom( + msg.sender, + address(this), + coins[_coinName], + _value, + "" + ); + // @dev _value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage(msg.sender, _to, _coinName, _value, _chargeAmt); + } + + /** + @notice This private function handles overlapping procedure before sending a service message to BSHPeriphery + @param _from An address of a Requester + @param _to BTP address of of Receiver on another chain + @param _coinName A given name of a requested coin + @param _value A requested amount to transfer from a Requester (include fee) + @param _chargeAmt An amount being charged for this request + */ + function _sendServiceMessage( + address _from, + string calldata _to, + string memory _coinName, + uint256 _value, + uint256 _chargeAmt + ) private { + // Lock this requested _value as a record of a pending transferring transaction + // @dev `_value` is a requested amount to transfer, from a Requester, including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[0] = _value.sub(_chargeAmt); + lockBalance(_from, _coinName, _value); + string[] memory _coins = new string[](1); + _coins[0] = _coinName; + uint256[] memory _amounts = new uint256[](1); + _amounts[0] = _value.sub(_chargeAmt); + uint256[] memory _fees = new uint256[](1); + _fees[0] = _chargeAmt; + + // @dev `_amounts` is a true amount to receive at a destination after deducting a charged fee + bshPeriphery.sendServiceMessage(_from, _to, _coins, _amounts, _fees); + } + + /** + @notice Allow users to transfer multiple coins/wrapped coins to another chain + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + In case of transferring a native coin, it also checks `msg.value` + The number of requested coins MUST be as the same as the number of requested values + The requested coins and values MUST be matched respectively + @param _coinNames A list of requested transferring coins/wrapped coins + @param _values A list of requested transferring values respectively with its coin name + @param _to Target BTP address. + */ + function transferBatch( + string[] calldata _coinNames, + uint256[] memory _values, + string calldata _to + ) external payable override { + require(_coinNames.length == _values.length, "InvalidRequest"); + uint256 size = + msg.value != 0 ? _coinNames.length.add(1) : _coinNames.length; + uint256[] memory _ids = new uint256[](_coinNames.length); + string[] memory _coins = new string[](size); + uint256[] memory _amounts = new uint256[](size); + uint256[] memory _chargeAmts = new uint256[](size); + + for (uint256 i = 0; i < _coinNames.length; i++) { + _ids[i] = coins[_coinNames[i]]; + // Does not need to check if _coinNames[i] == native_coin + // If _coinNames[i] is a native_coin, coins[_coinNames[i]] = 0 + require(_ids[i] != 0, "UnregisterCoin"); + + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[i] = _coinNames[i]; + _chargeAmts[i] = _values[i] + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[i] = _values[i].sub(_chargeAmts[i]); + + // Lock this requested _value as a record of a pending transferring transaction + // @dev Note that: _value is a requested amount to transfer from a Requester including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[i] = _values[i].sub(_chargeAmts[i]); + lockBalance(msg.sender, _coinNames[i], _values[i]); + } + + this.safeBatchTransferFrom( + msg.sender, + address(this), + _ids, + _values, + "" + ); + + if (msg.value != 0) { + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + _coins[size - 1] = coinsName[0]; // push native_coin at the end of request + _chargeAmts[size - 1] = msg + .value + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + _amounts[size - 1] = msg.value.sub(_chargeAmts[size - 1]); + lockBalance(msg.sender, coinsName[0], msg.value); + } + + // @dev `_amounts` is true amounts to receive at a destination after deducting charged fees + bshPeriphery.sendServiceMessage( + msg.sender, + _to, + _coins, + _amounts, + _chargeAmts + ); + } + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) + external + override + nonReentrant + { + require( + balances[msg.sender][_coinName].refundableBalance >= _value, + "Imbalance" + ); + + balances[msg.sender][_coinName].refundableBalance = balances[ + msg.sender + ][_coinName] + .refundableBalance + .sub(_value); + + this.refund(msg.sender, _coinName, _value); + } + + // Solidity does not allow using try_catch with interal/private function + // Thus, this function would be set as 'external` + // But, it has restriction. It should be called by this contract only + // In addition, there are only two functions calling this refund() + // + handleRequestService(): this function only called by BSHPeriphery + // + reclaim(): this function can be called by ANY + // In case of reentrancy attacks, the chance happenning on BSHPeriphery + // since it requires a request from BMC which requires verification fron BMV + // reclaim() has higher chance to have reentrancy attacks. + // So, it must be prevented by adding 'nonReentrant' + function refund( + address _to, + string calldata _coinName, + uint256 _value + ) external { + require(msg.sender == address(this), "Unauthorized"); + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + this.safeTransferFrom(address(this), _to, _id, _value, ""); + } + } + + function paymentTransfer(address payable _to, uint256 _amount) private { + (bool sent, ) = _to.call{value: _amount}(""); + require(sent, "Payment transfer failed"); + } + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external override onlyBSHPeriphery { + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + _mint(_to, _id, _value, ""); + } + } + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external override onlyBSHPeriphery { + // Fee Gathering and Transfer Coin Request use the same method + // and both have the same response + // In case of Fee Gathering's response, `_requester` is this contract's address + // Thus, check that first + // -- If `_requester` is this contract's address, then check whethere response's code is RC_ERR + // In case of RC_ERR, adding back charged fees to `aggregationFee` state variable + // In case of RC_OK, ignore and return + // -- Otherwise, handle service's response as normal + if (_requester == address(this)) { + if (_rspCode == RC_ERR) { + aggregationFee[_coinName] = aggregationFee[_coinName].add( + _value + ); + } + return; + } + uint256 _amount = _value.add(_fee); + balances[_requester][_coinName].lockedBalance = balances[_requester][ + _coinName + ] + .lockedBalance + .sub(_amount); + + // A new implementation has been proposed to prevent spam attacks + // In receiving error response, BSHCore refunds `_value`, not including `_fee`, back to Requestor + if (_rspCode == RC_ERR) { + try this.refund(_requester, _coinName, _value) {} catch { + balances[_requester][_coinName].refundableBalance = balances[ + _requester + ][_coinName] + .refundableBalance + .add(_value); + } + } else if (_rspCode == RC_OK) { + uint256 _id = coins[_coinName]; + if (_id != 0) { + _burn(address(this), _id, _value); + } + } + aggregationFee[_coinName] = aggregationFee[_coinName].add(_fee); + } + + /** + @notice Handle a request of Fee Gathering + Usage: Copy all charged fees to an array + @dev Caller must be an BSHPeriphery contract + */ + function transferFees(string calldata _fa) + external + override + onlyBSHPeriphery + { + // @dev Due to uncertainty in identifying a size of returning memory array + // and Solidity does not allow to use 'push' with memory array (only storage) + // thus, must use 'temp' storage state variable + for (uint256 i = 0; i < coinsName.length; i++) { + if (aggregationFee[coinsName[i]] != 0) { + chargedCoins.push(coinsName[i]); + chargedAmounts.push(aggregationFee[coinsName[i]]); + delete aggregationFee[coinsName[i]]; + } + } + bshPeriphery.sendServiceMessage( + address(this), + _fa, + chargedCoins, + chargedAmounts, + new uint256[](chargedCoins.length) // chargedFees is an array of 0 since this is a fee gathering request + ); + delete chargedCoins; + delete chargedAmounts; + } + + function lockBalance( + address _to, + string memory _coinName, + uint256 _value + ) private { + balances[_to][_coinName].lockedBalance = balances[_to][_coinName] + .lockedBalance + .add(_value); + } +} diff --git a/solidity/bsh/contracts/test/BSHPeripheryV1.sol b/solidity/bsh/contracts/test/BSHPeripheryV1.sol new file mode 100644 index 00000000..6bef8438 --- /dev/null +++ b/solidity/bsh/contracts/test/BSHPeripheryV1.sol @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../libraries/Types.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/String.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +/** + @title BSHPeriphery contract + @dev This contract is used to handle communications among BMCService and BSHCore contract + @dev OwnerUpgradeable has been removed. This contract does not have its own Owners + Instead, BSHCore manages ownership roles. + Thus, BSHPeriphery should call bshCore.isOwner() and pass an address for verification + in case of implementing restrictions, if needed, in the future. +*/ +contract BSHPeripheryV1 is Initializable, IBSHPeriphery { + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.Response; + using RLPDecodeStruct for bytes; + using SafeMathUpgradeable for uint256; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + + /** @notice Sends a receipt to user + The `_from` sender + The `_to` receiver. + The `_sn` sequence number of service message. + The `_assetDetails` a list of `_coinName` and `_value` + */ + event TransferStart( + address indexed _from, + string _to, + uint256 _sn, + Types.AssetTransferDetail[] _assetDetails + ); + + /** @notice Sends a final notification to a user + The `_from` sender + The `_sn` sequence number of service message. + The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 + The `_response` message of response if error + */ + event TransferEnd( + address indexed _from, + uint256 _sn, + uint256 _code, + string _response + ); + + /** @notice Notify that BSH contract has received unknown response + The `_from` sender + The `_sn` sequence number of service message + */ + event UnknownResponse(string _from, uint256 _sn); + + IBMCPeriphery private bmc; + IBSHCore internal bshCore; + mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests + string public serviceName; // BSH Service Name + + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + uint256 private serialNo; // a counter of sequence number of service message + uint256 private numOfPendingRequests; + + modifier onlyBMC { + require(msg.sender == address(bmc), "Unauthorized"); + _; + } + + function initialize( + address _bmc, + address _bshCore, + string memory _serviceName + ) public initializer { + bmc = IBMCPeriphery(_bmc); + bshCore = IBSHCore(_bshCore); + serviceName = _serviceName; + } + + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view override returns (bool) { + return numOfPendingRequests != 0; + } + + function sendServiceMessage( + address _from, + string memory _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external override { + // Send Service Message to BMC + // If '_to' address is an invalid BTP Address format + // VM throws an error and revert(). Thus, it does not need + // a try_catch at this point + (string memory _toNetwork, string memory _toAddress) = + _to.splitBTPAddress(); + Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); + Types.AssetTransferDetail[] memory _assetDetails = + new Types.AssetTransferDetail[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + _assets[i] = Types.Asset(_coinNames[i], _values[i]); + _assetDetails[i] = Types.AssetTransferDetail( + _coinNames[i], + _values[i], + _fees[i] + ); + } + + serialNo++; + + // Because `stack is too deep`, must create `_strFrom` to waive this error + // `_strFrom` is a string type of an address `_from` + string memory _strFrom = _from.toString(); + bmc.sendMessage( + _toNetwork, + serviceName, + serialNo, + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types + .TransferCoin(_strFrom, _toAddress, _assets) + .encodeTransferCoinMsg() + ) + .encodeServiceMessage() + ); + // Push pending tx into Record list + requests[serialNo] = Types.PendingTransferCoin( + _strFrom, + _to, + _coinNames, + _values, + _fees + ); + numOfPendingRequests++; + emit TransferStart(_from, _to, serialNo, _assetDetails); + } + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); + string memory errMsg; + + if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { + Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); + // checking receiving address whether is a valid address + // revert() if not a valid one + try this.checkParseAddress(_tc.to) { + try this.handleRequestService(_tc.to, _tc.assets) { + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + "", + RC_OK + ); + return; + } catch Error(string memory _err) { + errMsg = _err; + } + } catch { + errMsg = "InvalidAddress"; + } + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + errMsg, + RC_ERR + ); + } else if ( + _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE + ) { + // Check whether '_sn' is pending state + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + Types.Response memory response = _sm.data.decodeResponse(); + // @dev Not implement try_catch at this point + // + If RESPONSE_REQUEST_SERVICE: + // If RC_ERR, BSHCore proceeds a refund. If a refund is failed, BSHCore issues refundable Balance + // If RC_OK: + // - requested coin = native -> update aggregation fee (likely no issue) + // - requested coin = wrapped coin -> BSHCore calls itself to burn its tokens and update aggregation fee (likely no issue) + // The only issue, which might happen, is BSHCore's token balance lower than burning amount + // If so, there might be something went wrong before + // + If RESPONSE_FEE_GATHERING + // If RC_ERR, BSHCore saves charged fees back to `aggregationFee` state mapping variable + // If RC_OK: do nothing + handleResponseService(_sn, response.code, response.message); + } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { + emit UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + sendResponseMessage( + Types.ServiceType.UNKNOWN_TYPE, + _from, + _sn, + "Unknown", + RC_ERR + ); + } + } + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata, /* _src */ + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + handleResponseService(_sn, _code, _msg); + } + + function handleResponseService( + uint256 _sn, + uint256 _code, + string memory _msg + ) private { + address _caller = requests[_sn].from.parseAddress(); + uint256 loop = requests[_sn].coinNames.length; + for (uint256 i = 0; i < loop; i++) { + bshCore.handleResponseService( + _caller, + requests[_sn].coinNames[i], + requests[_sn].amounts[i], + requests[_sn].fees[i], + _code + ); + } + delete requests[_sn]; + numOfPendingRequests--; + emit TransferEnd(_caller, _sn, _code, _msg); + } + + /** + @notice Handle a list of minting/transferring coins/tokens + @dev Caller must be BMC contract only + @param _to An address to receive coins/tokens + @param _assets A list of requested coin respectively with an amount + */ + function handleRequestService( + string memory _to, + Types.Asset[] memory _assets + ) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint256 i = 0; i < _assets.length; i++) { + require( + bshCore.isValidCoin(_assets[i].coinName) == true, + "UnregisteredCoin" + ); + // @dev There might be many errors generating by BSHCore contract + // which includes also low-level error + // Thus, must use try_catch at this point so that it can return an expected response + try + bshCore.mint( + _to.parseAddress(), + _assets[i].coinName, + _assets[i].value + ) + {} catch { + revert("TransferFailed"); + } + } + } + + function sendResponseMessage( + Types.ServiceType _serviceType, + string memory _to, + uint256 _sn, + string memory _msg, + uint256 _code + ) private { + bmc.sendMessage( + _to, + serviceName, + _sn, + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage() + ); + } + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override + onlyBMC + { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + // If adress of Fee Aggregator (_fa) is invalid BTP address format + // revert(). Then, BMC will catch this error + _fa.splitBTPAddress(); + bshCore.transferFees(_fa); + } + + // @dev Solidity does not allow to use try_catch with internal function + // Thus, this is a work-around solution + // Since this function is basically checking whether a string address + // can be parsed to address type. Hence, it would not have any restrictions + function checkParseAddress(string calldata _to) external pure { + _to.parseAddress(); + } +} diff --git a/solidity/bsh/contracts/test/BSHPeripheryV2.sol b/solidity/bsh/contracts/test/BSHPeripheryV2.sol new file mode 100644 index 00000000..6d01c8e8 --- /dev/null +++ b/solidity/bsh/contracts/test/BSHPeripheryV2.sol @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../libraries/Types.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/String.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +/** + @title Interface of BSH Coin transfer service + @dev This contract use to handle coin transfer service + Note: The coin of following interface can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHPeripheryV2 is Initializable, IBSHPeriphery { + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.Response; + using RLPDecodeStruct for bytes; + using SafeMathUpgradeable for uint256; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + + /** @notice Sends a receipt to user + The `_from` sender + The `_to` receiver. + The `_sn` sequence number of service message. + The `_assetDetails` a list of `_coinName` and `_value` + */ + event TransferStart( + address indexed _from, + string _to, + uint256 _sn, + Types.AssetTransferDetail[] _assetDetails + ); + + /** @notice Sends a final notification to a user + The `_from` sender + The `_sn` sequence number of service message. + The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 + The `_response` message of response if error + */ + event TransferEnd( + address indexed _from, + uint256 _sn, + uint256 _code, + string _response + ); + + /** @notice Notify that BSH contract has received unknown response + The `_from` sender + The `_sn` sequence number of service message + */ + event UnknownResponse(string _from, uint256 _sn); + + IBMCPeriphery private bmc; + IBSHCore internal bshCore; + mapping(uint256 => Types.PendingTransferCoin) internal requests; // a list of transferring requests + string public serviceName; // BSH Service Name + + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + uint256 private serialNo; // a counter of sequence number of service message + uint256 private numOfPendingRequests; + + modifier onlyBMC { + require(msg.sender == address(bmc), "Unauthorized"); + _; + } + + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view override returns (bool) { + return numOfPendingRequests != 0; + } + + // @notice This is just an example of how to add more function in upgrading a contract + function getServiceName() external view returns (string memory) { + return serviceName; + } + + // @notice This is just an example of how to add more function in upgrading a contract + function getPendingRequest(uint256 _sn) + external + view + returns (Types.PendingTransferCoin memory) + { + return requests[_sn]; + } + + // @notice This is just an example of how to add more function in upgrading a contract + function getAggregationFeeOf(string calldata _coinName) + external + view + returns (uint256 _fee) + { + Types.Asset[] memory _fees = bshCore.getAccumulatedFees(); + for (uint256 i = 0; i < _fees.length; i++) { + if (_coinName.compareTo(_fees[i].coinName)) return _fees[i].value; + } + } + + function sendServiceMessage( + address _from, + string memory _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external override { + // Send Service Message to BMC + // If '_to' address is an invalid BTP Address format + // VM throws an error and revert(). Thus, it does not need + // a try_catch at this point + (string memory _toNetwork, string memory _toAddress) = + _to.splitBTPAddress(); + Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); + Types.AssetTransferDetail[] memory _assetDetails = + new Types.AssetTransferDetail[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + _assets[i] = Types.Asset(_coinNames[i], _values[i]); + _assetDetails[i] = Types.AssetTransferDetail( + _coinNames[i], + _values[i], + _fees[i] + ); + } + + serialNo++; + + // Because `stack is too deep`, must create `_strFrom` to waive this error + // `_strFrom` is a string type of an address `_from` + string memory _strFrom = _from.toString(); + bmc.sendMessage( + _toNetwork, + serviceName, + serialNo, + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types + .TransferCoin(_strFrom, _toAddress, _assets) + .encodeTransferCoinMsg() + ) + .encodeServiceMessage() + ); + // Push pending tx into Record list + requests[serialNo] = Types.PendingTransferCoin( + _strFrom, + _to, + _coinNames, + _values, + _fees + ); + numOfPendingRequests++; + emit TransferStart(_from, _to, serialNo, _assetDetails); + } + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); + string memory errMsg; + + if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { + Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); + // checking receiving address whether is a valid address + // revert() if not a valid one + try this.checkParseAddress(_tc.to) { + try this.handleRequestService(_tc.to, _tc.assets) { + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + "", + RC_OK + ); + return; + } catch Error(string memory _err) { + errMsg = _err; + } + } catch { + errMsg = "InvalidAddress"; + } + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + errMsg, + RC_ERR + ); + } else if ( + _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE + ) { + // Check whether '_sn' is pending state + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + Types.Response memory response = _sm.data.decodeResponse(); + // @dev Not implement try_catch at this point + // + If RESPONSE_REQUEST_SERVICE: + // If RC_ERR, BSHCore proceeds a refund. If a refund is failed, BSHCore issues refundable Balance + // If RC_OK: + // - requested coin = native -> update aggregation fee (likely no issue) + // - requested coin = wrapped coin -> BSHCore calls itself to burn its tokens and update aggregation fee (likely no issue) + // The only issue, which might happen, is BSHCore's token balance lower than burning amount + // If so, there might be something went wrong before + // + If RESPONSE_FEE_GATHERING + // If RC_ERR, BSHCore saves charged fees back to `aggregationFee` state mapping variable + // If RC_OK: do nothing + handleResponseService(_sn, response.code, response.message); + } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { + emit UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + sendResponseMessage( + Types.ServiceType.UNKNOWN_TYPE, + _from, + _sn, + "Unknown", + RC_ERR + ); + } + } + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata, /* _src */ + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + handleResponseService(_sn, _code, _msg); + } + + function handleResponseService( + uint256 _sn, + uint256 _code, + string memory _msg + ) private { + address _caller = requests[_sn].from.parseAddress(); + uint256 loop = requests[_sn].coinNames.length; + for (uint256 i = 0; i < loop; i++) { + bshCore.handleResponseService( + _caller, + requests[_sn].coinNames[i], + requests[_sn].amounts[i], + requests[_sn].fees[i], + _code + ); + } + delete requests[_sn]; + numOfPendingRequests--; + emit TransferEnd(_caller, _sn, _code, _msg); + } + + /** + @notice Handle a list of minting/transferring coins/tokens + @dev Caller must be BMC contract only + @param _to An address to receive coins/tokens + @param _assets A list of requested coin respectively with an amount + */ + function handleRequestService( + string memory _to, + Types.Asset[] memory _assets + ) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint256 i = 0; i < _assets.length; i++) { + require( + bshCore.isValidCoin(_assets[i].coinName) == true, + "UnregisteredCoin" + ); + // @dev There might be many errors generating by BSHCore contract + // which includes also low-level error + // Thus, must use try_catch at this point so that it can return an expected response + try + bshCore.mint( + _to.parseAddress(), + _assets[i].coinName, + _assets[i].value + ) + {} catch { + revert("TransferFailed"); + } + } + } + + function sendResponseMessage( + Types.ServiceType _serviceType, + string memory _to, + uint256 _sn, + string memory _msg, + uint256 _code + ) private { + bmc.sendMessage( + _to, + serviceName, + _sn, + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage() + ); + } + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override + onlyBMC + { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + // If adress of Fee Aggregator (_fa) is invalid BTP address format + // revert(). Then, BMC will catch this error + _fa.splitBTPAddress(); + bshCore.transferFees(_fa); + } + + // @dev Solidity does not allow to use try_catch with internal function + // Thus, this is a work-around solution + // Since this function is basically checking whether a string address + // can be parsed to address type. Hence, it would not have any restrictions + function checkParseAddress(string calldata _to) external pure { + _to.parseAddress(); + } +} diff --git a/solidity/bsh/contracts/test/CheckParseAddress.sol b/solidity/bsh/contracts/test/CheckParseAddress.sol new file mode 100644 index 00000000..b319482e --- /dev/null +++ b/solidity/bsh/contracts/test/CheckParseAddress.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/ParseAddress.sol"; + +contract CheckParseAddress { + using ParseAddress for address; + using ParseAddress for string; + + function convertAddressToString(address _addr) + external + pure + returns (string memory strAddr) + { + strAddr = _addr.toString(); + } + + function convertStringToAddress(string calldata _addr) + external + pure + returns (address addr) + { + addr = _addr.parseAddress(); + } +} diff --git a/solidity/bsh/contracts/test/EncodeMessage.sol b/solidity/bsh/contracts/test/EncodeMessage.sol new file mode 100644 index 00000000..2dc4a2b2 --- /dev/null +++ b/solidity/bsh/contracts/test/EncodeMessage.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; + +contract EncodeMessage { + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.Response; + using RLPEncodeStruct for Types.GatherFeeMessage; + using RLPEncodeStruct for Types.BMCService; + using RLPEncodeStruct for string[]; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + + function encodeTransferFeesBMCMessage( + string memory _fromBMC, + string memory _toBMC, + string memory _toAddress, + string memory _svc, + int256 _sn, + address _bsh, + Types.Asset[] memory _fees + ) external pure returns (bytes memory) { + (, string memory _to) = _toAddress.splitBTPAddress(); + return + Types + .BMCMessage( + _fromBMC, + _toBMC, + _svc, + _sn, + encodeServiceMessage(_bsh.toString(), _to, _fees) + ) + .encodeBMCMessage(); + } + + function encodeBMCService(string calldata _fa, string[] memory _svcs) + external + pure + returns (bytes memory) + { + return + Types + .BMCService( + "FeeGathering", + Types.GatherFeeMessage(_fa, _svcs).encodeGatherFeeMessage() + ) + .encodeBMCService(); + } + + function encodeResponseBMCMessage( + string memory _from, + string memory _to, + string memory _svc, + int256 _sn, + uint256 _code, + string memory _msg + ) external view returns (bytes memory) { + return + Types + .BMCMessage( + _from, + _to, + _svc, + _sn, + this.encodeResponseMsg( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _code, + _msg + ) + ) + .encodeBMCMessage(); + } + + function hashCoinName(string memory _coinName) + external + pure + returns (uint256) + { + return uint256(keccak256(abi.encodePacked(_coinName))); + } + + function encodeResponseMsg( + Types.ServiceType _serviceType, + uint256 _code, + string calldata _msg + ) external pure returns (bytes memory) { + return + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage(); + } + + function encodeBatchTransferMsgWithAddress( + string calldata _from, + address _to, + Types.Asset[] memory _assets + ) external pure returns (bytes memory) { + return encodeServiceMessage(_from, _to.toString(), _assets); + } + + function encodeBatchTransferMsgWithStringAddress( + string calldata _from, + string calldata _to, + Types.Asset[] memory _assets + ) external pure returns (bytes memory) { + return encodeServiceMessage(_from, _to, _assets); + } + + function encodeTransferMsgWithAddress( + string calldata _from, + address _to, + string memory _coinName, + uint256 _value + ) external pure returns (bytes memory) { + Types.Asset[] memory asset = new Types.Asset[](1); + asset[0] = Types.Asset(_coinName, _value); + return encodeServiceMessage(_from, _to.toString(), asset); + } + + function encodeTransferMsgWithStringAddress( + string calldata _from, + string calldata _to, + string calldata _coinName, + uint256 _value + ) external pure returns (bytes memory) { + Types.Asset[] memory asset = new Types.Asset[](1); + asset[0] = Types.Asset(_coinName, _value); + return encodeServiceMessage(_from, _to, asset); + } + + function encodeServiceMessage( + string memory _from, + string memory _to, + Types.Asset[] memory _asset + ) private pure returns (bytes memory) { + return + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types.TransferCoin(_from, _to, _asset).encodeTransferCoinMsg() + ) + .encodeServiceMessage(); + } +} diff --git a/solidity/bsh/contracts/test/Holder.sol b/solidity/bsh/contracts/test/Holder.sol new file mode 100644 index 00000000..bd32a412 --- /dev/null +++ b/solidity/bsh/contracts/test/Holder.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol"; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../libraries/String.sol"; + +contract Holder is ERC1155Holder { + IBSHPeriphery private bshp; + IBSHCore private bshc; + using String for string; + + function deposit() external payable {} + + function addBSHContract(address _bshp, address _bshc) external { + bshp = IBSHPeriphery(_bshp); + bshc = IBSHCore(_bshc); + } + + function setApprove(address _operator) external { + bshc.setApprovalForAll(_operator, true); + } + + function callTransfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external { + bshc.transfer(_coinName, _value, _to); + } + + // function isSendingNative(string[] memory _coinNames) + // private + // pure + // returns (int256) + // { + // for (uint256 i = 0; i < _coinNames.length; i++) { + // if (_coinNames[i].compareTo("PARA")) { + // return int256(i); + // } + // } + // return -1; + // } + + function callTransferBatch( + address _bsh, + string[] memory _coinNames, + uint256[] memory _values, + string calldata _to, + uint256 _native + ) external { + // int256 pos = isSendingNative(_coinNames); + if (_native != 0) { + (bool success, bytes memory err) = + _bsh.call{value: _native}( + abi.encodeWithSignature( + "transferBatch(string[],uint256[],string)", + _coinNames, + _values, + _to + ) + ); + require(success, string(err)); + } else { + bshc.transferBatch(_coinNames, _values, _to); + } + } +} diff --git a/solidity/bsh/contracts/test/MockBMC.sol b/solidity/bsh/contracts/test/MockBMC.sol new file mode 100644 index 00000000..9f9eff1d --- /dev/null +++ b/solidity/bsh/contracts/test/MockBMC.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./BMC.sol"; + +contract MockBMC is BMC { + constructor(string memory _network) BMC(_network) {} + + function receiveRequest( + string calldata _src, + string memory _dst, + string memory _svc, + uint256 _sn, + bytes calldata _msg + ) external { + handleMessage( + _src, + Types.BMCMessage(_src, _dst, _svc, int256(_sn), _msg) + ); + } + + function receiveResponse( + string calldata _from, + string memory _svc, + uint256 _sn, + bytes calldata _msg + ) external { + IBSH(bshServices[_svc]).handleBTPMessage(_from, _svc, _sn, _msg); + } + + function getBalance(address _addr) external view returns (uint256) { + return _addr.balance; + } +} diff --git a/solidity/bsh/contracts/test/MockBSHCore.sol b/solidity/bsh/contracts/test/MockBSHCore.sol new file mode 100644 index 00000000..d95b2a4b --- /dev/null +++ b/solidity/bsh/contracts/test/MockBSHCore.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../BSHCore.sol"; + +contract MockBSHCore is BSHCore { + function mintMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _mint(_acc, _id, _value, ""); + } + + function burnMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _burn(_acc, _id, _value); + } + + function setAggregationFee(string calldata _coinName, uint256 _value) + external + { + aggregationFee[_coinName] += _value; + } + + function clearAggregationFee() external { + for (uint256 i = 0; i < coinsName.length; i++) { + delete aggregationFee[coinsName[i]]; + } + } + + function clearBSHPerifSetting() external { + bshPeriphery = IBSHPeriphery(address(0)); + } + + function setRefundableBalance( + address _acc, + string calldata _coinName, + uint256 _value + ) external { + balances[_acc][_coinName].refundableBalance += _value; + } +} diff --git a/solidity/bsh/contracts/test/MockBSHPerif.sol b/solidity/bsh/contracts/test/MockBSHPerif.sol new file mode 100644 index 00000000..8fe1a50f --- /dev/null +++ b/solidity/bsh/contracts/test/MockBSHPerif.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../BSHPeriphery.sol"; +import "../BSHCore.sol"; + +contract MockBSHPeriphery is BSHPeriphery { + using String for string; + + function getFees(uint256 _sn) + external + view + returns (Types.PendingTransferCoin memory) + { + return requests[_sn]; + } + + function getAggregationFeeOf(string calldata _coinName) + external + view + returns (uint256 _fee) + { + Types.Asset[] memory _fees = bshCore.getAccumulatedFees(); + for (uint256 i = 0; i < _fees.length; i++) { + if (_coinName.compareTo(_fees[i].coinName)) return _fees[i].value; + } + } +} diff --git a/solidity/bsh/contracts/test/NonRefundable.sol b/solidity/bsh/contracts/test/NonRefundable.sol new file mode 100644 index 00000000..27308a66 --- /dev/null +++ b/solidity/bsh/contracts/test/NonRefundable.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +// Instead, it has a method deposit() payable to receive coins +contract NonRefundable { + function deposit() external payable {} + + function transfer( + address _bsh, + string calldata _to, + uint256 _amt + ) external { + (bool success, bytes memory err) = + _bsh.call{value: _amt}( + abi.encodeWithSignature("transferNativeCoin(string)", _to) + ); + require(success, string(err)); + } +} diff --git a/solidity/bsh/contracts/test/NotPayable.sol b/solidity/bsh/contracts/test/NotPayable.sol new file mode 100644 index 00000000..cb4d6d77 --- /dev/null +++ b/solidity/bsh/contracts/test/NotPayable.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +contract NotPayable { + +} diff --git a/solidity/bsh/contracts/test/Refundable.sol b/solidity/bsh/contracts/test/Refundable.sol new file mode 100644 index 00000000..79db438a --- /dev/null +++ b/solidity/bsh/contracts/test/Refundable.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +// Instead, it has a method deposit() payable to receive coins +contract Refundable { + function deposit() external payable {} + + function transfer( + address _bsh, + string calldata _to, + uint256 _amt + ) external { + (bool success, bytes memory err) = + _bsh.call{value: _amt}( + abi.encodeWithSignature("transferNativeCoin(string)", _to) + ); + require(success, string(err)); + } + + receive() external payable {} +} diff --git a/solidity/bsh/migrations/1_deploy_bsh.js b/solidity/bsh/migrations/1_deploy_bsh.js new file mode 100644 index 00000000..d372eba9 --- /dev/null +++ b/solidity/bsh/migrations/1_deploy_bsh.js @@ -0,0 +1,12 @@ +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +module.exports = async function (deployer, network) { + if (network !== "development") { + await deployProxy(BSHCore, [process.env.BSH_COIN_URL, process.env.BSH_COIN_NAME, parseInt(process.env.BSH_COIN_FEE), parseInt(process.env.BSH_FIXED_FEE)], { deployer }); + await deployProxy(BSHPeriphery, [process.env.BMC_PERIPHERY_ADDRESS, BSHCore.address, process.env.BSH_SERVICE], { deployer }); + const bshCore = await BSHCore.deployed(); + await bshCore.updateBSHPeriphery(BSHPeriphery.address); + } +}; diff --git a/solidity/bsh/package.json b/solidity/bsh/package.json new file mode 100644 index 00000000..cb2ef0f2 --- /dev/null +++ b/solidity/bsh/package.json @@ -0,0 +1,35 @@ +{ + "name": "pra-bsh", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@openzeppelin/contracts": "^3.4.1-solc-0.7-2", + "@openzeppelin/contracts-upgradeable": "^3.4.1-solc-0.7-2", + "@openzeppelin/truffle-upgrades": "^1.7.0", + "@truffle/hdwallet-provider": "^1.4.1" + }, + "devDependencies": { + "chai": "^4.3.4", + "husky": "^6.0.0", + "prettier": "^2.2.1", + "prettier-plugin-solidity": "^1.0.0-beta.7", + "rlp": "^2.2.6", + "solhint": "^3.3.4", + "solhint-plugin-prettier": "^0.0.5", + "truffle-assertions": "^0.9.2" + }, + "scripts": { + "linter": "./node_modules/.bin/solhint -f table ./contracts/**/*.sol -f table ./contracts/*.sol", + "prettier": "./node_modules/.bin/prettier --write ./contracts -l", + "contract:compile": "truffle compile --all", + "test": "yarn test:unit && yarn test:integration", + "test:unit": "rm -rf .openzeppelin && truffle test test/unit/*.js", + "test:integration" : "rm -rf .openzeppelin && truffle test test/integration/*.js" + + }, + "husky": { + "hooks": { + "pre-push": "yarn linter && yarn prettier" + } + } +} diff --git a/solidity/bsh/scripts/register_coin.js b/solidity/bsh/scripts/register_coin.js new file mode 100644 index 00000000..119f61c8 --- /dev/null +++ b/solidity/bsh/scripts/register_coin.js @@ -0,0 +1,13 @@ +const BSHCore = artifacts.require("BSHCore"); + +module.exports = async function (callback) { + try { + const bshCore = await BSHCore.deployed(); + console.log(await bshCore.register(process.env.NEXTLINK_BTP_NATIVECOIN_NAME)); + console.log(await bshCore.coinNames()); + } catch (error) { + console.log(error) + } finally { + callback() + } +} diff --git a/solidity/bsh/test/integration/1-upgradeability-native-coin-bsh.js b/solidity/bsh/test/integration/1-upgradeability-native-coin-bsh.js new file mode 100644 index 00000000..37720b0a --- /dev/null +++ b/solidity/bsh/test/integration/1-upgradeability-native-coin-bsh.js @@ -0,0 +1,2692 @@ +const BSHPerifV1 = artifacts.require("BSHPeripheryV1"); +const BSHPerifV2 = artifacts.require("BSHPeripheryV2"); +const BSHCoreV1 = artifacts.require("BSHCoreV1"); +const BSHCoreV2 = artifacts.require("BSHCoreV2"); +const BMC = artifacts.require("MockBMC"); +const Holder = artifacts.require("AnotherHolder"); +const NotPayable = artifacts.require("NotPayable"); +const NonRefundable = artifacts.require("NonRefundable"); +const Refundable = artifacts.require("Refundable"); +const EncodeMsg = artifacts.require("EncodeMessage"); +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades'); +const rlp = require('rlp'); + +let toHex = (buf) => { + buf = buf.toString('hex'); + if (buf.substring(0, 2) == '0x') + return buf; + return '0x' + buf.toString('hex'); +}; + +// All of these unit tests below check upgradability of smart contract using Openzeppelin Library and SDK +// BSHService and BSHCoin contracts have two versions +// These two versions cover many upgradeable features: +// - Adding additional state variables +// - Adding additional functions +contract('NativeCoinBSH contracts - After Upgrading Contract', (accounts) => { + describe('PRA BSHCore Query and Management - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp' + let _net = '1234.iconee'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + await bmc.addService(service, bsh_perifV2.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + }); + + it(`Scenario 1: Contract's owner to register a new coin`, async () => { + let _name = "ICON"; + await bsh_coreV2.register(_name); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' + ); + }); + + it('Scenario 2: Non-ownership role client registers a new coin', async () => { + let _name = "TRON"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 3: Contract’s owner registers an existed coin', async () => { + let _name = "ICON"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name), + "ExistToken" + ); + }); + + it('Scenario 4: Contract’s owner to update BSHPeriphery contract', async () => { + await bsh_coreV2.updateBSHPeriphery(bsh_perifV2.address); + }); + + it('Scenario 5: Non-ownership role client updates BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_coreV2.updateBSHPeriphery.call(bsh_perifV2.address, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 6: Contract’s owner updates BSHPeriphery while this contract has pending requests', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + await bsh_coreV2.transferNativeCoin(_to, {from: accounts[0], value: 100000000}); + await truffleAssert.reverts( + bsh_coreV2.updateBSHPeriphery.call(accounts[2]), + "HasPendingRequest" + ); + // Clear pending request + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net, service, 1, _msg); + }); + + + it('Scenario 7: Contract’s owner updates a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri); + }); + + it('Scenario 8: Non-ownership role client updates a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_coreV2.updateUri.call(new_uri, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it(`Scenario 9: Contract's owner updates fee ratio`, async () => { + let new_fee = 20; + await bsh_coreV2.setFeeRatio(new_fee); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 10: Non-ownership role client updates fee ratio', async () => { + let old_fee = 20; + let new_fee = 50; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 11: Fee Numerator is set higher than Fee Denominator', async () => { + let old_fee = 20; + let new_fee = 20000; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 12: Contract owner updates fixed fee', async () => { + let new_fixed_fee = 1000000; + await bsh_coreV2.setFixedFee(new_fixed_fee); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 13: Non-ownership role client updates fixed fee', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 2000000; + await truffleAssert.reverts( + bsh_coreV2.setFixedFee.call(new_fixed_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 14: Owner set fixed fee is zero', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 0; + await truffleAssert.reverts( + bsh_coreV2.setFixedFee.call(new_fixed_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 15: Query a valid supporting coin', async () => { + let _name1 = "wBTC"; let _name2 = "Ethereum"; + await bsh_coreV2.register(_name1); + await bsh_coreV2.register(_name2); + + let _query = "ICON"; + let id = web3.utils.keccak256(_query); + let result = await bsh_coreV2.coinId(_query); + assert( + web3.utils.BN(result).toString() === web3.utils.toBN(id).toString() + ); + }); + + it('Scenario 16: Query an invalid supporting coin', async () => { + let _query = "EOS"; + let result = await bsh_coreV2.coinId(_query); + assert( + web3.utils.BN(result).toNumber() === 0 + ); + }); + + it('Scenario 17: Non-Owner tries to add a new Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.addOwner.call(accounts[1], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 1 && newList[0] === accounts[0] + ); + }); + + it('Scenario 18: Current Owner adds a new Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await bsh_coreV2.addOwner(accounts[1]); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 19: After adding a new Owner, owner registers a new coin', async () => { + let _name3 = "TRON"; + await bsh_coreV2.register(_name3); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' + ); + }); + + it('Scenario 20: New Owner registers a new coin', async () => { + let _name3 = "BINANCE"; + await bsh_coreV2.register(_name3, {from: accounts[1]}); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 21: New owner updates BSHPeriphery contract', async () => { + let newBSHPerif = await BSHPerifV2.new(); + await bsh_coreV2.updateBSHPeriphery(newBSHPerif.address, {from: accounts[1]}); + }); + + it('Scenario 22: Old owner updates BSHPeriphery contract', async () => { + let newBSHPerif = await BSHPerifV2.new(); + await bsh_coreV2.updateBSHPeriphery(newBSHPerif.address, {from: accounts[0]}); + }); + + it('Scenario 23: New owner updates the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri, {from: accounts[1]}); + }); + + it('Scenario 24: Old owner updates the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri, {from: accounts[0]}); + }); + + it('Scenario 25: New owner updates new fee ratio', async () => { + let new_fee = 30; + await bsh_coreV2.setFeeRatio(new_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 26: Old owner updates new fee ratio - After adding new Owner', async () => { + let new_fee = 40; + await bsh_coreV2.setFeeRatio(new_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 27: New owner updates new fixed fee', async () => { + let new_fixed_fee = 3000000; + await bsh_coreV2.setFixedFee(new_fixed_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 28: Old owner updates new fixed fee - After adding new Owner', async () => { + let new_fixed_fee = 4000000; + await bsh_coreV2.setFixedFee(new_fixed_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 29: Non-Owner tries to remove an Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.removeOwner.call(accounts[0], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 30: Current Owner removes another Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await bsh_coreV2.removeOwner(accounts[0], {from: accounts[1]}); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 31: The last Owner removes him/herself', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.removeOwner.call(accounts[1], {from: accounts[1]}), + "Unable to remove last Owner" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 32: Removed Owner tries to register a new coin', async () => { + let _name3 = "KYBER"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name3), + 'Unauthorized' + ); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 33: Removed Owner tries to update BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_coreV2.updateBSHPeriphery.call(accounts[3], {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 34: Removed Owner tries to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_coreV2.updateUri.call(new_uri, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 35: Removed Owner tries to update new fee ratio', async () => { + let new_fee = 30; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee, {from: accounts[0]}), + 'Unauthorized' + ); + }); + }); + + describe('As a user, I want to send PRA to ICON blockchain - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, bmc, nonrefundable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let RC_OK = 0; let RC_ERR = 1; + let _fixed_fee = 500000; let deposit = 1000000000000; + let _native = 'PARA'; let _fee = 10; + let REPONSE_HANDLE_SERVICE = 2; let _uri = 'https://github.com/icon-project/btp'; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service,bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + nonrefundable = await NonRefundable.new(); + refundable = await Refundable.new(); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + }); + + it('Scenario 1: Transferring native coins to an invalid BTP Address format', async () => { + let invalid_destination = '1234.iconee/0x12345678'; + let amount = 600000; + await truffleAssert.reverts( + bsh_coreV2.transferNativeCoin.call(invalid_destination, {from: accounts[0], value: amount}), + "revert" + ); + bsh_coin_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + account_balance = await bsh_coreV2.getBalanceOf(accounts[0], _native); + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === 0 && + web3.utils.BN(account_balance._lockedBalance).toNumber() === 0 + ); + }); + + it('Scenario 2: Transferring zero coin' , async () => { + await truffleAssert.reverts( + bsh_coreV2.transferNativeCoin.call(_to, {from: accounts[0], value: 0}), + "revert" + ); + }); + + it('Scenario 3: msg.value less than fixed_fee' , async () => { + // fixed_fee = 500000; + let amount = 100000; + await truffleAssert.reverts( + bsh_coreV2.transferNativeCoin.call(_to, {from: accounts[0], value: amount}), + "revert" + ); + }); + + it('Scenario 4: Transferring to an invalid network/not supported network' , async () => { + let invalid_destination = 'btp://1234.eos/0x12345678'; + let amount = 600000; + await truffleAssert.reverts( + bsh_coreV2.transferNativeCoin.call(invalid_destination, {from: accounts[1], value: amount}), + "BMCRevertNotExistsBMV" + ); + }); + + it('Scenario 5: Account client transfers a valid native coin to a side chain', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let tx = await bsh_coreV2.transferNativeCoin(_to, {from: accounts[0], value: amount}); + let account_balanceAfter = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let bsh_coin_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let chargedFee = Math.floor(amount/ 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[0]); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === amount && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === amount + ); + }); + + it('Scenario 6: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let account_balanceAfter = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let fees = await bsh_coreV2.getAccumulatedFees(); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[0]); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert( + fees[0].coinName === _native && + Number(fees[0].value) === (Math.floor(amount/ 1000) + _fixed_fee) && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === amount && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === 0 + ); + }); + + it('Scenario 5: Account client transfers a valid native coin to a side chain', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let tx = await bsh_coreV2.transferNativeCoin(_to, {from: accounts[0], value: amount}); + let account_balanceAfter = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let bsh_coin_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[0]); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === 2 * amount && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === amount + ); + }); + + it('Scenario 7: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let account_balanceBefore = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let bsh_coin_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _msg); + let account_balanceAfter = await bsh_coreV2.getBalanceOf(accounts[0], _native); + let bsh_coin_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[0]); + assert.equal(event._sn, 2); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + // Unable to check balance of accounts[0] since this account has also paid gas fee + // It would be easier to check if this is a contract + // Requestor will be receive an amount of refund as + // refund = amount - chargeAmt + assert( + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === amount && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._refundableBalance).toNumber() === 0 && + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() === 2 * amount && + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() === amount + chargedFee + ); + }); + + it('Scenario 8: Non-refundable contract transfers a valid native coin to a side chain', async () => { + let amount = 600000; + await nonrefundable.deposit({from: accounts[2], value: deposit}); + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let tx = await nonrefundable.transfer(bsh_coreV2.address, _to, amount); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, nonrefundable.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 3); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 3); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), nonrefundable.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() === + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + amount && + web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber() === amount && + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() === amount + chargedFee && + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() === 2 * amount + chargedFee + ); + }); + + it(`Scenario 9: BSHPeriphery receives an error response of a recent request and fails to refund coins back to Non-refundable contract`, async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 3, _msg); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, nonrefundable.address); + assert.equal(event._sn, 3); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(contract_balanceAfter._refundableBalance).toNumber(), amount - chargedFee); + assert.equal( + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() + ); + }); + + it('Scenario 10: Refundable contract transfers a valid native coin to a side chain', async () => { + let amount = 600000; + await refundable.deposit({from: accounts[2], value: deposit}); + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let tx = await refundable.transfer(bsh_coreV2.address, _to, amount); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, refundable.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 4); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 4); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), refundable.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + amount + ); + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() + amount + ); + }); + + it('Scenario 11: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 4, _msg); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _native); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, refundable.address); + assert.equal(event._sn, 4); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber(), + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() + amount - chargedFee + ); + assert.equal(web3.utils.BN(contract_balanceAfter._refundableBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() + amount - chargedFee + ); + }); + }); + + describe('As a user, I want to send ERC1155_ICX to ICON blockchain - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name = 'ICON'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; let _value = 999999999999999; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; let RC_ERR = 1; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + holder = await Holder.new(); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perifV2.address, bsh_coreV2.address); + await bsh_coreV2.register(_name); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _name, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg); + id = await bsh_coreV2.coinId(_name); + }); + + it('Scenario 1: User has not yet set approval for token being transferred out by Operator', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 600000; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await truffleAssert.reverts( + holder.callTransfer.call(_name, _value, _to), + "ERC1155: caller is not owner nor approved" + ); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + }); + + it(`Scenario 2: User has set approval, but user's balance has insufficient amount`, async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 9999999999999999n; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, _value, _to), + "ERC1155: insufficient balance for transfer" + ); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + }); + + it('Scenario 3: User requests to transfer an invalid Token', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 9999999999999999n; + let _token = 'EOS'; + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_token, _value, _to), + "UnregisterCoin" + ); + + }); + + it('Scenario 4: User transfers Tokens to an invalid BTP Address format', async () => { + let _to = '1234.iconee/0x12345678'; + let amount = 600000; + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "VM Exception while processing transaction: revert" + ); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber(), + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), 0); + }); + + it('Scenario 5: User requests to transfer zero Token', async () => { + let _to = '1234.iconee/0x12345678'; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, 0, _to), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + }); + + it('Scenario 6: Transferring amount is less than fixed fee', async () => { + let _to = '1234.iconee/0x12345678'; + let _name = 'ICON'; + let amount = 100000; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + }); + + it('Scenario 7: User requests to transfer to an invalid network/Not Supported Network', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _name = 'ICON'; + let amount = 600000; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "BMCRevertNotExistsBMV" + ); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), 0); + }); + + it('Scenario 8: User sends a valid transferring request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let amount = 600000; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_coreV2.address); + let tx = await holder.callTransfer(_name, amount, _to); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, _name); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _name); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() - amount + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), amount); + }); + + it('Scenario 9: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let contract_balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + let fees = await bsh_coreV2.getAccumulatedFees(); + let bsh_core_balance = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), chargedFee); + assert.equal(fees[1].coinName, _name); + assert.equal(Number(fees[1].value), chargedFee) + }); + + it('Scenario 8: User sends a valid transferring request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let amount = 100000000000000; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + let bsh_core_balance_before = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + await holder.setApprove(bsh_coreV2.address); + let tx = await holder.callTransfer(_name, amount, _to); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + let bsh_core_balance_after = await bsh_coreV2.getBalanceOf(bsh_coreV2.address, _name); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, _name); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _name); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() - amount + ); + assert.equal( + web3.utils.BN(bsh_core_balance_after._usableBalance).toNumber(), + web3.utils.BN(bsh_core_balance_before._usableBalance).toNumber() + amount + ); + }); + + it('Scenario 10: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 100000000000000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let balanceBefore = await bsh_coreV2.getBalanceOf(holder.address, _name); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _msg); + let balanceAfter = await bsh_coreV2.getBalanceOf(holder.address, _name); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 2); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + amount - chargedFee + ); + assert.equal(web3.utils.BN(balanceAfter._refundableBalance).toNumber(), 0); + }); + }); + + describe('As a user, I want to receive PRA from ICON blockchain - After Upgrading Contract', () => { + let bmc, bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, notpayable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let RC_ERR = 1; let RC_OK = 0; + let _uri = 'https://github.com/icon-project/btp'; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + notpayable = await NotPayable.new(); + refundable = await Refundable.new(); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await bsh_coreV2.transferNativeCoin(_to, {from: accounts[0], value: 100000000}); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _from = '0x12345678'; + let _value = 1000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, _address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + assert( + output.logs[0].args._next === _bmcICON && output.logs[0].args._msg === _eventMsg + ); + }); + + it('Scenario 2: BSHCore has insufficient funds to transfer', async () => { + let _from = '0x12345678'; + let _value = 1000000000; + let balanceBefore = await bmc.getBalance(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(accounts[1]); + + assert.equal(web3.utils.BN(balanceAfter).toString(), web3.utils.BN(balanceBefore).toString()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 3: BSHCore tries to transfer PARA coins to a non-payable contract, but it fails`, async () => { + let _from = '0x12345678'; + let _value = 1000; + let balanceBefore = await bmc.getBalance(notpayable.address); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, notpayable.address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(notpayable.address); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: BSHPeriphery receives a request of transferring coins', async () => { + let _from = '0x12345678'; + let _value = 12345; + let balanceBefore = await bmc.getBalance(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(accounts[1]); + + assert.equal( + web3.utils.BN(balanceAfter).toString(), + web3.utils.BN(balanceBefore).add(new web3.utils.BN(_value)).toString() + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 5: BSHPeriphery receives a request of transferring coins`, async () => { + let _from = '0x12345678'; + let _value = 23456; + let balanceBefore = await bmc.getBalance(refundable.address); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, refundable.address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(refundable.address); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + }); + + describe('As a user, I want to receive ERC1155_ICX from ICON blockchain - After Upgrading Contract', () => { + let bmc, bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, holder, notpayable; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name = 'ICON'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; + let RC_ERR = 1; let RC_OK = 0; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + holder = await Holder.new(); + notpayable = await NotPayable.new(); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perifV2.address, bsh_coreV2.address); + await bsh_coreV2.register(_name); + id = await bsh_coreV2.coinId(_name); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _value = 1000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, _address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 2: Receiving contract does not implement ERC1155Holder / Receiver`, async () => { + let _value = 1000; + let balanceBefore = await bsh_coreV2.balanceOf(notpayable.address, id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, notpayable.address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_coreV2.balanceOf(notpayable.address, id); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 3: BSHPeriphery receives a request of invalid token', async () => { + let _value = 3000; + let _tokenName = 'Ethereum'; + let invalid_coin_id = await bsh_coreV2.coinId(_tokenName); + let balanceBefore = await bsh_coreV2.balanceOf(holder.address, invalid_coin_id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'UnregisteredCoin'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _tokenName, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_coreV2.balanceOf(holder.address, invalid_coin_id); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: Receiver is a ERC1155Holder contract', async () => { + let _value = 2500; + let balanceBefore = await bsh_coreV2.balanceOf(holder.address, id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_coreV2.balanceOf(holder.address, id); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 5: Receiver is an account client', async () => { + let _value = 5500; + let balanceBefore = await bsh_coreV2.balanceOf(accounts[1], id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_coreV2.balanceOf(accounts[1], id); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + }); + + describe('BSHs Handle Fee Aggregation - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name1 = 'ICON'; let _name2 = 'BINANCE'; let _name3 = 'ETHEREUM'; let _name4 = 'TRON'; + let _net1 = '1234.iconee'; let _net2 = '1234.binance'; + let _from1 = '0x12345678'; let _from2 = '0x12345678'; + let _value1 = 999999999999999; let _value2 = 999999999999999; + let _to1 = 'btp://1234.iconee/0x12345678'; let _to2 = 'btp://1234.binance/0x12345678'; + let _txAmt = 1000000; let _txAmt1 = 100000000; let _txAmt2 = 5000000; + let RC_OK = 0; let RC_ERR = 1; + let REPONSE_HANDLE_SERVICE = 2; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _sn0 = 1; let _sn1 = 2; let _sn2 = 3; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + holder = await Holder.new(); + btpAddr = await bmc.bmcAddress(); + await bmc.addVerifier(_net1, accounts[1]); + await bmc.addVerifier(_net2, accounts[2]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perifV2.address, bsh_coreV2.address); + await bsh_coreV2.register(_name1); + await bsh_coreV2.register(_name2); + await bsh_coreV2.register(_name3); + await bsh_coreV2.register(_name4); + let _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from1, holder.address, _name1, _value1); + await bmc.receiveRequest(_bmcICON, "", service, _sn0, _msg1); + let _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from2, holder.address, _name2, _value2); + await bmc.receiveRequest(_bmcICON, "", service, _sn1, _msg2); + await bsh_coreV2.transferNativeCoin(_to1, {from: accounts[0], value: _txAmt}); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net1, service, _sn0, _responseMsg); + await holder.setApprove(bsh_coreV2.address); + await holder.callTransfer(_name1, _txAmt1, _to1); + await bmc.receiveResponse(_net1, service, _sn1, _responseMsg); + await holder.callTransfer(_name2, _txAmt2, _to2); + await bmc.receiveResponse(_net1, service, _sn2, _responseMsg); + }); + + it(`Scenario 1: Query 'Aggregation Fee'`, async () => { + let aggregationFee = await bsh_coreV2.getAccumulatedFees(); + + assert.equal(aggregationFee.length, 5); + assert.equal(aggregationFee[0].coinName, 'PARA'); + assert.equal(aggregationFee[1].coinName, 'ICON'); + assert.equal(aggregationFee[2].coinName, 'BINANCE'); + assert.equal(aggregationFee[3].coinName, 'ETHEREUM'); + assert.equal(aggregationFee[4].coinName, 'TRON'); + + assert.equal(Number(aggregationFee[0].value), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[1].value), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[2].value), Math.floor(_txAmt2 / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[3].value), 0); + assert.equal(Number(aggregationFee[4].value), 0); + }); + + it('Scenario 2: Receiving a FeeGathering request not from BMCService', async () => { + let _sn3 = 3 + let FA1Before = await bsh_perifV2.getAggregationFeeOf(_native); // state Aggregation Fee of each type of Coins + let FA2Before = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA3Before = await bsh_perifV2.getAggregationFeeOf(_name2); + await truffleAssert.reverts( + bsh_perifV2.handleFeeGathering.call(_to1, service, {from: accounts[1]}), + 'Unauthorized' + ); + let FA1After = await bsh_perifV2.getAggregationFeeOf(_native); + let FA2After = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA3After = await bsh_perifV2.getAggregationFeeOf(_name2); + let fees = await bsh_perifV2.getPendingRequest(_sn3); // get pending Aggregation Fee list + + assert.equal( + web3.utils.BN(FA1Before).toNumber(), + web3.utils.BN(FA1After).toNumber() + ); + assert.equal( + web3.utils.BN(FA2Before).toNumber(), + web3.utils.BN(FA2After).toNumber() + ); + assert.equal( + web3.utils.BN(FA3Before).toNumber(), + web3.utils.BN(FA3After).toNumber() + ); + assert.equal(fees.amounts.length, 0); + }); + + // Before: + // + state Aggregation Fee of each type of Coins are set + // + pendingAggregation Fee list is empty + // After: + // + all states of Aggregation Fee are push into pendingAggregation Fee list + // + state Aggregation Fee of each type of Coins are reset + it('Scenario 3: Handle GatherFee request from BMCService contract', async () => { + let _sn3 = 4; + let FA1Before = await bsh_perifV2.getAggregationFeeOf(_native); // state Aggregation Fee of each type of Coins + let FA2Before = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA3Before = await bsh_perifV2.getAggregationFeeOf(_name2); + let _bmcService = await encode_msg.encodeBMCService(_to1, [service]); + let output = await bmc.receiveRequest(_bmcICON, '', 'bmc', 100, _bmcService); + let FA1After = await bsh_perifV2.getAggregationFeeOf(_native); + let FA2After = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA3After = await bsh_perifV2.getAggregationFeeOf(_name2); + let fees = await bsh_perifV2.getPendingRequest(_sn3); // get pending Aggregation Fee list + let list = []; + for (let i = 0; i < fees.amounts.length; i++) { + list[i] = [fees.coinNames[i], fees.amounts[i]]; + } + let _eventMsg = await encode_msg.encodeTransferFeesBMCMessage( + btpAddr, _bmcICON, _to1, service, _sn3, bsh_coreV2.address, list + ); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: output.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, bsh_coreV2.address); + assert.equal(event._to, _to1); + assert.equal(event._sn, _sn3); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _native); + assert.equal(event._assetDetails[0].value, fees.amounts[0]); + assert.equal(event._assetDetails[0].fee, 0); + assert.equal(event._assetDetails[1].coinName, _name1); + assert.equal(event._assetDetails[1].value, fees.amounts[1]); + assert.equal(event._assetDetails[1].fee, 0); + assert.equal(event._assetDetails[2].coinName, _name2); + assert.equal(event._assetDetails[2].value, fees.amounts[2]); + assert.equal(event._assetDetails[2].fee, 0); + + assert.equal(web3.utils.BN(FA1Before).toNumber(), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA2Before).toNumber(), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA3Before).toNumber(), Math.floor(_txAmt2 / 1000) + _fixed_fee); + + assert.equal(web3.utils.BN(FA1After).toNumber(), 0); + assert.equal(web3.utils.BN(FA2After).toNumber(), 0); + assert.equal(web3.utils.BN(FA3After).toNumber(), 0); + + assert.equal(fees.coinNames[0], _native); + assert.equal(fees.coinNames[1], _name1); + assert.equal(fees.coinNames[2], _name2); + + assert.equal(Number(fees.amounts[0]), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(fees.amounts[1]), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(fees.amounts[2]), Math.floor(_txAmt2 / 1000) + _fixed_fee); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: Receiving a successful response', async () => { + let _sn3 = 4; + let feesBefore = await bsh_perifV2.getPendingRequest(_sn3); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net1, service, _sn3, _responseMsg); + let feesAfter = await bsh_perifV2.getPendingRequest(_sn3); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, bsh_coreV2.address); + assert.equal(event._sn, _sn3); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(feesBefore.amounts.length, 3); + assert.equal(feesBefore.coinNames[0], _native); + assert.equal(feesBefore.coinNames[1], _name1); + assert.equal(feesBefore.coinNames[2], _name2); + assert.equal(Number(feesBefore.amounts[0]), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[1]), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[2]), Math.floor(_txAmt2 / 1000) + _fixed_fee); + assert.equal(feesAfter.amounts.length, 0); + }); + + it('Scenario 5: Receiving an error response', async () => { + let _sn4 = 5; let _sn5 = 6; let _sn6 = 7; + let _amt1 = 2000000; let _amt2 = 6000000; + await holder.callTransfer(_name1, _amt1, _to1); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net1, service, _sn4, _responseMsg); + await holder.callTransfer(_name2, _amt2, _to2); + await bmc.receiveResponse(_net2, service, _sn5, _responseMsg); + let _bmcService = await encode_msg.encodeBMCService(_to1, [service]); + await bmc.receiveRequest(_bmcICON, '', 'bmc', 100, _bmcService); + + let FA1Before = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA2Before = await bsh_perifV2.getAggregationFeeOf(_name2); + let feesBefore = await bsh_perifV2.getPendingRequest(_sn6); + let _errMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net1, service, _sn6, _errMsg); + let FA1After = await bsh_perifV2.getAggregationFeeOf(_name1); + let FA2After = await bsh_perifV2.getAggregationFeeOf(_name2); + let feesAfter = await bsh_perifV2.getPendingRequest(_sn6); + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, bsh_coreV2.address); + assert.equal(event._sn, _sn6); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(feesBefore.amounts.length, 2); + assert.equal(feesBefore.coinNames[0], _name1); + assert.equal(feesBefore.coinNames[1], _name2); + assert.equal(Number(feesBefore.amounts[0]), Math.floor(_amt1 / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[1]), Math.floor(_amt2 / 1000) + _fixed_fee); + + assert.equal(web3.utils.BN(FA1Before).toNumber(), 0); + assert.equal(web3.utils.BN(FA2Before).toNumber(), 0); + assert.equal(feesAfter.amounts.length, 0); + assert.equal(web3.utils.BN(FA1After).toNumber(), Math.floor(_amt1 / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA2After).toNumber(), Math.floor(_amt2 / 1000) + _fixed_fee); + }); + }); + + describe('As a user, I want to receive multiple Coins/Tokens from ICON blockchain - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, bmc, holder, refundable; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name1 = 'ICON'; let _name2 = 'BINANCE'; let _name3 = 'ETHEREUM'; let _name4 = 'TRON'; + let _net1 = '1234.iconee'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let RC_OK = 0; let RC_ERR = 1; + let _from1 = '0x12345678'; let _to = 'btp://1234.iconee/0x12345678'; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + holder = await Holder.new(); + refundable = await Refundable.new(); + btpAddr = await bmc.bmcAddress(); + await bmc.addVerifier(_net1, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perifV2.address, bsh_coreV2.address); + await bsh_coreV2.register(_name1); + await bsh_coreV2.register(_name2); + await bsh_coreV2.register(_name3); + await bsh_coreV2.register(_name4); + await bsh_coreV2.transferNativeCoin(_to, {from: accounts[0], value: 10000000}); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeBatchTransferMsgWithStringAddress( + _from1, _address, [[_native, _value1], [_name1, _value2], [_name2, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 2: BSHPerphery receives a request of invalid token', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let _invalid_token = 'EOS'; + let balance1Before = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_coreV2.getBalanceOf(holder.address, _invalid_token); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'UnregisteredCoin'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_invalid_token, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_coreV2.getBalanceOf(holder.address, _invalid_token); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 3: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 20000000; + let balance1Before = await bsh_coreV2.getBalanceOf(accounts[1], _name1); + let balance2Before = await bsh_coreV2.getBalanceOf(accounts[1], _name2); + let balance3Before = await bsh_coreV2.getBalanceOf(accounts[1], _native); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, accounts[1], [[_name1, _value1], [_name2, _value2], [_native, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(accounts[1], _name1); + let balance2After = await bsh_coreV2.getBalanceOf(accounts[1], _name2); + let balance3After = await bsh_coreV2.getBalanceOf(accounts[1], _native); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let balance2Before = await bsh_coreV2.getBalanceOf(refundable.address, _name1); + let balance3Before = await bsh_coreV2.getBalanceOf(refundable.address, _name2); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, refundable.address, [[_native, _value1], [_name1, _value2], [_name2, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(refundable.address, _native); + let balance2After = await bsh_coreV2.getBalanceOf(refundable.address, _name1); + let balance3After = await bsh_coreV2.getBalanceOf(refundable.address, _name2); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 5: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_coreV2.getBalanceOf(holder.address, _native); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_native, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_coreV2.getBalanceOf(holder.address, _native); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 6: Receiving a successful TransferBatch request', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_coreV2.getBalanceOf(holder.address, _name3); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_name3, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_coreV2.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_coreV2.getBalanceOf(holder.address, _name3); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), _value1); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), _value2); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), _value3); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 7: Receiving a successful TransferBatch request', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_coreV2.getBalanceOf(accounts[1], _native); + let balance2Before = await bsh_coreV2.getBalanceOf(accounts[1], _name2); + let balance3Before = await bsh_coreV2.getBalanceOf(accounts[1], _name3); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, accounts[1], [[_native, _value1], [_name2, _value2], [_name3, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_coreV2.getBalanceOf(accounts[1], _native); + let balance2After = await bsh_coreV2.getBalanceOf(accounts[1], _name2); + let balance3After = await bsh_coreV2.getBalanceOf(accounts[1], _name3); + + assert.equal( + web3.utils.BN(balance1After._usableBalance).toString(), + web3.utils.BN(balance1Before._usableBalance).add(new web3.utils.BN(_value1)).toString() + ); + assert.equal( + web3.utils.BN(balance2After._usableBalance).toNumber(), + web3.utils.BN(balance2Before._usableBalance).toNumber() + _value2 + ); + assert.equal( + web3.utils.BN(balance3After._usableBalance).toNumber(), + web3.utils.BN(balance3Before._usableBalance).toNumber() + _value3 + ); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + }); + + describe('As a user, I want to send multiple coins/tokens to ICON blockchain - After Upgrading Contract', () => { + let bsh_perifV1, bsh_perifV2, bsh_coreV1, bsh_coreV2, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _net = '1234.iconee'; let _from = '0x12345678'; let _value = 999999999999999; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; let RC_ERR = 1; + let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _coin1 = 'ICON'; let _coin2 = 'TRON'; let _coin3 = 'BINANCE'; + let initAmt = 1000000000000000; + + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_perifV1 = await deployProxy(BSHPerifV1, [bmc.address, bsh_coreV1.address, service]); + encode_msg = await EncodeMsg.new(); + await bsh_coreV1.updateBSHPeriphery(bsh_perifV1.address); + await bmc.addService(service, bsh_perifV1.address); + bsh_perifV2 = await upgradeProxy(bsh_perifV1.address, BSHPerifV2); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + holder = await Holder.new(); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perifV2.address, bsh_coreV2.address); + await bsh_coreV2.register(_coin1); + await bsh_coreV2.register(_coin2); + await bsh_coreV2.register(_coin3); + await bsh_coreV2.transferNativeCoin('btp://1234.iconee/0x12345678', {from: accounts[0], value: initAmt}); + await holder.deposit({from: accounts[1], value: 100000000000000}); + let _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin1, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg1); + let _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin2, _value); + await bmc.receiveRequest(_bmcICON, "", service, 1, _msg2); + let _msg3 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin3, _value); + await bmc.receiveRequest(_bmcICON, "", service, 2, _msg3); + + _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin1, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg1); + _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin2, _value); + await bmc.receiveRequest(_bmcICON, "", service, 1, _msg2); + _msg3 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin3, _value); + await bmc.receiveRequest(_bmcICON, "", service, 2, _msg3); + }); + + it('Scenario 1: User has not yet set approval for token being transferred out by Operator', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _native_amt = 800000; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it(`Scenario 2: User has set approval, but user's balance has insufficient amount`, async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 9999999999999999n]; + let _native_amt = 700000; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 3: User requests to transfer an invalid Token', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let invalid_token = 'EOS'; + let _coins = [_coin1, invalid_token]; + let _values = [600000, 700000]; + let _native_amt = 800000; + let _query = [_native, _coin1, invalid_token]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 4: User transfers Tokens to an invalid BTP Address format', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 5: User requests to transfer zero Token', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 0]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 6: Transffering amount is less than fixed fee', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 300000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 7: User requests to transfer to an invalid network/Not Supported Network', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 8: Account client sends an invalid request of transferBatch', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_native, _native, _native]; + let _values = [600000, 600000, 600000]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(accounts[2], _coins); + await truffleAssert.reverts( + bsh_coreV2.transferBatch.call(_coins, _values, _to, {from: accounts[2], value: 600000}), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(accounts[2], _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), initAmt); + }); + + it('Scenario 9: Contract client sends an invalid request of transferBatch', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _coins = [_native, _coin1, _coin2]; + let _values = [600000, 700000]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + await holder.setApprove(bsh_coreV2.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_coreV2.address, _coins, _values, _to, 0), + "revert" + ); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 10: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value2, _value3]; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + let tx = await holder.callTransferBatch(bsh_coreV2.address, _coins, _values, _to, _value1); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin1); + assert.equal(event._assetDetails[0].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[0].fee, chargedFee2); + assert.equal(event._assetDetails[1].coinName, _coin2); + assert.equal(event._assetDetails[1].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[1].fee, chargedFee3); + assert.equal(event._assetDetails[2].coinName, _native); + assert.equal(event._assetDetails[2].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[2].fee, chargedFee1); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value3 - chargedFee3); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value1 - chargedFee1); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt + _value1); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), _value3); + }); + + it('Scenario 11: BSHPeriphery receives a successful response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _responseMsg); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let fees = await bsh_coreV2.getAccumulatedFees(); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 2); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(fees[0].coinName, _native); assert.equal(Number(fees[0].value), chargedFee1); + assert.equal(fees[1].coinName, _coin1); assert.equal(Number(fees[1].value), chargedFee2); + assert.equal(fees[2].coinName, _coin2); assert.equal(Number(fees[2].value), chargedFee3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt + _value1); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), chargedFee2); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), chargedFee3); + }); + + it('Scenario 12: Account client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin3, _coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value1, _value2, _value3]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(accounts[1], _coins); + await bsh_coreV2.setApprovalForAll(bsh_coreV2.address, true, {from: accounts[1]}); + let tx = await bsh_coreV2.transferBatch(_coins, _values, _to, {from: accounts[1]}); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(accounts[1], _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[1]); + assert.equal(event._to, _to); + assert.equal(event._sn, 3); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin3); + assert.equal(event._assetDetails[0].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[0].fee, chargedFee1); + assert.equal(event._assetDetails[1].coinName, _coin1); + assert.equal(event._assetDetails[1].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[1].fee, chargedFee2); + assert.equal(event._assetDetails[2].coinName, _coin2); + assert.equal(event._assetDetails[2].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[2].fee, chargedFee3); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 3); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin3); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value1 - chargedFee1); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value3 - chargedFee3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), _value1); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + chargedFee3 + ); + }); + + it('Scenario 13: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_coin3, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(accounts[1], _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 3, _responseMsg); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(accounts[1], _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[1]); + assert.equal(event._sn, 3); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + _value1 - chargedFee1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 2 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 2 * chargedFee3 + ); + }); + + it('Scenario 14: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin3, _coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value1, _value2, _value3]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + await holder.setApprove(bsh_coreV2.address); + let tx = await holder.callTransferBatch(bsh_coreV2.address, _coins, _values, _to, 0); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 4); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin3); + assert.equal(event._assetDetails[0].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[0].fee, chargedFee1); + assert.equal(event._assetDetails[1].coinName, _coin1); + assert.equal(event._assetDetails[1].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[1].fee, chargedFee2); + assert.equal(event._assetDetails[2].coinName, _coin2); + assert.equal(event._assetDetails[2].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[2].fee, chargedFee3); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 4); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin3); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value1 - chargedFee1); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value3 - chargedFee3); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + _value1 + chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + 2 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + 2 * chargedFee3 + ); + }); + + it('Scenario 15: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_coin3, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 4, _responseMsg); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 4); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + _value1 - chargedFee1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + 2 * chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 3 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 3 * chargedFee3 + ); + }); + + // This test is replicated from Scenario 10 + it('Scenario 16: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value2, _value3]; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_coreV2.address); + let tx = await holder.callTransferBatch(bsh_coreV2.address, _coins, _values, _to, _value1); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _query); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 5); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin1); + assert.equal(event._assetDetails[0].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[0].fee, chargedFee2); + assert.equal(event._assetDetails[1].coinName, _coin2); + assert.equal(event._assetDetails[1].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[1].fee, chargedFee3); + assert.equal(event._assetDetails[2].coinName, _native); + assert.equal(event._assetDetails[2].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[2].fee, chargedFee1); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 5); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value3 - chargedFee3); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value1 - chargedFee1); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + initAmt + 2 * _value1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + 3 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + 3 * chargedFee3 + ); + }); + + it('Scenario 17: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 5, _responseMsg); + let balanceAfter = await bsh_coreV2.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_coreV2.getBalanceOfBatch(bsh_coreV2.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perifV2.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 5); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + assert.equal(web3.utils.BN(balanceBefore._refundableBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._refundableBalances[0]).toNumber(), _value1 - chargedFee1); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + initAmt + 2 * _value1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 4 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 4 * chargedFee3 + ); + }); + }); +}); \ No newline at end of file diff --git a/solidity/bsh/test/integration/2-native-coin-bsh.js b/solidity/bsh/test/integration/2-native-coin-bsh.js new file mode 100644 index 00000000..a74e9661 --- /dev/null +++ b/solidity/bsh/test/integration/2-native-coin-bsh.js @@ -0,0 +1,2682 @@ +const MockBSHPeriphery = artifacts.require("MockBSHPeriphery"); +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const BMC = artifacts.require("MockBMC"); +const Holder = artifacts.require("Holder"); +const NotPayable = artifacts.require("NotPayable"); +const NonRefundable = artifacts.require("NonRefundable"); +const Refundable = artifacts.require("Refundable"); +const EncodeMsg = artifacts.require("EncodeMessage"); +const { assert, AssertionError } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const rlp = require('rlp'); + +let toHex = (buf) => { + buf = buf.toString('hex'); + if (buf.substring(0, 2) == '0x') + return buf; + return '0x' + buf.toString('hex'); +}; + + +contract('PRA BSHCore Query and Management', (accounts) => { + let bsh_core, bsh_perif; let _uri = 'https://github.com/icon-project/btp' + let _native = 'PARA'; let service = 'Coin/WrappedCoin'; + let _net = '1234.iconee'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; + let _fee = 10; let _fixed_fee = 500000; + before(async () => { + bmc = await BMC.new('1234.pra'); + bsh_core = await BSHCore.new(); + bsh_perif = await BSHPeriphery.new(); + encode_msg = await EncodeMsg.new(); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + }); + + it(`Scenario 1: Contract's owner to register a new coin`, async () => { + let _name = "ICON"; + await bsh_core.register(_name); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' + ); + }); + + it('Scenario 2: Non-ownership role client registers a new coin', async () => { + let _name = "TRON"; + await truffleAssert.reverts( + bsh_core.register.call(_name, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 3: Contract’s owner registers an existed coin', async () => { + let _name = "ICON"; + await truffleAssert.reverts( + bsh_core.register.call(_name), + "ExistToken" + ); + }); + + it('Scenario 4: Contract’s owner to update BSHPeriphery contract', async () => { + await bsh_core.updateBSHPeriphery(bsh_perif.address); + }); + + it('Scenario 5: Non-ownership role client updates BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_core.updateBSHPeriphery.call(accounts[2], {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 6: Contract’s owner updates BSHPeriphery while this contract has pending requests', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + await bsh_core.transferNativeCoin(_to, {from: accounts[0], value: 100000000}); + await truffleAssert.reverts( + bsh_core.updateBSHPeriphery.call(accounts[2]), + "HasPendingRequest" + ); + // Clear pending request + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net, service, 1, _msg); + }); + + it(`Scenario 7: Contract’s owner updates a new URI`, async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri); + }); + + it('Scenario 8: Non-ownership role client updates a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_core.updateUri.call(new_uri, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it(`Scenario 9: Contract's owner updates fee ratio`, async () => { + let new_fee = 20; + await bsh_core.setFeeRatio(new_fee); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 10: Non-ownership role client updates fee ratio', async () => { + let old_fee = 20; + let new_fee = 50; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 11: Fee Numerator is set higher than Fee Denominator', async () => { + let old_fee = 20; + let new_fee = 20000; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 12: Contract owner updates fixed fee', async () => { + let new_fixed_fee = 1000000; + await bsh_core.setFixedFee(new_fixed_fee); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 13: Non-ownership role client updates fixed fee', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 2000000; + await truffleAssert.reverts( + bsh_core.setFixedFee.call(new_fixed_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 14: Owner set fixed fee is zero', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 0; + await truffleAssert.reverts( + bsh_core.setFixedFee.call(new_fixed_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 15: Query a valid supporting coin', async () => { + let _name1 = "wBTC"; let _name2 = "Ethereum"; + await bsh_core.register(_name1); + await bsh_core.register(_name2); + + let _query = "ICON"; + let id = web3.utils.keccak256(_query); + let result = await bsh_core.coinId(_query); + assert( + web3.utils.BN(result).toString() === web3.utils.toBN(id).toString() + ); + }); + + it('Scenario 16: Query an invalid supporting coin', async () => { + let _query = "EOS"; + let result = await bsh_core.coinId(_query); + assert( + web3.utils.BN(result).toNumber() === 0 + ); + }); + + it('Scenario 17: Non-Owner tries to add a new Owner', async () => { + let oldList = await bsh_core.getOwners(); + try { + await bsh_core.addOwner(accounts[1], {from: accounts[2]}); + }catch (err) { + assert(err, "exited with an error (status 0)"); + } + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 1 && newList[0] === accounts[0] + ); + }); + + it('Scenario 18: Current Owner adds a new Owner', async () => { + let oldList = await bsh_core.getOwners(); + await bsh_core.addOwner(accounts[1]); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 19: After adding a new Owner, owner registers a new coin', async () => { + let _name3 = "TRON"; + await bsh_core.register(_name3); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' + ); + }); + + it('Scenario 20: New Owner registers a new coin', async () => { + let _name3 = "BINANCE"; + await bsh_core.register(_name3, {from: accounts[1]}); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 21: New owner updates BSHPeriphery contract', async () => { + let newBSHPerif = await BSHPeriphery.new(); + await bsh_core.updateBSHPeriphery(newBSHPerif.address, {from: accounts[1]}); + }); + + it('Scenario 22: Old owner updates BSHPeriphery contract', async () => { + let newBSHPerif = await BSHPeriphery.new(); + await bsh_core.updateBSHPeriphery(newBSHPerif.address, {from: accounts[0]}); + }); + + it('Scenario 23: New owner updates the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri, {from: accounts[1]}); + }); + + it('Scenario 24: Old owner updates the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri, {from: accounts[0]}); + }); + + it('Scenario 25: New owner updates new fee ratio', async () => { + let new_fee = 30; + await bsh_core.setFeeRatio(new_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 26: Old owner updates new fee ratio - After adding new Owner', async () => { + let new_fee = 40; + await bsh_core.setFeeRatio(new_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 27: New owner updates new fixed fee', async () => { + let new_fixed_fee = 3000000; + await bsh_core.setFixedFee(new_fixed_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 28: Old owner updates new fixed fee - After adding new Owner', async () => { + let new_fixed_fee = 4000000; + await bsh_core.setFixedFee(new_fixed_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 29: Non-Owner tries to remove an Owner', async () => { + let oldList = await bsh_core.getOwners(); + await truffleAssert.reverts( + bsh_core.removeOwner.call(accounts[0], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 30: Current Owner removes another Owner', async () => { + let oldList = await bsh_core.getOwners(); + await bsh_core.removeOwner(accounts[0], {from: accounts[1]}); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 31: The last Owner removes him/herself', async () => { + let oldList = await bsh_core.getOwners(); + await truffleAssert.reverts( + bsh_core.removeOwner.call(accounts[1], {from: accounts[1]}), + "Unable to remove last Owner" + ); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 32: Removed Owner tries to register a new coin', async () => { + let _name3 = "KYBER"; + await truffleAssert.reverts( + bsh_core.register.call(_name3), + 'Unauthorized' + ); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 33: Removed Owner tries to update BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_core.updateBSHPeriphery.call(accounts[3], {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 34: Removed Owner tries to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_core.updateUri.call(new_uri, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 35: Removed Owner tries to update new fee ratio', async () => { + let new_fee = 30; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee, {from: accounts[0]}), + 'Unauthorized' + ); + }); +}); + +contract('As a user, I want to send PRA to ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, nonrefundable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let RC_OK = 0; let RC_ERR = 1; + let _native = 'PARA'; let deposit = 1000000000000; + let _fee = 10; let _fixed_fee = 500000; + let REPONSE_HANDLE_SERVICE = 2; let _uri = 'https://github.com/icon-project/btp'; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + nonrefundable = await NonRefundable.new(); + refundable = await Refundable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + }); + + it('Scenario 1: Transferring native coins to an invalid BTP Address format', async () => { + let invalid_destination = '1234.iconee/0x12345678'; + let amount = 600000; + await truffleAssert.reverts( + bsh_core.transferNativeCoin.call(invalid_destination, {from: accounts[0], value: amount}), + "revert" + ); + bsh_coin_balance = await bsh_core.getBalanceOf(bsh_core.address, _native); + account_balance = await bsh_core.getBalanceOf(accounts[0], _native); + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === 0 && + web3.utils.BN(account_balance._lockedBalance).toNumber() === 0 + ); + }); + + it('Scenario 2: Transferring zero coin' , async () => { + await truffleAssert.reverts( + bsh_core.transferNativeCoin.call(_to, {from: accounts[0], value: 0}), + "revert" + ); + }); + + it('Scenario 3: msg.value less than fixed_fee' , async () => { + // fixed_fee = 500000; + let amount = 100000; + await truffleAssert.reverts( + bsh_core.transferNativeCoin.call(_to, {from: accounts[0], value: amount}), + "revert" + ); + }); + + it('Scenario 4: Transferring to an invalid network/not supported network' , async () => { + let invalid_destination = 'btp://1234.eos/0x12345678'; + let amount = 600000; + await truffleAssert.reverts( + bsh_core.transferNativeCoin.call(invalid_destination, {from: accounts[1], value: amount}), + "BMCRevertNotExistsBMV" + ); + }); + + it('Scenario 5: Account client transfers a valid native coin to a side chain', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let tx = await bsh_core.transferNativeCoin(_to, {from: accounts[0], value: amount}); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let bsh_coin_balance = await bsh_core.getBalanceOf(bsh_core.address, _native); + let chargedFee = Math.floor(amount/ 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[0]); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === amount && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === amount + ); + }); + + it('Scenario 6: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let fees = await bsh_core.getAccumulatedFees(); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[0]); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert( + fees[0].coinName === _native && + Number(fees[0].value) === (Math.floor(amount/ 1000) + _fixed_fee) && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === amount && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === 0 + ); + }); + + it('Scenario 5: Account client transfers a valid native coin to a side chain', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let tx = await bsh_core.transferNativeCoin(_to, {from: accounts[0], value: amount}); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let bsh_coin_balance = await bsh_core.getBalanceOf(bsh_core.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[0]); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === 2 * amount && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === amount + ); + }); + + it('Scenario 7: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let bsh_coin_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _msg); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let bsh_coin_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _native); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[0]); + assert.equal(event._sn, 2); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + // Unable to check balance of accounts[0] since this account has also paid gas fee + // It would be easier to check if this is a contract + // Requestor will be receive an amount of refund as + // refund = amount - chargeAmt + assert( + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === amount && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._refundableBalance).toNumber() === 0 && + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() === 2 * amount && + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() === amount + chargedFee + ); + }); + + it('Scenario 8: Non-refundable contract transfers a valid native coin to a side chain', async () => { + let amount = 600000; + await nonrefundable.deposit({from: accounts[2], value: deposit}); + let contract_balanceBefore = await bsh_core.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _native); + let tx = await nonrefundable.transfer(bsh_core.address, _to, amount); + let contract_balanceAfter = await bsh_core.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, nonrefundable.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 3); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 3); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), nonrefundable.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() === + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + amount && + web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber() === amount && + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() === amount + chargedFee && + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() === 2 * amount + chargedFee + ); + }); + + it(`Scenario 9: BSHPeriphery receives an error response of a recent request and fails to refund coins back to Non-refundable contract`, async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_core.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 3, _msg); + let contract_balanceAfter = await bsh_core.getBalanceOf(nonrefundable.address, _native); + let bsh_coin_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _native); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, nonrefundable.address); + assert.equal(event._sn, 3); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(contract_balanceAfter._refundableBalance).toNumber(), amount - chargedFee); + assert.equal( + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() + ); + }); + + it('Scenario 10: Refundable contract transfers a valid native coin to a side chain', async () => { + let amount = 600000; + await refundable.deposit({from: accounts[2], value: deposit}); + let contract_balanceBefore = await bsh_core.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _native); + let tx = await refundable.transfer(bsh_core.address, _to, amount); + let contract_balanceAfter = await bsh_core.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, refundable.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 4); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'PARA'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 4); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), refundable.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + amount + ); + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber() + amount + ); + }); + + it('Scenario 11: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_core.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 4, _msg); + let contract_balanceAfter = await bsh_core.getBalanceOf(refundable.address, _native); + let bsh_coin_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _native); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, refundable.address); + assert.equal(event._sn, 4); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber(), + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() + amount - chargedFee + ); + assert.equal(web3.utils.BN(contract_balanceAfter._refundableBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(bsh_coin_balance_before._usableBalance).toNumber(), + web3.utils.BN(bsh_coin_balance_after._usableBalance).toNumber() + amount - chargedFee + ); + }); +}); + +contract('As a user, I want to send ERC1155_ICX to ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name = 'ICON'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; let _value = 999999999999999; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; let RC_ERR = 1; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + await bsh_core.register(_name); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _name, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg); + id = await bsh_core.coinId(_name); + }); + + it('Scenario 1: User has not yet set approval for token being transferred out by Operator', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 600000; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await truffleAssert.reverts( + holder.callTransfer.call(_name, _value, _to), + "ERC1155: caller is not owner nor approved" + ); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + }); + + it(`Scenario 2: User has set approval, but user's balance has insufficient amount`, async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 9999999999999999n; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, _value, _to), + "ERC1155: insufficient balance for transfer" + ); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + }); + + it('Scenario 3: User requests to transfer an invalid Token', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _value = 9999999999999999n; + let _token = 'EOS'; + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_token, _value, _to), + "UnregisterCoin" + ); + + }); + + it('Scenario 4: User transfers Tokens to an invalid BTP Address format', async () => { + let _to = '1234.iconee/0x12345678'; + let amount = 600000; + let contract_balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "VM Exception while processing transaction: revert" + ); + let contract_balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _name); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber(), + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), 0); + }); + + it('Scenario 5: User requests to transfer zero Token', async () => { + let _to = '1234.iconee/0x12345678'; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, 0, _to), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + }); + + it('Scenario 6: Transferring amount is less than fixed fee', async () => { + let _to = '1234.iconee/0x12345678'; + let _name = 'ICON'; + let amount = 100000; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + }); + + it('Scenario 7: User requests to transfer to an invalid network/Not Supported Network', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _name = 'ICON'; + let amount = 600000; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransfer.call(_name, amount, _to), + "BMCRevertNotExistsBMV" + ); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _name); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), 0); + }); + + it('Scenario 8: User sends a valid transferring request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let amount = 600000; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + await holder.setApprove(bsh_core.address); + let tx = await holder.callTransfer(_name, amount, _to); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _name); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, _name); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _name); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() - amount + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), amount); + }); + + it('Scenario 9: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let contract_balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + let fees = await bsh_core.getAccumulatedFees(); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _name); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), chargedFee); + assert.equal(fees[1].coinName, _name); + assert.equal(Number(fees[1].value), chargedFee) + }); + + it('Scenario 8: User sends a valid transferring request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let amount = 100000000000000; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + let bsh_core_balance_before = await bsh_core.getBalanceOf(bsh_core.address, _name); + await holder.setApprove(bsh_core.address); + let tx = await holder.callTransfer(_name, amount, _to); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + let bsh_core_balance_after = await bsh_core.getBalanceOf(bsh_core.address, _name); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, _name); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _name); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() - amount + ); + assert.equal( + web3.utils.BN(bsh_core_balance_after._usableBalance).toNumber(), + web3.utils.BN(bsh_core_balance_before._usableBalance).toNumber() + amount + ); + }); + + it('Scenario 10: BSHPeriphery receives an error response of a recent request', async () => { + let amount = 100000000000000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let balanceBefore = await bsh_core.getBalanceOf(holder.address, _name); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _msg); + let balanceAfter = await bsh_core.getBalanceOf(holder.address, _name); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 2); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() + amount - chargedFee + ); + assert.equal(web3.utils.BN(balanceAfter._refundableBalance).toNumber(), 0); + }); +}); + +contract('As a user, I want to receive PRA from ICON blockchain', (accounts) => { + let bmc, bsh_perif, bsh_core, notpayable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let RC_ERR = 1; let RC_OK = 0; + let _uri = 'https://github.com/icon-project/btp'; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + notpayable = await NotPayable.new(); + refundable = await Refundable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await bsh_core.transferNativeCoin(_to, {from: accounts[0], value: 100000000}); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _from = '0x12345678'; + let _value = 1000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, _address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + assert( + output.logs[0].args._next === _bmcICON && output.logs[0].args._msg === _eventMsg + ); + }); + + it('Scenario 2: BSHCore has insufficient funds to transfer', async () => { + let _from = '0x12345678'; + let _value = 1000000000; + let balanceBefore = await bmc.getBalance(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(accounts[1]); + + assert.equal(web3.utils.BN(balanceAfter).toString(), web3.utils.BN(balanceBefore).toString()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 3: BSHCore tries to transfer PARA coins to a non-payable contract, but it fails`, async () => { + let _from = '0x12345678'; + let _value = 1000; + let balanceBefore = await bmc.getBalance(notpayable.address); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, notpayable.address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(notpayable.address); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: BSHPeriphery receives a request of transferring coins', async () => { + let _from = '0x12345678'; + let _value = 12345; + let balanceBefore = await bmc.getBalance(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(accounts[1]); + + assert.equal( + web3.utils.BN(balanceAfter).toString(), + web3.utils.BN(balanceBefore).add(new web3.utils.BN(_value)).toString() + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 5: BSHPeriphery receives a request of transferring coins`, async () => { + let _from = '0x12345678'; + let _value = 23456; + let balanceBefore = await bmc.getBalance(refundable.address); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, refundable.address, _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(refundable.address); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); +}); + +contract('As a user, I want to receive ERC1155_ICX from ICON blockchain', (accounts) => { + let bmc, bsh_perif, bsh_core, holder, notpayable; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name = 'ICON'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; + let RC_ERR = 1; let RC_OK = 0; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + notpayable = await NotPayable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + await bsh_core.register(_name); + id = await bsh_core.coinId(_name); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _value = 1000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeTransferMsgWithStringAddress(_from, _address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it(`Scenario 2: Receiving contract does not implement ERC1155Holder / Receiver`, async () => { + let _value = 1000; + let balanceBefore = await bsh_core.balanceOf(notpayable.address, id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, notpayable.address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_core.balanceOf(notpayable.address, id); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 3: BSHPeriphery receives a request of invalid token', async () => { + let _value = 3000; + let _tokenName = 'Ethereum'; + let invalid_coin_id = await bsh_core.coinId(_tokenName); + let balanceBefore = await bsh_core.balanceOf(holder.address, invalid_coin_id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'UnregisteredCoin'); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _tokenName, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_core.balanceOf(holder.address, invalid_coin_id); + + assert.equal(web3.utils.BN(balanceAfter).toNumber(), web3.utils.BN(balanceBefore).toNumber()); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: Receiver is a ERC1155Holder contract', async () => { + let _value = 2500; + let balanceBefore = await bsh_core.balanceOf(holder.address, id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_core.balanceOf(holder.address, id); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 5: Receiver is an account client', async () => { + let _value = 5500; + let balanceBefore = await bsh_core.balanceOf(accounts[1], id); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _name, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_core.balanceOf(accounts[1], id); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); +}); + +contract('BSHs handle Gather Fee Service Requests', (accounts) => { + let bsh_perif, bsh_core, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name1 = 'ICON'; let _name2 = 'BINANCE'; let _name3 = 'ETHEREUM'; let _name4 = 'TRON'; + let _net1 = '1234.iconee'; let _net2 = '1234.binance'; + let _from1 = '0x12345678'; let _from2 = '0x12345678'; + let _value1 = 999999999999999; let _value2 = 999999999999999; + let _to1 = 'btp://1234.iconee/0x12345678'; let _to2 = 'btp://1234.binance/0x12345678'; + let _txAmt = 1000000; let _txAmt1 = 100000000; let _txAmt2 = 5000000; + let RC_OK = 0; let RC_ERR = 1; + let REPONSE_HANDLE_SERVICE = 2; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _sn0 = 1; let _sn1 = 2; let _sn2 = 3; + + before(async () => { + bsh_perif = await MockBSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + btpAddr = await bmc.bmcAddress(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net1, accounts[1]); + await bmc.addVerifier(_net2, accounts[2]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + await bsh_core.register(_name1); + await bsh_core.register(_name2); + await bsh_core.register(_name3); + await bsh_core.register(_name4); + let _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from1, holder.address, _name1, _value1); + await bmc.receiveRequest(_bmcICON, "", service, _sn0, _msg1); + let _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from2, holder.address, _name2, _value2); + await bmc.receiveRequest(_bmcICON, "", service, _sn1, _msg2); + await bsh_core.transferNativeCoin(_to1, {from: accounts[0], value: _txAmt}); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net1, service, _sn0, _responseMsg); + await holder.setApprove(bsh_core.address); + await holder.callTransfer(_name1, _txAmt1, _to1); + await bmc.receiveResponse(_net1, service, _sn1, _responseMsg); + await holder.callTransfer(_name2, _txAmt2, _to2); + await bmc.receiveResponse(_net1, service, _sn2, _responseMsg); + }); + + it(`Scenario 1: Query 'Aggregation Fee'`, async () => { + let aggregationFee = await bsh_core.getAccumulatedFees(); + + assert.equal(aggregationFee.length, 5); + assert.equal(aggregationFee[0].coinName, 'PARA'); + assert.equal(aggregationFee[1].coinName, 'ICON'); + assert.equal(aggregationFee[2].coinName, 'BINANCE'); + assert.equal(aggregationFee[3].coinName, 'ETHEREUM'); + assert.equal(aggregationFee[4].coinName, 'TRON'); + + assert.equal(Number(aggregationFee[0].value), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[1].value), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[2].value), Math.floor(_txAmt2 / 1000) + _fixed_fee); + assert.equal(Number(aggregationFee[3].value), 0); + assert.equal(Number(aggregationFee[4].value), 0); + }); + + it('Scenario 2: Receiving a FeeGathering request not from BMCService', async () => { + let _sn3 = 3 + let FA1Before = await bsh_perif.getAggregationFeeOf(_native); // state Aggregation Fee of each type of Coins + let FA2Before = await bsh_perif.getAggregationFeeOf(_name1); + let FA3Before = await bsh_perif.getAggregationFeeOf(_name2); + await truffleAssert.reverts( + bsh_perif.handleFeeGathering.call(_to1, service, {from: accounts[1]}), + 'Unauthorized' + ); + let FA1After = await bsh_perif.getAggregationFeeOf(_native); + let FA2After = await bsh_perif.getAggregationFeeOf(_name1); + let FA3After = await bsh_perif.getAggregationFeeOf(_name2); + let fees = await bsh_perif.getFees(_sn3); // get pending Aggregation Fee list + + assert.equal( + web3.utils.BN(FA1Before).toNumber(), + web3.utils.BN(FA1After).toNumber() + ); + assert.equal( + web3.utils.BN(FA2Before).toNumber(), + web3.utils.BN(FA2After).toNumber() + ); + assert.equal( + web3.utils.BN(FA3Before).toNumber(), + web3.utils.BN(FA3After).toNumber() + ); + assert.equal(fees.amounts.length, 0); + }); + + // Before: + // + state Aggregation Fee of each type of Coins are set + // + pendingAggregation Fee list is empty + // After: + // + all states of Aggregation Fee are push into pendingAggregation Fee list + // + state Aggregation Fee of each type of Coins are reset + it('Scenario 3: Handle GatherFee request from BMCService contract', async () => { + let _sn3 = 4; + let FA1Before = await bsh_perif.getAggregationFeeOf(_native); // state Aggregation Fee of each type of Coins + let FA2Before = await bsh_perif.getAggregationFeeOf(_name1); + let FA3Before = await bsh_perif.getAggregationFeeOf(_name2); + let _bmcService = await encode_msg.encodeBMCService(_to1, [service]); + let output = await bmc.receiveRequest(_bmcICON, '', 'bmc', 100, _bmcService); + let FA1After = await bsh_perif.getAggregationFeeOf(_native); + let FA2After = await bsh_perif.getAggregationFeeOf(_name1); + let FA3After = await bsh_perif.getAggregationFeeOf(_name2); + let fees = await bsh_perif.getFees(_sn3); // get pending Aggregation Fee list + let list = []; + for (let i = 0; i < fees.amounts.length; i++) { + list[i] = [fees.coinNames[i], fees.amounts[i]]; + } + let _eventMsg = await encode_msg.encodeTransferFeesBMCMessage( + btpAddr, _bmcICON, _to1, service, _sn3, bsh_core.address, list + ); + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: output.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, bsh_core.address); + assert.equal(event._to, _to1); + assert.equal(event._sn, _sn3); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _native); + assert.equal(event._assetDetails[0].value, fees.amounts[0]); + assert.equal(event._assetDetails[0].fee, 0); + assert.equal(event._assetDetails[1].coinName, _name1); + assert.equal(event._assetDetails[1].value, fees.amounts[1]); + assert.equal(event._assetDetails[1].fee, 0); + assert.equal(event._assetDetails[2].coinName, _name2); + assert.equal(event._assetDetails[2].value, fees.amounts[2]); + assert.equal(event._assetDetails[2].fee, 0); + + assert.equal(web3.utils.BN(FA1Before).toNumber(), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA2Before).toNumber(), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA3Before).toNumber(), Math.floor(_txAmt2 / 1000) + _fixed_fee); + + assert.equal(web3.utils.BN(FA1After).toNumber(), 0); + assert.equal(web3.utils.BN(FA2After).toNumber(), 0); + assert.equal(web3.utils.BN(FA3After).toNumber(), 0); + + assert.equal(fees.coinNames[0], _native); + assert.equal(fees.coinNames[1], _name1); + assert.equal(fees.coinNames[2], _name2); + + assert.equal(Number(fees.amounts[0]), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(fees.amounts[1]), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(fees.amounts[2]), Math.floor(_txAmt2 / 1000) + _fixed_fee); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: Receiving a successful response', async () => { + let _sn3 = 4; + let feesBefore = await bsh_perif.getFees(_sn3); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net1, service, _sn3, _responseMsg); + let feesAfter = await bsh_perif.getFees(_sn3); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, bsh_core.address); + assert.equal(event._sn, _sn3); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(feesBefore.amounts.length, 3); + assert.equal(feesBefore.coinNames[0], _native); + assert.equal(feesBefore.coinNames[1], _name1); + assert.equal(feesBefore.coinNames[2], _name2); + assert.equal(Number(feesBefore.amounts[0]), Math.floor(_txAmt / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[1]), Math.floor(_txAmt1 / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[2]), Math.floor(_txAmt2 / 1000) + _fixed_fee); + assert.equal(feesAfter.amounts.length, 0); + }); + + it('Scenario 5: Receiving an error response', async () => { + let _sn4 = 5; let _sn5 = 6; let _sn6 = 7; + let _amt1 = 2000000; let _amt2 = 6000000; + await holder.callTransfer(_name1, _amt1, _to1); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + await bmc.receiveResponse(_net1, service, _sn4, _responseMsg); + await holder.callTransfer(_name2, _amt2, _to2); + await bmc.receiveResponse(_net2, service, _sn5, _responseMsg); + let _bmcService = await encode_msg.encodeBMCService(_to1, [service]); + await bmc.receiveRequest(_bmcICON, '', 'bmc', 100, _bmcService); + + let FA1Before = await bsh_perif.getAggregationFeeOf(_name1); + let FA2Before = await bsh_perif.getAggregationFeeOf(_name2); + let feesBefore = await bsh_perif.getFees(_sn6); + let _errMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net1, service, _sn6, _errMsg); + let FA1After = await bsh_perif.getAggregationFeeOf(_name1); + let FA2After = await bsh_perif.getAggregationFeeOf(_name2); + let feesAfter = await bsh_perif.getFees(_sn6); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, bsh_core.address); + assert.equal(event._sn, _sn6); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(feesBefore.amounts.length, 2); + assert.equal(feesBefore.coinNames[0], _name1); + assert.equal(feesBefore.coinNames[1], _name2); + assert.equal(Number(feesBefore.amounts[0]), Math.floor(_amt1 / 1000) + _fixed_fee); + assert.equal(Number(feesBefore.amounts[1]), Math.floor(_amt2 / 1000) + _fixed_fee); + + assert.equal(web3.utils.BN(FA1Before).toNumber(), 0); + assert.equal(web3.utils.BN(FA2Before).toNumber(), 0); + assert.equal(feesAfter.amounts.length, 0); + assert.equal(web3.utils.BN(FA1After).toNumber(), Math.floor(_amt1 / 1000) + _fixed_fee); + assert.equal(web3.utils.BN(FA2After).toNumber(), Math.floor(_amt2 / 1000) + _fixed_fee); + }); +}); + +contract('As a user, I want to receive multiple Coins/Tokens from ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, holder, refundable; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _name1 = 'ICON'; let _name2 = 'BINANCE'; let _name3 = 'ETHEREUM'; let _name4 = 'TRON'; + let _net1 = '1234.iconee'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let RC_OK = 0; let RC_ERR = 1; + let _from1 = '0x12345678'; let _to = 'btp://1234.iconee/0x12345678'; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + refundable = await Refundable.new(); + btpAddr = await bmc.bmcAddress(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net1, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + await bsh_core.register(_name1); + await bsh_core.register(_name2); + await bsh_core.register(_name3); + await bsh_core.register(_name4); + await bsh_core.transferNativeCoin(_to, {from: accounts[0], value: 10000000}); + }); + + it('Scenario 1: Receiving address is invalid', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let _address = '0x1234567890123456789'; + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'InvalidAddress'); + let _msg = await encode_msg.encodeBatchTransferMsgWithStringAddress( + _from1, _address, [[_native, _value1], [_name1, _value2], [_name2, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 2: BSHPerphery receives a request of invalid token', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let _invalid_token = 'EOS'; + let balance1Before = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_core.getBalanceOf(holder.address, _invalid_token); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'UnregisteredCoin'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_invalid_token, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_core.getBalanceOf(holder.address, _invalid_token); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 3: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 20000000; + let balance1Before = await bsh_core.getBalanceOf(accounts[1], _name1); + let balance2Before = await bsh_core.getBalanceOf(accounts[1], _name2); + let balance3Before = await bsh_core.getBalanceOf(accounts[1], _native); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, accounts[1], [[_name1, _value1], [_name2, _value2], [_native, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(accounts[1], _name1); + let balance2After = await bsh_core.getBalanceOf(accounts[1], _name2); + let balance3After = await bsh_core.getBalanceOf(accounts[1], _native); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 4: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_core.getBalanceOf(refundable.address, _native); + let balance2Before = await bsh_core.getBalanceOf(refundable.address, _name1); + let balance3Before = await bsh_core.getBalanceOf(refundable.address, _name2); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, refundable.address, [[_native, _value1], [_name1, _value2], [_name2, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(refundable.address, _native); + let balance2After = await bsh_core.getBalanceOf(refundable.address, _name1); + let balance3After = await bsh_core.getBalanceOf(refundable.address, _name2); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 5: One of requests is failed in TransferBatch', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_core.getBalanceOf(holder.address, _native); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_ERR, 'TransferFailed'); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_native, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_core.getBalanceOf(holder.address, _native); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), 0); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 6: Receiving a successful TransferBatch request', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2Before = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3Before = await bsh_core.getBalanceOf(holder.address, _name3); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, holder.address, [[_name1, _value1], [_name2, _value2], [_name3, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(holder.address, _name1); + let balance2After = await bsh_core.getBalanceOf(holder.address, _name2); + let balance3After = await bsh_core.getBalanceOf(holder.address, _name3); + + assert.equal(web3.utils.BN(balance1Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance2Before._usableBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balance3Before._usableBalance).toNumber(), 0); + + assert.equal(web3.utils.BN(balance1After._usableBalance).toNumber(), _value1); + assert.equal(web3.utils.BN(balance2After._usableBalance).toNumber(), _value2); + assert.equal(web3.utils.BN(balance3After._usableBalance).toNumber(), _value3); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); + + it('Scenario 7: Receiving a successful TransferBatch request', async () => { + let _value1 = 1000; let _value2 = 10000; let _value3 = 40000; + let balance1Before = await bsh_core.getBalanceOf(accounts[1], _native); + let balance2Before = await bsh_core.getBalanceOf(accounts[1], _name2); + let balance3Before = await bsh_core.getBalanceOf(accounts[1], _name3); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeBatchTransferMsgWithAddress( + _from1, accounts[1], [[_native, _value1], [_name2, _value2], [_name3, _value3]] + ); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balance1After = await bsh_core.getBalanceOf(accounts[1], _native); + let balance2After = await bsh_core.getBalanceOf(accounts[1], _name2); + let balance3After = await bsh_core.getBalanceOf(accounts[1], _name3); + + assert.equal( + web3.utils.BN(balance1After._usableBalance).toString(), + web3.utils.BN(balance1Before._usableBalance).add(new web3.utils.BN(_value1)).toString() + ); + assert.equal( + web3.utils.BN(balance2After._usableBalance).toNumber(), + web3.utils.BN(balance2Before._usableBalance).toNumber() + _value2 + ); + assert.equal( + web3.utils.BN(balance3After._usableBalance).toNumber(), + web3.utils.BN(balance3Before._usableBalance).toNumber() + _value3 + ); + + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); +}); + +contract('As a user, I want to send multiple coins/tokens to ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, holder; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'PARA'; let _fee = 10; let _fixed_fee = 500000; + let _net = '1234.iconee'; let _from = '0x12345678'; let _value = 999999999999999; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; let RC_ERR = 1; + let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _coin1 = 'ICON'; let _coin2 = 'TRON'; let _coin3 = 'BINANCE'; + let initAmt = 1000000000000000; + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.pra'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + await bsh_core.register(_coin1); + await bsh_core.register(_coin2); + await bsh_core.register(_coin3); + await bsh_core.transferNativeCoin('btp://1234.iconee/0x12345678', {from: accounts[0], value: initAmt}); + await holder.deposit({from: accounts[1], value: 100000000000000}); + let _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin1, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg1); + let _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin2, _value); + await bmc.receiveRequest(_bmcICON, "", service, 1, _msg2); + let _msg3 = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _coin3, _value); + await bmc.receiveRequest(_bmcICON, "", service, 2, _msg3); + + _msg1 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin1, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg1); + _msg2 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin2, _value); + await bmc.receiveRequest(_bmcICON, "", service, 1, _msg2); + _msg3 = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _coin3, _value); + await bmc.receiveRequest(_bmcICON, "", service, 2, _msg3); + }); + + it('Scenario 1: User has not yet set approval for token being transferred out by Operator', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _native_amt = 800000; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it(`Scenario 2: User has set approval, but user's balance has insufficient amount`, async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 9999999999999999n]; + let _native_amt = 700000; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 3: User requests to transfer an invalid Token', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let invalid_token = 'EOS'; + let _coins = [_coin1, invalid_token]; + let _values = [600000, 700000]; + let _native_amt = 800000; + let _query = [_native, _coin1, invalid_token]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 4: User transfers Tokens to an invalid BTP Address format', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 5: User requests to transfer zero Token', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 0]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 6: Transffering amount is less than fixed fee', async () => { + let _to = '1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 300000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 800000; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 7: User requests to transfer to an invalid network/Not Supported Network', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _coins = [_coin1, _coin2]; + let _values = [600000, 700000]; + let _query = [_native, _coin1, _coin2]; + let _native_amt = 1000; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, _native_amt), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 8: Account client sends an invalid request of transferBatch', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_native, _native, _native]; + let _values = [600000, 600000, 600000]; + let balanceBefore = await bsh_core.getBalanceOfBatch(accounts[2], _coins); + await truffleAssert.reverts( + bsh_core.transferBatch.call(_coins, _values, _to, {from: accounts[2], value: 600000}), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(accounts[2], _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), initAmt); + }); + + it('Scenario 9: Contract client sends an invalid request of transferBatch', async () => { + let _to = 'btp://1234.eos/0x12345678'; + let _coins = [_native, _coin1, _coin2]; + let _values = [600000, 700000]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _coins); + await holder.setApprove(bsh_core.address); + await truffleAssert.reverts( + holder.callTransferBatch.call(bsh_core.address, _coins, _values, _to, 0), + "revert" + ); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), 0); + }); + + it('Scenario 10: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value2, _value3]; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + let tx = await holder.callTransferBatch(bsh_core.address, _coins, _values, _to, _value1); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 2); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin1); + assert.equal(event._assetDetails[0].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[0].fee, chargedFee2); + assert.equal(event._assetDetails[1].coinName, _coin2); + assert.equal(event._assetDetails[1].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[1].fee, chargedFee3); + assert.equal(event._assetDetails[2].coinName, _native); + assert.equal(event._assetDetails[2].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[2].fee, chargedFee1); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 2); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value3 - chargedFee3); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value1 - chargedFee1); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt + _value1); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), _value3); + }); + + it('Scenario 11: BSHPeriphery receives a successful response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 2, _responseMsg); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let fees = await bsh_core.getAccumulatedFees(); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 2); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + ); + + assert.equal(fees[0].coinName, _native); assert.equal(Number(fees[0].value), chargedFee1); + assert.equal(fees[1].coinName, _coin1); assert.equal(Number(fees[1].value), chargedFee2); + assert.equal(fees[2].coinName, _coin2); assert.equal(Number(fees[2].value), chargedFee3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), initAmt + _value1); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), chargedFee2); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), chargedFee3); + }); + + it('Scenario 12: Account client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin3, _coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value1, _value2, _value3]; + let balanceBefore = await bsh_core.getBalanceOfBatch(accounts[1], _coins); + await bsh_core.setApprovalForAll(bsh_core.address, true, {from: accounts[1]}); + let tx = await bsh_core.transferBatch(_coins, _values, _to, {from: accounts[1]}); + let balanceAfter = await bsh_core.getBalanceOfBatch(accounts[1], _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[1]); + assert.equal(event._to, _to); + assert.equal(event._sn, 3); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin3); + assert.equal(event._assetDetails[0].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[0].fee, chargedFee1); + assert.equal(event._assetDetails[1].coinName, _coin1); + assert.equal(event._assetDetails[1].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[1].fee, chargedFee2); + assert.equal(event._assetDetails[2].coinName, _coin2); + assert.equal(event._assetDetails[2].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[2].fee, chargedFee3); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 3); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin3); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value1 - chargedFee1); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value3 - chargedFee3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), _value1); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + chargedFee3 + ); + }); + + it('Scenario 13: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_coin3, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(accounts[1], _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 3, _responseMsg); + let balanceAfter = await bsh_core.getBalanceOfBatch(accounts[1], _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[1]); + assert.equal(event._sn, 3); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + _value1 - chargedFee1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 2 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 2 * chargedFee3 + ); + }); + + it('Scenario 14: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin3, _coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value1, _value2, _value3]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _coins); + await holder.setApprove(bsh_core.address); + let tx = await holder.callTransferBatch(bsh_core.address, _coins, _values, _to, 0); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 4); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin3); + assert.equal(event._assetDetails[0].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[0].fee, chargedFee1); + assert.equal(event._assetDetails[1].coinName, _coin1); + assert.equal(event._assetDetails[1].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[1].fee, chargedFee2); + assert.equal(event._assetDetails[2].coinName, _coin2); + assert.equal(event._assetDetails[2].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[2].fee, chargedFee3); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 4); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin3); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value1 - chargedFee1); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value3 - chargedFee3); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + _value1 + chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + 2 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + 2 * chargedFee3 + ); + }); + + it('Scenario 15: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_coin3, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 4, _responseMsg); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 4); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + _value1 - chargedFee1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + 2 * chargedFee1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 3 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 3 * chargedFee3 + ); + }); + + // This test is replicated from Scenario 10 + it('Scenario 16: Contract client sends a valid transferBatch request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let _coins = [_coin1, _coin2]; + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _values = [_value2, _value3]; + let _query = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _query); + await holder.setApprove(bsh_core.address); + let tx = await holder.callTransferBatch(bsh_core.address, _coins, _values, _to, _value1); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _query); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _query); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, holder.address); + assert.equal(event._to, _to); + assert.equal(event._sn, 5); + assert.equal(event._assetDetails.length, 3); + assert.equal(event._assetDetails[0].coinName, _coin1); + assert.equal(event._assetDetails[0].value, _value2 - chargedFee2); + assert.equal(event._assetDetails[0].fee, chargedFee2); + assert.equal(event._assetDetails[1].coinName, _coin2); + assert.equal(event._assetDetails[1].value, _value3 - chargedFee3); + assert.equal(event._assetDetails[1].fee, chargedFee3); + assert.equal(event._assetDetails[2].coinName, _native); + assert.equal(event._assetDetails[2].value, _value1 - chargedFee1); + assert.equal(event._assetDetails[2].fee, chargedFee1); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 5); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), holder.address); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _coin1); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), _value2 - chargedFee2); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][1][0])), _coin2); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][1][1])), _value3 - chargedFee3); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][2][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][2][1])), _value1 - chargedFee1); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), 0); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), _value3); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() - _value1 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() - _value2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() - _value3 + ); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + initAmt + 2 * _value1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + _value2 + 3 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + _value3 + 3 * chargedFee3 + ); + }); + + it('Scenario 17: BSHPeriphery receives an error response of a recent request', async () => { + let _value1 = 600000; let _value2 = 700000; let _value3 = 800000; + let _coins = [_native, _coin1, _coin2]; + let balanceBefore = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let _responseMsg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_ERR, ""); + let tx = await bmc.receiveResponse(_net, service, 5, _responseMsg); + let balanceAfter = await bsh_core.getBalanceOfBatch(holder.address, _coins); + let bsh_core_balance = await bsh_core.getBalanceOfBatch(bsh_core.address, _coins); + let chargedFee1 = Math.floor(_value1 / 1000) + _fixed_fee; + let chargedFee2 = Math.floor(_value2 / 1000) + _fixed_fee; + let chargedFee3 = Math.floor(_value3 / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest'}); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, holder.address); + assert.equal(event._sn, 5); + assert.equal(event._code, 1); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[0]).toNumber(), _value1); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[1]).toNumber(), _value2); + assert.equal(web3.utils.BN(balanceBefore._lockedBalances[2]).toNumber(), _value3); + + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[1]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalances[2]).toNumber(), 0); + + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[0]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[0]).toNumber() + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[1]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[1]).toNumber() + _value2 - chargedFee2 + ); + assert.equal( + web3.utils.BN(balanceAfter._usableBalances[2]).toNumber(), + web3.utils.BN(balanceBefore._usableBalances[2]).toNumber() + _value3 - chargedFee3 + ); + assert.equal(web3.utils.BN(balanceBefore._refundableBalances[0]).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._refundableBalances[0]).toNumber(), _value1 - chargedFee1); + + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[0]).toNumber(), + initAmt + 2 * _value1 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[1]).toNumber(), + 4 * chargedFee2 + ); + assert.equal( + web3.utils.BN(bsh_core_balance._usableBalances[2]).toNumber(), + 4 * chargedFee3 + ); + }); +}); \ No newline at end of file diff --git a/solidity/bsh/test/unit/1-upgradeability-native-coin-bsh.js b/solidity/bsh/test/unit/1-upgradeability-native-coin-bsh.js new file mode 100644 index 00000000..32ce3793 --- /dev/null +++ b/solidity/bsh/test/unit/1-upgradeability-native-coin-bsh.js @@ -0,0 +1,452 @@ +const BSHCoreV1 = artifacts.require("BSHCoreV1"); +const BSHCoreV2 = artifacts.require("BSHCoreV2"); +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +contract('BSHCore Unit Tests - After Upgrading Contract', (accounts) => { + let bsh_coreV1, bsh_coreV2; + let _native = 'PARA'; let _uri = 'https://github.com/icon-project/btp' + let _fee = 10; let _fixed_fee = 500000; + before(async () => { + bsh_coreV1 = await deployProxy(BSHCoreV1, [_uri, _native, _fee, _fixed_fee]); + bsh_coreV2 = await upgradeProxy(bsh_coreV1.address, BSHCoreV2); + }); + + it(`Scenario 1: Should allow contract's owner to register a new coin`, async () => { + let _name = "ICON"; + await bsh_coreV2.register(_name); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' + ); + }); + + it('Scenario 2: Should revert when an arbitrary client tries to register a new coin', async () => { + let _name = "TRON"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 3: Should revert when contract owner registers an existed coin', async () => { + let _name = "ICON"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name), + "ExistToken" + ); + }); + + it('Scenario 4: Should allow contract owner to update BSHPeriphery contract', async () => { + await bsh_coreV2.updateBSHPeriphery(accounts[2]); + }); + + it('Scenario 5: Should revert when arbitrary client updates BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_coreV2.updateBSHPeriphery.call(accounts[2], {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 6: Should allow contract owner to update a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri); + }); + + it('Scenario 7: Should revert when arbitrary client update a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_coreV2.updateUri.call(new_uri, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 8: Should allow contract owner to update fee ratio', async () => { + let new_fee = 20; + await bsh_coreV2.setFeeRatio(new_fee); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 9: Should revert when arbitrary client updates fee ratio', async () => { + let old_fee = 20; + let new_fee = 50; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 10: Should revert when Fee Numerator is higher than Fee Denominator', async () => { + let old_fee = 20; + let new_fee = 20000; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 11: Should allow contract owner to update fixed fee', async () => { + let new_fixed_fee = 1000000; + await bsh_coreV2.setFixedFee(new_fixed_fee); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 12: Should revert when arbitrary client updates fixed fee', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 2000000; + await truffleAssert.reverts( + bsh_coreV2.setFixedFee.call(new_fixed_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 13: Should revert when Owner set fixed fee is zero', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 0; + await truffleAssert.reverts( + bsh_coreV2.setFixedFee.call(new_fixed_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 14: Should receive an id of a given coin name when querying a valid supporting coin', async () => { + let _name1 = "wBTC"; let _name2 = "Ethereum"; + await bsh_coreV2.register(_name1); + await bsh_coreV2.register(_name2); + + let _query = "ICON"; + let id = web3.utils.keccak256(_query); + let result = await bsh_coreV2.coinId(_query); + assert( + web3.utils.BN(result).toString() === web3.utils.toBN(id).toString() + ); + }); + + it('Scenario 15: Should receive an id = 0 when querying an invalid supporting coin', async () => { + let _query = "EOS"; + let result = await bsh_coreV2.coinId(_query); + assert( + web3.utils.BN(result).toNumber() === 0 + ); + }); + + it('Scenario 16: Should revert when a non-Owner tries to add a new Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.addOwner.call(accounts[1], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 1 && newList[0] === accounts[0] + ); + }); + + it('Scenario 17: Should allow a current Owner to add a new Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await bsh_coreV2.addOwner(accounts[1]); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 18: Should allow old owner to register a new coin - After adding new Owner', async () => { + let _name3 = "TRON"; + await bsh_coreV2.register(_name3); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' + ); + }); + + it('Scenario 19: Should allow new owner to register a new coin', async () => { + let _name3 = "BINANCE"; + await bsh_coreV2.register(_name3, {from: accounts[1]}); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 20: Should allow new owner to update BSHPeriphery contract', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_coreV2.clearBSHPerifSetting(); + await bsh_coreV2.updateBSHPeriphery(accounts[3], {from: accounts[1]}); + }); + + it('Scenario 21: Should also allow old owner to update BSHPeriphery contract - After adding new Owner', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_coreV2.clearBSHPerifSetting(); + await bsh_coreV2.updateBSHPeriphery(accounts[3], {from: accounts[0]}); + }); + + it('Scenario 22: Should allow new owner to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri, {from: accounts[1]}); + }); + + it('Scenario 23: Should also allow old owner to update the new URI - After adding new Owner', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_coreV2.updateUri(new_uri, {from: accounts[0]}); + }); + + it('Scenario 24: Should allow new owner to update new fee ratio', async () => { + let new_fee = 30; + await bsh_coreV2.setFeeRatio(new_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 25: Should also allow old owner to update new fee ratio - After adding new Owner', async () => { + let new_fee = 40; + await bsh_coreV2.setFeeRatio(new_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_coreV2.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 26: Should allow new owner to update new fixed fee', async () => { + let new_fixed_fee = 3000000; + await bsh_coreV2.setFixedFee(new_fixed_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 27: Should also allow old owner to update new fixed fee - After adding new Owner', async () => { + let new_fixed_fee = 4000000; + await bsh_coreV2.setFixedFee(new_fixed_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_coreV2.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 28: Should revert when non-Owner tries to remove an Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.removeOwner.call(accounts[0], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 29: Should allow one current Owner to remove another Owner', async () => { + let oldList = await bsh_coreV2.getOwners(); + await bsh_coreV2.removeOwner(accounts[0], {from: accounts[1]}); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 30: Should revert when the last Owner removes him/herself', async () => { + let oldList = await bsh_coreV2.getOwners(); + await truffleAssert.reverts( + bsh_coreV2.removeOwner.call(accounts[1], {from: accounts[1]}), + "Unable to remove last Owner" + ); + let newList = await bsh_coreV2.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 31: Should revert when removed Owner tries to register a new coin', async () => { + let _name3 = "KYBER"; + await truffleAssert.reverts( + bsh_coreV2.register.call(_name3), + 'Unauthorized' + ); + output = await bsh_coreV2.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 32: Should revert when removed Owner tries to update BSHPeriphery contract', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_coreV2.clearBSHPerifSetting(); + await truffleAssert.reverts( + bsh_coreV2.updateBSHPeriphery.call(accounts[3], {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 33: Should revert when removed Owner tries to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_coreV2.updateUri.call(new_uri, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 34: Should revert when removed Owner tries to update new fee ratio', async () => { + let new_fee = 30; + await truffleAssert.reverts( + bsh_coreV2.setFeeRatio.call(new_fee, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 35: Should allow arbitrary client to query balance of an account', async () => { + let _coin = 'ICON'; + let _id = await bsh_coreV2.coinId(_coin); + let _value = 2000; + await bsh_coreV2.mintMock(accounts[2], _id, _value); + let balance = await bsh_coreV2.getBalanceOf(accounts[2], _coin, {from: accounts[2]}); + assert( + web3.utils.BN(balance._usableBalance).toNumber() === _value && + web3.utils.BN(balance._lockedBalance).toNumber() === 0 && + web3.utils.BN(balance._refundableBalance).toNumber() === 0 + ); + }); + + it('Scenario 36: Should allow arbitrary client to query a batch of balances of an account', async () => { + let _coin1 = 'ICON'; let _coin2 = 'TRON'; + let _id = await bsh_coreV2.coinId(_coin2); + let _value = 10000; let another_value = 2000; + await bsh_coreV2.mintMock(accounts[2], _id, _value); + let balance = await bsh_coreV2.getBalanceOfBatch(accounts[2], [_coin1,_coin2], {from: accounts[2]}); + assert( + web3.utils.BN(balance._usableBalances[0]).toNumber() === another_value && + web3.utils.BN(balance._lockedBalances[0]).toNumber() === 0 && + web3.utils.BN(balance._refundableBalances[0]).toNumber() === 0 && + + web3.utils.BN(balance._usableBalances[1]).toNumber() === _value && + web3.utils.BN(balance._lockedBalances[1]).toNumber() === 0 && + web3.utils.BN(balance._refundableBalances[1]).toNumber() === 0 + ); + }); + + it('Scenario 37: Should allow arbitrary client to query an Accumulated Fees', async () => { + let _coin1 = 'ICON'; let _coin2 = 'TRON'; + let _value1 = 40000; let _value2 = 10000; let _native_value = 2000; + await bsh_coreV2.setAggregationFee(_coin1, _value1); + await bsh_coreV2.setAggregationFee(_coin2, _value2); + await bsh_coreV2.setAggregationFee(_native, _native_value); + let fees = await bsh_coreV2.getAccumulatedFees({from: accounts[3]}); + assert( + fees[0].coinName === _native && Number(fees[0].value) === _native_value && + fees[1].coinName === _coin1 && Number(fees[1].value) === _value1 && + fees[2].coinName === 'wBTC' && Number(fees[2].value) === 0 && + fees[3].coinName === 'Ethereum' && Number(fees[3].value) === 0 && + fees[4].coinName === _coin2 && Number(fees[4].value) === _value2 + ); + }); + + it('Scenario 38: Should revert when a client reclaims an exceeding amount', async () => { + let _coin = 'ICON'; let _value = 10000; let _exceedAmt = 20000; + await bsh_coreV2.setRefundableBalance(accounts[2], _coin, _value); + await truffleAssert.reverts( + bsh_coreV2.reclaim.call(_coin, _exceedAmt, {from: accounts[2]}), + "Imbalance" + ); + }); + + it('Scenario 39: Should revert when a client, which does not own a refundable, tries to reclaim', async () => { + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_coreV2.reclaim.call(_coin, _value, {from: accounts[3]}), + "Imbalance" + ); + }); + + it('Scenario 40: Should succeed when a client, which owns a refundable, tries to reclaim', async () => { + let _coin = 'ICON'; let _value = 10000; + let _id = await bsh_coreV2.coinId(_coin); + await bsh_coreV2.mintMock(bsh_coreV2.address, _id, _value); + let balanceBefore = await bsh_coreV2.getBalanceOf(accounts[2], _coin); + await bsh_coreV2.reclaim(_coin, _value, {from: accounts[2]}); + let balanceAfter = await bsh_coreV2.getBalanceOf(accounts[2], _coin); + assert( + web3.utils.BN(balanceAfter._usableBalance).toNumber() === + web3.utils.BN(balanceBefore._usableBalance).toNumber() + _value + ); + }); + + it('Scenario 41: Should not allow any clients (even a contract owner) to call a refund()', async () => { + // This function should be called only by itself (BSHCore contract) + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_coreV2.refund.call(accounts[0], _coin, _value), + "Unauthorized" + ); + }); + + it('Scenario 42: Should not allow any clients (even a contract owner) to call a mint()', async () => { + // This function should be called only by BSHPeriphery contract + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_coreV2.mint.call(accounts[0], _coin, _value), + "Unauthorized" + ); + }); + + it('Scenario 43: Should not allow any clients (even a contract owner) to call a handleResponseService()', async () => { + // This function should be called only by BSHPeriphery contract + let RC_OK = 0; + let _coin = 'ICON'; let _value = 10000; let _fee = 1; let _rspCode = RC_OK; + await truffleAssert.reverts( + bsh_coreV2.handleResponseService.call(accounts[0], _coin, _value, _fee, _rspCode), + "Unauthorized" + ); + }); + + it('Scenario 44: Should not allow any clients (even a contract owner) to call a transferFees()', async () => { + // This function should be called only by BSHPeriphery contract + let _fa = 'btp://1234.iconee/0x1234567812345678'; + await truffleAssert.reverts( + bsh_coreV2.transferFees.call(_fa), + "Unauthorized" + ); + }); +}); \ No newline at end of file diff --git a/solidity/bsh/test/unit/2-native-coin-bsh.js b/solidity/bsh/test/unit/2-native-coin-bsh.js new file mode 100644 index 00000000..1627f1ac --- /dev/null +++ b/solidity/bsh/test/unit/2-native-coin-bsh.js @@ -0,0 +1,601 @@ +const BSHPerif = artifacts.require("BSHPeriphery"); +const MockBSHCore = artifacts.require("MockBSHCore"); +const CheckParseAddress = artifacts.require("CheckParseAddress"); +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); + +contract('BSHCore Unit Tests', (accounts) => { + let bsh_core; + let _native = 'PARA'; let _uri = 'https://github.com/icon-project/btp' + let _fee = 10; let _fixed_fee = 500000; + + before(async () => { + bsh_core = await MockBSHCore.new(); + await bsh_core.initialize(_uri, _native, _fee, _fixed_fee); + }); + + it(`Scenario 1: Should allow contract's owner to register a new coin`, async () => { + let _name = "ICON"; + await bsh_core.register(_name); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' + ); + }); + + it('Scenario 2: Should revert when an arbitrary client tries to register a new coin', async () => { + let _name = "TRON"; + await truffleAssert.reverts( + bsh_core.register.call(_name, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 3: Should revert when contract owner registers an existed coin', async () => { + let _name = "ICON"; + await truffleAssert.reverts( + bsh_core.register.call(_name), + "ExistToken" + ); + }); + + it('Scenario 4: Should allow contract owner to update BSHPeriphery contract', async () => { + await bsh_core.updateBSHPeriphery(accounts[2]); + }); + + it('Scenario 5: Should revert when arbitrary client updates BSHPeriphery contract', async () => { + await truffleAssert.reverts( + bsh_core.updateBSHPeriphery.call(accounts[2], {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 6: Should allow contract owner to update a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri); + }); + + it('Scenario 7: Should revert when arbitrary client update a new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_core.updateUri.call(new_uri, {from: accounts[1]}), + "Unauthorized" + ); + }); + + it('Scenario 8: Should allow contract owner to update fee ratio', async () => { + let new_fee = 20; + await bsh_core.setFeeRatio(new_fee); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 9: Should revert when arbitrary client updates fee ratio', async () => { + let old_fee = 20; + let new_fee = 50; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 10: Should revert when Fee Numerator is higher than Fee Denominator', async () => { + let old_fee = 20; + let new_fee = 20000; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === old_fee + ); + }); + + it('Scenario 11: Should allow contract owner to update fixed fee', async () => { + let new_fixed_fee = 1000000; + await bsh_core.setFixedFee(new_fixed_fee); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 12: Should revert when arbitrary client updates fixed fee', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 2000000; + await truffleAssert.reverts( + bsh_core.setFixedFee.call(new_fixed_fee, {from: accounts[1]}), + "Unauthorized" + ); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 13: Should revert when Owner set fixed fee is zero', async () => { + let old_fixed_fee = 1000000; + let new_fixed_fee = 0; + await truffleAssert.reverts( + bsh_core.setFixedFee.call(new_fixed_fee), + "InvalidSetting" + ); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === old_fixed_fee + ); + }); + + it('Scenario 14: Should receive an id of a given coin name when querying a valid supporting coin', async () => { + let _name1 = "wBTC"; let _name2 = "Ethereum"; + await bsh_core.register(_name1); + await bsh_core.register(_name2); + + let _query = "ICON"; + let id = web3.utils.keccak256(_query); + let result = await bsh_core.coinId(_query); + assert( + web3.utils.BN(result).toString() === web3.utils.toBN(id).toString() + ); + }); + + it('Scenario 15: Should receive an id = 0 when querying an invalid supporting coin', async () => { + let _query = "EOS"; + let result = await bsh_core.coinId(_query); + assert( + web3.utils.BN(result).toNumber() === 0 + ); + }); + + it('Scenario 16: Should revert when a non-Owner tries to add a new Owner', async () => { + let oldList = await bsh_core.getOwners(); + await truffleAssert.reverts( + bsh_core.addOwner.call(accounts[1], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 1 && newList[0] === accounts[0] + ); + }); + + it('Scenario 17: Should allow a current Owner to add a new Owner', async () => { + let oldList = await bsh_core.getOwners(); + await bsh_core.addOwner(accounts[1]); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[0] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 18: Should allow old owner to register a new coin - After adding new Owner', async () => { + let _name3 = "TRON"; + await bsh_core.register(_name3); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' + ); + }); + + it('Scenario 19: Should allow new owner to register a new coin', async () => { + let _name3 = "BINANCE"; + await bsh_core.register(_name3, {from: accounts[1]}); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 20: Should allow new owner to update BSHPeriphery contract', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_core.clearBSHPerifSetting(); + await bsh_core.updateBSHPeriphery(accounts[3], {from: accounts[1]}); + }); + + it('Scenario 21: Should also allow old owner to update BSHPeriphery contract - After adding new Owner', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_core.clearBSHPerifSetting(); + await bsh_core.updateBSHPeriphery(accounts[3], {from: accounts[0]}); + }); + + it('Scenario 22: Should allow new owner to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri, {from: accounts[1]}); + }); + + it('Scenario 23: Should also allow old owner to update the new URI - After adding new Owner', async () => { + let new_uri = 'https://1234.iconee/' + await bsh_core.updateUri(new_uri, {from: accounts[0]}); + }); + + it('Scenario 24: Should allow new owner to update new fee ratio', async () => { + let new_fee = 30; + await bsh_core.setFeeRatio(new_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 25: Should also allow old owner to update new fee ratio - After adding new Owner', async () => { + let new_fee = 40; + await bsh_core.setFeeRatio(new_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_core.feeNumerator()).toNumber() === new_fee + ); + }); + + it('Scenario 26: Should allow new owner to update new fixed fee', async () => { + let new_fixed_fee = 3000000; + await bsh_core.setFixedFee(new_fixed_fee, {from: accounts[1]}); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 27: Should also allow old owner to update new fixed fee - After adding new Owner', async () => { + let new_fixed_fee = 4000000; + await bsh_core.setFixedFee(new_fixed_fee, {from: accounts[0]}); + + assert( + web3.utils.BN(await bsh_core.fixedFee()).toNumber() === new_fixed_fee + ); + }); + + it('Scenario 28: Should revert when non-Owner tries to remove an Owner', async () => { + let oldList = await bsh_core.getOwners(); + await truffleAssert.reverts( + bsh_core.removeOwner.call(accounts[0], {from: accounts[2]}), + "Unauthorized" + ); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 2 && newList[0] === accounts[0] && newList[1] === accounts[1] + ); + }); + + it('Scenario 29: Should allow one current Owner to remove another Owner', async () => { + let oldList = await bsh_core.getOwners(); + await bsh_core.removeOwner(accounts[0], {from: accounts[1]}); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 2 && oldList[0] === accounts[0] && oldList[1] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 30: Should revert when the last Owner removes him/herself', async () => { + let oldList = await bsh_core.getOwners(); + await truffleAssert.reverts( + bsh_core.removeOwner.call(accounts[1], {from: accounts[1]}), + "Unable to remove last Owner" + ); + let newList = await bsh_core.getOwners(); + assert( + oldList.length === 1 && oldList[0] === accounts[1] && + newList.length === 1 && newList[0] === accounts[1] + ); + }); + + it('Scenario 31: Should revert when removed Owner tries to register a new coin', async () => { + let _name3 = "KYBER"; + await truffleAssert.reverts( + bsh_core.register.call(_name3), + 'Unauthorized' + ); + output = await bsh_core.coinNames(); + assert( + output[0] === _native && output[1] === 'ICON' && + output[2] === 'wBTC' && output[3] === 'Ethereum' && + output[4] === 'TRON' && output[5] === 'BINANCE' + ); + }); + + it('Scenario 32: Should revert when removed Owner tries to update BSHPeriphery contract', async () => { + // Must clear BSHPerif setting since BSHPeriphery has been set + // The requirement: if (BSHPerif.address != address(0)) must check whether BSHPerif has any pending requests + // Instead of creating a MockBSHPerif, just clear BSHPerif setting + await bsh_core.clearBSHPerifSetting(); + await truffleAssert.reverts( + bsh_core.updateBSHPeriphery.call(accounts[3], {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 33: Should revert when removed Owner tries to update the new URI', async () => { + let new_uri = 'https://1234.iconee/' + await truffleAssert.reverts( + bsh_core.updateUri.call(new_uri, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 34: Should revert when removed Owner tries to update new fee ratio', async () => { + let new_fee = 30; + await truffleAssert.reverts( + bsh_core.setFeeRatio.call(new_fee, {from: accounts[0]}), + 'Unauthorized' + ); + }); + + it('Scenario 35: Should allow arbitrary client to query balance of an account', async () => { + let _coin = 'ICON'; + let _id = await bsh_core.coinId(_coin); + let _value = 2000; + await bsh_core.mintMock(accounts[2], _id, _value); + let balance = await bsh_core.getBalanceOf(accounts[2], _coin, {from: accounts[2]}); + assert( + web3.utils.BN(balance._usableBalance).toNumber() === _value && + web3.utils.BN(balance._lockedBalance).toNumber() === 0 && + web3.utils.BN(balance._refundableBalance).toNumber() === 0 + ); + }); + + it('Scenario 36: Should allow arbitrary client to query a batch of balances of an account', async () => { + let _coin1 = 'ICON'; let _coin2 = 'TRON'; + let _id = await bsh_core.coinId(_coin2); + let _value = 10000; let another_value = 2000; + await bsh_core.mintMock(accounts[2], _id, _value); + let balance = await bsh_core.getBalanceOfBatch(accounts[2], [_coin1,_coin2], {from: accounts[2]}); + assert( + web3.utils.BN(balance._usableBalances[0]).toNumber() === another_value && + web3.utils.BN(balance._lockedBalances[0]).toNumber() === 0 && + web3.utils.BN(balance._refundableBalances[0]).toNumber() === 0 && + + web3.utils.BN(balance._usableBalances[1]).toNumber() === _value && + web3.utils.BN(balance._lockedBalances[1]).toNumber() === 0 && + web3.utils.BN(balance._refundableBalances[1]).toNumber() === 0 + ); + }); + + it('Scenario 37: Should allow arbitrary client to query an Accumulated Fees', async () => { + let _coin1 = 'ICON'; let _coin2 = 'TRON'; + let _value1 = 40000; let _value2 = 10000; let _native_value = 2000; + await bsh_core.setAggregationFee(_coin1, _value1); + await bsh_core.setAggregationFee(_coin2, _value2); + await bsh_core.setAggregationFee(_native, _native_value); + let fees = await bsh_core.getAccumulatedFees({from: accounts[3]}); + assert( + fees[0].coinName === _native && Number(fees[0].value) === _native_value && + fees[1].coinName === _coin1 && Number(fees[1].value) === _value1 && + fees[2].coinName === 'wBTC' && Number(fees[2].value) === 0 && + fees[3].coinName === 'Ethereum' && Number(fees[3].value) === 0 && + fees[4].coinName === _coin2 && Number(fees[4].value) === _value2 + ); + }); + + it('Scenario 38: Should revert when a client reclaims an exceeding amount', async () => { + let _coin = 'ICON'; let _value = 10000; let _exceedAmt = 20000; + await bsh_core.setRefundableBalance(accounts[2], _coin, _value); + await truffleAssert.reverts( + bsh_core.reclaim.call(_coin, _exceedAmt, {from: accounts[2]}), + "Imbalance" + ); + }); + + it('Scenario 39: Should revert when a client, which does not own a refundable, tries to reclaim', async () => { + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_core.reclaim.call(_coin, _value, {from: accounts[3]}), + "Imbalance" + ); + }); + + it('Scenario 40: Should succeed when a client, which owns a refundable, tries to reclaim', async () => { + let _coin = 'ICON'; let _value = 10000; + let _id = await bsh_core.coinId(_coin); + await bsh_core.mintMock(bsh_core.address, _id, _value); + let balanceBefore = await bsh_core.getBalanceOf(accounts[2], _coin); + await bsh_core.reclaim(_coin, _value, {from: accounts[2]}); + let balanceAfter = await bsh_core.getBalanceOf(accounts[2], _coin); + assert( + web3.utils.BN(balanceAfter._usableBalance).toNumber() === + web3.utils.BN(balanceBefore._usableBalance).toNumber() + _value + ); + }); + + it('Scenario 41: Should not allow any clients (even a contract owner) to call a refund()', async () => { + // This function should be called only by itself (BSHCore contract) + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_core.refund.call(accounts[0], _coin, _value), + "Unauthorized" + ); + }); + + it('Scenario 42: Should not allow any clients (even a contract owner) to call a mint()', async () => { + // This function should be called only by BSHPeriphery contract + let _coin = 'ICON'; let _value = 10000; + await truffleAssert.reverts( + bsh_core.mint.call(accounts[0], _coin, _value), + "Unauthorized" + ); + }); + + it('Scenario 43: Should not allow any clients (even a contract owner) to call a handleResponseService()', async () => { + // This function should be called only by BSHPeriphery contract + let RC_OK = 0; + let _coin = 'ICON'; let _value = 10000; let _fee = 1; let _rspCode = RC_OK; + await truffleAssert.reverts( + bsh_core.handleResponseService.call(accounts[0], _coin, _value, _fee, _rspCode), + "Unauthorized" + ); + }); + + it('Scenario 44: Should not allow any clients (even a contract owner) to call a transferFees()', async () => { + // This function should be called only by BSHPeriphery contract + let _fa = 'btp://1234.iconee/0x1234567812345678'; + await truffleAssert.reverts( + bsh_core.transferFees.call(_fa), + "Unauthorized" + ); + }); +}); + +// BSHPeriphery is being used for communications among BSHCore and BMCPeriphery contract +// Thus, all tests relating to BSHPeriphery will be moved to Integration Test +// This part just covers some basic feature which is checking an authorization +contract('BSHPeriphery Unit Tests', (accounts) => { + let bsh_perif; + + before(async () => { + bsh_perif = await BSHPerif.new(); + }); + + it('Scenario 1: Should revert when a client, which is not a BMCPeriphery contract, calls handleBTPMessage()', async () => { + let _from = '1234.iconee'; let _svc = 'Coin/WrappedCoin'; let _sn = 10; + await truffleAssert.reverts( + bsh_perif.handleBTPMessage.call(_from, _svc, _sn, '0x'), + "Unauthorized" + ); + }); + + it('Scenario 2: Should revert when a client, which is not a BMCPeriphery contract, calls handleBTPError()', async () => { + let _from = '1234.iconee'; let _svc = 'Coin/WrappedCoin'; let _sn = 10; let RC_OK = 0; + await truffleAssert.reverts( + bsh_perif.handleBTPError.call(_from, _svc, _sn, RC_OK, ''), + "Unauthorized" + ); + }); + + it('Scenario 3: Should revert when any clients try to call handleRequestService()', async () => { + // This function should only be called internally even though it was set external + let _to = '1234.iconee'; let _svc = 'Coin/WrappedCoin'; let _sn = 10; let RC_OK = 0; + let _assets = [ ['PARA', 1000], ['ICON', 1000], ['TRON', 1000], ['Ethereum', 1000] ]; + await truffleAssert.reverts( + bsh_perif.handleRequestService.call(_to, _assets), + "Unauthorized" + ); + }); + + it('Scenario 4: Should revert when a client, which is not a BMCPeriphery contract, calls handleFeeGathering()', async () => { + let _fa = 'btp://1234.iconee/0x12345678012345678'; let _svc = 'Coin/WrappedCoin'; + await truffleAssert.reverts( + bsh_perif.handleFeeGathering.call(_fa, _svc), + "Unauthorized" + ); + }); +}); + +contract('ParseAddress Library Unit Test', (accounts) => { + let cpa; + + before(async () => { + cpa = await CheckParseAddress.new(); + }); + + describe('Convert String to Adress', () => { + it('Should revert when string address has invalid length', async() => { + const strAddr = accounts[0] + '1'; + await truffleAssert.reverts( + cpa.convertStringToAddress(strAddr), + "Invalid address format" + ); + }); + + it('Should revert when string address has invalid prefix', async() => { + const strAddr = accounts[0]; + const new1 = '1' + strAddr.slice(1, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new1), + "Invalid address format" + ); + + const new2 = 'o' + strAddr.slice(1, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new2), + "Invalid address format" + ); + + const new3 = 'h' + strAddr.slice(1, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new3), + "Invalid address format" + ); + + const new4 = 'c' + strAddr.slice(1, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new4), + "Invalid address format" + ); + + const new5 = '0a' + strAddr.slice(2, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new5), + "Invalid address format" + ); + }); + + it('Should revert when string address has no prefix', async() => { + const strAddr = accounts[0]; + const new1 = strAddr.slice(2, strAddr.length - 1); + await truffleAssert.reverts( + cpa.convertStringToAddress(new1), + "Invalid address format" + ); + }); + + // This unit test verifies the case that a string address + // has length of 42 and has a prefix '0x' + // but the address is generated randomly without checksum + it('Should revert when string address is an arbitrary string that looks like a valid address', async() => { + const strAddr = '0xa0ff1ad998affec330ffca12389edab4312a2b81' + await truffleAssert.reverts( + cpa.convertStringToAddress(strAddr), + "Invalid checksum" + ); + }); + + it('Should revert when string address is an arbitrary string', async() => { + const strAddr = '0xddawjoh2j3h1kjhffklashhsdklj1h2i3h121h23'; + await truffleAssert.reverts( + cpa.convertStringToAddress(strAddr), + "Invalid address" + ); + }); + + it('Should return an address when string address is valid', async() => { + let strAddr = '0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac'; + let res = await cpa.convertStringToAddress(strAddr); + assert( + web3.utils.isAddress(res) + ); + + strAddr = web3.utils.toChecksumAddress('0xc1912fee45d61c87cc5ea59dae31190fffff232d'); + res = await cpa.convertStringToAddress(strAddr); + assert( + web3.utils.isAddress(res) + ); + }) + }); + + describe('Convert Address to String', () => { + it('Should convert address to string with a valid checksum', async () => { + const account = web3.eth.accounts.create(); + const res = await cpa.convertAddressToString(account.address.toLowerCase()); + + assert.equal(res, account.address); + assert.isTrue(web3.utils.checkAddressChecksum(res)); + }); + }); +}) \ No newline at end of file diff --git a/solidity/bsh/truffle-config.js b/solidity/bsh/truffle-config.js new file mode 100644 index 00000000..ecfc3c39 --- /dev/null +++ b/solidity/bsh/truffle-config.js @@ -0,0 +1,77 @@ +const HDWalletProvider = require('@truffle/hdwallet-provider'); +const web3 = require("web3") + +const privKeys = (process.env.PRIVATE_KEYS) ? process.env.PRIVATE_KEYS.split(',') : + [ + '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // Alith + '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // Baltathar + '0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b', // Charleth + '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68', // Dorothy + '0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4', // Ethan + '0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df', // Faith + '0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18', // Goliath + '0x0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab', // Heath + '0x4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8', // Ida + '0x94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b' // Judith + ]; + +module.exports = { + networks: { + development: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbeamlocal: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbase: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.testnet.moonbeam.network", + }), + network_id: 1287, + networkCheckTimeout: 100000, + // Make deploy faster for deployment + gasPrice: web3.utils.toWei("2", "Gwei"), + }, + moonriver: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.moonriver.moonbeam.network", + }), + network_id: 1285, + networkCheckTimeout: 100000, + }, + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.7.6", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 10 + }, + evmVersion: "petersburg" + } + } + }, + + db: { + enabled: false + } +}; diff --git a/solidity/bsh/yarn.lock b/solidity/bsh/yarn.lock new file mode 100644 index 00000000..434640d1 --- /dev/null +++ b/solidity/bsh/yarn.lock @@ -0,0 +1,7698 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.14.5": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.15.8.tgz#45990c47adadb00c03677baa89221f7cc23d2503" + integrity sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg== + dependencies: + "@babel/highlight" "^7.14.5" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.15.0": + version "7.15.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.15.0.tgz#2dbaf8b85334796cafbb0f5793a90a2fc010b176" + integrity sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA== + +"@babel/generator@^7.15.4": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.15.8.tgz#fa56be6b596952ceb231048cf84ee499a19c0cd1" + integrity sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g== + dependencies: + "@babel/types" "^7.15.6" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.13.0": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz#cf6d94f30fbefc139123e27dd6b02f65aeedb7b9" + integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== + dependencies: + "@babel/compat-data" "^7.15.0" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.16.6" + semver "^6.3.0" + +"@babel/helper-define-polyfill-provider@^0.2.2": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz#0525edec5094653a282688d34d846e4c75e9c0b6" + integrity sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-function-name@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz#845744dafc4381a4a5fb6afa6c3d36f98a787ebc" + integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== + dependencies: + "@babel/helper-get-function-arity" "^7.15.4" + "@babel/template" "^7.15.4" + "@babel/types" "^7.15.4" + +"@babel/helper-get-function-arity@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz#098818934a137fce78b536a3e015864be1e2879b" + integrity sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA== + dependencies: + "@babel/types" "^7.15.4" + +"@babel/helper-hoist-variables@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz#09993a3259c0e918f99d104261dfdfc033f178df" + integrity sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA== + dependencies: + "@babel/types" "^7.15.4" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz#e18007d230632dea19b47853b984476e7b4e103f" + integrity sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA== + dependencies: + "@babel/types" "^7.15.4" + +"@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" + integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== + +"@babel/helper-split-export-declaration@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz#aecab92dcdbef6a10aa3b62ab204b085f776e257" + integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== + dependencies: + "@babel/types" "^7.15.4" + +"@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9": + version "7.15.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.15.4": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016" + integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA== + +"@babel/plugin-transform-runtime@^7.5.5": + version "7.15.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.15.8.tgz#9d15b1e94e1c7f6344f65a8d573597d93c6cd886" + integrity sha512-+6zsde91jMzzvkzuEA3k63zCw+tm/GvuuabkpisgbDMTPQsIMHllE3XczJFFtEHLjjhKQFZmGQVRdELetlWpVw== + dependencies: + "@babel/helper-module-imports" "^7.15.4" + "@babel/helper-plugin-utils" "^7.14.5" + babel-plugin-polyfill-corejs2 "^0.2.2" + babel-plugin-polyfill-corejs3 "^0.2.5" + babel-plugin-polyfill-regenerator "^0.2.2" + semver "^6.3.0" + +"@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" + integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/parser" "^7.15.4" + "@babel/types" "^7.15.4" + +"@babel/traverse@^7.13.0": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" + integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== + dependencies: + "@babel/code-frame" "^7.14.5" + "@babel/generator" "^7.15.4" + "@babel/helper-function-name" "^7.15.4" + "@babel/helper-hoist-variables" "^7.15.4" + "@babel/helper-split-export-declaration" "^7.15.4" + "@babel/parser" "^7.15.4" + "@babel/types" "^7.15.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.15.4", "@babel/types@^7.15.6": + version "7.15.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.6.tgz#99abdc48218b2881c058dd0a7ab05b99c9be758f" + integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + to-fast-properties "^2.0.0" + +"@ensdomains/address-encoder@^0.1.7": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz#f948c485443d9ef7ed2c0c4790e931c33334d02d" + integrity sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg== + dependencies: + bech32 "^1.1.3" + blakejs "^1.1.0" + bn.js "^4.11.8" + bs58 "^4.0.1" + crypto-addr-codec "^0.1.7" + nano-base32 "^1.0.1" + ripemd160 "^2.0.2" + +"@ensdomains/ens@0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.3.tgz#f4a6b55146fe526c9a50e13f373bf90d36ca94dc" + integrity sha512-btC+fGze//ml8SMNCx5DgwM8+kG2t+qDCZrqlL/2+PV4CNxnRIpR3egZ49D9FqS52PFoYLmz6MaQfl7AO3pUMA== + dependencies: + bluebird "^3.5.2" + eth-ens-namehash "^2.0.8" + ethereumjs-testrpc "^6.0.3" + ganache-cli "^6.1.0" + solc "^0.4.20" + testrpc "0.0.1" + web3-utils "^1.0.0-beta.31" + +"@ensdomains/ensjs@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@ensdomains/ensjs/-/ensjs-2.0.1.tgz#c27438f9ca074825ddb08430988c7decf2062a91" + integrity sha512-gZLntzE1xqPNkPvaHdJlV5DXHms8JbHBwrXc2xNrL1AylERK01Lj/txCCZyVQqFd3TvUO1laDbfUv8VII0qrjg== + dependencies: + "@babel/runtime" "^7.4.4" + "@ensdomains/address-encoder" "^0.1.7" + "@ensdomains/ens" "0.4.3" + "@ensdomains/resolver" "0.2.4" + content-hash "^2.5.2" + eth-ens-namehash "^2.0.8" + ethers "^5.0.13" + js-sha3 "^0.8.0" + +"@ensdomains/resolver@0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" + integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== + +"@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.5.0.tgz#ec61551b31bef7a69d1dc634d8932468866a4268" + integrity sha512-DEHjW6e38o+JmB/NO3GZBpW4lpaiBpkFgXF6jLcJ6gETBYpEyaA5nTimsWBUJR3Vmtm/didUEbNjajskugZORg== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.1" + +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" + integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== + dependencies: + "@ethereumjs/common" "^2.5.0" + ethereumjs-util "^7.1.2" + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@5.4.1", "@ethersproject/abi@^5.4.0": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.4.1.tgz#6ac28fafc9ef6f5a7a37e30356a2eb31fa05d39b" + integrity sha512-9mhbjUk76BiSluiiW4BaYyI58KSbDMMQpCLdsAR+RsT2GyATiNYxVv+pGWRrekmsIdY3I+hOqsYQSTkc8L/mcg== + dependencies: + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/abstract-provider@5.4.1", "@ethersproject/abstract-provider@^5.4.0": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.4.1.tgz#e404309a29f771bd4d28dbafadcaa184668c2a6e" + integrity sha512-3EedfKI3LVpjSKgAxoUaI+gB27frKsxzm+r21w9G60Ugk+3wVLQwhi1LsEJAKNV7WoZc8CIpNrATlL1QFABjtQ== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/networks" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/web" "^5.4.0" + +"@ethersproject/abstract-signer@5.4.1", "@ethersproject/abstract-signer@^5.4.0": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.4.1.tgz#e4e9abcf4dd4f1ba0db7dff9746a5f78f355ea81" + integrity sha512-SkkFL5HVq1k4/25dM+NWP9MILgohJCgGv5xT5AcRruGz4ILpfHeBtO/y6j+Z3UN/PAjDeb4P7E51Yh8wcGNLGA== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + +"@ethersproject/address@5.4.0", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.4.0.tgz#ba2d00a0f8c4c0854933b963b9a3a9f6eb4a37a3" + integrity sha512-SD0VgOEkcACEG/C6xavlU1Hy3m5DGSXW3CUHkaaEHbAPPsgi0coP5oNPsxau8eTlZOk/bpa/hKeCNoK5IzVI2Q== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + +"@ethersproject/base64@5.4.0", "@ethersproject/base64@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.4.0.tgz#7252bf65295954c9048c7ca5f43e5c86441b2a9a" + integrity sha512-CjQw6E17QDSSC5jiM9YpF7N1aSCHmYGMt9bWD8PWv6YPMxjsys2/Q8xLrROKI3IWJ7sFfZ8B3flKDTM5wlWuZQ== + dependencies: + "@ethersproject/bytes" "^5.4.0" + +"@ethersproject/basex@5.4.0", "@ethersproject/basex@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.4.0.tgz#0a2da0f4e76c504a94f2b21d3161ed9438c7f8a6" + integrity sha512-J07+QCVJ7np2bcpxydFVf/CuYo9mZ7T73Pe7KQY4c1lRlrixMeblauMxHXD0MPwFmUHZIILDNViVkykFBZylbg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + +"@ethersproject/bignumber@5.4.2", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.4.0": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.4.2.tgz#44232e015ae4ce82ac034de549eb3583c71283d8" + integrity sha512-oIBDhsKy5bs7j36JlaTzFgNPaZjiNDOXsdSgSpXRucUl+UA6L/1YLlFeI3cPAoodcenzF4nxNPV13pcy7XbWjA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@5.4.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.4.0.tgz#56fa32ce3bf67153756dbaefda921d1d4774404e" + integrity sha512-H60ceqgTHbhzOj4uRc/83SCN9d+BSUnOkrr2intevqdtEMO1JFVZ1XL84OEZV+QjV36OaZYxtnt4lGmxcGsPfA== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/constants@5.4.0", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.4.0.tgz#ee0bdcb30bf1b532d2353c977bf2ef1ee117958a" + integrity sha512-tzjn6S7sj9+DIIeKTJLjK9WGN2Tj0P++Z8ONEIlZjyoTkBuODN+0VfhAyYksKi43l1Sx9tX2VlFfzjfmr5Wl3Q== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + +"@ethersproject/contracts@5.4.1": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.4.1.tgz#3eb4f35b7fe60a962a75804ada2746494df3e470" + integrity sha512-m+z2ZgPy4pyR15Je//dUaymRUZq5MtDajF6GwFbGAVmKz/RF+DNIPwF0k5qEcL3wPGVqUjFg2/krlCRVTU4T5w== + dependencies: + "@ethersproject/abi" "^5.4.0" + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + +"@ethersproject/hash@5.4.0", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.4.0.tgz#d18a8e927e828e22860a011f39e429d388344ae0" + integrity sha512-xymAM9tmikKgbktOCjW60Z5sdouiIIurkZUr9oW5NOex5uwxrbsYG09kb5bMcNjlVeJD3yPivTNzViIs1GCbqA== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/hdnode@5.4.0", "@ethersproject/hdnode@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.4.0.tgz#4bc9999b9a12eb5ce80c5faa83114a57e4107cac" + integrity sha512-pKxdS0KAaeVGfZPp1KOiDLB0jba11tG6OP1u11QnYfb7pXn6IZx0xceqWRr6ygke8+Kw74IpOoSi7/DwANhy8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/basex" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/pbkdf2" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/wordlists" "^5.4.0" + +"@ethersproject/json-wallets@5.4.0", "@ethersproject/json-wallets@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.4.0.tgz#2583341cfe313fc9856642e8ace3080154145e95" + integrity sha512-igWcu3fx4aiczrzEHwG1xJZo9l1cFfQOWzTqwRw/xcvxTk58q4f9M7cjh51EKphMHvrJtcezJ1gf1q1AUOfEQQ== + dependencies: + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hdnode" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/pbkdf2" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.4.0", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.4.0.tgz#7143b8eea4976080241d2bd92e3b1f1bf7025318" + integrity sha512-FBI1plWet+dPUvAzPAeHzRKiPpETQzqSUWR1wXJGHVWi4i8bOSrpC3NwpkPjgeXG7MnugVc1B42VbfnQikyC/A== + dependencies: + "@ethersproject/bytes" "^5.4.0" + js-sha3 "0.5.7" + +"@ethersproject/logger@5.4.1", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.4.0": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.4.1.tgz#503bd33683538b923c578c07d1c2c0dd18672054" + integrity sha512-DZ+bRinnYLPw1yAC64oRl0QyVZj43QeHIhVKfD/+YwSz4wsv1pfwb5SOFjz+r710YEWzU6LrhuSjpSO+6PeE4A== + +"@ethersproject/networks@5.4.2", "@ethersproject/networks@^5.4.0": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.4.2.tgz#2247d977626e97e2c3b8ee73cd2457babde0ce35" + integrity sha512-eekOhvJyBnuibfJnhtK46b8HimBc5+4gqpvd1/H9LEl7Q7/qhsIhM81dI9Fcnjpk3jB1aTy6bj0hz3cifhNeYw== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/pbkdf2@5.4.0", "@ethersproject/pbkdf2@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.4.0.tgz#ed88782a67fda1594c22d60d0ca911a9d669641c" + integrity sha512-x94aIv6tiA04g6BnazZSLoRXqyusawRyZWlUhKip2jvoLpzJuLb//KtMM6PEovE47pMbW+Qe1uw+68ameJjB7g== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + +"@ethersproject/properties@5.4.1", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.4.0": + version "5.4.1" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.4.1.tgz#9f051f976ce790142c6261ccb7b826eaae1f2f36" + integrity sha512-cyCGlF8wWlIZyizsj2PpbJ9I7rIlUAfnHYwy/T90pdkSn/NFTa5YWZx2wTJBe9V7dD65dcrrEMisCRUJiq6n3w== + dependencies: + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/providers@5.4.5": + version "5.4.5" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.4.5.tgz#eb2ea2a743a8115f79604a8157233a3a2c832928" + integrity sha512-1GkrvkiAw3Fj28cwi1Sqm8ED1RtERtpdXmRfwIBGmqBSN5MoeRUHuwHPppMtbPayPgpFcvD7/Gdc9doO5fGYgw== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/basex" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/networks" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/web" "^5.4.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.4.0", "@ethersproject/random@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.4.0.tgz#9cdde60e160d024be39cc16f8de3b9ce39191e16" + integrity sha512-pnpWNQlf0VAZDEOVp1rsYQosmv2o0ITS/PecNw+mS2/btF8eYdspkN0vIXrCMtkX09EAh9bdk8GoXmFXM1eAKw== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/rlp@5.4.0", "@ethersproject/rlp@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.4.0.tgz#de61afda5ff979454e76d3b3310a6c32ad060931" + integrity sha512-0I7MZKfi+T5+G8atId9QaQKHRvvasM/kqLyAH4XxBCBchAooH2EX5rL9kYZWwcm3awYV+XC7VF6nLhfeQFKVPg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/sha2@5.4.0", "@ethersproject/sha2@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.4.0.tgz#c9a8db1037014cbc4e9482bd662f86c090440371" + integrity sha512-siheo36r1WD7Cy+bDdE1BJ8y0bDtqXCOxRMzPa4bV1TGt/eTUUt03BHoJNB6reWJD8A30E/pdJ8WFkq+/uz4Gg== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.4.0", "@ethersproject/signing-key@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.4.0.tgz#2f05120984e81cf89a3d5f6dec5c68ee0894fbec" + integrity sha512-q8POUeywx6AKg2/jX9qBYZIAmKSB4ubGXdQ88l40hmATj29JnG5pp331nAWwwxPn2Qao4JpWHNZsQN+bPiSW9A== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.4.0.tgz#1305e058ea02dc4891df18b33232b11a14ece9ec" + integrity sha512-XFQTZ7wFSHOhHcV1DpcWj7VXECEiSrBuv7JErJvB9Uo+KfCdc3QtUZV+Vjh/AAaYgezUEKbCtE6Khjm44seevQ== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/sha2" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/strings@5.4.0", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.4.0.tgz#fb12270132dd84b02906a8d895ae7e7fa3d07d9a" + integrity sha512-k/9DkH5UGDhv7aReXLluFG5ExurwtIpUfnDNhQA29w896Dw3i4uDTz01Quaptbks1Uj9kI8wo9tmW73wcIEaWA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/transactions@5.4.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.4.0.tgz#a159d035179334bd92f340ce0f77e83e9e1522e0" + integrity sha512-s3EjZZt7xa4BkLknJZ98QGoIza94rVjaEed0rzZ/jB9WrIuu/1+tjvYCWzVrystXtDswy7TPBeIepyXwSYa4WQ== + dependencies: + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/rlp" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + +"@ethersproject/units@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.4.0.tgz#d57477a4498b14b88b10396062c8cbbaf20c79fe" + integrity sha512-Z88krX40KCp+JqPCP5oPv5p750g+uU6gopDYRTBGcDvOASh6qhiEYCRatuM/suC4S2XW9Zz90QI35MfSrTIaFg== + dependencies: + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/constants" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + +"@ethersproject/wallet@5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.4.0.tgz#fa5b59830b42e9be56eadd45a16a2e0933ad9353" + integrity sha512-wU29majLjM6AjCjpat21mPPviG+EpK7wY1+jzKD0fg3ui5fgedf2zEu1RDgpfIMsfn8fJHJuzM4zXZ2+hSHaSQ== + dependencies: + "@ethersproject/abstract-provider" "^5.4.0" + "@ethersproject/abstract-signer" "^5.4.0" + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/hdnode" "^5.4.0" + "@ethersproject/json-wallets" "^5.4.0" + "@ethersproject/keccak256" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/random" "^5.4.0" + "@ethersproject/signing-key" "^5.4.0" + "@ethersproject/transactions" "^5.4.0" + "@ethersproject/wordlists" "^5.4.0" + +"@ethersproject/web@5.4.0", "@ethersproject/web@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.4.0.tgz#49fac173b96992334ed36a175538ba07a7413d1f" + integrity sha512-1bUusGmcoRLYgMn6c1BLk1tOKUIFuTg8j+6N8lYlbMpDesnle+i3pGSagGNvwjaiLo4Y5gBibwctpPRmjrh4Og== + dependencies: + "@ethersproject/base64" "^5.4.0" + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@ethersproject/wordlists@5.4.0", "@ethersproject/wordlists@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.4.0.tgz#f34205ec3bbc9e2c49cadaee774cf0b07e7573d7" + integrity sha512-FemEkf6a+EBKEPxlzeVgUaVSodU7G0Na89jqKjmWMlDB0tomoU8RlEMgUvXyqtrg8N4cwpLh8nyRnm1Nay1isA== + dependencies: + "@ethersproject/bytes" "^5.4.0" + "@ethersproject/hash" "^5.4.0" + "@ethersproject/logger" "^5.4.0" + "@ethersproject/properties" "^5.4.0" + "@ethersproject/strings" "^5.4.0" + +"@openzeppelin/contracts-upgradeable@^3.4.1-solc-0.7-2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.2.tgz#2c2a1b0fa748235a1f495b6489349776365c51b3" + integrity sha512-mDlBS17ymb2wpaLcrqRYdnBAmP1EwqhOXMvqWk2c5Q1N1pm5TkiCtXM9Xzznh4bYsQBq0aIWEkFFE2+iLSN1Tw== + +"@openzeppelin/contracts@^3.4.1-solc-0.7-2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== + +"@openzeppelin/truffle-upgrades@^1.7.0": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.9.1.tgz#15aa24948c44182b565414bdf40e5a23079ded40" + integrity sha512-CralkzlQ5OfdEjh35HLOlT5XCRG12+1+ITl4v6DamL6jEypFslGLU9PwAr5itfpkmuebYHznqAbgI+Ec6eM68Q== + dependencies: + "@openzeppelin/upgrades-core" "^1.9.0" + "@truffle/contract" "^4.3.26" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.9.0": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.9.2.tgz#9d7497f58b1f2bb704c162c716302bfff7923749" + integrity sha512-LU2NMvnz+6jXheh3Rnfql4UgtS7ViHWwcivS3JRI9DMCazmlyibwMYz5QMakrNNGAF7bY0t0Sw1UCfe5qTYxjA== + dependencies: + bn.js "^5.1.2" + cbor "^8.0.0" + chalk "^4.1.0" + compare-versions "^3.6.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@solidity-parser/parser@^0.13.2": + version "0.13.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.13.2.tgz#b6c71d8ca0b382d90a7bbed241f9bc110af65cbe" + integrity sha512-RwHnpRnfrnD2MSPveYoPh8nhofEvX7fgjHk1Oq+NNvCcLx4r1js91CO9o+F/F3fBzOCyvm8kKRTriFICX/odWw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/abi-utils@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@truffle/abi-utils/-/abi-utils-0.2.4.tgz#9fc8bfc95bbe29a33cca3ab9028865b078e2f051" + integrity sha512-ICr5Sger6r5uj2G5GN9Zp9OQDCaCqe2ZyAEyvavDoFB+jX0zZFUCfDnv5jllGRhgzdYJ3mec2390mjUyz9jSZA== + dependencies: + change-case "3.0.2" + faker "^5.3.1" + fast-check "^2.12.1" + +"@truffle/blockchain-utils@^0.0.31": + version "0.0.31" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.31.tgz#0503d9fb2ce3e05c167c27294927f2f88d70a24d" + integrity sha512-BFo/nyxwhoHqPrqBQA1EAmSxeNnspGLiOCMa9pAL7WYSjyNBlrHaqCMO/F2O87G+NUK/u06E70DiSP2BFP0ZZw== + +"@truffle/codec@^0.11.17": + version "0.11.17" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.11.17.tgz#451ad820b0c7abaf78d52fa8bf310e000d04c1aa" + integrity sha512-WO9D5TVyTf9czqdsfK/qqYeSS//zWcHBgQgSNKPlCDb6koCNLxG5yGbb4P+0bZvTUNS2e2iIdN92QHg00wMbSQ== + dependencies: + "@truffle/abi-utils" "^0.2.4" + "@truffle/compile-common" "^0.7.22" + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + utf8 "^3.0.0" + web3-utils "1.5.3" + +"@truffle/compile-common@^0.7.22": + version "0.7.22" + resolved "https://registry.yarnpkg.com/@truffle/compile-common/-/compile-common-0.7.22.tgz#c376eea36f59dc770ece3bc8cbb7132f49352846" + integrity sha512-afFKh0Wphn8JrCSjOORKjO8/E1X0EtQv6GpFJpQCAWo3/i4VGcSVKR1rjkknnExtjEGe9PJH/Ym/opGH3pQyDw== + dependencies: + "@truffle/error" "^0.0.14" + colors "^1.4.0" + +"@truffle/contract-schema@^3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.3.tgz#c1bcde343f70b9438314202e103a7d77d684603c" + integrity sha512-pgaTgF4CKIpkqVYZVr2qGTxZZQOkNCWOXW9VQpKvLd4G0SNF2Y1gyhrFbBhoOUtYlbbSty+IEFFHsoAqpqlvpQ== + dependencies: + ajv "^6.10.0" + debug "^4.3.1" + +"@truffle/contract@^4.3.26": + version "4.3.38" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.38.tgz#51627cda844ed123f609e49a56016ecc0ed870b1" + integrity sha512-11HL9IJTmd45pVXJvEaRYeyuhf8GmAgRD7bTYBZj2CiMBnt0337Fg7Zz/GuTpUUW2h3fbyTYO4hgOntxdQjZ5A== + dependencies: + "@ensdomains/ensjs" "^2.0.1" + "@truffle/blockchain-utils" "^0.0.31" + "@truffle/contract-schema" "^3.4.3" + "@truffle/debug-utils" "^5.1.18" + "@truffle/error" "^0.0.14" + "@truffle/interface-adapter" "^0.5.8" + bignumber.js "^7.2.1" + ethers "^4.0.32" + web3 "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" + +"@truffle/debug-utils@^5.1.18": + version "5.1.18" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.1.18.tgz#6068673f3536149c0584a3c1193eb933f4663dc6" + integrity sha512-QBq1vA/YozksQZGjyA7o482AuT8KW5gvO8VmYM/PIDllCIqDruEZuz4DZ+zpVUPXyVoJycFo+RKnM/TLE1AZRQ== + dependencies: + "@truffle/codec" "^0.11.17" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlightjs-solidity "^2.0.1" + +"@truffle/error@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.14.tgz#59683b5407bede7bddf16d80dc5592f9c5e5fa05" + integrity sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA== + +"@truffle/hdwallet-provider@^1.4.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.5.1.tgz#05522f70e713fe1a4966706bb13960133b920e96" + integrity sha512-sVt78Idr6k2Loi40Igc3A8ncy4zvUWdWaYYNRGaBPq67usR34tkp+RH+23PqU+alk44P0JwG3fNNEg1Hqwa3Xw== + dependencies: + "@ethereumjs/common" "^2.4.0" + "@ethereumjs/tx" "^3.3.0" + "@trufflesuite/web3-provider-engine" "15.0.13-1" + eth-sig-util "^3.0.1" + ethereum-cryptography "^0.1.3" + ethereum-protocol "^1.0.1" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^1.0.1" + +"@truffle/interface-adapter@^0.5.8": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.8.tgz#76cfd34374d85849e1164de1a3d5a3dce0dc5d01" + integrity sha512-vvy3xpq36oLgjjy8KE9l2Jabg3WcGPOt18tIyMfTQX9MFnbHoQA2Ne2i8xsd4p6KfxIqSjAB53Q9/nScAqY0UQ== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + web3 "1.5.3" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@trufflesuite/eth-json-rpc-filters@^4.1.2-1": + version "4.1.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz#61ab78c52e98a883e5cf086925b34a30297b1824" + integrity sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-0" + await-semaphore "^0.1.3" + eth-query "^2.1.2" + json-rpc-engine "^5.1.3" + lodash.flatmap "^4.5.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-json-rpc-infura@^4.0.3-0": + version "4.0.3-0" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz#6d22122937cf60ec9d21a02351c101fdc608c4fe" + integrity sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + cross-fetch "^2.1.1" + eth-json-rpc-errors "^1.0.1" + json-rpc-engine "^5.1.3" + +"@trufflesuite/eth-json-rpc-middleware@^4.4.2-0", "@trufflesuite/eth-json-rpc-middleware@^4.4.2-1": + version "4.4.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz#8c3638ed8a7ed89a1e5e71407de068a65bef0df2" + integrity sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA== + dependencies: + "@trufflesuite/eth-sig-util" "^1.4.2" + btoa "^1.2.1" + clone "^2.1.1" + eth-json-rpc-errors "^1.0.1" + eth-query "^2.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.6.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^5.1.3" + json-stable-stringify "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-sig-util@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz#b529e2f38ac08e652116f48981132a26242a4f08" + integrity sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + +"@trufflesuite/web3-provider-engine@15.0.13-1": + version "15.0.13-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.13-1.tgz#f6a7f7131a2fdc4ab53976318ed13ce83e8e4bcb" + integrity sha512-6u3x/iIN5fyj8pib5QTUDmIOUiwAGhaqdSTXdqCu6v9zo2BEwdCqgEJd1uXDh3DBmPRDfiZ/ge8oUPy7LerpHg== + dependencies: + "@trufflesuite/eth-json-rpc-filters" "^4.1.2-1" + "@trufflesuite/eth-json-rpc-infura" "^4.0.3-0" + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + "@trufflesuite/eth-sig-util" "^1.4.2" + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^4.4.2" + eth-json-rpc-errors "^2.0.2" + ethereumjs-block "^1.2.2" + ethereumjs-tx "^1.2.0" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "16.10.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.3.tgz#7a8f2838603ea314d1d22bb3171d899e15c57bd5" + integrity sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ== + +"@types/node@^12.12.6": + version "12.20.28" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.28.tgz#4b20048c6052b5f51a8d5e0d2acbf63d5a17e1e2" + integrity sha512-cBw8gzxUPYX+/5lugXIPksioBSbE42k0fZ39p+4yRzfYjN6++eq9kAPdlY9qm+MXyfbk9EmvCYAYRn380sF46w== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ= + dependencies: + acorn "^4.0.3" + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^4.0.3: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= + +acorn@^5.0.0: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv-keywords@^3.1.0: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +await-semaphore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/await-semaphore/-/await-semaphore-0.1.3.tgz#2b88018cc8c28e06167ae1cdff02504f1f9688d3" + integrity sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-plugin-polyfill-corejs2@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz#e9124785e6fd94f94b618a7954e5693053bf5327" + integrity sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.2.2" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz#2779846a16a1652244ae268b1e906ada107faf92" + integrity sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + core-js-compat "^3.16.2" + +babel-plugin-polyfill-regenerator@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz#b310c8d642acada348c1fa3b3e6ce0e851bee077" + integrity sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.2.2" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4, bech32@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +big-integer@1.6.36: + version "1.6.36" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" + integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +blakejs@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" + integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== + +bluebird@^3.5.0, bluebird@^3.5.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.16.6, browserslist@^4.17.3: + version "4.17.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.3.tgz#2844cd6eebe14d12384b0122d217550160d2d624" + integrity sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ== + dependencies: + caniuse-lite "^1.0.30001264" + electron-to-chromium "^1.3.857" + escalade "^3.1.1" + node-releases "^1.1.77" + picocolors "^0.2.1" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.4.tgz#ab81373d313a6ead0d734e98c448c722734ae7bb" + integrity sha512-VNxjXUCrF3LvbLgwfkTb5LsFvk6pGIn7OBb9x+3o+iJ6mKw0JTUp4chBFc88hi1aspeZGeZG9jAIbpFYPQSLZw== + dependencies: + node-gyp-build "^4.2.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001264: + version "1.0.30001265" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3" + integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +cbor@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.0.2.tgz#d0f5088423437efcc160e9304bd0576f45d06abb" + integrity sha512-H5WTjQYgyHQI0VrCmbyQBOPy1353MjmUi/r3DbPib4U13vuyqm7es9Mfpe8G58bN/mCdRlJWkiCrPl1uM1wAlg== + dependencies: + nofilter "^3.0.3" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +change-case@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.2.tgz#fd48746cce02f03f0a672577d1d3a8dc2eceb037" + integrity sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA== + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.3.2" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +cheerio-select@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" + integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== + dependencies: + css-select "^4.1.3" + css-what "^5.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.7.0" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" + integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.16.2: + version "3.18.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.18.2.tgz#e40c266fbd613948dd8d2d2156345da8ac03c142" + integrity sha512-25VJYCJtGjZwLguj7d66oiHfmnVw3TMOZ0zV8DyMJp/aeQ3OjR519iOOeck08HMyVVRAqXxafc2Hl+5QstJrsQ== + dependencies: + browserslist "^4.17.3" + semver "7.0.0" + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +crc-32@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.5.tgz#afaf5729f3b6c78d89c9296115c9f142541a5705" + integrity sha512-xqYAhQb4NhCJSRym03dwxpP1bYXpK3y7UN83Bo2WFi3x1Zmzn0SL/6xGoPr+gpt4WmNrgCCX3HPysvOwFOW36w== + dependencies: + node-fetch "2.6.1" + whatwg-fetch "2.0.4" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-addr-codec@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz#e16cea892730178fe25a38f6d15b680cab3124ae" + integrity sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg== + dependencies: + base-x "^3.0.8" + big-integer "1.6.36" + blakejs "^1.1.0" + bs58 "^4.0.1" + ripemd160-min "0.0.6" + safe-buffer "^5.2.0" + sha3 "^2.1.1" + +crypto-browserify@3.12.0, crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0, css-what@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1, dom-serializer@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" + integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" + integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= + dependencies: + no-case "^2.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.857: + version "1.3.862" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.862.tgz#c1c5d4382449e2c9b0e67fe1652f4fc451d6d8c0" + integrity sha512-o+FMbCD+hAUJ9S8bfz/FaqA0gE8OpCCm58KhhGogOEqiA1BLFSoVYLi+tW+S/ZavnqBn++n0XZm7HQiBVPs8Jg== + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24= + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +errno@^0.1.3, errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.5: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^4.4.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz#766a0a0eb4a52c867a28328e9ae21353812cf626" + integrity sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw== + dependencies: + "@babel/plugin-transform-runtime" "^7.5.5" + "@babel/runtime" "^7.5.5" + eth-query "^2.1.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-errors@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" + integrity sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-sig-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-3.0.1.tgz#8753297c83a3f58346bd13547b59c4b2cd110c96" + integrity sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-testrpc@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz#7a0b87bf3670f92f607f98fa6a78801d9741b124" + integrity sha512-lAxxsxDKK69Wuwqym2K49VpXtBvLEsXr1sryNG4AkvL5DomMdeCBbu3D87UEevKenLHBiT8GTjARwN6Yj039gA== + dependencies: + webpack "^3.0.0" + +ethereumjs-tx@^1.2.0, ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.2.tgz#cfd79a9a3f5cdc042d1abf29964de9caf10ec238" + integrity sha512-xCV3PTAhW8Q2k88XZn9VcO4OrjpeXAlDm5LQTaOLp81SjNSSY6+MwuGXrx6vafOMheWSmZGxIXUbue5e9UvUBw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.1.tgz#664a4bcacfc1291ca2703de066df1178938dba1c" + integrity sha512-3Z5g1hG1das0JWU6cQ9HWWTY2nt9nXCcwj7eXVNAHKbo00XAZO8+NHlwdgXDWrL0SXVQMvTWN8Q/82DRH/JhPw== + dependencies: + aes-js "^3.1.1" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.0.2" + randombytes "^2.0.6" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^3.3.2" + +ethers@^4.0.32: + version "4.0.49" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" + integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== + dependencies: + aes-js "3.0.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^5.0.13: + version "5.4.7" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.4.7.tgz#0fd491a5da7c9793de2d6058d76b41b1e7efba8f" + integrity sha512-iZc5p2nqfWK1sj8RabwsPM28cr37Bpq7ehTQ5rWExBr2Y09Sn1lDKZOED26n+TsZMye7Y6mIgQ/1cwpSD8XZew== + dependencies: + "@ethersproject/abi" "5.4.1" + "@ethersproject/abstract-provider" "5.4.1" + "@ethersproject/abstract-signer" "5.4.1" + "@ethersproject/address" "5.4.0" + "@ethersproject/base64" "5.4.0" + "@ethersproject/basex" "5.4.0" + "@ethersproject/bignumber" "5.4.2" + "@ethersproject/bytes" "5.4.0" + "@ethersproject/constants" "5.4.0" + "@ethersproject/contracts" "5.4.1" + "@ethersproject/hash" "5.4.0" + "@ethersproject/hdnode" "5.4.0" + "@ethersproject/json-wallets" "5.4.0" + "@ethersproject/keccak256" "5.4.0" + "@ethersproject/logger" "5.4.1" + "@ethersproject/networks" "5.4.2" + "@ethersproject/pbkdf2" "5.4.0" + "@ethersproject/properties" "5.4.1" + "@ethersproject/providers" "5.4.5" + "@ethersproject/random" "5.4.0" + "@ethersproject/rlp" "5.4.0" + "@ethersproject/sha2" "5.4.0" + "@ethersproject/signing-key" "5.4.0" + "@ethersproject/solidity" "5.4.0" + "@ethersproject/strings" "5.4.0" + "@ethersproject/transactions" "5.4.0" + "@ethersproject/units" "5.4.0" + "@ethersproject/wallet" "5.4.0" + "@ethersproject/web" "5.4.0" + "@ethersproject/wordlists" "5.4.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" + integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== + dependencies: + type "^2.5.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +faker@^5.3.1: + version "5.5.3" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== + +fast-check@^2.12.1: + version "2.17.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.17.0.tgz#9b9637684332be386219a5f73a4799874da7461c" + integrity sha512-fNNKkxNEJP+27QMcEzF6nbpOYoSZIS0p+TyB+xh/jXqRBxRhLkiZSREly4ruyV8uJi7nwH1YWAhi7OOK5TubRw== + dependencies: + pure-rand "^5.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.6: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +ganache-cli@^6.1.0: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.2.tgz#c0920f7db0d4ac062ffe2375cb004089806f627a" + integrity sha512-bnmwnJDBDsOWBUP8E/BExWf85TsdDEFelQSzihSJm9VChVO1SHp94YXLP5BlA4j/OTxp0wR4R1Tje9OHOuAJVw== + dependencies: + ethereumjs-util "6.2.1" + source-map-support "0.5.12" + yargs "13.2.4" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.2, glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +header-case@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" + integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + +highlight.js@^10.4.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-solidity@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-2.0.1.tgz#ee1beb6f353d4503aa3a011bbb86577976365b59" + integrity sha512-9YY+HQpXMTrF8HgRByjeQhd21GXAz2ktMPTcs6oWSj5HJR52fgsNoelMOmgigwcpt9j4tu4IVSaWaJB2n2TbvQ== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +husky@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-core-module@^2.2.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.7.0.tgz#3c0ef7d31b4acfc574f80c58409d568a836848e3" + integrity sha512-ByY+tjCciCr+9nLryBYcSD50EOGWt95c7tIsKTG1J2ixKKXPvF7Ej3AVd+UfDydAJom3biBGDBALaO79ktwgEQ== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-weakref@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" + integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ== + dependencies: + call-bind "^1.0.0" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-loader@^0.5.4: + version "0.5.7" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-rpc-engine@^5.1.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-runner@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= + dependencies: + mimic-fn "^1.0.0" + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.50.0: + version "1.50.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f" + integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.33" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb" + integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g== + dependencies: + mime-db "1.50.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nan@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + +nano-base32@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nano-base32/-/nano-base32-1.0.1.tgz#ba548c879efcfb90da1c4d9e097db4a46c9255ef" + integrity sha1-ulSMh578+5DaHE2eCX20pGySVe8= + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0, no-case@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + +node-libs-browser@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^1.1.77: + version "1.1.77" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" + integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +nofilter@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.0.3.tgz#3ff3b142efdccb403434ccae4a0c2c835cb9b522" + integrity sha512-TN/MCrQmXQk5DyUJ8TGUq1Il8rv4fTsjddLmMopV006QP8DMkglmGgYfQKD5620vXLRXfr8iGI6ZZ4/ZWld2cQ== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +nth-check@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + dependencies: + boolbase "^1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" + integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +param-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" + integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" + integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" + integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= + dependencies: + no-case "^2.2.0" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-beta.7: + version "1.0.0-beta.18" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.18.tgz#9705453bacd55b3242110d472f23f624ae6777fc" + integrity sha512-ezWdsG/jIeClmYBzg8V9Voy8jujt+VxWF8OS3Vld+C3c+3cPVib8D9l8ahTod7O5Df1anK9zo+WiiS5wb1mLmg== + dependencies: + "@solidity-parser/parser" "^0.13.2" + emoji-regex "^9.2.2" + escape-string-regexp "^4.0.0" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.2" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.2.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== + +printj@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pure-rand@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.0.tgz#87f5bdabeadbd8904e316913a5c0b8caac517b37" + integrity sha512-lD2/y78q+7HqBx2SaT6OT4UcwtvXNRfEpzYEzl0EQ+9gZq2Qi3fa0HDnYPeqQwhlHJFBUhT7AO3mLU3+8bynHA== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.0.6, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= + dependencies: + align-text "^0.1.1" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160-min@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/ripemd160-min/-/ripemd160-min-0.0.6.tgz#a904b77658114474d02503e819dcc55853b67e62" + integrity sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A== + +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +sentence-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" + integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha3@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/sha3/-/sha3-2.1.4.tgz#000fac0fe7c2feac1f48a25e7a31b52a6492cc8f" + integrity sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg== + dependencies: + buffer "6.0.3" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.5" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" + integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +solc@^0.4.20: + version "0.4.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.26.tgz#5390a62a99f40806b86258c737c1cf653cc35cb5" + integrity sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA== + dependencies: + fs-extra "^0.30.0" + memorystream "^0.3.1" + require-from-string "^1.1.0" + semver "^5.3.0" + yargs "^4.7.1" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.6.tgz#abe9af185a9a7defefba480047b3e42cbe9a1210" + integrity sha512-HWUxTAv2h7hx3s3hAab3ifnlwb02ZWhwFU/wSudUHqteMS3ll9c+m1FlGn9V8ztE2rf3Z82fQZA005Wv7KpcFA== + dependencies: + "@solidity-parser/parser" "^0.13.2" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-ast@^0.4.15: + version "0.4.27" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.27.tgz#7eff60a2269ab24a440ef2bb4baaa088e0a3b4be" + integrity sha512-kCP7njjZlZzl2Ijur7gFwcmuAGBgz+v17xMDFmF9B9GOIljMS+6uJ6aUrbuMEcCuff/aDsW7HyMYMccJDxGbiw== + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.10" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.10.tgz#0d9becccde7003d6c658d487dd48a32f0bf3014b" + integrity sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0, string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@^4.2.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s= + dependencies: + has-flag "^2.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^0.2.7: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" + integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A== + +tar@^4.0.2: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +testrpc@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" + integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +title-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" + integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +truffle-assertions@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/truffle-assertions/-/truffle-assertions-0.9.2.tgz#0f8360f53ad92b6d8fdb8ceb5dce54c1fc392e23" + integrity sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA== + dependencies: + assertion-error "^1.1.0" + lodash.isequal "^4.5.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +uglify-js@^2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + integrity sha1-uVH0q7a9YX5m9j64kUmOORdj4wk= + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +utf-8-validate@^5.0.2: + version "5.0.6" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.6.tgz#e1b3e0a5cc8648a3b44c1799fbb170d1aaaffe80" + integrity sha512-hoY0gOf9EkCw+nimK21FVKHUIG1BMqSiRwxB/q3A9yKZOrOI99PP77BxmarDqWz6rG3vVYiBWfhG8z2Tl+7fZA== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.4.0: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" + +web3-bzz@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.5.3.tgz#e36456905ce051138f9c3ce3623cbc73da088c2b" + integrity sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + +web3-core-helpers@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz#099030235c477aadf39a94199ef40092151d563c" + integrity sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw== + dependencies: + web3-eth-iban "1.5.3" + web3-utils "1.5.3" + +web3-core-method@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.5.3.tgz#6cff97ed19fe4ea2e9183d6f703823a079f5132c" + integrity sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg== + dependencies: + "@ethereumjs/common" "^2.4.0" + "@ethersproject/transactions" "^5.0.0-beta.135" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-utils "1.5.3" + +web3-core-promievent@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz#3f11833c3dc6495577c274350b61144e0a4dba01" + integrity sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz#b339525815fd40e3a2a81813c864ddc413f7b6f7" + integrity sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg== + dependencies: + util "^0.12.0" + web3-core-helpers "1.5.3" + web3-providers-http "1.5.3" + web3-providers-ipc "1.5.3" + web3-providers-ws "1.5.3" + +web3-core-subscriptions@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz#d7d69c4caad65074212028656e9dc56ca5c2159d" + integrity sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.5.3" + +web3-core@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.5.3.tgz#59f8728b27c8305b349051326aa262b9b7e907bf" + integrity sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-requestmanager "1.5.3" + web3-utils "1.5.3" + +web3-eth-abi@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz#5aea9394d797f99ca0d9bd40c3417eb07241c96c" + integrity sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg== + dependencies: + "@ethersproject/abi" "5.0.7" + web3-utils "1.5.3" + +web3-eth-accounts@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz#076c816ff4d68c9dffebdc7fd2bfaddcfc163d77" + integrity sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw== + dependencies: + "@ethereumjs/common" "^2.3.0" + "@ethereumjs/tx" "^3.2.1" + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-util "^7.0.10" + scrypt-js "^3.0.1" + uuid "3.3.2" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" + +web3-eth-contract@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz#12b03a4a16ce583a945f874bea2ff2fb4c5b81ad" + integrity sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg== + dependencies: + "@types/bn.js" "^4.11.5" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" + +web3-eth-ens@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz#ef6eee1ddf32b1ff9536fc7c599a74f2656bafe1" + integrity sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-contract "1.5.3" + web3-utils "1.5.3" + +web3-eth-iban@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz#91b1475893a877b10eac1de5cce6eb379fb81b5d" + integrity sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw== + dependencies: + bn.js "^4.11.9" + web3-utils "1.5.3" + +web3-eth-personal@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz#4ebe09e9a77dd49d23d93b36b36cfbf4a6dae713" + integrity sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" + +web3-eth@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.5.3.tgz#d7d1ac7198f816ab8a2088c01e0bf1eda45862fe" + integrity sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q== + dependencies: + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-accounts "1.5.3" + web3-eth-contract "1.5.3" + web3-eth-ens "1.5.3" + web3-eth-iban "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" + +web3-net@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.5.3.tgz#545fee49b8e213b0c55cbe74ffd0295766057463" + integrity sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ== + dependencies: + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" + +web3-providers-http@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.5.3.tgz#74f170fc3d79eb7941d9fbc34e2a067d61ced0b2" + integrity sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw== + dependencies: + web3-core-helpers "1.5.3" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz#4bd7f5e445c2f3c2595fce0929c72bb879320a3f" + integrity sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg== + dependencies: + oboe "2.1.5" + web3-core-helpers "1.5.3" + +web3-providers-ws@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz#eec6cfb32bb928a4106de506f13a49070a21eabf" + integrity sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.5.3" + websocket "^1.0.32" + +web3-shh@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.5.3.tgz#3c04aa4cda9ba0b746d7225262401160f8e38b13" + integrity sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q== + dependencies: + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-net "1.5.3" + +web3-utils@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.5.3.tgz#e914c9320cd663b2a09a5cb920ede574043eb437" + integrity sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31: + version "1.6.0" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.6.0.tgz#1975c5ee5b7db8a0836eb7004848a7cd962d1ddc" + integrity sha512-bgCAWAeQnJF035YTFxrcHJ5mGEfTi/McsjqldZiXRwlHK7L1PyOqvXiQLE053dlzvy1kdAxWl/sSSfLMyNUAXg== + dependencies: + bn.js "^4.11.9" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.5.3.tgz#11882679453c645bf33620fbc255a243343075aa" + integrity sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w== + dependencies: + web3-bzz "1.5.3" + web3-core "1.5.3" + web3-eth "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-shh "1.5.3" + web3-utils "1.5.3" + +webpack-sources@^1.0.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^3.0.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" + integrity sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ== + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + async "^2.1.2" + enhanced-resolve "^3.4.0" + escope "^3.6.0" + interpret "^1.0.0" + json-loader "^0.5.4" + json5 "^0.5.1" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + mkdirp "~0.5.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" + supports-color "^4.2.1" + tapable "^0.2.7" + uglifyjs-webpack-plugin "^0.4.6" + watchpack "^1.4.0" + webpack-sources "^1.0.1" + yargs "^8.0.2" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.7" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" + integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== + dependencies: + async-limiter "~1.0.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" + integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^13.1.0: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= + dependencies: + camelcase "^4.1.0" + +yargs@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + +yargs@^4.7.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A= + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" diff --git a/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV1.sol b/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV1.sol new file mode 100644 index 00000000..79b5db63 --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV1.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./StringsLib.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155HolderUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +/** + @title Interface of BSH Coin transfer service + @dev This contract use to handle coin transfer service + Note: The coin of following interface can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHCoreV1 is Initializable, ERC1155Upgradeable, ERC1155HolderUpgradeable, OwnableUpgradeable { + using Strings for string; + struct Balance { + uint256 lockedBalance; + uint256 refundableBalance; + } + + mapping(address => mapping(string => Balance)) private balances; + mapping(string => uint256) private coins; // a list of all supported coins + string[] private coinsName; // a string array stores names of supported coins + + function initialize ( + string calldata _uri, + string calldata _nativeCoinName + ) public initializer { + __ERC1155_init(_uri); + __ERC1155Holder_init(); + __Ownable_init(); + + coins[_nativeCoinName] = 0; + coinsName.push(_nativeCoinName); + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Contract Owner + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register( + string calldata _name + ) external onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names in other networks by the BSH contract + @dev + @return _names An array of strings. + */ + function coinNames() external view returns (string[] memory _names) { + return coinsName; + } + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) external view returns (uint256 _coinId) { + return coins[_coinName]; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) external view returns (bool _valid) { + if (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])) { + return true; + } + return false; + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + + function getBalanceOf(address _owner, string memory _coinName) + external + view + returns (uint256 _usableBalance, uint256 _lockedBalance, uint256 _refundableBalance) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner, coins[_coinName]), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + returns + ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ){ + _usableBalances = new uint256[](_coinNames.length); + _lockedBalances = new uint256[](_coinNames.length); + _refundableBalances = new uint256[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + (_usableBalances[i], _lockedBalances[i], _refundableBalances[i]) = + this.getBalanceOf(_owner, _coinNames[i]); + } + return (_usableBalances, _lockedBalances, _refundableBalances); + } +} diff --git a/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV2.sol b/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV2.sol new file mode 100644 index 00000000..d0c8c9a5 --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/contracts/BSHCoreV2.sol @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./StringsLib.sol"; +import "./IBSHPeriphery.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155HolderUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +/** + @title Interface of BSH Coin transfer service + @dev This contract use to handle coin transfer service + Note: The coin of following interface can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +contract BSHCoreV2 is Initializable, ERC1155Upgradeable, ERC1155HolderUpgradeable, OwnableUpgradeable { + using Strings for string; + struct Balance { + uint256 lockedBalance; + uint256 refundableBalance; + } + + mapping(address => mapping(string => Balance)) private balances; + mapping(string => uint256) private coins; // a list of all supported coins + string[] private coinsName; // a string array stores names of supported coins + // Add more state variables at the end of previous ones + IBSHPeriphery private bshPeriphery; + uint256 private constant FEE_DENOMINATOR = 10**4; + uint256 private feeNumerator; + + function initialize ( + string calldata _uri, + string calldata _nativeCoinName + ) public initializer { + __ERC1155_init(_uri); + __ERC1155Holder_init(); + __Ownable_init(); + + coins[_nativeCoinName] = 0; + coinsName.push(_nativeCoinName); + } + + /** + @notice update bsh service address. + @dev Caller must be an operator of BTP network + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery bsh service address. + */ + function updateBSHPeriphery(address _bshPeriphery) external onlyOwner { + bshPeriphery = IBSHPeriphery(_bshPeriphery); + } + + /** + @notice update base uri. + @dev Caller must be an operator of BTP network + the uri must be initilized in construction. + @param _newURI new uri + */ + function updateUri(string calldata _newURI) external onlyOwner { + _setURI(_newURI); + } + + /** + @notice set fee ratio. + @dev Caller must be an operator of BTP network + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR and greater than 1 + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external onlyOwner { + feeNumerator = _feeNumerator; + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Contract Owner + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register( + string calldata _name + ) external onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names in other networks by the BSH contract + @dev + @return _names An array of strings. + */ + function coinNames() external view returns (string[] memory _names) { + return coinsName; + } + + /** + @notice Return an _id number of Coin whose name is the same with given _coinName. + @dev Return nullempty if not found. + @return _coinId An ID number of _coinName. + */ + function coinId(string calldata _coinName) external view returns (uint256 _coinId) { + return coins[_coinName]; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) external view returns (bool _valid) { + if (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])) { + return true; + } + return false; + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + + function getBalanceOf(address _owner, string memory _coinName) + external + view + returns (uint256 _usableBalance, uint256 _lockedBalance, uint256 _refundableBalance) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner, coins[_coinName]), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list Balance of an account. + @dev The order of request's coinNames must be the same with the order of return balance + Return 0 if not found. + @return _usableBalances An array of Usable Balances + @return _lockedBalances An array of Locked Balances + @return _refundableBalances An array of Refundable Balances + */ + + function getBalanceOfBatch(address _owner, string[] calldata _coinNames) + external + view + returns + ( + uint256[] memory _usableBalances, + uint256[] memory _lockedBalances, + uint256[] memory _refundableBalances + ){ + _usableBalances = new uint256[](_coinNames.length); + _lockedBalances = new uint256[](_coinNames.length); + _refundableBalances = new uint256[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + (_usableBalances[i], _lockedBalances[i], _refundableBalances[i]) = + this.getBalanceOf(_owner, _coinNames[i]); + } + return (_usableBalances, _lockedBalances, _refundableBalances); + } +} diff --git a/solidity/doc/UpgradeContract/ExampleProject/contracts/IBSHPeriphery.sol b/solidity/doc/UpgradeContract/ExampleProject/contracts/IBSHPeriphery.sol new file mode 100644 index 00000000..912e57c7 --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/contracts/IBSHPeriphery.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +/** + @title Interface of BSHPeriphery transfer service + @dev This contract use to handle the communication + among BMCService contracts and BSHCore contract +*/ +interface IBSHPeriphery { + struct AssetTransferDetail { + string _coinName; + uint256 _value; + uint256 _fee; + } + + /** + @notice Send Service Message from BSHCore contract to BMCService contract + @dev Caller must be BSHCore only + @param _to A network address of destination chain + @param _coinName A coin name that is requested to transfer + @param _value An amount to receive at destination chain + @param _fee An amount of charging fee + */ + function sendServiceMessage( + address _from, + string calldata _to, + string memory _coinName, + uint256 _value, + uint256 _fee + ) external; + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external; + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering( + string calldata _fa, + string calldata _svc + ) external; +} diff --git a/solidity/doc/UpgradeContract/ExampleProject/contracts/StringsLib.sol b/solidity/doc/UpgradeContract/ExampleProject/contracts/StringsLib.sol new file mode 100644 index 00000000..e27d96df --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/contracts/StringsLib.sol @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.8.0; + +/** + * Strings Library + * + * This is a simple library of string functions which try to simplify + * string operations in solidity. + * + * Please be aware some of these functions can be quite gas heavy so use them only when necessary + * + * The original library was modified. If you want to know more about the original version + * please check this link: https://github.com/willitscale/solidity-util.git + */ +library Strings { + /** + * splitBTPAddress + * + * Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + * into Network_address (1234.iconee) and Server_address (0x123456789) + * + * @param _base String base BTP Address format to be split + * @dev _base must follow a BTP Address format + * + * @return string, string The resulting strings of Network_address and Server_address + */ + function splitBTPAddress(string memory _base) + internal + pure + returns (string memory, string memory) + { + string[] memory temp = split(_base, "/"); + return (temp[2], temp[3]); + } + + /** + * Concat + * + * Appends two strings together and returns a new value + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string which will be the concatenated + * prefix + * @param _value The value to be the concatenated suffix + * @return string The resulting string from combinging the base and value + */ + function concat(string memory _base, string memory _value) + internal + pure + returns (string memory) + { + return string(abi.encodePacked(_base, _value)); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function indexOf(string memory _base, string memory _value) + internal + pure + returns (int256) + { + return _indexOf(_base, _value, 0); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string starting + * from a defined offset + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @param _offset The starting point to start searching from which can start + * from 0, but must not exceed the length of the string + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function _indexOf( + string memory _base, + string memory _value, + uint256 _offset + ) internal pure returns (int256) { + bytes memory _baseBytes = bytes(_base); + bytes memory _valueBytes = bytes(_value); + + assert(_valueBytes.length == 1); + + for (uint256 i = _offset; i < _baseBytes.length; i++) { + if (_baseBytes[i] == _valueBytes[0]) { + return int256(i); + } + } + + return -1; + } + + /** + * Length + * + * Returns the length of the specified string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string to be measured + * @return uint The length of the passed string + */ + function length(string memory _base) internal pure returns (uint256) { + bytes memory _baseBytes = bytes(_base); + return _baseBytes.length; + } + + /* + * String Split (Very high gas cost) + * + * Splits a string into an array of strings based off the delimiter value. + * Please note this can be quite a gas expensive function due to the use of + * storage so only use if really required. + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string value to be split. + * @param _value The delimiter to split the string on which must be a single + * character + * @return string[] An array of values split based off the delimiter, but + * do not container the delimiter. + */ + function split(string memory _base, string memory _value) + internal + pure + returns (string[] memory splitArr) + { + bytes memory _baseBytes = bytes(_base); + + uint256 _offset = 0; + uint256 _splitsCount = 1; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) break; + else { + _splitsCount++; + _offset = uint256(_limit) + 1; + } + } + + splitArr = new string[](_splitsCount); + + _offset = 0; + _splitsCount = 0; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) { + _limit = int256(_baseBytes.length); + } + + string memory _tmp = new string(uint256(_limit) - _offset); + bytes memory _tmpBytes = bytes(_tmp); + + uint256 j = 0; + for (uint256 i = _offset; i < uint256(_limit); i++) { + _tmpBytes[j++] = _baseBytes[i]; + } + _offset = uint256(_limit) + 1; + splitArr[_splitsCount++] = string(_tmpBytes); + } + return splitArr; + } + + /** + * Compare To + * + * Compares the characters of two strings, to ensure that they have an + * identical footprint + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string base to compare against + * @param _value The string the base is being compared to + * @return bool Simply notates if the two string have an equivalent + */ + function compareTo(string memory _base, string memory _value) + internal + pure + returns (bool) + { + if ( + keccak256(abi.encodePacked(_base)) == + keccak256(abi.encodePacked(_value)) + ) { + return true; + } + return false; + } +} diff --git a/solidity/doc/UpgradeContract/ExampleProject/migrations/1_deploy.js b/solidity/doc/UpgradeContract/ExampleProject/migrations/1_deploy.js new file mode 100644 index 00000000..39eb0d8e --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/migrations/1_deploy.js @@ -0,0 +1,23 @@ +const BSHCoreV1 = artifacts.require("BSHCoreV1"); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +var _uri = 'https://1234.iconee/'; +var _native_coin = 'PARA'; + +module.exports = async function (deployer, network) { + /********************************************************************************************** + Deploy A Contract and the Proxy Contract + - Before running a script, make sure to delete + + a file '.openzeppelin/unknown-1337.json' (if existed) + - Deploy the first part by a following command: `yarn truffle:deploy` + **********************************************************************************************/ + + await deployProxy(BSHCoreV1, + [_uri, _native_coin], // calling initialize() and passing params into initialize() + { deployer } // specify an account to deploy a contract + // if omit, default 'deployer' will be chosen + ); + const version1 = await BSHCoreV1.deployed(); + await version1.register("Coin1"); + await version1.register("Coin2"); +} \ No newline at end of file diff --git a/solidity/doc/UpgradeContract/ExampleProject/migrations/2_upgrade.js b/solidity/doc/UpgradeContract/ExampleProject/migrations/2_upgrade.js new file mode 100644 index 00000000..5f1f9050 --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/migrations/2_upgrade.js @@ -0,0 +1,24 @@ +const BSHCoreV2 = artifacts.require("BSHCoreV2"); +const ProxyAddr = require('../.openzeppelin/unknown-1337.json'); +const { upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +var _uri = 'https://1234.iconee/'; +var _native_coin = 'PARA'; + +module.exports = async function (deployer, network) { + /********************************************************************************************** + - Upgrade the contract by a following command: `yarn truffle:upgrade` + **********************************************************************************************/ + + await upgradeProxy(ProxyAddr.proxies[0].address, BSHCoreV2); + const version2 = await BSHCoreV2.deployed(); + // It prints out an array of three: 'PARA', 'Coin1', and 'Coin2' + console.log(await version2.coinNames()); + await version2.register("Coin3"); + // It prints out an array of four: 'PARA', 'Coin1', 'Coin2', 'Coin3' + console.log(await version2.coinNames()); + var coin1_id = await version2.coinId('Coin1'); + var coin3_id = await version2.coinId('Coin3'); + console.log('ID of Coin1 ---> ', web3.utils.BN(coin1_id).toString()); + console.log('ID of Coin3 ---> ', web3.utils.BN(coin3_id).toString()); +}; \ No newline at end of file diff --git a/solidity/doc/UpgradeContract/ExampleProject/package.json b/solidity/doc/UpgradeContract/ExampleProject/package.json new file mode 100644 index 00000000..4b06bf0d --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/package.json @@ -0,0 +1,17 @@ +{ + "name": "pra-bsh", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@openzeppelin/contracts": "^3.4.1-solc-0.7-2", + "@openzeppelin/contracts-upgradeable": "^3.4.1-solc-0.7-2", + "@openzeppelin/truffle-upgrades": "^1.7.0", + "chai": "^4.3.4", + "truffle-assertions": "^0.9.2" + }, + "scripts": { + "compile": "truffle compile", + "truffle:deploy": "truffle migrate -f 1 --to 1 --network development", + "truffle:upgrade": "truffle migrate -f 2 --to 2 --network development" + } +} diff --git a/solidity/doc/UpgradeContract/ExampleProject/truffle-config.js b/solidity/doc/UpgradeContract/ExampleProject/truffle-config.js new file mode 100644 index 00000000..89802c82 --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/truffle-config.js @@ -0,0 +1,108 @@ +/** + * Use this file to configure your truffle project. It's seeded with some + * common settings for different networks and features like migrations, + * compilation and testing. Uncomment the ones you need or modify + * them to suit your project as necessary. + * + * More information about configuration can be found at: + * + * trufflesuite.com/docs/advanced/configuration + * + * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) + * to sign your transactions before they're sent to a remote public node. Infura accounts + * are available for free at: infura.io/register. + * + * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate + * public/private key pairs. If you're publishing your code to GitHub make sure you load this + * phrase from a file you've .gitignored so it doesn't accidentally become public. + * + */ + +// const HDWalletProvider = require('@truffle/hdwallet-provider'); +// const infuraKey = "fj4jll3k....."; +// +// const fs = require('fs'); +// const mnemonic = fs.readFileSync(".secret").toString().trim(); + +module.exports = { + /** + * Networks define how you connect to your ethereum client and let you set the + * defaults web3 uses to send transactions. If you don't specify one truffle + * will spin up a development blockchain for you on port 9545 when you + * run `develop` or `test`. You can ask a truffle command to use a specific + * network from the command line, e.g + * + * $ truffle test --network + */ + + networks: { + // Useful for testing. The `development` name is special - truffle uses it by default + // if it's defined here and no other network is specified at the command line. + // You should run a client (like ganache-cli, geth or parity) in a separate terminal + // tab if you use this network and you must also set the `host`, `port` and `network_id` + // options below to some value. + // + development: { + host: "127.0.0.1", // Localhost (default: none) + port: 8545, // Standard Ethereum port (default: none) + network_id: "*", // Any network (default: none) + gas: 12000000 + } + // Another network with more advanced options... + // advanced: { + // host: "127.0.0.1", + // port: 7545, // Custom port + // network_id: "*", // Custom network + // gas: 8500000, // Gas sent with each transaction (default: ~6700000) + // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) + // from:
, // Account to send txs from (default: accounts[0]) + // websocket: true // Enable EventEmitter interface for web3 (default: false) + // }, + // Useful for deploying to a public network. + // NB: It's important to wrap the provider as a function. + // ropsten: { + // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), + // network_id: 3, // Ropsten's id + // gas: 5500000, // Ropsten has a lower block limit than mainnet + // confirmations: 2, // # of confs to wait between deployments. (default: 0) + // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) + // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) + // }, + // Useful for private networks + // private: { + // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), + // network_id: 2111, // This network is yours, in the cloud. + // production: true // Treats this network as if it was a public net. (default: false) + // } + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.7.6", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 200 + }, + evmVersion: "petersburg" + } + } + }, + + // Truffle DB is currently disabled by default; to enable it, change enabled: false to enabled: true + // + // Note: if you migrated your contracts prior to enabling this field in your Truffle project and want + // those previously migrated contracts available in the .db directory, you will need to run the following: + // $ truffle migrate --reset --compile-all + + db: { + enabled: false + } +}; diff --git a/solidity/doc/UpgradeContract/ExampleProject/yarn.lock b/solidity/doc/UpgradeContract/ExampleProject/yarn.lock new file mode 100644 index 00000000..ce1f9c8d --- /dev/null +++ b/solidity/doc/UpgradeContract/ExampleProject/yarn.lock @@ -0,0 +1,3374 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@cto.af/textdecoder@^0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@cto.af/textdecoder/-/textdecoder-0.0.0.tgz#e1e8d84c936c30a0f4619971f19ca41941af9fdc" + integrity sha512-sJpx3F5xcVV/9jNYJQtvimo4Vfld/nD3ph+ZWtQzZ03Zo8rJC7QKQTRcIGS13Rcz80DwFNthCWMrd58vpY4ZAQ== + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abstract-provider@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.3.0.tgz#f4c0ae4a4cef9f204d7781de805fd44b72756c81" + integrity sha512-1+MLhGP1GwxBDBNwMWVmhCsvKwh4gK7oIfOrmlmePNeskg1NhIrYssraJBieaFNHUYfKEd/1DjiVZMw8Qu5Cxw== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/networks" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/transactions" "^5.3.0" + "@ethersproject/web" "^5.3.0" + +"@ethersproject/abstract-signer@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.3.0.tgz#05172b653e15b535ed5854ef5f6a72f4b441052d" + integrity sha512-w8IFwOYqiPrtvosPuArZ3+QPR2nmdVTRrVY8uJYL3NNfMmQfTy3V3l2wbzX47UUlNbPJY+gKvzJAyvK1onZxJg== + dependencies: + "@ethersproject/abstract-provider" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + +"@ethersproject/address@^5.0.4", "@ethersproject/address@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.3.0.tgz#e53b69eacebf332e8175de814c5e6507d6932518" + integrity sha512-29TgjzEBK+gUEUAOfWCG7s9IxLNLCqvr+oDSk6L9TXD0VLvZJKhJV479tKQqheVA81OeGxfpdxYtUVH8hqlCvA== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/rlp" "^5.3.0" + +"@ethersproject/base64@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.3.0.tgz#b831fb35418b42ad24d943c557259062b8640824" + integrity sha512-JIqgtOmgKcbc2sjGWTXyXktqUhvFUDte8fPVsAaOrcPiJf6YotNF+nsrOYGC9pbHBEGSuSBp3QR0varkO8JHEw== + dependencies: + "@ethersproject/bytes" "^5.3.0" + +"@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.3.0.tgz#74ab2ec9c3bda4e344920565720a6ee9c794e9db" + integrity sha512-5xguJ+Q1/zRMgHgDCaqAexx/8DwDVLRemw2i6uR8KyGjwGdXI8f32QZZ1cKGucBN6ekJvpUpHy6XAuQnTv0mPA== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.3.0.tgz#473e0da7f831d535b2002be05e6f4ca3729a1bc9" + integrity sha512-rqLJjdVqCcn7glPer7Fxh87PRqlnRScVAoxcIP3PmOUNApMWJ6yRdOFfo2KvPAdO7Le3yEI1o0YW+Yvr7XCYvw== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.3.0.tgz#a5d6d86c0eec2c64c3024479609493b9afb3fc77" + integrity sha512-4y1feNOwEpgjAfiCFWOHznvv6qUF/H6uI0UKp8xdhftb+H+FbKflXg1pOgH5qs4Sr7EYBL+zPyPb+YD5g1aEyw== + dependencies: + "@ethersproject/bignumber" "^5.3.0" + +"@ethersproject/hash@^5.0.4": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.3.0.tgz#f65e3bf3db3282df4da676db6cfa049535dd3643" + integrity sha512-gAFZSjUPQ32CIfoKSMtMEQ+IO0kQxqhwz9fCIFt2DtAq2u4pWt8mL9Z5P0r6KkLcQU8LE9FmuPPyd+JvBzmr1w== + dependencies: + "@ethersproject/abstract-signer" "^5.3.0" + "@ethersproject/address" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/strings" "^5.3.0" + +"@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.3.0.tgz#fb5cd36bdfd6fa02e2ea84964078a9fc6bd731be" + integrity sha512-Gv2YqgIUmRbYVNIibafT0qGaeGYLIA/EdWHJ7JcVxVSs2vyxafGxOJ5VpSBHWeOIsE6OOaCelYowhuuTicgdFQ== + dependencies: + "@ethersproject/bytes" "^5.3.0" + js-sha3 "0.5.7" + +"@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.3.0.tgz#7a69fa1d4ca0d4b7138da1627eb152f763d84dd0" + integrity sha512-8bwJ2gxJGkZZnpQSq5uSiZSJjyVTWmlGft4oH8vxHdvO1Asy4TwVepAhPgxIQIMxXZFUNMych1YjIV4oQ4I7dA== + +"@ethersproject/networks@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.3.0.tgz#d8ad06eb107c69fb8651f4c81ddd0e88944fdfea" + integrity sha512-XGbD9MMgqrR7SYz8o6xVgdG+25v7YT5vQG8ZdlcLj2I7elOBM7VNeQrnxfSN7rWQNcqu2z80OM29gGbQz+4Low== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.3.0.tgz#feef4c4babeb7c10a6b3449575016f4ad2c092b2" + integrity sha512-PaHxJyM5/bfusk6vr3yP//JMnm4UEojpzuWGTmtL5X4uNhNnFNvlYilZLyDr4I9cTkIbipCMsAuIcXWsmdRnEw== + dependencies: + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/rlp@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.3.0.tgz#7cb93a7b5dfa69163894153c9d4b0d936f333188" + integrity sha512-oI0joYpsRanl9guDubaW+1NbcpK0vJ3F/6Wpcanzcnqq+oaW9O5E98liwkEDPcb16BUTLIJ+ZF8GPIHYxJ/5Pw== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/signing-key@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.3.0.tgz#a96c88f8173e1abedfa35de32d3e5db7c48e5259" + integrity sha512-+DX/GwHAd0ok1bgedV1cKO0zfK7P/9aEyNoaYiRsGHpCecN7mhLqcdoUiUzE7Uz86LBsxm5ssK0qA1kBB47fbQ== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.3.0.tgz#a6b640aab56a18e0909f657da798eef890968ff0" + integrity sha512-j/AzIGZ503cvhuF2ldRSjB0BrKzpsBMtCieDtn4TYMMZMQ9zScJn9wLzTQl/bRNvJbBE6TOspK0r8/Ngae/f2Q== + dependencies: + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/constants" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + +"@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.3.0.tgz#49b86f2bafa4d0bdf8e596578fc795ee47c50458" + integrity sha512-cdfK8VVyW2oEBCXhURG0WQ6AICL/r6Gmjh0e4Bvbv6MCn/GBd8FeBH3rtl7ho+AW50csMKeGv3m3K1HSHB2jMQ== + dependencies: + "@ethersproject/address" "^5.3.0" + "@ethersproject/bignumber" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/constants" "^5.3.0" + "@ethersproject/keccak256" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/rlp" "^5.3.0" + "@ethersproject/signing-key" "^5.3.0" + +"@ethersproject/web@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.3.0.tgz#7959c403f6476c61515008d8f92da51c553a8ee1" + integrity sha512-Ni6/DHnY6k/TD41LEkv0RQDx4jqWz5e/RZvrSecsxGYycF+MFy2z++T/yGc2peRunLOTIFwEksgEGGlbwfYmhQ== + dependencies: + "@ethersproject/base64" "^5.3.0" + "@ethersproject/bytes" "^5.3.0" + "@ethersproject/logger" "^5.3.0" + "@ethersproject/properties" "^5.3.0" + "@ethersproject/strings" "^5.3.0" + +"@openzeppelin/contracts-upgradeable@^3.4.1-solc-0.7-2": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.1.tgz#38dfdfa86fda0a088c6fcdebe6870cfaf897b471" + integrity sha512-wBGlUzEkOxcj/ghtcF2yKc8ZYh+PTUtm1mK38zoENulJ6aplij7eH8quo3lMugfzPJy+V6V5qI8QhdQmCn7hkQ== + +"@openzeppelin/contracts@^3.4.1-solc-0.7-2": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.1.tgz#03c891fec7f93be0ae44ed74e57a122a38732ce7" + integrity sha512-cUriqMauq1ylzP2TxePNdPqkwI7Le3Annh4K9rrpvKfSBB/bdW+Iu1ihBaTIABTAAJ85LmKL5SSPPL9ry8d1gQ== + +"@openzeppelin/truffle-upgrades@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.7.0.tgz#cbc0d3a2f32d1acb8a31bcc9b8c8f0f7eb01f510" + integrity sha512-0Bb7Qr6ULUYKB//TJF7s0++z4QKONR9b5/UCnqb+po1TYqxEBwftjebVR4rj7C1tWH8cj+igEMkyQb0ep9r9Lw== + dependencies: + "@openzeppelin/upgrades-core" "^1.7.0" + "@truffle/contract" "^4.2.12" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.7.0": + version "1.7.6" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.7.6.tgz#2c5e89b272aaf2164f51ca99167ffeecdab13749" + integrity sha512-xXoUJGWI2MHLtMwS2GN3PiymZ9WIj+uRAMrm8HIz0genGSIO6PwPMlmaUyq3O8/JzTMgpVV92aMcPjOkvI5SmQ== + dependencies: + bn.js "^5.1.2" + cbor "^7.0.0" + chalk "^4.1.0" + compare-versions "^3.6.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/blockchain-utils@^0.0.30": + version "0.0.30" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.30.tgz#1fafbd8e8694d79280177b5eff167b0690838855" + integrity sha512-3hkHSHxVavoALcxpBqD4YwHuCmkBrvjq6PAGw93i6WCB+pnejBD5sFjVCiZZKCogh4kGObxxcwu53+3dyT/6IQ== + +"@truffle/codec@^0.10.9": + version "0.10.9" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.9.tgz#9c6f6a57b12894ad44fc37f41ddce18ebfc7b7e5" + integrity sha512-+xBcn1mTAqBhVaFULkMC+pJnUp3prL9QZtE5I4XhlCar3QLkSGR9Oy+Bm5qZwH72rctBRD/lGp2ezUo/oFc2MQ== + dependencies: + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + utf8 "^3.0.0" + web3-utils "1.3.6" + +"@truffle/contract-schema@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.1.tgz#13b404383d438b48960862022a20102970323666" + integrity sha512-2gvu6gxJtbbI67H2Bwh2rBuej+1uCV3z4zKFzQZP00hjNoL+QfybrmBcOVB88PflBeEB+oUXuwQfDoKX3TXlnQ== + dependencies: + ajv "^6.10.0" + crypto-js "^3.1.9-1" + debug "^4.3.1" + +"@truffle/contract@^4.2.12": + version "4.3.19" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.19.tgz#cbd6b4f08b7d3bb5e7cdaba205f17c80cc3f7b60" + integrity sha512-asBwoxUePLwNAuYfHO/SvyZyqFQ9QMeUieJ6PXD9DBrUdNpjgJvk+WHYnXH4bWHgHakbTpL1MEBa1qmcAL+LMw== + dependencies: + "@truffle/blockchain-utils" "^0.0.30" + "@truffle/contract-schema" "^3.4.1" + "@truffle/debug-utils" "^5.0.19" + "@truffle/error" "^0.0.14" + "@truffle/interface-adapter" "^0.5.0" + bignumber.js "^7.2.1" + ethereum-ens "^0.8.0" + ethers "^4.0.32" + web3 "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +"@truffle/debug-utils@^5.0.19": + version "5.0.19" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.19.tgz#d23a4bfd9a8cacd922e0d5763aaee63836ca9553" + integrity sha512-wCF5fwyJTHGBwQD+m8npRCrUIgSPw9jRiZlvyuE+TDcVNBpV4A1NcdsTJR/E5lAViLDTp9lpelsgA/Mw65kU+w== + dependencies: + "@truffle/codec" "^0.10.9" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlight.js "^10.4.0" + highlightjs-solidity "^1.1.0" + +"@truffle/error@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.14.tgz#59683b5407bede7bddf16d80dc5592f9c5e5fa05" + integrity sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA== + +"@truffle/interface-adapter@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.0.tgz#00c9e16fe0edafbfbf4b331fb4db178c9148c8fc" + integrity sha512-0MRt9orgQqo0knyKDy0fGRqnI+alkuK0BUAvHB1/VUJgCKyWBNAUUZO5gPjuj75qCjV4Rw+W6SKDQpn2xOWsXw== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + web3 "1.3.6" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "15.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d" + integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww== + +"@types/node@^12.12.6": + version "12.20.15" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.15.tgz#10ee6a6a3f971966fddfa3f6e89ef7a73ec622df" + integrity sha512-F6S4Chv4JicJmyrwlDkxUdGNSplsQdGwp1A0AJloEVDirWdZOAiRHhovDlsFkKUrquUXhz1imJhXHsf59auyAg== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.2.tgz#20c29a87149d980f64464e56539bf4810fdb5d1d" + integrity sha512-QMg+9v0bbNJ2peLuHRWxzmy0HRJIG6gFZNhaRSp7S3ggSbCCxiqQB2/ybvhXyhHOCequpNkrx7OavNhrWOsW0A== + dependencies: + "@types/node" "*" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +ajv@^6.10.0, ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +available-typed-arrays@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9" + integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.8" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d" + integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== + +blakejs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.0.tgz#69df92ef953aa88ca51a32df6ab1c54a155fc7a5" + integrity sha1-ad+S75U6qIylGjLfarHFShVfx6U= + +bluebird@^3.4.7, bluebird@^3.5.0: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.9, bn.js@^4.4.0: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0, body-parser@^1.16.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== + dependencies: + node-gyp-build "^4.2.0" + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +cbor@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-7.0.5.tgz#ed54cdbc19fa7352bb328d00a5393aa7ce45a10f" + integrity sha512-0aaAPgW92lLmypb9iCd22k7tSD1FbF6dps8VQzmIBKY6ych2gO09b2vo/SbaLTmezJuB8Kh88Rvpl/Uq52mNZg== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + nofilter "^2.0.3" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +cheerio-select@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" + integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== + dependencies: + css-select "^4.1.3" + css-what "^5.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.7.0" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" + integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +crypto-browserify@3.12.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0, css-what@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.1.1, debug@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms "2.1.2" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dom-serializer@^1.0.1, dom-serializer@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" + integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" + integrity sha512-8eaHa17IwJUPAiB+SoTYBo5mCdeMgdcAoXJ59m6DT1vw+5iLS3gNoqYaRowaBKtGVrOF1Jz4yDTgYKLK2kvfJg== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +elliptic@6.5.3: + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.0: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +ethereum-bloom-filters@^1.0.6: + version "1.0.9" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.9.tgz#4a59dead803af0c9e33834170bd7695df67061ec" + integrity sha512-GiK/RQkAkcVaEdxKVkPcG07PQ5vD7v2MFSHgZmBJSfMzNRHimntdBithsHAT89tAXnIpzVDWt8iaCD1DvkaxGg== + dependencies: + js-sha3 "^0.8.0" + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-ens@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" + integrity sha512-a8cBTF4AWw1Q1Y37V1LSCS9pRY4Mh3f8vCg5cbXCCEJ3eno1hbI/+Ccv9SZLISYpqQhaglP3Bxb/34lS4Qf7Bg== + dependencies: + bluebird "^3.4.7" + eth-ens-namehash "^2.0.0" + js-sha3 "^0.5.7" + pako "^1.0.4" + underscore "^1.8.3" + web3 "^1.0.0-beta.34" + +ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-tx@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.0.3: + version "7.0.10" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.10.tgz#5fb7b69fa1fda0acc59634cf39d6b0291180fc1f" + integrity sha512-c/xThw6A+EAnej5Xk5kOzFzyoSnw0WX0tSlZ6pAsfGVvQj3TItaDg9b1+Fz1RJXA+y2YksKwQnuzgt1eY6LKzw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.4" + +ethers@^4.0.32: + version "4.0.48" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" + integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== + dependencies: + aes-js "3.0.0" + bn.js "^4.4.0" + elliptic "6.5.3" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" + integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== + dependencies: + type "^2.0.0" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +highlight.js@^10.4.0, highlight.js@^10.4.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-solidity@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.1.0.tgz#bdf0adba6deffdb1651f8fa63bee4dacc3dc4e00" + integrity sha512-LtH7uuoe+FOmtQd41ozAZKLJC2chqdqs461FJcWAx00R3VcBhSQTRFfzRGtTQqu2wsVIEdHiyynrPrEDDWyIMw== + +hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + +is-bigint@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" + integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== + +is-boolean-object@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" + integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== + dependencies: + call-bind "^1.0.2" + +is-callable@^1.1.4, is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + +is-date-object@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" + integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c" + integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A== + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== + +is-number-object@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" + integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-stream@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5, is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" + integrity sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.48.0: + version "1.48.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.48.0.tgz#e35b31045dd7eada3aaad537ed88a33afbef2d1d" + integrity sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.31" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.31.tgz#a00d76b74317c61f9c2db2218b8e9f8e9c5c9e6b" + integrity sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + dependencies: + mime-db "1.48.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-gyp-build@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +nofilter@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-2.0.3.tgz#f5460f3cb33147005883e3f5d4476239501fa187" + integrity sha512-FbuXC+lK+GU2+63D1kC1ETiZo+Z7SIi7B+mxKTCH1byrh6WFvfBCN/wpherFz0a0bjGd7EKTst/cz0yLeNngug== + dependencies: + "@cto.af/textdecoder" "^0.0.0" + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +pako@^1.0.4: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" + integrity sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA== + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +request@^2.79.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3, rlp@^2.2.4: + version "2.2.6" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" + integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== + dependencies: + bn.js "^4.11.1" + +safe-buffer@5.1.2, safe-buffer@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semver@^7.3.4: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +solidity-ast@^0.4.15: + version "0.4.25" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.25.tgz#1e0dc3d6024e69c29698c49d74e5ee9421fe426d" + integrity sha512-8IpweS/vgHEpKvY4l0sfr3EsHk+JFIzRWqq/0JefRjzP/Wyi2xZYfx8aHlJ9kcEn2M2RCQK9PexuZ+scaa83OQ== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +tar@^4.0.2: + version "4.4.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" + integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.8.6" + minizlib "^1.2.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.3" + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +truffle-assertions@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/truffle-assertions/-/truffle-assertions-0.9.2.tgz#0f8360f53ad92b6d8fdb8ceb5dce54c1fc392e23" + integrity sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA== + dependencies: + assertion-error "^1.1.0" + lodash.isequal "^4.5.0" + +tslib@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +underscore@1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== + +underscore@^1.8.3: + version "1.13.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" + integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +utf-8-validate@^5.0.2: + version "5.0.5" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.5.tgz#dd32c2e82c72002dc9f02eb67ba6761f43456ca1" + integrity sha512-+pnxRYsS/axEpkrrEpzYfNZGXp0IjC/9RIxwM5gntY4Koi8SHmUGSfxfWqxZdRxrtaoVstuOzUp/rbs3JSPELQ== + dependencies: + node-gyp-build "^4.2.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +web3-bzz@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.6.tgz#95f370aecc3ff6ad07f057e6c0c916ef09b04dde" + integrity sha512-ibHdx1wkseujFejrtY7ZyC0QxQ4ATXjzcNUpaLrvM6AEae8prUiyT/OloG9FWDgFD2CPLwzKwfSQezYQlANNlw== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + underscore "1.12.1" + +web3-core-helpers@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.6.tgz#c478246a9abe4e5456acf42657dac2f7c330be74" + integrity sha512-nhtjA2ZbkppjlxTSwG0Ttu6FcPkVu1rCN5IFAOVpF/L0SEt+jy+O5l90+cjDq0jAYvlBwUwnbh2mR9hwDEJCNA== + dependencies: + underscore "1.12.1" + web3-eth-iban "1.3.6" + web3-utils "1.3.6" + +web3-core-method@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.6.tgz#4b0334edd94b03dfec729d113c69a4eb6ebc68ae" + integrity sha512-RyegqVGxn0cyYW5yzAwkPlsSEynkdPiegd7RxgB4ak1eKk2Cv1q2x4C7D2sZjeeCEF+q6fOkVmo2OZNqS2iQxg== + dependencies: + "@ethersproject/transactions" "^5.0.0-beta.135" + underscore "1.12.1" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-utils "1.3.6" + +web3-core-promievent@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.6.tgz#6c27dc79de8f71b74f5d17acaf9aaf593d3cb0c9" + integrity sha512-Z+QzfyYDTXD5wJmZO5wwnRO8bAAHEItT1XNSPVb4J1CToV/I/SbF7CuF8Uzh2jns0Cm1109o666H7StFFvzVKw== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.6.tgz#4fea269fe913fd4fca464b4f7c65cb94857b5b2a" + integrity sha512-2rIaeuqeo7QN1Eex7aXP0ZqeteJEPWXYFS/M3r3LXMiV8R4STQBKE+//dnHJXoo2ctzEB5cgd+7NaJM8S3gPyA== + dependencies: + underscore "1.12.1" + util "^0.12.0" + web3-core-helpers "1.3.6" + web3-providers-http "1.3.6" + web3-providers-ipc "1.3.6" + web3-providers-ws "1.3.6" + +web3-core-subscriptions@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.6.tgz#ee24e7974d1d72ff6c992c599deba4ef9b308415" + integrity sha512-wi9Z9X5X75OKvxAg42GGIf81ttbNR2TxzkAsp1g+nnp5K8mBwgZvXrIsDuj7Z7gx72Y45mWJADCWjk/2vqNu8g== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-core@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.6.tgz#a6a761d1ff2f3ee462b8dab679229d2f8e267504" + integrity sha512-gkLDM4T1Sc0T+HZIwxrNrwPg0IfWI0oABSglP2X5ZbBAYVUeEATA0o92LWV8BeF+okvKXLK1Fek/p6axwM/h3Q== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-requestmanager "1.3.6" + web3-utils "1.3.6" + +web3-eth-abi@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.6.tgz#4272ca48d817aa651bbf97b269f5ff10abc2b8a9" + integrity sha512-Or5cRnZu6WzgScpmbkvC6bfNxR26hqiKK4i8sMPFeTUABQcb/FU3pBj7huBLYbp9dH+P5W79D2MqwbWwjj9DoQ== + dependencies: + "@ethersproject/abi" "5.0.7" + underscore "1.12.1" + web3-utils "1.3.6" + +web3-eth-accounts@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.6.tgz#f9fcb50b28ee58090ab292a10d996155caa2b474" + integrity sha512-Ilr0hG6ONbCdSlVKffasCmNwftD5HsNpwyQASevocIQwHdTlvlwO0tb3oGYuajbKOaDzNTwXfz25bttAEoFCGA== + dependencies: + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-common "^1.3.2" + ethereumjs-tx "^2.1.1" + scrypt-js "^3.0.1" + underscore "1.12.1" + uuid "3.3.2" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-eth-contract@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.6.tgz#cccf4d32dc56917fb6923e778498a9ba2a5ba866" + integrity sha512-8gDaRrLF2HCg+YEZN1ov0zN35vmtPnGf3h1DxmJQK5Wm2lRMLomz9rsWsuvig3UJMHqZAQKD7tOl3ocJocQsmA== + dependencies: + "@types/bn.js" "^4.11.5" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-promievent "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-utils "1.3.6" + +web3-eth-ens@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.6.tgz#0d28c5d4ea7b4462ef6c077545a77956a6cdf175" + integrity sha512-n27HNj7lpSkRxTgSx+Zo7cmKAgyg2ElFilaFlUu/X2CNH23lXfcPm2bWssivH9z0ndhg0OyR4AYFZqPaqDHkJA== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-promievent "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-contract "1.3.6" + web3-utils "1.3.6" + +web3-eth-iban@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.6.tgz#0d6ba21fe78f190af8919e9cd5453882457209e0" + integrity sha512-nfMQaaLA/zsg5W4Oy/EJQbs8rSs1vBAX6b/35xzjYoutXlpHMQadujDx2RerTKhSHqFXSJeQAfE+2f6mdhYkRQ== + dependencies: + bn.js "^4.11.9" + web3-utils "1.3.6" + +web3-eth-personal@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.6.tgz#226137916754c498f0284f22c55924c87a2efcf0" + integrity sha512-pOHU0+/h1RFRYoh1ehYBehRbcKWP4OSzd4F7mDljhHngv6W8ewMHrAN8O1ol9uysN2MuCdRE19qkRg5eNgvzFQ== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-eth@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.6.tgz#2c650893d540a7a0eb1365dd5b2dca24ac919b7c" + integrity sha512-9+rnywRRpyX3C4hfsAQXPQh6vHh9XzQkgLxo3gyeXfbhbShUoq2gFVuy42vsRs//6JlsKdyZS7Z3hHPHz2wreA== + dependencies: + underscore "1.12.1" + web3-core "1.3.6" + web3-core-helpers "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-eth-abi "1.3.6" + web3-eth-accounts "1.3.6" + web3-eth-contract "1.3.6" + web3-eth-ens "1.3.6" + web3-eth-iban "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-utils "1.3.6" + +web3-net@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.6.tgz#a56492e2227475e38db29394f8bac305a2446e41" + integrity sha512-KhzU3wMQY/YYjyMiQzbaLPt2kut88Ncx2iqjy3nw28vRux3gVX0WOCk9EL/KVJBiAA/fK7VklTXvgy9dZnnipw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-utils "1.3.6" + +web3-providers-http@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.6.tgz#36e8724a7424d52827819d53fd75dbf31f5422c2" + integrity sha512-OQkT32O1A06dISIdazpGLveZcOXhEo5cEX6QyiSQkiPk/cjzDrXMw4SKZOGQbbS1+0Vjizm1Hrp7O8Vp2D1M5Q== + dependencies: + web3-core-helpers "1.3.6" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.6.tgz#cef8d12c1ebb47adce5ebf597f553c623362cb4a" + integrity sha512-+TVsSd2sSVvVgHG4s6FXwwYPPT91boKKcRuEFXqEfAbUC5t52XOgmyc2LNiD9LzPhed65FbV4LqICpeYGUvSwA== + dependencies: + oboe "2.1.5" + underscore "1.12.1" + web3-core-helpers "1.3.6" + +web3-providers-ws@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.6.tgz#e1df617bc89d66165abdf2191da0014c505bfaac" + integrity sha512-bk7MnJf5or0Re2zKyhR3L3CjGululLCHXx4vlbc/drnaTARUVvi559OI5uLytc/1k5HKUUyENAxLvetz2G1dnQ== + dependencies: + eventemitter3 "4.0.4" + underscore "1.12.1" + web3-core-helpers "1.3.6" + websocket "^1.0.32" + +web3-shh@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.6.tgz#4e3486c7eca5cbdb87f88910948223a5b7ea6c20" + integrity sha512-9zRo415O0iBslxBnmu9OzYjNErzLnzOsy+IOvSpIreLYbbAw0XkDWxv3SfcpKnTIWIACBR4AYMIxmmyi5iB3jw== + dependencies: + web3-core "1.3.6" + web3-core-method "1.3.6" + web3-core-subscriptions "1.3.6" + web3-net "1.3.6" + +web3-utils@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.6.tgz#390bc9fa3a7179746963cfaca55bb80ac4d8dc10" + integrity sha512-hHatFaQpkQgjGVER17gNx8u1qMyaXFZtM0y0XLGH1bzsjMPlkMPLRcYOrZ00rOPfTEuYFOdrpGOqZXVmGrMZRg== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + underscore "1.12.1" + utf8 "3.0.0" + +web3@1.3.6, web3@^1.0.0-beta.34: + version "1.3.6" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.6.tgz#599425461c3f9a8cbbefa70616438995f4a064cc" + integrity sha512-jEpPhnL6GDteifdVh7ulzlPrtVQeA30V9vnki9liYlUvLV82ZM7BNOQJiuzlDePuE+jZETZSP/0G/JlUVt6pOA== + dependencies: + web3-bzz "1.3.6" + web3-core "1.3.6" + web3-eth "1.3.6" + web3-eth-personal "1.3.6" + web3-net "1.3.6" + web3-shh "1.3.6" + web3-utils "1.3.6" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^3.0.0, yallist@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/solidity/doc/UpgradeContract/ProxyPattern.png b/solidity/doc/UpgradeContract/ProxyPattern.png new file mode 100644 index 00000000..a21e2ed6 Binary files /dev/null and b/solidity/doc/UpgradeContract/ProxyPattern.png differ diff --git a/solidity/doc/UpgradeContract/Readme.md b/solidity/doc/UpgradeContract/Readme.md new file mode 100644 index 00000000..1c3dbc87 --- /dev/null +++ b/solidity/doc/UpgradeContract/Readme.md @@ -0,0 +1,224 @@ +

Contract Upgradeability

+ +### Why do we need to upgrade a contract? + +_____ +In Ethereum, smart contracts, by default, are immutable. When contracts are created and deployed, it cannot be modified. However, it indeed is expect to be able to modify for some cases, i.e. fixing bugs, adding additional features during the development of a project, or just simply change logic of implementations. Thus, contract upgradedability is an essential feature that developers obviously need when developing their project. + +### How contract upgradeability works? + +_____ +Basically, the idea is using a proxy for upgrading contracts. When your smart contract is deployed, it would be splitted into two parts - a `proxy contract` that holds contract's state variables and a second contract that contains logic implementations. When interacting with a contract, users actually interact directly with a wrapper `proxy contract` which is in charge of delegating transaction calls to and from the logic implementation contract. From this design, we can obviously perceive a key concept that is the `proxy contract` must not be altered whereas the logic contract implementation can be replaced or upgraded. The contract's state variables therein are still maintained whereas the wrapper `proxy contract` can point to a different logic implementation in doing so called "contract upgradability" + +![](ProxyPattern.png) + +For more information, you can check this link [here](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies) + +### Upgrading Contracts using the Openzeppelin Upgrades Plugins + +_____ +In this project, we are going to use the OpenZeppelin Upgrades for providing an easy to use, simple, robust, and opt-in upgrading mechanism for our smart contracts. Contracts are going to be deployed with upgradability using Openzeppelin Upgrades Plugins. You will need to use the Upgrade Safe variant of Openzeppelin Contracts which is available as a seperate package called `@openzeppelin/contracts-upgradeable`. This package is likely the same as the structure of the regular Openzeppelin Contracts package, but every file and contract is added the suffix `Upgradeable` + +For more information, please check out these links: [here](https://docs.openzeppelin.com/contracts/3.x/upgradeable) and [here](https://docs.openzeppelin.com/learn/upgrading-smart-contracts) + +### Cautions in Writing Upgradeable Contracts + +_____ + +There are a few rules of thumb to keep in mind when writing/upgrading your Solidity code, especially when working with upgradeable contracts using Openzeppelin Upgrades libraries. + + **DON'T** + +* Do not write any `constructor` in your smart contracts. You can write your Solidity contracts with Openzeppelin Upgrades regularly, except for the constructors. Please check it out for the reasons behind this restriction [here](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#the-constructor-caveat). Please be aware that the restriction affects your contracts, but also the contracts that you import from a library. + +* Do not initialize a value in field declarations. But, it is fine to define a constant state variables. For example: + +```solidity +contract ExampleContract { + uint256 private version = 1; // Don't + uint256 private constant DECIMAL_PRECISION = 10**6; // Fine +} +``` + +* Do not create new instances from your contract code. This creation is handled directly by Solidity and not by Openzeppelin Upgrades. Hence, it would not be upgradeable. For example: + +```solidity +// contracts/MyContract.sol +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MyContract is Initializable { + ERC20 public token; + + function initialize() public initializer { + token = new ERC20("Test", "TST"); // This contract will not be upgradeable + } +} +``` + +* Please be aware that do not use either `selfdestruct` nor `delegatecall` in your contracts. If your contract contains these restrictions, it is likely failed to make your contract upgradaeble. + +* Please be cautious that you cannot change the order of contract's state variables and also their types. +![](Warning.png) + +* Do not introduce a new state variable before existing ones. + + **DO** + +* Only allow Proxy Admin to upgrade contracts. + +* Should be familiar with and have a basic understanding of what a proxy is. + +* Have to know how to write an upgradeable smart contracts. + +* Instead of using `constructors`, your contracts should use `initializer` functions. + +* Initializing values of state variables, when a contract is deployed, can be placed inside the `initializer` functions. Please be aware that the `initializer` function can be called only once when a contract is firstly deployed. + +* Always extend storage (adding more state variables) instead of modifying it, i.e. removing state variables, changing a layout order of state variables. + +* When new state variables are added, make sure that these variables would be added at the end of existing ones. For example: + +```solidity +// Before adding a new state variable +contract ExampleContract { + uint256 private version; + string private network; +} +``` + +```solidity +// After adding a new state variable +contract ExampleContract { + uint256 private version; + string private network; + address owner; +} +``` + +* Logic implementations can be modified without any restrictions which means that you can either modify current implementation to fix bug, remove existing functions, or adding more functions provide additional features of your project at any part of your codes + +For more instructions, please check out this link [here](https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable) + +### How to Deploy and Upgrade Smart Contracts using Truffle and Openzeppelin SDK + +_____ + +``` +Requirement: +- Truffle v5.3.0 (core: 5.3.0) +- Solidity v0.5.16 (solc-js) +- Node v15.12.0 +- Web3.js v1.2. +- @openzeppelin/contracts-upgradeable": "^3.4.1-solc-0.7-2" +- @openzeppelin/truffle-upgrades": "^1.7.0" +``` +**Required Files:** + +> - BSHCoreV1: version 1 of BSHCore contract [BSHCoreV1](BSHCoreV1.sol) +> - BSHCoreV2: version 2 of BSHCore contract [BSHCoreV2](BSHCoreV2.sol) +> - IBSHPeriphery: interface contract of BSHPeriphery [IBSHPerif](IBSHPeriphery.sol) +> - Strings: utility library [Strings](StringsLib.sol) +> - Truffle Configuration: [truffle-config.js](truffle-config.js) +> - NodeJS package JSON: [package.json](package.json) +> - Migration javascript: script to deploy/upgrade contract to local network [script](1_upgradeable.js) + +* Installation and Preparation: +```cmd +npm install --global truffle yarn ganache-cli + +// cd back to folder 'ExampleProject' +yarn + +// Open new command window and run a following command +ganache-cli -l 12000000 --allowUnlimitedContractSize +``` + +* Deploy and Upgrade Contract using Truffle: + +**Note that: This is just an example to show how to deploy and to upgrade a contract using a local network. When deploying and upgrading your contract to the mainnet, please make sure you understand how to do it properly.** + +**1_deploy.js**: a script to deploy example contract to a local network +```javascript +const BSHCoreV1 = artifacts.require("BSHCoreV1"); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +var _uri = 'https://1234.iconee/'; +var _native_coin = 'PARA'; + +module.exports = async function (deployer, network) { + /********************************************************************************************** + Deploy A Contract and the Proxy Contract + - Before running a script, make sure to delete + + a file '.openzeppelin/unknown-1337.json' (if existed) + - Deploy the first part by a following command: `yarn truffle:deploy` + **********************************************************************************************/ + + await deployProxy(BSHCoreV1, + [_uri, _native_coin], // calling initialize() and passing params into initialize() + { deployer } // specify an account to deploy a contract + // if omit, default 'deployer' will be chosen + ); + const version1 = await BSHCoreV1.deployed(); + await version1.register("Coin1"); + await version1.register("Coin2"); +} +``` + +**2_upgrade.js**: a script to upgrade deployed contract +```javascript +const BSHCoreV2 = artifacts.require("BSHCoreV2"); +const ProxyAddr = require('../.openzeppelin/unknown-1337.json'); +const { upgradeProxy } = require('@openzeppelin/truffle-upgrades'); + +var _uri = 'https://1234.iconee/'; +var _native_coin = 'PARA'; + +module.exports = async function (deployer, network) { + /********************************************************************************************** + - Upgrade the contract by a following command: `yarn truffle:upgrade` + **********************************************************************************************/ + + await upgradeProxy(ProxyAddr.proxies[0].address, BSHCoreV2); + const version2 = await BSHCoreV2.deployed(); + // It prints out an array of three: 'PARA', 'Coin1', and 'Coin2' + console.log(await version2.coinNames()); + await version2.register("Coin3"); + // It prints out an array of four: 'PARA', 'Coin1', 'Coin2', 'Coin3' + console.log(await version2.coinNames()); + var coin1_id = await version2.coinId('Coin1'); + var coin3_id = await version2.coinId('Coin3'); + console.log('ID of Coin1 ---> ', web3.utils.BN(coin1_id).toString()); + console.log('ID of Coin3 ---> ', web3.utils.BN(coin3_id).toString()); +}; +``` + +---The first file `1_deploy.js` is to depploy a contract and also a Proxy Contract. +```javascript +const ProxyAddr = require('../.openzeppelin/unknown-1337.json'); +``` +before running a script command +``` +yarn truffle:deploy +``` + After running a command, you would have a result as: + ![](deploy.png) + + There are three contracts being created: + + - Your logic contract implementation **BSHCoreV1** + + - **Proxy Admin** contract which takes responsibility to save and validate an authority to upgrade the Proxy Contract + + - Proxy Contract (**TransparentUpgradeableProxy**) is the contract that all Users would interact with instead of **BSHCoreV1** contract's address + + ---Next, run the second file `2_upgrade.js` to upgrade **BSHCoreV1** contract to **BSHCoreV2**. + +``` +yarn truffle:upgrade +``` +You would, thereafter have a result as: +![](upgrade.png) \ No newline at end of file diff --git a/solidity/doc/UpgradeContract/Warning.png b/solidity/doc/UpgradeContract/Warning.png new file mode 100644 index 00000000..7d1209d8 Binary files /dev/null and b/solidity/doc/UpgradeContract/Warning.png differ diff --git a/solidity/doc/UpgradeContract/deploy.png b/solidity/doc/UpgradeContract/deploy.png new file mode 100644 index 00000000..ccd435f7 Binary files /dev/null and b/solidity/doc/UpgradeContract/deploy.png differ diff --git a/solidity/doc/UpgradeContract/upgrade.png b/solidity/doc/UpgradeContract/upgrade.png new file mode 100644 index 00000000..1aeb533a Binary files /dev/null and b/solidity/doc/UpgradeContract/upgrade.png differ diff --git a/solidity/nativecoinERC20/README.md b/solidity/nativecoinERC20/README.md new file mode 100644 index 00000000..dde536fa --- /dev/null +++ b/solidity/nativecoinERC20/README.md @@ -0,0 +1,33 @@ +## Set up +Node >= 10.x +``` +$ node --version +v15.12.0 +``` +Install tools +``` +$ npm install --global yarn truffle@5.3.0 +``` +Install dependencies +``` +$ yarn +``` + +## Test +1. Run in a background process or seperate terminal window +``` +$ docker run --rm -d -p 9933:9933 -p 9944:9944 purestake/moonbeam:v0.9.2 --dev --ws-external --rpc-external +``` +2. Compile contracts +``` +$ yarn contract:compile +``` +3. Run unit and integration test +``` +$ yarn test +``` +- Run specific test +``` +$ yarn test:unit +$ yarn test:integration +``` diff --git a/solidity/nativecoinERC20/contracts/BSHCore.sol b/solidity/nativecoinERC20/contracts/BSHCore.sol new file mode 100644 index 00000000..f9685612 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/BSHCore.sol @@ -0,0 +1,541 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./interfaces/IBSHPeriphery.sol"; +import "./interfaces/IBSHCore.sol"; +import "./libraries/String.sol"; +import "./libraries/Types.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; + +/** + @title BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following contract can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC20 version of another native coin like ICX +*/ +contract BSHCore is + Initializable, + IBSHCore, + ERC20Upgradeable, + ReentrancyGuardUpgradeable +{ + using SafeMathUpgradeable for uint256; + using String for string; + event SetOwnership(address indexed promoter, address indexed newOwner); + event RemoveOwnership(address indexed remover, address indexed formerOwner); + + modifier onlyOwner() { + require(owners[msg.sender] == true, "Unauthorized"); + _; + } + + modifier onlyBSHPeriphery() { + require(msg.sender == address(bshPeriphery), "Unauthorized"); + _; + } + + uint256 private constant FEE_DENOMINATOR = 10**4; + uint256 public feeNumerator; + uint256 public fixedFee; + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + + IBSHPeriphery internal bshPeriphery; + + address[] private listOfOwners; + uint256[] private chargedAmounts; // a list of amounts have been charged so far (use this when Fee Gathering occurs) + string[] internal coinsName; // a string array stores names of supported coins + string[] private chargedCoins; // a list of coins' names have been charged so far (use this when Fee Gathering occurs) + + mapping(address => bool) private owners; + mapping(string => uint256) internal aggregationFee; // storing Aggregation Fee in state mapping variable. + mapping(address => mapping(string => Types.Balance)) internal balances; + mapping(string => uint256) private coins; // a list of all supported coins + + function initialize( + string calldata _nativeCoinName, + uint256 _feeNumerator, + uint256 _fixedFee, + string calldata _tokenName, + string calldata _tokenSymbol, + uint256 _initialSupply + ) public initializer { + __ERC20_init(_tokenName, _tokenSymbol); + owners[msg.sender] = true; + listOfOwners.push(msg.sender); + emit SetOwnership(address(0), msg.sender); + + coins[_nativeCoinName] = 0; + coins[_tokenName] = 1; + feeNumerator = _feeNumerator; + fixedFee = _fixedFee; + coinsName.push(_nativeCoinName); + coinsName.push(_tokenName); + _mint(msg.sender, _initialSupply); + } + + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external override onlyOwner { + require(owners[_owner] == false, "ExistedOwner"); + owners[_owner] = true; + listOfOwners.push(_owner); + emit SetOwnership(msg.sender, _owner); + } + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external override onlyOwner { + require(listOfOwners.length > 1, "Unable to remove last Owner"); + require(owners[_owner] == true, "Removing Owner not found"); + delete owners[_owner]; + _remove(_owner); + emit RemoveOwnership(msg.sender, _owner); + } + + function _remove(address _addr) internal { + for (uint256 i = 0; i < listOfOwners.length; i++) + if (listOfOwners[i] == _addr) { + listOfOwners[i] = listOfOwners[listOfOwners.length - 1]; + listOfOwners.pop(); + break; + } + } + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view override returns (bool) { + return owners[_owner]; + } + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + function getOwners() external view override returns (address[] memory) { + return listOfOwners; + } + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) + external + override + onlyOwner + { + require(_bshPeriphery != address(0), "InvalidSetting"); + if (address(bshPeriphery) != address(0)) { + require( + bshPeriphery.hasPendingRequest() == false, + "HasPendingRequest" + ); + } + bshPeriphery = IBSHPeriphery(_bshPeriphery); + } + + /** + @notice set fee ratio. + @dev Caller must be an Owner of this contract + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external override onlyOwner { + require(_feeNumerator <= FEE_DENOMINATOR, "InvalidSetting"); + feeNumerator = _feeNumerator; + } + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external override onlyOwner { + require(_fixedFee > 0, "InvalidSetting"); + fixedFee = _fixedFee; + } + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external override onlyOwner { + require(coins[_name] == 0, "ExistToken"); + coins[_name] = uint256(keccak256(abi.encodePacked(_name))); + coinsName.push(_name); + } + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() + external + view + override + returns (string[] memory _names) + { + return coinsName; + } + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + override + returns (bool _valid) + { + return (coins[_coinName] != 0 || _coinName.compareTo(coinsName[0])); + } + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + override + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ) + { + if (_coinName.compareTo(coinsName[0])) { + return ( + address(_owner).balance, + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + return ( + this.balanceOf(_owner), + balances[_owner][_coinName].lockedBalance, + balances[_owner][_coinName].refundableBalance + ); + } + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + override + returns (Types.Asset[] memory _accumulatedFees) + { + _accumulatedFees = new Types.Asset[](coinsName.length); + for (uint256 i = 0; i < coinsName.length; i++) { + _accumulatedFees[i] = ( + Types.Asset(coinsName[i], aggregationFee[coinsName[i]]) + ); + } + return _accumulatedFees; + } + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable override { + // Aggregation Fee will be charged on BSH Contract + // A new charging fee has been proposed. `fixedFee` is introduced + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + // If msg.value less than _chargeAmt, it likely fails when calculating + // _amount = _value - _chargeAmt + uint256 _chargeAmt = msg + .value + .mul(feeNumerator) + .div(FEE_DENOMINATOR) + .add(fixedFee); + + // @dev msg.value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage( + msg.sender, + _to, + coinsName[0], + msg.value, + _chargeAmt + ); + } + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer from a Requester (include fee) + @param _to Target BTP address. + */ + function transferWrappedCoin( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external override { + //TODO: check how to keep method name with overloading + require(this.isValidCoin(_coinName), "UnregisterCoin"); + // _chargeAmt = fixedFee + msg.value * feeNumerator / FEE_DENOMINATOR + // Thus, it's likely that _chargeAmt is always greater than 0 + // require(_chargeAmt > 0) can be omitted + // If _value less than _chargeAmt, it likely fails when calculating + // _amount = _value - _chargeAmt + uint256 _chargeAmt = _value.mul(feeNumerator).div(FEE_DENOMINATOR).add( + fixedFee + ); + // Transfer and Lock Token processes: + // BSHCore contract calls transferFrom() to transfer the Token from Caller's account (msg.sender) to this Contract + // Before that, Caller must approve (ERC20's approve() method) to approve this BSH contract to act as Operator for caller funds + // If this requirement is failed, a transaction is reverted. + // After transferring token, BSHCore contract updates Caller's locked balance + // as a record of pending transfer transaction + // When a transaction is completed without any error on another chain, + // Locked Token amount (bind to an address of caller) will be reset/subtract, + // then emit a successful TransferEnd event as a notification + // Otherwise, the locked amount will also be updated + // but BSHCore contract will issue a refund to Caller before emitting an error TransferEnd event + this.transferFrom(msg.sender, address(this), _value); + // @dev _value is an amount request to transfer (include fee) + // Later on, it will be calculated a true amount that should be received at a destination + _sendServiceMessage(msg.sender, _to, _coinName, _value, _chargeAmt); + } + + /** + @notice This private function handles overlapping procedure before sending a service message to BSHPeriphery + @param _from An address of a Requester + @param _to BTP address of of Receiver on another chain + @param _coinName A given name of a requested coin + @param _value A requested amount to transfer from a Requester (include fee) + @param _chargeAmt An amount being charged for this request + */ + function _sendServiceMessage( + address _from, + string calldata _to, + string memory _coinName, + uint256 _value, + uint256 _chargeAmt + ) private { + // Lock this requested _value as a record of a pending transferring transaction + // @dev `_value` is a requested amount to transfer, from a Requester, including charged fee + // The true amount to receive at a destination receiver is calculated by + // _amounts[0] = _value.sub(_chargeAmt); + lockBalance(_from, _coinName, _value); + string[] memory _coins = new string[](1); + _coins[0] = _coinName; + uint256[] memory _amounts = new uint256[](1); + _amounts[0] = _value.sub(_chargeAmt); + uint256[] memory _fees = new uint256[](1); + _fees[0] = _chargeAmt; + + // @dev `_amounts` is a true amount to receive at a destination after deducting a charged fee + bshPeriphery.sendServiceMessage(_from, _to, _coins, _amounts, _fees); + } + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) + external + override + nonReentrant + { + require( + balances[msg.sender][_coinName].refundableBalance >= _value, + "Imbalance" + ); + + balances[msg.sender][_coinName].refundableBalance = balances[ + msg.sender + ][_coinName].refundableBalance.sub(_value); + + this.refund(msg.sender, _coinName, _value); + } + + // Solidity does not allow using try_catch with interal/private function + // Thus, this function would be set as 'external` + // But, it has restriction. It should be called by this contract only + // In addition, there are only two functions calling this refund() + // + handleRequestService(): this function only called by BSHPeriphery + // + reclaim(): this function can be called by ANY + // In case of reentrancy attacks, the chance happenning on BSHPeriphery + // since it requires a request from BMC which requires verification fron BMV + // reclaim() has higher chance to have reentrancy attacks. + // So, it must be prevented by adding 'nonReentrant' + function refund( + address _to, + string calldata _coinName, + uint256 _value + ) external { + require(msg.sender == address(this), "Unauthorized"); + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + this.transferFrom(address(this), _to, _value); + } + } + + function paymentTransfer(address payable _to, uint256 _amount) private { + (bool sent, ) = _to.call{value: _amount}(""); + require(sent, "Payment transfer failed"); + } + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external override onlyBSHPeriphery { + uint256 _id = coins[_coinName]; + if (_id == 0) { + paymentTransfer(payable(_to), _value); + } else { + _mint(_to, _value); + } + } + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external override onlyBSHPeriphery { + // Fee Gathering and Transfer Coin Request use the same method + // and both have the same response + // In case of Fee Gathering's response, `_requester` is this contract's address + // Thus, check that first + // -- If `_requester` is this contract's address, then check whethere response's code is RC_ERR + // In case of RC_ERR, adding back charged fees to `aggregationFee` state variable + // In case of RC_OK, ignore and return + // -- Otherwise, handle service's response as normal + if (_requester == address(this)) { + if (_rspCode == RC_ERR) { + aggregationFee[_coinName] = aggregationFee[_coinName].add( + _value + ); + } + return; + } + uint256 _amount = _value.add(_fee); + balances[_requester][_coinName].lockedBalance = balances[_requester][ + _coinName + ].lockedBalance.sub(_amount); + + // A new implementation has been proposed to prevent spam attacks + // In receiving error response, BSHCore refunds `_value`, not including `_fee`, back to Requestor + if (_rspCode == RC_ERR) { + try this.refund(_requester, _coinName, _value) {} catch { + balances[_requester][_coinName].refundableBalance = balances[ + _requester + ][_coinName].refundableBalance.add(_value); + } + } else if (_rspCode == RC_OK) { + uint256 _id = coins[_coinName]; + if (_id != 0) { + _burn(address(this), _value); + } + } + aggregationFee[_coinName] = aggregationFee[_coinName].add(_fee); + } + + /** + @notice Handle a request of Fee Gathering + Usage: Copy all charged fees to an array + @dev Caller must be an BSHPeriphery contract + */ + function transferFees(string calldata _fa) + external + override + onlyBSHPeriphery + { + // @dev Due to uncertainty in identifying a size of returning memory array + // and Solidity does not allow to use 'push' with memory array (only storage) + // thus, must use 'temp' storage state variable + for (uint256 i = 0; i < coinsName.length; i++) { + if (aggregationFee[coinsName[i]] != 0) { + chargedCoins.push(coinsName[i]); + chargedAmounts.push(aggregationFee[coinsName[i]]); + delete aggregationFee[coinsName[i]]; + } + } + bshPeriphery.sendServiceMessage( + address(this), + _fa, + chargedCoins, + chargedAmounts, + new uint256[](chargedCoins.length) // chargedFees is an array of 0 since this is a fee gathering request + ); + delete chargedCoins; + delete chargedAmounts; + } + + function lockBalance( + address _to, + string memory _coinName, + uint256 _value + ) private { + balances[_to][_coinName].lockedBalance = balances[_to][_coinName] + .lockedBalance + .add(_value); + } +} \ No newline at end of file diff --git a/solidity/nativecoinERC20/contracts/BSHPeriphery.sol b/solidity/nativecoinERC20/contracts/BSHPeriphery.sol new file mode 100644 index 00000000..78639287 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/BSHPeriphery.sol @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./interfaces/IBSHPeriphery.sol"; +import "./interfaces/IBSHCore.sol"; +import "./interfaces/IBMCPeriphery.sol"; +import "./libraries/Types.sol"; +import "./libraries/RLPEncodeStruct.sol"; +import "./libraries/RLPDecodeStruct.sol"; +import "./libraries/ParseAddress.sol"; +import "./libraries/String.sol"; +import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +/** + @title BSHPeriphery contract + @dev This contract is used to handle communications among BMCService and BSHCore contract + @dev OwnerUpgradeable has been removed. This contract does not have its own Owners + Instead, BSHCore manages ownership roles. + Thus, BSHPeriphery should call bshCore.isOwner() and pass an address for verification + in case of implementing restrictions, if needed, in the future. +*/ +contract BSHPeriphery is Initializable, IBSHPeriphery { + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.Response; + using RLPDecodeStruct for bytes; + using SafeMathUpgradeable for uint256; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + using String for uint256; + + /** @notice Sends a receipt to user + The `_from` sender + The `_to` receiver. + The `_sn` sequence number of service message. + The `_assetDetails` a list of `_coinName` and `_value` + */ + event TransferStart( + address indexed _from, + string _to, + uint256 _sn, + Types.AssetTransferDetail[] _assetDetails + ); + + /** @notice Sends a final notification to a user + The `_from` sender + The `_sn` sequence number of service message. + The `_code` response code, i.e. RC_OK = 0, RC_ERR = 1 + The `_response` message of response if error + */ + event TransferEnd( + address indexed _from, + uint256 _sn, + uint256 _code, + string _response + ); + + /** @notice Notify that BSH contract has received unknown response + The `_from` sender + The `_sn` sequence number of service message + */ + event UnknownResponse(string _from, uint256 _sn); + + IBMCPeriphery private bmc; + IBSHCore internal bshCore; + mapping(uint256 => Types.PendingTransferCoin) public requests; // a list of transferring requests + string public serviceName; // BSH Service Name + + uint256 private constant RC_OK = 0; + uint256 private constant RC_ERR = 1; + uint256 private serialNo; // a counter of sequence number of service message + uint256 private numOfPendingRequests; + + modifier onlyBMC { + require(msg.sender == address(bmc), "Unauthorized"); + _; + } + + function initialize( + address _bmc, + address _bshCore, + string memory _serviceName + ) public initializer { + bmc = IBMCPeriphery(_bmc); + bshCore = IBSHCore(_bshCore); + serviceName = _serviceName; + } + + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view override returns (bool) { + return numOfPendingRequests != 0; + } + + function sendServiceMessage( + address _from, + string memory _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external override { + // Send Service Message to BMC + // If '_to' address is an invalid BTP Address format + // VM throws an error and revert(). Thus, it does not need + // a try_catch at this point + (string memory _toNetwork, string memory _toAddress) = + _to.splitBTPAddress(); + Types.Asset[] memory _assets = new Types.Asset[](_coinNames.length); + Types.AssetTransferDetail[] memory _assetDetails = + new Types.AssetTransferDetail[](_coinNames.length); + for (uint256 i = 0; i < _coinNames.length; i++) { + _assets[i] = Types.Asset(_coinNames[i], _values[i]); + _assetDetails[i] = Types.AssetTransferDetail( + _coinNames[i], + _values[i], + _fees[i] + ); + } + + serialNo++; + + // Because `stack is too deep`, must create `_strFrom` to waive this error + // `_strFrom` is a string type of an address `_from` + string memory _strFrom = _from.toString(); + bmc.sendMessage( + _toNetwork, + serviceName, + serialNo, + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types + .TransferCoin(_strFrom, _toAddress, _assets) + .encodeTransferCoinMsg() + ) + .encodeServiceMessage() + ); + // Push pending tx into Record list + requests[serialNo] = Types.PendingTransferCoin( + _strFrom, + _to, + _coinNames, + _values, + _fees + ); + numOfPendingRequests++; + emit TransferStart(_from, _to, serialNo, _assetDetails); + } + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + Types.ServiceMessage memory _sm = _msg.decodeServiceMessage(); + string memory errMsg; + + if (_sm.serviceType == Types.ServiceType.REQUEST_COIN_TRANSFER) { + Types.TransferCoin memory _tc = _sm.data.decodeTransferCoinMsg(); + // checking receiving address whether is a valid address + // revert() if not a valid one + try this.checkParseAddress(_tc.to) { + try this.handleRequestService(_tc.to, _tc.assets) { + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + "", + RC_OK + ); + return; + } catch Error(string memory _err) { + errMsg = _err; + } + } catch { + errMsg = "InvalidAddress"; + } + sendResponseMessage( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _from, + _sn, + errMsg, + RC_ERR + ); + } else if ( + _sm.serviceType == Types.ServiceType.REPONSE_HANDLE_SERVICE + ) { + // Check whether '_sn' is pending state + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + Types.Response memory response = _sm.data.decodeResponse(); + // @dev Not implement try_catch at this point + // + If RESPONSE_REQUEST_SERVICE: + // If RC_ERR, BSHCore proceeds a refund. If a refund is failed, BSHCore issues refundable Balance + // If RC_OK: + // - requested coin = native -> update aggregation fee (likely no issue) + // - requested coin = wrapped coin -> BSHCore calls itself to burn its tokens and update aggregation fee (likely no issue) + // The only issue, which might happen, is BSHCore's token balance lower than burning amount + // If so, there might be something went wrong before + // + If RESPONSE_FEE_GATHERING + // If RC_ERR, BSHCore saves charged fees back to `aggregationFee` state mapping variable + // If RC_OK: do nothing + handleResponseService(_sn, response.code, response.message); + } else if (_sm.serviceType == Types.ServiceType.UNKNOWN_TYPE) { + emit UnknownResponse(_from, _sn); + } else { + // If none of those types above, BSH responds a message of RES_UNKNOWN_TYPE + sendResponseMessage( + Types.ServiceType.UNKNOWN_TYPE, + _from, + _sn, + "Unknown", + RC_ERR + ); + } + } + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata, /* _src */ + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override onlyBMC { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + require(bytes(requests[_sn].from).length != 0, "InvalidSN"); + string memory _emitMsg = + string("errCode: ") + .concat(_code.toString()) + .concat(", errMsg: ") + .concat(_msg); + handleResponseService(_sn, RC_ERR, _emitMsg); + } + + function handleResponseService( + uint256 _sn, + uint256 _code, + string memory _msg + ) private { + address _caller = requests[_sn].from.parseAddress(); + uint256 loop = requests[_sn].coinNames.length; + for (uint256 i = 0; i < loop; i++) { + bshCore.handleResponseService( + _caller, + requests[_sn].coinNames[i], + requests[_sn].amounts[i], + requests[_sn].fees[i], + _code + ); + } + delete requests[_sn]; + numOfPendingRequests--; + emit TransferEnd(_caller, _sn, _code, _msg); + } + + /** + @notice Handle a list of minting/transferring coins/tokens + @dev Caller must be BMC contract only + @param _to An address to receive coins/tokens + @param _assets A list of requested coin respectively with an amount + */ + function handleRequestService( + string memory _to, + Types.Asset[] memory _assets + ) external { + require(msg.sender == address(this), "Unauthorized"); + for (uint256 i = 0; i < _assets.length; i++) { + require( + bshCore.isValidCoin(_assets[i].coinName) == true, + "UnregisteredCoin" + ); + // @dev There might be many errors generating by BSHCore contract + // which includes also low-level error + // Thus, must use try_catch at this point so that it can return an expected response + try + bshCore.mint( + _to.parseAddress(), + _assets[i].coinName, + _assets[i].value + ) + {} catch { + revert("TransferFailed"); + } + } + } + + function sendResponseMessage( + Types.ServiceType _serviceType, + string memory _to, + uint256 _sn, + string memory _msg, + uint256 _code + ) private { + bmc.sendMessage( + _to, + serviceName, + _sn, + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage() + ); + } + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override + onlyBMC + { + require(_svc.compareTo(serviceName) == true, "InvalidSvc"); + // If adress of Fee Aggregator (_fa) is invalid BTP address format + // revert(). Then, BMC will catch this error + // @dev this part simply check whether `_fa` is splittable (`prefix` + `_net` + `dstAddr`) + // checking validity of `_net` and `dstAddr` does not belong to BSHPeriphery's scope + _fa.splitBTPAddress(); + bshCore.transferFees(_fa); + } + + // @dev Solidity does not allow to use try_catch with internal function + // Thus, this is a work-around solution + // Since this function is basically checking whether a string address + // can be parsed to address type. Hence, it would not have any restrictions + function checkParseAddress(string calldata _to) external pure { + _to.parseAddress(); + } +} diff --git a/solidity/nativecoinERC20/contracts/interfaces/IBMCPeriphery.sol b/solidity/nativecoinERC20/contracts/interfaces/IBMCPeriphery.sol new file mode 100644 index 00000000..78787903 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/interfaces/IBMCPeriphery.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; + +interface IBMCPeriphery { + /** + @notice Get BMC BTP address + */ + function getBmcBtpAddress() external view returns (string memory); + + /** + @notice Verify and decode RelayMessage with BMV, and dispatch BTP Messages to registered BSHs + @dev Caller must be a registered relayer. + @param _prev BTP Address of the BMC generates the message + @param _msg base64 encoded string of serialized bytes of Relay Message refer RelayMessage structure + */ + function handleRelayMessage(string calldata _prev, string calldata _msg) + external; + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string calldata _to, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC + @return _linkStats The link status + */ + function getStatus(string calldata _link) + external + view + returns (Types.LinkStats memory _linkStats); +} diff --git a/solidity/nativecoinERC20/contracts/interfaces/IBMV.sol b/solidity/nativecoinERC20/contracts/interfaces/IBMV.sol new file mode 100644 index 00000000..e370af4e --- /dev/null +++ b/solidity/nativecoinERC20/contracts/interfaces/IBMV.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBMV { + /** + @return base64EncodedMTA Base64 encode of Merkle Tree + */ + function getMTA() external view returns (string memory base64EncodedMTA); + + /** + @return addr connected BMC address + */ + function getConnectedBMC() external view returns (address addr); + + /** + @return net network address of the blockchain + */ + function getNetAddress() external view returns (string memory net); + + /** + @return serializedHash hash of RLP encode from given list of validators + @return addresses list of validators' addresses + */ + function getValidators() + external + view + returns (bytes32 serializedHash, address[] memory addresses); + + /** + @notice Used by the relay to resolve next BTP Message to send. + Called by BMC. + @return height height of MerkleTreeAccumulator + @return offset offset of MerkleTreeAccumulator + @return lastHeight block height of last relayed BTP Message + */ + function getStatus() + external + view + returns ( + uint256 height, + uint256 offset, + uint256 lastHeight + ); + + /** + @notice Decodes Relay Messages and process BTP Messages. + If there is an error, then it sends a BTP Message containing the Error Message. + BTP Messages with old sequence numbers are ignored. A BTP Message contains future sequence number will fail. + @param _bmc BTP Address of the BMC handling the message + @param _prev BTP Address of the previous BMC + @param _seq next sequence number to get a message + @param _msg serialized bytes of Relay Message + @return serializedMessages List of serialized bytes of a BTP Message + */ + function handleRelayMessage( + string calldata _bmc, + string calldata _prev, + uint256 _seq, + string calldata _msg + ) external returns (bytes[] memory serializedMessages); +} diff --git a/solidity/nativecoinERC20/contracts/interfaces/IBSH.sol b/solidity/nativecoinERC20/contracts/interfaces/IBSH.sol new file mode 100644 index 00000000..26769f6e --- /dev/null +++ b/solidity/nativecoinERC20/contracts/interfaces/IBSH.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +interface IBSH { + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external; + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSH contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external; + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external; +} diff --git a/solidity/nativecoinERC20/contracts/interfaces/IBSHCore.sol b/solidity/nativecoinERC20/contracts/interfaces/IBSHCore.sol new file mode 100644 index 00000000..70da97f6 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/interfaces/IBSHCore.sol @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/Types.sol"; + +/** + @title Interface of BSHCore contract + @dev This contract is used to handle coin transferring service + Note: The coin of following interface can be: + Native Coin : The native coin of this chain + Wrapped Native Coin : A tokenized ERC1155 version of another native coin like ICX +*/ +interface IBSHCore{ + /** + @notice Adding another Onwer. + @dev Caller must be an Onwer of BTP network + @param _owner Address of a new Onwer. + */ + function addOwner(address _owner) external; + + /** + @notice Removing an existing Owner. + @dev Caller must be an Owner of BTP network + @dev If only one Owner left, unable to remove the last Owner + @param _owner Address of an Owner to be removed. + */ + function removeOwner(address _owner) external; + + /** + @notice Checking whether one specific address has Owner role. + @dev Caller can be ANY + @param _owner Address needs to verify. + */ + function isOwner(address _owner) external view returns (bool); + + /** + @notice Get a list of current Owners + @dev Caller can be ANY + @return An array of addresses of current Owners + */ + + function getOwners() external view returns (address[] memory); + + /** + @notice update BSH Periphery address. + @dev Caller must be an Owner of this contract + _bshPeriphery Must be different with the existing one. + @param _bshPeriphery BSHPeriphery contract address. + */ + function updateBSHPeriphery(address _bshPeriphery) external; + + + /** + @notice set fee ratio. + @dev Caller must be an Owner of this contract + The transfer fee is calculated by feeNumerator/FEE_DEMONINATOR. + The feeNumetator should be less than FEE_DEMONINATOR + _feeNumerator is set to `10` in construction by default, which means the default fee ratio is 0.1%. + @param _feeNumerator the fee numerator + */ + function setFeeRatio(uint256 _feeNumerator) external; + + /** + @notice set Fixed Fee. + @dev Caller must be an Owner + @param _fixedFee A new value of Fixed Fee + */ + function setFixedFee(uint256 _fixedFee) external; + + /** + @notice Registers a wrapped coin and id number of a supporting coin. + @dev Caller must be an Owner of this contract + _name Must be different with the native coin name. + @dev '_id' of a wrapped coin is generated by using keccak256 + '_id' = 0 is fixed to assign to native coin + @param _name Coin name. + */ + function register(string calldata _name) external; + + /** + @notice Return all supported coins names + @dev + @return _names An array of strings. + */ + function coinNames() external view returns (string[] memory _names); + + /** + @notice Check Validity of a _coinName + @dev Call by BSHPeriphery contract to validate a requested _coinName + @return _valid true of false + */ + function isValidCoin(string calldata _coinName) + external + view + returns (bool _valid); + + /** + @notice Return a usable/locked/refundable balance of an account based on coinName. + @return _usableBalance the balance that users are holding. + @return _lockedBalance when users transfer the coin, + it will be locked until getting the Service Message Response. + @return _refundableBalance refundable balance is the balance that will be refunded to users. + */ + function getBalanceOf(address _owner, string memory _coinName) + external + view + returns ( + uint256 _usableBalance, + uint256 _lockedBalance, + uint256 _refundableBalance + ); + + /** + @notice Return a list accumulated Fees. + @dev only return the asset that has Asset's value greater than 0 + @return _accumulatedFees An array of Asset + */ + function getAccumulatedFees() + external + view + returns (Types.Asset[] memory _accumulatedFees); + + /** + @notice Allow users to deposit `msg.value` native coin into a BSHCore contract. + @dev MUST specify msg.value + @param _to An address that a user expects to receive an amount of tokens. + */ + function transferNativeCoin(string calldata _to) external payable; + + /** + @notice Allow users to deposit an amount of wrapped native coin `_coinName` from the `msg.sender` address into the BSHCore contract. + @dev Caller must set to approve that the wrapped tokens can be transferred out of the `msg.sender` account by BSHCore contract. + It MUST revert if the balance of the holder for token `_coinName` is lower than the `_value` sent. + @param _coinName A given name of a wrapped coin + @param _value An amount request to transfer. + @param _to Target BTP address. + */ + function transferWrappedCoin( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external; + + /** + @notice Reclaim the token's refundable balance by an owner. + @dev Caller must be an owner of coin + The amount to claim must be smaller or equal than refundable balance + @param _coinName A given name of coin + @param _value An amount of re-claiming tokens + */ + function reclaim(string calldata _coinName, uint256 _value) external; + + /** + @notice mint the wrapped coin. + @dev Caller must be an BSHPeriphery contract + Invalid _coinName will have an _id = 0. However, _id = 0 is also dedicated to Native Coin + Thus, BSHPeriphery will check a validity of a requested _coinName before calling + for the _coinName indicates with id = 0, it should send the Native Coin (Example: PRA) to user account + @param _to the account receive the minted coin + @param _coinName coin name + @param _value the minted amount + */ + function mint( + address _to, + string calldata _coinName, + uint256 _value + ) external; + + /** + @notice Handle a request of Fee Gathering + @dev Caller must be an BSHPeriphery contract + @param _fa BTP Address of Fee Aggregator + */ + function transferFees(string calldata _fa) external; + + /** + @notice Handle a response of a requested service + @dev Caller must be an BSHPeriphery contract + @param _requester An address of originator of a requested service + @param _coinName A name of requested coin + @param _value An amount to receive on a destination chain + @param _fee An amount of charged fee + */ + function handleResponseService( + address _requester, + string calldata _coinName, + uint256 _value, + uint256 _fee, + uint256 _rspCode + ) external; +} diff --git a/solidity/nativecoinERC20/contracts/interfaces/IBSHPeriphery.sol b/solidity/nativecoinERC20/contracts/interfaces/IBSHPeriphery.sol new file mode 100644 index 00000000..7fbe4534 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/interfaces/IBSHPeriphery.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./IBSH.sol"; + +/** + @title Interface of BSHPeriphery contract + @dev This contract is used to handle communications among BMCService and BSHCore contract +*/ +interface IBSHPeriphery is IBSH { + /** + @notice Check whether BSHPeriphery has any pending transferring requests + @return true or false + */ + function hasPendingRequest() external view returns (bool); + + /** + @notice Send Service Message from BSHCore contract to BMCService contract + @dev Caller must be BSHCore only + @param _to A network address of destination chain + @param _coinNames A list of coin name that are requested to transfer + @param _values A list of an amount to receive at destination chain respectively with its coin name + @param _fees A list of an amount of charging fee respectively with its coin name + */ + function sendServiceMessage( + address _from, + string calldata _to, + string[] memory _coinNames, + uint256[] memory _values, + uint256[] memory _fees + ) external; + + /** + @notice BSH handle BTP Message from BMC contract + @dev Caller must be BMC contract only + @param _from An originated network address of a request + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _msg An RLP message of a service request/service response + */ + function handleBTPMessage( + string calldata _from, + string calldata _svc, + uint256 _sn, + bytes calldata _msg + ) external override; + + /** + @notice BSH handle BTP Error from BMC contract + @dev Caller must be BMC contract only + @param _svc A service name of BSHPeriphery contract + @param _sn A serial number of a service request + @param _code A response code of a message (RC_OK / RC_ERR) + @param _msg A response message + */ + function handleBTPError( + string calldata _src, + string calldata _svc, + uint256 _sn, + uint256 _code, + string calldata _msg + ) external override; + + /** + @notice BSH handle Gather Fee Message request from BMC contract + @dev Caller must be BMC contract only + @param _fa A BTP address of fee aggregator + @param _svc A name of the service + */ + function handleFeeGathering(string calldata _fa, string calldata _svc) + external + override; +} diff --git a/solidity/nativecoinERC20/contracts/libraries/DecodeBase64.sol b/solidity/nativecoinERC20/contracts/libraries/DecodeBase64.sol new file mode 100644 index 00000000..283540d5 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/DecodeBase64.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title DecodeBase64 + * @dev A simple Base64 decoding library. + * @author Quang Tran + */ + +library DecodeBase64 { + function decode(string memory _str) internal pure returns (bytes memory) { + bytes memory _bs = bytes(_str); + uint256 remove = 0; + if (_bs[_bs.length - 1] == "=" && _bs[_bs.length - 2] == "=") { + remove += 2; + } else if (_bs[_bs.length - 1] == "=") { + remove++; + } + uint256 resultLength = (_bs.length / 4) * 3 - remove; + bytes memory result = new bytes(resultLength); + + uint256 i = 0; + uint256 j = 0; + for (; i + 4 < _bs.length; i += 4) { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[i]), + mapBase64Char(_bs[i + 1]), + mapBase64Char(_bs[i + 2]), + mapBase64Char(_bs[i + 3]) + ); + j += 3; + } + if (remove == 1) { + (result[j], result[j + 1], ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + 0 + ); + } else if (remove == 2) { + (result[j], , ) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + 0, + 0 + ); + } else { + (result[j], result[j + 1], result[j + 2]) = decode4( + mapBase64Char(_bs[_bs.length - 4]), + mapBase64Char(_bs[_bs.length - 3]), + mapBase64Char(_bs[_bs.length - 2]), + mapBase64Char(_bs[_bs.length - 1]) + ); + } + return result; + } + + function mapBase64Char(bytes1 _char) private pure returns (uint8) { + // solhint-disable-next-line + uint8 A = 0; + uint8 a = 26; + uint8 zero = 52; + if (uint8(_char) == 45) { + return 62; + } else if (uint8(_char) == 95) { + return 63; + } else if (uint8(_char) >= 48 && uint8(_char) <= 57) { + return zero + (uint8(_char) - 48); + } else if (uint8(_char) >= 65 && uint8(_char) <= 90) { + return A + (uint8(_char) - 65); + } else if (uint8(_char) >= 97 && uint8(_char) <= 122) { + return a + (uint8(_char) - 97); + } + return 0; + } + + function decode4( + uint256 a0, + uint256 a1, + uint256 a2, + uint256 a3 + ) + private + pure + returns ( + bytes1, + bytes1, + bytes1 + ) + { + uint256 n = + ((a0 & 63) << 18) | + ((a1 & 63) << 12) | + ((a2 & 63) << 6) | + (a3 & 63); + uint256 b0 = (n >> 16) & 255; + uint256 b1 = (n >> 8) & 255; + uint256 b2 = (n) & 255; + return (bytes1(uint8(b0)), bytes1(uint8(b1)), bytes1(uint8(b2))); + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/EncodeBase64.sol b/solidity/nativecoinERC20/contracts/libraries/EncodeBase64.sol new file mode 100644 index 00000000..5a09250f --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/EncodeBase64.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title EncodeBase64 + * @dev A simple Base64 encoding library. + * The original library was modified to make it work for our case + * For more info, please check the link: https://github.com/OpenZeppelin/solidity-jwt.git + */ +library EncodeBase64 { + bytes private constant BASE64URLCHARS = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + + function encode(bytes memory _bs) internal pure returns (string memory) { + // bytes memory _bs = bytes(_str); + uint256 rem = _bs.length % 3; + uint256 resLength; + if (_bs.length % 3 != 0) { + resLength = (_bs.length / 3) * 4 + 4; + } else { + resLength = (_bs.length / 3) * 4; + } + + // uint256 res_length = (_bs.length + 2) / 3 * 4 - ((3 - rem) % 3); + bytes memory res = new bytes(resLength); + uint256 i = 0; + uint256 j = 0; + + for (; i + 3 <= _bs.length; i += 3) { + (res[j], res[j + 1], res[j + 2], res[j + 3]) = encode3( + uint8(_bs[i]), + uint8(_bs[i + 1]), + uint8(_bs[i + 2]) + ); + j += 4; + } + + if (rem != 0) { + uint8 la0 = uint8(_bs[_bs.length - rem]); + uint8 la1 = 0; + if (rem == 2) { + la1 = uint8(_bs[_bs.length - 1]); + } + (bytes1 b0, bytes1 b1, bytes1 b2, ) = encode3(la0, la1, 0); + res[j] = b0; + res[j + 1] = b1; + if (rem == 1) { + res[j + 2] = "="; + res[j + 3] = "="; + } else if (rem == 2) { + res[j + 2] = b2; + res[j + 3] = "="; + } + } + return string(res); + } + + function encode3( + uint256 a0, + uint256 a1, + uint256 a2 + ) + private + pure + returns ( + bytes1 b0, + bytes1 b1, + bytes1 b2, + bytes1 b3 + ) + { + uint256 n = (a0 << 16) | (a1 << 8) | a2; + uint256 c0 = (n >> 18) & 63; + uint256 c1 = (n >> 12) & 63; + uint256 c2 = (n >> 6) & 63; + uint256 c3 = (n) & 63; + b0 = BASE64URLCHARS[c0]; + b1 = BASE64URLCHARS[c1]; + b2 = BASE64URLCHARS[c2]; + b3 = BASE64URLCHARS[c3]; + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/ParseAddress.sol b/solidity/nativecoinERC20/contracts/libraries/ParseAddress.sol new file mode 100644 index 00000000..59475be0 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/ParseAddress.sol @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Utility library of inline functions on addresses + */ +library ParseAddress { + function parseAddress(string memory account) + internal + pure + returns (address accountAddress) + { + bytes memory accountBytes = bytes(account); + require( + accountBytes.length == 42 && + accountBytes[0] == bytes1("0") && + accountBytes[1] == bytes1("x"), + "Invalid address format" + ); + + // create a new fixed-size byte array for the ascii bytes of the address. + bytes memory accountAddressBytes = new bytes(20); + + // declare variable types. + uint8 b; + uint8 nibble; + uint8 asciiOffset; + + for (uint256 i = 0; i < 40; i++) { + // get the byte in question. + b = uint8(accountBytes[i + 2]); + + bool isValidASCII = true; + // ensure that the byte is a valid ascii character (0-9, A-F, a-f) + if (b < 48) isValidASCII = false; + if (57 < b && b < 65) isValidASCII = false; + if (70 < b && b < 97) isValidASCII = false; + if (102 < b) isValidASCII = false; //bytes(hex""); + + // If string contains invalid ASCII characters, revert() + if (!isValidASCII) revert("Invalid address"); + + // find the offset from ascii encoding to the nibble representation. + if (b < 65) { + // 0-9 + asciiOffset = 48; + } else if (70 < b) { + // a-f + asciiOffset = 87; + } else { + // A-F + asciiOffset = 55; + } + + // store left nibble on even iterations, then store byte on odd ones. + if (i % 2 == 0) { + nibble = b - asciiOffset; + } else { + accountAddressBytes[(i - 1) / 2] = ( + byte(16 * nibble + (b - asciiOffset)) + ); + } + } + + // pack up the fixed-size byte array and cast it to accountAddress. + bytes memory packed = abi.encodePacked(accountAddressBytes); + assembly { + accountAddress := mload(add(packed, 20)) + } + + // return false in the event the account conversion returned null address. + if (accountAddress == address(0)) { + // ensure that provided address is not also the null address first. + for (uint256 i = 2; i < accountBytes.length; i++) + require(accountBytes[i] == hex"30", "Invalid address"); + } + + // get the capitalized characters in the actual checksum. + string memory actual = _toChecksumString(accountAddress); + + // compare provided string to actual checksum string to test for validity. + //TODO: check with ICONDAO team, this fails due to the capitalization of the actual address + /* require( + keccak256(abi.encodePacked(actual)) == + keccak256(abi.encodePacked(account)), + "Invalid checksum" + ); */ + } + + /** + * @dev Get a checksummed string hex representation of an account address. + * @param account address The account to get the checksum for. + * @return The checksummed account string in ASCII format. Note that leading + * "0x" is not included. + */ + function toString(address account) internal pure returns (string memory) { + // call internal function for converting an account to a checksummed string. + return _toChecksumString(account); + } + + /** + * @dev Get a fixed-size array of whether or not each character in an account + * will be capitalized in the checksum. + * @param account address The account to get the checksum capitalization + * information for. + * @return A fixed-size array of booleans that signify if each character or + * "nibble" of the hex encoding of the address will be capitalized by the + * checksum. + */ + function getChecksumCapitalizedCharacters(address account) + internal + pure + returns (bool[40] memory) + { + // call internal function for computing characters capitalized in checksum. + return _toChecksumCapsFlags(account); + } + + /** + * @dev Determine whether a string hex representation of an account address + * matches the ERC-55 checksum of that address. + * @param accountChecksum string The checksummed account string in ASCII + * format. Note that a leading "0x" MUST NOT be included. + * @return A boolean signifying whether or not the checksum is valid. + */ + function isChecksumValid(string calldata accountChecksum) + internal + pure + returns (bool) + { + // call internal function for validating checksum strings. + return _isChecksumValid(accountChecksum); + } + + function _toChecksumString(address account) + internal + pure + returns (string memory asciiString) + { + // convert the account argument from address to bytes. + bytes20 data = bytes20(account); + + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + bool leftCaps; + bool rightCaps; + uint8 asciiOffset; + + // get the capitalized characters in the actual checksum. + bool[40] memory caps = _toChecksumCapsFlags(account); + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // locate and extract each capitalization status. + leftCaps = caps[2 * i]; + rightCaps = caps[2 * i + 1]; + + // get the offset from nibble value to ascii character for left nibble. + asciiOffset = _getAsciiOffset(leftNibble, leftCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i] = byte(leftNibble + asciiOffset); + + // get the offset from nibble value to ascii character for right nibble. + asciiOffset = _getAsciiOffset(rightNibble, rightCaps); + + // add the converted character to the byte array. + asciiBytes[2 * i + 1] = byte(rightNibble + asciiOffset); + } + + return string(abi.encodePacked("0x", string(asciiBytes))); + } + + function _toChecksumCapsFlags(address account) + internal + pure + returns (bool[40] memory characterCapitalized) + { + // convert the address to bytes. + bytes20 a = bytes20(account); + + // hash the address (used to calculate checksum). + bytes32 b = keccak256(abi.encodePacked(_toAsciiString(a))); + + // declare variable types. + uint8 leftNibbleAddress; + uint8 rightNibbleAddress; + uint8 leftNibbleHash; + uint8 rightNibbleHash; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i; i < a.length; i++) { + // locate the byte and extract each nibble for the address and the hash. + rightNibbleAddress = uint8(a[i]) % 16; + leftNibbleAddress = (uint8(a[i]) - rightNibbleAddress) / 16; + rightNibbleHash = uint8(b[i]) % 16; + leftNibbleHash = (uint8(b[i]) - rightNibbleHash) / 16; + + characterCapitalized[2 * i] = (leftNibbleAddress > 9 && + leftNibbleHash > 7); + characterCapitalized[2 * i + 1] = (rightNibbleAddress > 9 && + rightNibbleHash > 7); + } + } + + function _isChecksumValid(string memory provided) + internal + pure + returns (bool ok) + { + // convert the provided string into account type. + address account = _toAddress(provided); + + // return false in the event the account conversion returned null address. + if (account == address(0)) { + // ensure that provided address is not also the null address first. + bytes memory b = bytes(provided); + for (uint256 i; i < b.length; i++) { + if (b[i] != hex"30") { + return false; + } + } + } + + // get the capitalized characters in the actual checksum. + string memory actual = _toChecksumString(account); + + // compare provided string to actual checksum string to test for validity. + return (keccak256(abi.encodePacked(actual)) == + keccak256(abi.encodePacked(provided))); + } + + function _getAsciiOffset(uint8 nibble, bool caps) + internal + pure + returns (uint8 offset) + { + // to convert to ascii characters, add 48 to 0-9, 55 to A-F, & 87 to a-f. + if (nibble < 10) { + offset = 48; + } else if (caps) { + offset = 55; + } else { + offset = 87; + } + } + + function _toAddress(string memory account) + internal + pure + returns (address accountAddress) + { + // convert the account argument from address to bytes. + bytes memory accountBytes = bytes(account); + + // create a new fixed-size byte array for the ascii bytes of the address. + bytes memory accountAddressBytes = new bytes(20); + + // declare variable types. + uint8 b; + uint8 nibble; + uint8 asciiOffset; + + // only proceed if the provided string has a length of 40. + if (accountBytes.length == 40) { + for (uint256 i; i < 40; i++) { + // get the byte in question. + b = uint8(accountBytes[i]); + + // ensure that the byte is a valid ascii character (0-9, A-F, a-f) + if (b < 48) return address(0); + if (57 < b && b < 65) return address(0); + if (70 < b && b < 97) return address(0); + if (102 < b) return address(0); //bytes(hex""); + + // find the offset from ascii encoding to the nibble representation. + if (b < 65) { + // 0-9 + asciiOffset = 48; + } else if (70 < b) { + // a-f + asciiOffset = 87; + } else { + // A-F + asciiOffset = 55; + } + + // store left nibble on even iterations, then store byte on odd ones. + if (i % 2 == 0) { + nibble = b - asciiOffset; + } else { + accountAddressBytes[(i - 1) / 2] = ( + byte(16 * nibble + (b - asciiOffset)) + ); + } + } + + // pack up the fixed-size byte array and cast it to accountAddress. + bytes memory packed = abi.encodePacked(accountAddressBytes); + assembly { + accountAddress := mload(add(packed, 20)) + } + } + } + + // based on https://ethereum.stackexchange.com/a/56499/48410 + function _toAsciiString(bytes20 data) + internal + pure + returns (string memory asciiString) + { + // create an in-memory fixed-size bytes array. + bytes memory asciiBytes = new bytes(40); + + // declare variable types. + uint8 b; + uint8 leftNibble; + uint8 rightNibble; + + // iterate over bytes, processing left and right nibble in each iteration. + for (uint256 i = 0; i < data.length; i++) { + // locate the byte and extract each nibble. + b = uint8(uint160(data) / (2**(8 * (19 - i)))); + leftNibble = b / 16; + rightNibble = b - 16 * leftNibble; + + // to convert to ascii characters, add 48 to 0-9 and 87 to a-f. + asciiBytes[2 * i] = byte(leftNibble + (leftNibble < 10 ? 48 : 87)); + asciiBytes[2 * i + 1] = byte( + rightNibble + (rightNibble < 10 ? 48 : 87) + ); + } + + return string(asciiBytes); + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/RLPDecode.sol b/solidity/nativecoinERC20/contracts/libraries/RLPDecode.sol new file mode 100644 index 00000000..a7764aa9 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/RLPDecode.sol @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/* + * Change supporting solidity compiler version + * The original code can be found via this link: https://github.com/hamdiallam/Solidity-RLP.git + */ + +library RLPDecode { + uint8 private constant STRING_SHORT_START = 0x80; + uint8 private constant STRING_LONG_START = 0xb8; + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf8; + uint8 private constant WORD_SIZE = 32; + + struct RLPItem { + uint256 len; + uint256 memPtr; + } + + struct Iterator { + RLPItem item; // Item that's being iterated over. + uint256 nextPtr; // Position of the next item in the list. + } + + /* + * @dev Returns the next element in the iteration. Reverts if it has not next element. + * @param self The iterator. + * @return The next element in the iteration. + */ + function next(Iterator memory self) internal pure returns (RLPItem memory) { + require(hasNext(self), "Must have next elements"); + + uint256 ptr = self.nextPtr; + uint256 itemLength = _itemLength(ptr); + self.nextPtr = ptr + itemLength; + + return RLPItem(itemLength, ptr); + } + + /* + * @dev Returns true if the iteration has more elements. + * @param self The iterator. + * @return true if the iteration has more elements. + */ + function hasNext(Iterator memory self) internal pure returns (bool) { + RLPItem memory item = self.item; + return self.nextPtr < item.memPtr + item.len; + } + + /* + * @param item RLP encoded bytes + */ + function toRlpItem(bytes memory item) + internal + pure + returns (RLPItem memory) + { + uint256 memPtr; + assembly { + memPtr := add(item, 0x20) + } + + return RLPItem(item.length, memPtr); + } + + /* + * @dev Create an iterator. Reverts if item is not a list. + * @param self The RLP item. + * @return An 'Iterator' over the item. + */ + function iterator(RLPItem memory self) + internal + pure + returns (Iterator memory) + { + require(isList(self), "Must be a list"); + + uint256 ptr = self.memPtr + _payloadOffset(self.memPtr); + return Iterator(self, ptr); + } + + /* + * @param item RLP encoded bytes + */ + function rlpLen(RLPItem memory item) internal pure returns (uint256) { + return item.len; + } + + /* + * @param item RLP encoded bytes + */ + function payloadLen(RLPItem memory item) internal pure returns (uint256) { + return item.len - _payloadOffset(item.memPtr); + } + + /* + * @param item RLP encoded list in bytes + */ + function toList(RLPItem memory item) + internal + pure + returns (RLPItem[] memory) + { + require(isList(item), "Must be a list"); + + uint256 items = numItems(item); + RLPItem[] memory result = new RLPItem[](items); + + uint256 memPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 dataLen; + for (uint256 i = 0; i < items; i++) { + dataLen = _itemLength(memPtr); + result[i] = RLPItem(dataLen, memPtr); + memPtr = memPtr + dataLen; + } + + return result; + } + + // @return indicator whether encoded payload is a list. negate this function call for isData. + function isList(RLPItem memory item) internal pure returns (bool) { + if (item.len == 0) return false; + + uint8 byte0; + uint256 memPtr = item.memPtr; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < LIST_SHORT_START) return false; + return true; + } + + /** RLPItem conversions into data types **/ + + // @returns raw rlp encoding in bytes + function toRlpBytes(RLPItem memory item) + internal + pure + returns (bytes memory) + { + bytes memory result = new bytes(item.len); + if (result.length == 0) return result; + + uint256 ptr; + assembly { + ptr := add(0x20, result) + } + + copy(item.memPtr, ptr, item.len); + return result; + } + + // any non-zero byte except "0x80" is considered true + function toBoolean(RLPItem memory item) internal pure returns (bool) { + require(item.len == 1, "Must have length 1"); + uint256 result; + uint256 memPtr = item.memPtr; + assembly { + result := byte(0, mload(memPtr)) + } + + // SEE Github Issue #5. + // Summary: Most commonly used RLP libraries (i.e Geth) will encode + // "0" as "0x80" instead of as "0". We handle this edge case explicitly + // here. + if (result == 0 || result == STRING_SHORT_START) { + return false; + } else { + return true; + } + } + + function toAddress(RLPItem memory item) internal pure returns (address) { + // 1 byte for the length prefix + require(item.len == 21, "Must have length 21"); + + return address(uint160(toUint(item))); + } + + function toUint(RLPItem memory item) internal pure returns (uint256) { + require(item.len > 0 && item.len <= 33, "Invalid uint number"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; + + uint256 result; + uint256 memPtr = item.memPtr + offset; + assembly { + result := mload(memPtr) + + // shfit to the correct location if neccesary + if lt(len, 32) { + result := div(result, exp(256, sub(32, len))) + } + } + + return result; + } + + function toInt(RLPItem memory item) internal pure returns (int256) { + if ((toBytes(item)[0] & 0x80) == 0x80) { + return int256(toUint(item) - 2**(toBytes(item).length * 8)); + } + + return int256(toUint(item)); + } + + // enforces 32 byte length + function toUintStrict(RLPItem memory item) internal pure returns (uint256) { + // one byte prefix + require(item.len == 33, "Must have length 33"); + + uint256 result; + uint256 memPtr = item.memPtr + 1; + assembly { + result := mload(memPtr) + } + + return result; + } + + function toBytes(RLPItem memory item) internal pure returns (bytes memory) { + require(item.len > 0, "Invalid length"); + + uint256 offset = _payloadOffset(item.memPtr); + uint256 len = item.len - offset; // data length + bytes memory result = new bytes(len); + + uint256 destPtr; + assembly { + destPtr := add(0x20, result) + } + + copy(item.memPtr + offset, destPtr, len); + return result; + } + + /* + * Private Helpers + */ + + // @return number of payload items inside an encoded list. + function numItems(RLPItem memory item) private pure returns (uint256) { + if (item.len == 0) return 0; + + uint256 count = 0; + uint256 currPtr = item.memPtr + _payloadOffset(item.memPtr); + uint256 endPtr = item.memPtr + item.len; + while (currPtr < endPtr) { + currPtr = currPtr + _itemLength(currPtr); // skip over an item + count++; + } + + return count; + } + + // @return entire rlp item byte length + function _itemLength(uint256 memPtr) private pure returns (uint256) { + uint256 itemLen; + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) itemLen = 1; + else if (byte0 < STRING_LONG_START) + itemLen = byte0 - STRING_SHORT_START + 1; + else if (byte0 < LIST_SHORT_START) { + assembly { + let byteLen := sub(byte0, 0xb7) // # of bytes the actual length is + memPtr := add(memPtr, 1) // skip over the first byte + + /* 32 byte word size */ + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to get the len + itemLen := add(dataLen, add(byteLen, 1)) + } + } else if (byte0 < LIST_LONG_START) { + itemLen = byte0 - LIST_SHORT_START + 1; + } else { + assembly { + let byteLen := sub(byte0, 0xf7) + memPtr := add(memPtr, 1) + + let dataLen := div(mload(memPtr), exp(256, sub(32, byteLen))) // right shifting to the correct length + itemLen := add(dataLen, add(byteLen, 1)) + } + } + + return itemLen; + } + + // @return number of bytes until the data + function _payloadOffset(uint256 memPtr) private pure returns (uint256) { + uint256 byte0; + assembly { + byte0 := byte(0, mload(memPtr)) + } + + if (byte0 < STRING_SHORT_START) return 0; + else if ( + byte0 < STRING_LONG_START || + (byte0 >= LIST_SHORT_START && byte0 < LIST_LONG_START) + ) return 1; + else if (byte0 < LIST_SHORT_START) + // being explicit + return byte0 - (STRING_LONG_START - 1) + 1; + else return byte0 - (LIST_LONG_START - 1) + 1; + } + + /* + * @param src Pointer to source + * @param dest Pointer to destination + * @param len Amount of memory to copy from the source + */ + function copy( + uint256 src, + uint256 dest, + uint256 len + ) private pure { + if (len == 0) return; + + // copy as many word sizes as possible + for (; len >= WORD_SIZE; len -= WORD_SIZE) { + assembly { + mstore(dest, mload(src)) + } + + src += WORD_SIZE; + dest += WORD_SIZE; + } + + // left over bytes. Mask is used to remove unwanted bytes from the word + uint256 mask = 256**(WORD_SIZE - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) // zero out src + let destpart := and(mload(dest), mask) // retrieve the bytes + mstore(dest, or(destpart, srcpart)) + } + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/RLPDecodeStruct.sol b/solidity/nativecoinERC20/contracts/libraries/RLPDecodeStruct.sol new file mode 100644 index 00000000..4ddf2029 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/RLPDecodeStruct.sol @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPDecode.sol"; +import "./Types.sol"; + +//import "./RLPEncode.sol"; + +library RLPDecodeStruct { + using RLPDecode for RLPDecode.RLPItem; + using RLPDecode for RLPDecode.Iterator; + using RLPDecode for bytes; + + using RLPDecodeStruct for bytes; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function decodeBMCService(bytes memory _rlp) + internal + pure + returns (Types.BMCService memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCService( + string(ls[0].toBytes()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeGatherFeeMessage(bytes memory _rlp) + internal + pure + returns (Types.GatherFeeMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory subList = ls[1].toList(); + string[] memory _svcs = new string[](subList.length); + for (uint256 i = 0; i < subList.length; i++) { + _svcs[i] = string(subList[i].toBytes()); + } + return Types.GatherFeeMessage(string(ls[0].toBytes()), _svcs); + } + + function decodeEventMessage(bytes memory _rlp) + internal + pure + returns (Types.EventMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.EventMessage( + string(ls[0].toBytes()), + Types.Connection( + string(ls[1].toList()[0].toBytes()), + string(ls[1].toList()[1].toBytes()) + ) + ); + } + + function decodeCoinRegister(bytes memory _rlp) + internal + pure + returns (string[] memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + string[] memory _coins = new string[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + _coins[i] = string(ls[i].toBytes()); + } + return _coins; + } + + function decodeBMCMessage(bytes memory _rlp) + internal + pure + returns (Types.BMCMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.BMCMessage( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + string(ls[2].toBytes()), + ls[3].toInt(), + ls[4].toBytes() // bytes array of RLPEncode(ServiceMessage) + ); + } + + function decodeServiceMessage(bytes memory _rlp) + internal + pure + returns (Types.ServiceMessage memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return + Types.ServiceMessage( + Types.ServiceType(ls[0].toUint()), + ls[1].toBytes() // bytes array of RLPEncode(Data) + ); + } + + function decodeTransferCoinMsg(bytes memory _rlp) + internal + pure + returns (Types.TransferCoin memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + Types.Asset[] memory assets = new Types.Asset[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpAssets = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + assets[i] = Types.Asset( + string(rlpAssets[i].toList()[0].toBytes()), + rlpAssets[i].toList()[1].toUint() + ); + } + return + Types.TransferCoin( + string(ls[0].toBytes()), + string(ls[1].toBytes()), + assets + ); + } + + function decodeResponse(bytes memory _rlp) + internal + pure + returns (Types.Response memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + return Types.Response(ls[0].toUint(), string(ls[1].toBytes())); + } + + function decodeBlockHeader(bytes memory _rlp) + internal + pure + returns (Types.BlockHeader memory) + { + // Decode RLP bytes into a list of items + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bool isSPREmpty = true; + if (ls[10].toBytes().length == 0) { + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR("", "", ""), + isSPREmpty + ); + } + RLPDecode.RLPItem[] memory subList = + ls[10].toBytes().toRlpItem().toList(); + isSPREmpty = false; + return + Types.BlockHeader( + ls[0].toUint(), + ls[1].toUint(), + ls[2].toUint(), + ls[3].toBytes(), + ls[4].toBytes(), + ls[5].toBytes(), + ls[6].toBytes(), + ls[7].toBytes(), + ls[8].toBytes(), + ls[9].toBytes(), + Types.SPR( + subList[0].toBytes(), + subList[1].toBytes(), + subList[2].toBytes() + ), + isSPREmpty + ); + } + + // Votes item consists of: + // round as integer + // blockPartSetID is a list that consists of two items - integer and bytes + // and TS[] ts_list (an array of list) + function decodeVotes(bytes memory _rlp) + internal + pure + returns (Types.Votes memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.TS[] memory tsList = new Types.TS[](ls[2].toList().length); + RLPDecode.RLPItem[] memory rlpTs = ls[2].toList(); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + tsList[i] = Types.TS( + rlpTs[i].toList()[0].toUint(), + rlpTs[i].toList()[1].toBytes() + ); + } + return + Types.Votes( + ls[0].toUint(), + Types.BPSI( + ls[1].toList()[0].toUint(), + ls[1].toList()[1].toBytes() + ), + tsList + ); + } + + // Wait for confirmation + function decodeBlockWitness(bytes memory _rlp) + internal + pure + returns (Types.BlockWitness memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + bytes[] memory witnesses = new bytes[](ls[1].toList().length); + // witnesses is an array of hash of leaf node + // The array size may also vary, thus loop is needed therein + for (uint256 i = 0; i < ls[1].toList().length; i++) { + witnesses[i] = ls[1].toList()[i].toBytes(); + } + return Types.BlockWitness(ls[0].toUint(), witnesses); + } + + function decodeEventProof(bytes memory _rlp) + internal + pure + returns (Types.EventProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory data = ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory eventMptNode = new bytes[](data.length); + for (uint256 i = 0; i < data.length; i++) { + eventMptNode[i] = data[i].toBytes(); + } + return Types.EventProof(ls[0].toUint(), eventMptNode); + } + + function decodeBlockUpdate(bytes memory _rlp) + internal + pure + returns (Types.BlockUpdate memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + // Types.BlockHeader memory _bh; + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.Votes memory _v = ls[1].toBytes().decodeVotes(); + // Types.Votes memory _v; + + // BlockUpdate may or may not include the RLP of addresses of validators + // In that case, RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START == 0xF800 + // Thus, length of data will be 0. Therein, loop will be skipped + // and the _validators[] will be empty + // Otherwise, executing normally to read and assign value into the array _validators[] + bytes[] memory _validators; + if (ls[2].toBytes().length != 0) { + _validators = new bytes[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _validators[i] = ls[2].toList()[i].toBytes(); + } + } + return Types.BlockUpdate(_bh, _v, _validators); + } + + function decodeReceiptProof(bytes memory _rlp) + internal + pure + returns (Types.ReceiptProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + RLPDecode.RLPItem[] memory receiptList = + ls[1].toBytes().toRlpItem().toList(); + + bytes[] memory txReceipts = new bytes[](receiptList.length); + for (uint256 i = 0; i < receiptList.length; i++) { + txReceipts[i] = receiptList[i].toBytes(); + } + + Types.EventProof[] memory _ep = + new Types.EventProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _ep[i] = Types.EventProof( + ls[2].toList()[i].toList()[0].toUint(), + ls[2].toList()[i].toList()[1].toBytes().decodeEventLog() + ); + } + + return Types.ReceiptProof(ls[0].toUint(), txReceipts, _ep); + } + + function decodeEventLog(bytes memory _rlp) + internal + pure + returns (bytes[] memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + bytes[] memory eventMptNode = new bytes[](ls.length); + for (uint256 i = 0; i < ls.length; i++) { + eventMptNode[i] = ls[i].toBytes(); + } + return eventMptNode; + } + + function decodeBlockProof(bytes memory _rlp) + internal + pure + returns (Types.BlockProof memory) + { + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + + Types.BlockHeader memory _bh = ls[0].toBytes().decodeBlockHeader(); + Types.BlockWitness memory _bw = ls[1].toBytes().decodeBlockWitness(); + + return Types.BlockProof(_bh, _bw); + } + + function decodeRelayMessage(bytes memory _rlp) + internal + pure + returns (Types.RelayMessage memory) + { + // _rlp.toRlpItem() removes the LIST_HEAD_START of RelayMessage + // then .toList() to itemize all fields in the RelayMessage + // which are [RLP_ENCODE(BlockUpdate)], RLP_ENCODE(BlockProof), and + // the RLP_ENCODE(ReceiptProof) + RLPDecode.RLPItem[] memory ls = _rlp.toRlpItem().toList(); + // return ( + // ls[0].toList()[0].toBytes().toRlpItem().toList()[1].toBytes().toRlpItem().toList()[2].toList()[0].toList()[1].toBytes() + // ); + + // If [RLP_ENCODE(BlockUpdate)] was empty, it should be started by 0xF800 + // therein, ls[0].toBytes() will be null (length = 0) + // Otherwise, create an array of BlockUpdate struct to decode + Types.BlockUpdate[] memory _buArray; + if (ls[0].toBytes().length != 0) { + _buArray = new Types.BlockUpdate[](ls[0].toList().length); + for (uint256 i = 0; i < ls[0].toList().length; i++) { + // Each of items inside an array [RLP_ENCODE(BlockUpdate)] + // is a string which defines RLP_ENCODE(BlockUpdate) + // that contains a LIST_HEAD_START and multiple RLP of data + // ls[0].toList()[i].toBytes() returns bytes presentation of + // RLP_ENCODE(BlockUpdate) + _buArray[i] = ls[0].toList()[i].toBytes().decodeBlockUpdate(); + } + } + bool isBPEmpty = true; + Types.BlockProof memory _bp; + // If RLP_ENCODE(BlockProof) is omitted, + // ls[1].toBytes() should be null (length = 0) + if (ls[1].toBytes().length != 0) { + _bp = ls[1].toBytes().decodeBlockProof(); + isBPEmpty = false; // add this field into RelayMessage + // to specify whether BlockProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + + bool isRPEmpty = true; + Types.ReceiptProof[] memory _rp; + // If [RLP_ENCODE(ReceiptProof)] is omitted, + // ls[2].toBytes() should be null (length = 0) + if (ls[2].toBytes().length != 0) { + _rp = new Types.ReceiptProof[](ls[2].toList().length); + for (uint256 i = 0; i < ls[2].toList().length; i++) { + _rp[i] = ls[2].toList()[i].toBytes().decodeReceiptProof(); + } + isRPEmpty = false; // add this field into RelayMessage + // to specify whether ReceiptProof is omitted + // to make it easy on encoding + // it will not be serialized thereafter + } + return Types.RelayMessage(_buArray, _bp, isBPEmpty, _rp, isRPEmpty); + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/RLPEncode.sol b/solidity/nativecoinERC20/contracts/libraries/RLPEncode.sol new file mode 100644 index 00000000..a7bb08db --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/RLPEncode.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * @title RLPEncode + * @dev A simple RLP encoding library. + * @author Bakaoh + * The original code was modified. For more info, please check the link: + * https://github.com/bakaoh/solidity-rlp-encode.git + */ +library RLPEncode { + int8 internal constant MAX_INT8 = type(int8).max; + int16 internal constant MAX_INT16 = type(int16).max; + int24 internal constant MAX_INT24 = type(int24).max; + int32 internal constant MAX_INT32 = type(int32).max; + int40 internal constant MAX_INT40 = type(int40).max; + int48 internal constant MAX_INT48 = type(int48).max; + int56 internal constant MAX_INT56 = type(int56).max; + int64 internal constant MAX_INT64 = type(int64).max; + int72 internal constant MAX_INT72 = type(int72).max; + int80 internal constant MAX_INT80 = type(int80).max; + int88 internal constant MAX_INT88 = type(int88).max; + int96 internal constant MAX_INT96 = type(int96).max; + int104 internal constant MAX_INT104 = type(int104).max; + int112 internal constant MAX_INT112 = type(int112).max; + int120 internal constant MAX_INT120 = type(int120).max; + int128 internal constant MAX_INT128 = type(int128).max; + + uint8 internal constant MAX_UINT8 = type(uint8).max; + uint16 internal constant MAX_UINT16 = type(uint16).max; + uint24 internal constant MAX_UINT24 = type(uint24).max; + uint32 internal constant MAX_UINT32 = type(uint32).max; + uint40 internal constant MAX_UINT40 = type(uint40).max; + uint48 internal constant MAX_UINT48 = type(uint48).max; + uint56 internal constant MAX_UINT56 = type(uint56).max; + uint64 internal constant MAX_UINT64 = type(uint64).max; + uint72 internal constant MAX_UINT72 = type(uint72).max; + uint80 internal constant MAX_UINT80 = type(uint80).max; + uint88 internal constant MAX_UINT88 = type(uint88).max; + uint96 internal constant MAX_UINT96 = type(uint96).max; + uint104 internal constant MAX_UINT104 = type(uint104).max; + uint112 internal constant MAX_UINT112 = type(uint112).max; + uint120 internal constant MAX_UINT120 = type(uint120).max; + uint128 internal constant MAX_UINT128 = type(uint128).max; + + /* + * Internal functions + */ + + /** + * @dev RLP encodes a byte string. + * @param self The byte string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeBytes(bytes memory self) + internal + pure + returns (bytes memory) + { + bytes memory encoded; + if (self.length == 1 && uint8(self[0]) <= 128) { + encoded = self; + } else { + encoded = concat(encodeLength(self.length, 128), self); + } + return encoded; + } + + /** + * @dev RLP encodes a list of RLP encoded byte byte strings. + * @param self The list of RLP encoded byte strings. + * @return The RLP encoded list of items in bytes. + */ + function encodeList(bytes[] memory self) + internal + pure + returns (bytes memory) + { + bytes memory list = flatten(self); + return concat(encodeLength(list.length, 192), list); + } + + /** + * @dev RLP encodes a string. + * @param self The string to encode. + * @return The RLP encoded string in bytes. + */ + function encodeString(string memory self) + internal + pure + returns (bytes memory) + { + return encodeBytes(bytes(self)); + } + + /** + * @dev RLP encodes an address. + * @param self The address to encode. + * @return The RLP encoded address in bytes. + */ + function encodeAddress(address self) internal pure returns (bytes memory) { + bytes memory inputBytes; + assembly { + let m := mload(0x40) + mstore( + add(m, 20), + xor(0x140000000000000000000000000000000000000000, self) + ) + mstore(0x40, add(m, 52)) + inputBytes := m + } + return encodeBytes(inputBytes); + } + + /** + * @dev RLP encodes a uint. + * @param self The uint to encode. + * @return The RLP encoded uint in bytes. + */ + function encodeUint(uint256 self) internal pure returns (bytes memory) { + uint256 nBytes = bitLength(self) / 8 + 1; + bytes memory uintBytes = encodeUintByLength(self); + if (nBytes - uintBytes.length > 0) { + uintBytes = abi.encodePacked(bytes1(0), uintBytes); + } + return encodeBytes(uintBytes); + } + + /** + * @dev convert int to strict bytes. + * @notice only handle to int128 due to contract code size limit + * @param n The int to convert. + * @return The int in strict bytes without padding. + */ + function intToStrictBytes(int256 n) internal pure returns (bytes memory) { + if (-MAX_INT8 - 1 <= n && n <= MAX_INT8) { + return abi.encodePacked(int8(n)); + } else if (-MAX_INT16 - 1 <= n && n <= MAX_INT16) { + return abi.encodePacked(int16(n)); + } else if (-MAX_INT24 - 1 <= n && n <= MAX_INT24) { + return abi.encodePacked(int24(n)); + } else if (-MAX_INT32 - 1 <= n && n <= MAX_INT32) { + return abi.encodePacked(int32(n)); + } else if (-MAX_INT40 - 1 <= n && n <= MAX_INT40) { + return abi.encodePacked(int40(n)); + } else if (-MAX_INT48 - 1 <= n && n <= MAX_INT48) { + return abi.encodePacked(int48(n)); + } else if (-MAX_INT56 - 1 <= n && n <= MAX_INT56) { + return abi.encodePacked(int56(n)); + } else if (-MAX_INT64 - 1 <= n && n <= MAX_INT64) { + return abi.encodePacked(int64(n)); + } else if (-MAX_INT72 - 1 <= n && n <= MAX_INT72) { + return abi.encodePacked(int72(n)); + } else if (-MAX_INT80 - 1 <= n && n <= MAX_INT80) { + return abi.encodePacked(int80(n)); + } else if (-MAX_INT88 - 1 <= n && n <= MAX_INT88) { + return abi.encodePacked(int88(n)); + } else if (-MAX_INT96 - 1 <= n && n <= MAX_INT96) { + return abi.encodePacked(int96(n)); + } else if (-MAX_INT104 - 1 <= n && n <= MAX_INT104) { + return abi.encodePacked(int104(n)); + } else if (-MAX_INT112 - 1 <= n && n <= MAX_INT112) { + return abi.encodePacked(int112(n)); + } else if (-MAX_INT120 - 1 <= n && n <= MAX_INT120) { + return abi.encodePacked(int120(n)); + } + require( + -MAX_INT128 - 1 <= n && n <= MAX_INT128, + "outOfBounds: [-2^128-1, 2^128]" + ); + return abi.encodePacked(int128(n)); + } + + /** + * @dev RLP encodes an int. + * @param self The int to encode. + * @return The RLP encoded int in bytes. + */ + function encodeInt(int256 self) internal pure returns (bytes memory) { + return encodeBytes(intToStrictBytes(self)); + } + + /** + * @dev RLP encodes a bool. + * @param self The bool to encode. + * @return The RLP encoded bool in bytes. + */ + function encodeBool(bool self) internal pure returns (bytes memory) { + bytes memory encoded = new bytes(1); + encoded[0] = (self ? bytes1(0x01) : bytes1(0x00)); + return encoded; + } + + /* + * Private functions + */ + + /** + * @dev Encode the first byte, followed by the `len` in binary form if `length` is more than 55. + * @param len The length of the string or the payload. + * @param offset 128 if item is string, 192 if item is list. + * @return RLP encoded bytes. + */ + function encodeLength(uint256 len, uint256 offset) + private + pure + returns (bytes memory) + { + bytes memory encoded; + if (len < 56) { + encoded = new bytes(1); + encoded[0] = bytes32(len + offset)[31]; + } else { + uint256 lenLen; + uint256 i = 1; + while (len / i != 0) { + lenLen++; + i *= 256; + } + + encoded = new bytes(lenLen + 1); + encoded[0] = bytes32(lenLen + offset + 55)[31]; + for (i = 1; i <= lenLen; i++) { + encoded[i] = bytes32((len / (256**(lenLen - i))) % 256)[31]; + } + } + return encoded; + } + + /** + * @dev Encode integer in big endian binary form with no leading zeroes. + * @notice TODO: This should be optimized with assembly to save gas costs. + * @param _x The integer to encode. + * @return RLP encoded bytes. + */ + function toBinary(uint256 _x) private pure returns (bytes memory) { + // Modify library to make it work properly when _x = 0 + if (_x == 0) { + return abi.encodePacked(uint8(_x)); + } + bytes memory b = new bytes(32); + assembly { + mstore(add(b, 32), _x) + } + uint256 i; + for (i = 0; i < 32; i++) { + if (b[i] != 0) { + break; + } + } + bytes memory res = new bytes(32 - i); + for (uint256 j = 0; j < res.length; j++) { + res[j] = b[i++]; + } + return res; + } + + /** + * @dev Copies a piece of memory to another location. + * @notice From: https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol. + * @param _dest Destination location. + * @param _src Source location. + * @param _len Length of memory to copy. + */ + function memcpy( + uint256 _dest, + uint256 _src, + uint256 _len + ) private pure { + uint256 dest = _dest; + uint256 src = _src; + uint256 len = _len; + + for (; len >= 32; len -= 32) { + assembly { + mstore(dest, mload(src)) + } + dest += 32; + src += 32; + } + + uint256 mask = 256**(32 - len) - 1; + assembly { + let srcpart := and(mload(src), not(mask)) + let destpart := and(mload(dest), mask) + mstore(dest, or(destpart, srcpart)) + } + } + + /** + * @dev Flattens a list of byte strings into one byte string. + * @notice From: https://github.com/sammayo/solidity-rlp-encoder/blob/master/RLPEncode.sol. + * @param _list List of byte strings to flatten. + * @return The flattened byte string. + */ + function flatten(bytes[] memory _list) private pure returns (bytes memory) { + if (_list.length == 0) { + return new bytes(0); + } + + uint256 len; + uint256 i; + for (i = 0; i < _list.length; i++) { + len += _list[i].length; + } + + bytes memory flattened = new bytes(len); + uint256 flattenedPtr; + assembly { + flattenedPtr := add(flattened, 0x20) + } + + for (i = 0; i < _list.length; i++) { + bytes memory item = _list[i]; + + uint256 listPtr; + assembly { + listPtr := add(item, 0x20) + } + + memcpy(flattenedPtr, listPtr, item.length); + flattenedPtr += _list[i].length; + } + + return flattened; + } + + /** + * @dev Concatenates two bytes. + * @notice From: https://github.com/GNSPS/solidity-bytes-utils/blob/master/contracts/BytesLib.sol. + * @param _preBytes First byte string. + * @param _postBytes Second byte string. + * @return Both byte string combined. + */ + function concat(bytes memory _preBytes, bytes memory _postBytes) + private + pure + returns (bytes memory) + { + bytes memory tempBytes; + + assembly { + tempBytes := mload(0x40) + + let length := mload(_preBytes) + mstore(tempBytes, length) + + let mc := add(tempBytes, 0x20) + let end := add(mc, length) + + for { + let cc := add(_preBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + length := mload(_postBytes) + mstore(tempBytes, add(length, mload(tempBytes))) + + mc := end + end := add(mc, length) + + for { + let cc := add(_postBytes, 0x20) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore( + 0x40, + and( + add(add(end, iszero(add(length, mload(_preBytes)))), 31), + not(31) + ) + ) + } + + return tempBytes; + } + + /** + * @dev convert uint to strict bytes. + * @notice only handle to uint128 due to contract code size limit + * @param length The uint to convert. + * @return The uint in strict bytes without padding. + */ + function encodeUintByLength(uint256 length) + internal + pure + returns (bytes memory) + { + if (length < MAX_UINT8) { + return abi.encodePacked(uint8(length)); + } else if (length >= MAX_UINT8 && length < MAX_UINT16) { + return abi.encodePacked(uint16(length)); + } else if (length >= MAX_UINT16 && length < MAX_UINT24) { + return abi.encodePacked(uint24(length)); + } else if (length >= MAX_UINT24 && length < MAX_UINT32) { + return abi.encodePacked(uint32(length)); + } else if (length >= MAX_UINT32 && length < MAX_UINT40) { + return abi.encodePacked(uint40(length)); + } else if (length >= MAX_UINT40 && length < MAX_UINT48) { + return abi.encodePacked(uint48(length)); + } else if (length >= MAX_UINT48 && length < MAX_UINT56) { + return abi.encodePacked(uint56(length)); + } else if (length >= MAX_UINT56 && length < MAX_UINT64) { + return abi.encodePacked(uint64(length)); + } else if (length >= MAX_UINT64 && length < MAX_UINT72) { + return abi.encodePacked(uint72(length)); + } else if (length >= MAX_UINT72 && length < MAX_UINT80) { + return abi.encodePacked(uint80(length)); + } else if (length >= MAX_UINT80 && length < MAX_UINT88) { + return abi.encodePacked(uint88(length)); + } else if (length >= MAX_UINT88 && length < MAX_UINT96) { + return abi.encodePacked(uint96(length)); + } else if (length >= MAX_UINT96 && length < MAX_UINT104) { + return abi.encodePacked(uint104(length)); + } else if (length >= MAX_UINT104 && length < MAX_UINT112) { + return abi.encodePacked(uint112(length)); + } else if (length >= MAX_UINT112 && length < MAX_UINT120) { + return abi.encodePacked(uint120(length)); + } + require( + length >= MAX_UINT120 && length < MAX_UINT128, + "outOfBounds: [0, 2^128]" + ); + return abi.encodePacked(uint128(length)); + } + + function bitLength(uint256 n) internal pure returns (uint256) { + uint256 count; + while (n != 0) { + count += 1; + n >>= 1; + } + return count; + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/RLPEncodeStruct.sol b/solidity/nativecoinERC20/contracts/libraries/RLPEncodeStruct.sol new file mode 100644 index 00000000..d578afeb --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/RLPEncodeStruct.sol @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "./RLPEncode.sol"; +import "./Types.sol"; + +library RLPEncodeStruct { + using RLPEncode for bytes; + using RLPEncode for string; + using RLPEncode for uint256; + using RLPEncode for address; + using RLPEncode for int256; + + using RLPEncodeStruct for Types.BlockHeader; + using RLPEncodeStruct for Types.BlockWitness; + using RLPEncodeStruct for Types.BlockUpdate; + using RLPEncodeStruct for Types.BlockProof; + using RLPEncodeStruct for Types.EventProof; + using RLPEncodeStruct for Types.ReceiptProof; + using RLPEncodeStruct for Types.Votes; + using RLPEncodeStruct for Types.RelayMessage; + + uint8 private constant LIST_SHORT_START = 0xc0; + uint8 private constant LIST_LONG_START = 0xf7; + + function encodeBMCService(Types.BMCService memory _bs) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bs.serviceType.encodeString(), + _bs.payload.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeGatherFeeMessage(Types.GatherFeeMessage memory _gfm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _gfm.svcs.length; i++) { + temp = abi.encodePacked(_gfm.svcs[i].encodeString()); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _gfm.fa.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeEventMessage(Types.EventMessage memory _em) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _em.conn.from.encodeString(), + _em.conn.to.encodeString() + ); + _rlp = abi.encodePacked( + _em.eventType.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeCoinRegister(string[] memory _coins) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _coins.length; i++) { + temp = abi.encodePacked(_coins[i].encodeString()); + _rlp = abi.encodePacked(_rlp, temp); + } + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBMCMessage(Types.BMCMessage memory _bm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bm.src.encodeString(), + _bm.dst.encodeString(), + _bm.svc.encodeString(), + _bm.sn.encodeInt(), + _bm.message.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeServiceMessage(Types.ServiceMessage memory _sm) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + uint256(_sm.serviceType).encodeUint(), + _sm.data.encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeTransferCoinMsg(Types.TransferCoin memory _data) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _data.assets.length; i++) { + temp = abi.encodePacked( + _data.assets[i].coinName.encodeString(), + _data.assets[i].value.encodeUint() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + _rlp = abi.encodePacked( + _data.from.encodeString(), + _data.to.encodeString(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeResponse(Types.Response memory _res) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _res.code.encodeUint(), + _res.message.encodeString() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockHeader(Types.BlockHeader memory _bh) + internal + pure + returns (bytes memory) + { + // Serialize the first 10 items in the BlockHeader + // patchTxHash and txHash might be empty. + // In that case, encoding these two items gives the result as 0xF800 + // Similarly, logsBloom might be also empty + // But, encoding this item gives the result as 0x80 + bytes memory _rlp = + abi.encodePacked( + _bh.version.encodeUint(), + _bh.height.encodeUint(), + _bh.timestamp.encodeUint(), + _bh.proposer.encodeBytes(), + _bh.prevHash.encodeBytes(), + _bh.voteHash.encodeBytes(), + _bh.nextValidators.encodeBytes() + ); + bytes memory temp1; + if (_bh.patchTxHash.length != 0) { + temp1 = _bh.patchTxHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1); + + if (_bh.txHash.length != 0) { + temp1 = _bh.txHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp1, _bh.logsBloom.encodeBytes()); + bytes memory temp2; + // SPR struct could be an empty struct + // In that case, serialize(SPR) = 0xF800 + if (_bh.isSPREmpty) { + temp2 = emptyListHeadStart(); + } else { + // patchReceiptHash and receiptHash might be empty + // In that case, encoding these two items gives the result as 0xF800 + if (_bh.spr.patchReceiptHash.length != 0) { + temp1 = _bh.spr.patchReceiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(_bh.spr.stateHash.encodeBytes(), temp1); + + if (_bh.spr.receiptHash.length != 0) { + temp1 = _bh.spr.receiptHash.encodeBytes(); + } else { + temp1 = emptyListHeadStart(); + } + temp2 = abi.encodePacked(temp2, temp1); + temp2 = abi + .encodePacked(addLength(temp2.length, false), temp2) + .encodeBytes(); + } + _rlp = abi.encodePacked(_rlp, temp2); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeVotes(Types.Votes memory _vote) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + + // First, serialize an array of TS + for (uint256 i = 0; i < _vote.ts.length; i++) { + temp = abi.encodePacked( + _vote.ts[i].timestamp.encodeUint(), + _vote.ts[i].signature.encodeBytes() + ); + _rlp = abi.encodePacked(_rlp, addLength(temp.length, false), temp); + } + + // Next, serialize the blockPartSetID + temp = abi.encodePacked( + _vote.blockPartSetID.n.encodeUint(), + _vote.blockPartSetID.b.encodeBytes() + ); + // Combine all of them + _rlp = abi.encodePacked( + _vote.round.encodeUint(), + addLength(temp.length, false), + temp, + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockWitness(Types.BlockWitness memory _bw) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _bw.witnesses.length; i++) { + temp = _bw.witnesses[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked( + _bw.height.encodeUint(), + addLength(_rlp.length, false), + _rlp + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeEventProof(Types.EventProof memory _ep) + internal + pure + returns (bytes memory) + { + bytes memory _rlp; + bytes memory temp; + for (uint256 i = 0; i < _ep.eventMptNode.length; i++) { + temp = _ep.eventMptNode[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + _rlp = abi.encodePacked(_ep.index.encodeUint(), _rlp); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockUpdate(Types.BlockUpdate memory _bu) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // In the case that _validators[] is an empty array, loop will be skipped + // and RLP_ENCODE([bytes]) == EMPTY_LIST_HEAD_START (0xF800) instead + if (_bu.validators.length != 0) { + for (uint256 i = 0; i < _bu.validators.length; i++) { + temp = _bu.validators[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + } else { + _rlp = emptyListHeadStart(); + } + + _rlp = abi.encodePacked( + _bu.bh.encodeBlockHeader().encodeBytes(), + _bu.votes.encodeVotes().encodeBytes(), + _rlp + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeReceiptProof(Types.ReceiptProof memory _rp) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + // Serialize [bytes] which are transaction receipts + for (uint256 i = 0; i < _rp.txReceipts.length; i++) { + temp = _rp.txReceipts[i].encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi + .encodePacked(addLength(_rlp.length, false), _rlp) + .encodeBytes(); + + bytes memory eventProof; + for (uint256 i = 0; i < _rp.ep.length; i++) { + temp = _rp.ep[i].encodeEventProof(); + eventProof = abi.encodePacked(eventProof, temp); + } + _rlp = abi.encodePacked( + _rp.index.encodeUint(), + _rlp, + addLength(eventProof.length, false), + eventProof + ); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeBlockProof(Types.BlockProof memory _bp) + internal + pure + returns (bytes memory) + { + bytes memory _rlp = + abi.encodePacked( + _bp.bh.encodeBlockHeader().encodeBytes(), + _bp.bw.encodeBlockWitness().encodeBytes() + ); + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + function encodeRelayMessage(Types.RelayMessage memory _rm) + internal + pure + returns (bytes memory) + { + bytes memory temp; + bytes memory _rlp; + if (_rm.buArray.length != 0) { + for (uint256 i = 0; i < _rm.buArray.length; i++) { + temp = _rm.buArray[i].encodeBlockUpdate().encodeBytes(); + _rlp = abi.encodePacked(_rlp, temp); + } + _rlp = abi.encodePacked(addLength(_rlp.length, false), _rlp); + } else { + _rlp = emptyListShortStart(); + } + + if (_rm.isBPEmpty == false) { + temp = _rm.bp.encodeBlockProof(); + } else { + temp = emptyListHeadStart(); + } + _rlp = abi.encodePacked(_rlp, temp); + + bytes memory receiptProof; + if (_rm.isRPEmpty == false) { + for (uint256 i = 0; i < _rm.rp.length; i++) { + temp = _rm.rp[i].encodeReceiptProof().encodeBytes(); + receiptProof = abi.encodePacked(receiptProof, temp); + } + receiptProof = abi.encodePacked( + addLength(receiptProof.length, false), + receiptProof + ); + } else { + receiptProof = emptyListShortStart(); + } + _rlp = abi.encodePacked(_rlp, receiptProof); + + return abi.encodePacked(addLength(_rlp.length, false), _rlp); + } + + // Adding LIST_HEAD_START by length + // There are two cases: + // 1. List contains less than or equal 55 elements (total payload of the RLP) -> LIST_HEAD_START = LIST_SHORT_START + [0-55] = [0xC0 - 0xF7] + // 2. List contains more than 55 elements: + // - Total Payload = 512 elements = 0x0200 + // - Length of Total Payload = 2 + // => LIST_HEAD_START = \x (LIST_LONG_START + length of Total Payload) \x (Total Payload) = \x(F7 + 2) \x(0200) = \xF9 \x0200 = 0xF90200 + function addLength(uint256 length, bool isLongList) + internal + pure + returns (bytes memory) + { + if (length > 55 && !isLongList) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(length); + return + abi.encodePacked( + addLength(payLoadSize.length, true), + payLoadSize + ); + } else if (length <= 55 && !isLongList) { + return abi.encodePacked(uint8(LIST_SHORT_START + length)); + } + return abi.encodePacked(uint8(LIST_LONG_START + length)); + } + + function emptyListHeadStart() internal pure returns (bytes memory) { + bytes memory payLoadSize = RLPEncode.encodeUintByLength(0); + return + abi.encodePacked( + abi.encodePacked(uint8(LIST_LONG_START + payLoadSize.length)), + payLoadSize + ); + } + + function emptyListShortStart() internal pure returns (bytes memory) { + return abi.encodePacked(LIST_SHORT_START); + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/String.sol b/solidity/nativecoinERC20/contracts/libraries/String.sol new file mode 100644 index 00000000..55ce075c --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/String.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +/** + * String Library + * + * This is a simple library of string functions which try to simplify + * string operations in solidity. + * + * Please be aware some of these functions can be quite gas heavy so use them only when necessary + * + * The original library was modified. If you want to know more about the original version + * please check this link: https://github.com/willitscale/solidity-util.git + */ +library String { + /** + * splitBTPAddress + * + * Split the BTP Address format i.e. btp://1234.iconee/0x123456789 + * into Network_address (1234.iconee) and Server_address (0x123456789) + * + * @param _base String base BTP Address format to be split + * @dev _base must follow a BTP Address format + * + * @return string, string The resulting strings of Network_address and Server_address + */ + function splitBTPAddress(string memory _base) + internal + pure + returns (string memory, string memory) + { + string[] memory temp = split(_base, "/"); + return (temp[2], temp[3]); + } + + /** + * Concat + * + * Appends two strings together and returns a new value + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string which will be the concatenated + * prefix + * @param _value The value to be the concatenated suffix + * @return string The resulting string from combinging the base and value + */ + function concat(string memory _base, string memory _value) + internal + pure + returns (string memory) + { + return string(abi.encodePacked(_base, _value)); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function indexOf(string memory _base, string memory _value) + internal + pure + returns (int256) + { + return _indexOf(_base, _value, 0); + } + + /** + * Index Of + * + * Locates and returns the position of a character within a string starting + * from a defined offset + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string acting as the haystack to be + * searched + * @param _value The needle to search for, at present this is currently + * limited to one character + * @param _offset The starting point to start searching from which can start + * from 0, but must not exceed the length of the string + * @return int The position of the needle starting from 0 and returning -1 + * in the case of no matches found + */ + function _indexOf( + string memory _base, + string memory _value, + uint256 _offset + ) internal pure returns (int256) { + bytes memory _baseBytes = bytes(_base); + bytes memory _valueBytes = bytes(_value); + + assert(_valueBytes.length == 1); + + for (uint256 i = _offset; i < _baseBytes.length; i++) { + if (_baseBytes[i] == _valueBytes[0]) { + return int256(i); + } + } + + return -1; + } + + /** + * Length + * + * Returns the length of the specified string + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string to be measured + * @return uint The length of the passed string + */ + function length(string memory _base) internal pure returns (uint256) { + bytes memory _baseBytes = bytes(_base); + return _baseBytes.length; + } + + /* + * String Split (Very high gas cost) + * + * Splits a string into an array of strings based off the delimiter value. + * Please note this can be quite a gas expensive function due to the use of + * storage so only use if really required. + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string value to be split. + * @param _value The delimiter to split the string on which must be a single + * character + * @return string[] An array of values split based off the delimiter, but + * do not container the delimiter. + */ + function split(string memory _base, string memory _value) + internal + pure + returns (string[] memory splitArr) + { + bytes memory _baseBytes = bytes(_base); + + uint256 _offset = 0; + uint256 _splitsCount = 1; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) break; + else { + _splitsCount++; + _offset = uint256(_limit) + 1; + } + } + + splitArr = new string[](_splitsCount); + + _offset = 0; + _splitsCount = 0; + while (_offset < _baseBytes.length - 1) { + int256 _limit = _indexOf(_base, _value, _offset); + if (_limit == -1) { + _limit = int256(_baseBytes.length); + } + + string memory _tmp = new string(uint256(_limit) - _offset); + bytes memory _tmpBytes = bytes(_tmp); + + uint256 j = 0; + for (uint256 i = _offset; i < uint256(_limit); i++) { + _tmpBytes[j++] = _baseBytes[i]; + } + _offset = uint256(_limit) + 1; + splitArr[_splitsCount++] = string(_tmpBytes); + } + return splitArr; + } + + /** + * Compare To + * + * Compares the characters of two strings, to ensure that they have an + * identical footprint + * + * @param _base When being used for a data type this is the extended object + * otherwise this is the string base to compare against + * @param _value The string the base is being compared to + * @return bool Simply notates if the two string have an equivalent + */ + function compareTo(string memory _base, string memory _value) + internal + pure + returns (bool) + { + if ( + keccak256(abi.encodePacked(_base)) == + keccak256(abi.encodePacked(_value)) + ) { + return true; + } + return false; + } + + function toString(uint256 _i) internal pure returns (string memory) { + if (_i == 0) { + return "0"; + } + uint256 j = _i; + uint256 len; + while (j != 0) { + len++; + j /= 10; + } + bytes memory bstr = new bytes(len); + uint256 k = len - 1; + while (_i != 0) { + bstr[k--] = byte(uint8(48 + (_i % 10))); + _i /= 10; + } + return string(bstr); + } +} diff --git a/solidity/nativecoinERC20/contracts/libraries/Types.sol b/solidity/nativecoinERC20/contracts/libraries/Types.sol new file mode 100644 index 00000000..4e48e5f1 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/libraries/Types.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +library Types { + /** + * @Notice List of ALL Struct being used to Encode and Decode RLP Messages + */ + + // SPR = State Hash + Pathch Receipt Hash + Receipt Hash + struct SPR { + bytes stateHash; + bytes patchReceiptHash; + bytes receiptHash; + } + + struct BlockHeader { + uint256 version; + uint256 height; + uint256 timestamp; + bytes proposer; + bytes prevHash; + bytes voteHash; + bytes nextValidators; + bytes patchTxHash; + bytes txHash; + bytes logsBloom; + SPR spr; + bool isSPREmpty; // add to check whether SPR is an empty struct + // It will not be included in serializing thereafter + } + + // TS = Timestamp + Signature + struct TS { + uint256 timestamp; + bytes signature; + } + + // BPSI = blockPartSetID + struct BPSI { + uint256 n; + bytes b; + } + + struct Votes { + uint256 round; + BPSI blockPartSetID; + TS[] ts; + } + + struct BlockWitness { + uint256 height; + bytes[] witnesses; + } + + struct EventProof { + uint256 index; + bytes[] eventMptNode; + } + + struct BlockUpdate { + BlockHeader bh; + Votes votes; + bytes[] validators; + } + + struct ReceiptProof { + uint256 index; + bytes[] txReceipts; + EventProof[] ep; + } + + struct BlockProof { + BlockHeader bh; + BlockWitness bw; + } + + struct RelayMessage { + BlockUpdate[] buArray; + BlockProof bp; + bool isBPEmpty; // add to check in a case BlockProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + ReceiptProof[] rp; + bool isRPEmpty; // add to check in a case ReceiptProof is an empty struct + // when RLP RelayMessage, this field will not be serialized + } + + /** + * @Notice List of ALL Structs being used by a BSH contract + */ + enum ServiceType { + REQUEST_COIN_TRANSFER, + REQUEST_COIN_REGISTER, + REPONSE_HANDLE_SERVICE, + UNKNOWN_TYPE + } + + struct PendingTransferCoin { + string from; + string to; + string[] coinNames; + uint256[] amounts; + uint256[] fees; + } + + struct TransferCoin { + string from; + string to; + Asset[] assets; + } + + struct Asset { + string coinName; + uint256 value; + } + + struct AssetTransferDetail { + string coinName; + uint256 value; + uint256 fee; + } + + struct Response { + uint256 code; + string message; + } + + struct ServiceMessage { + ServiceType serviceType; + bytes data; + } + + struct Coin { + uint256 id; + string symbol; + uint256 decimals; + } + + struct Balance { + uint256 lockedBalance; + uint256 refundableBalance; + } + + struct Request { + string serviceName; + address bsh; + } + + /** + * @Notice List of ALL Structs being used by a BMC contract + */ + + struct VerifierStats { + uint256 heightMTA; // MTA = Merkle Trie Accumulator + uint256 offsetMTA; + uint256 lastHeight; // Block height of last verified message which is BTP-Message contained + bytes extra; + } + + struct Service { + string svc; + address addr; + } + + struct Verifier { + string net; + address addr; + } + + struct Route { + string dst; // BTP Address of destination BMC + string next; // BTP Address of a BMC before reaching dst BMC + } + + struct Link { + address[] relays; // Address of multiple Relays handle for this link network + uint256 rxSeq; + uint256 txSeq; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 maxAggregation; + uint256 delayLimit; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rxHeight; + uint256 rxHeightSrc; + bool isConnected; + } + + struct LinkStats { + uint256 rxSeq; + uint256 txSeq; + VerifierStats verifier; + RelayStats[] relays; + uint256 relayIdx; + uint256 rotateHeight; + uint256 rotateTerm; + uint256 delayLimit; + uint256 maxAggregation; + uint256 rxHeightSrc; + uint256 rxHeight; + uint256 blockIntervalSrc; + uint256 blockIntervalDst; + uint256 currentHeight; + } + + struct RelayStats { + address addr; + uint256 blockCount; + uint256 msgCount; + } + + struct BMCMessage { + string src; // an address of BMC (i.e. btp://1234.PARA/0x1234) + string dst; // an address of destination BMC + string svc; // service name of BSH + int256 sn; // sequence number of BMC + bytes message; // serializef Service Message from BSH + } + + struct Connection { + string from; + string to; + } + + struct EventMessage { + string eventType; + Connection conn; + } + + struct BMCService { + string serviceType; + bytes payload; + } + + struct GatherFeeMessage { + string fa; // BTP address of Fee Aggregator + string[] svcs; // a list of services + } +} diff --git a/solidity/nativecoinERC20/contracts/test/BMC.sol b/solidity/nativecoinERC20/contracts/test/BMC.sol new file mode 100644 index 00000000..8f977373 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/BMC.sol @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../interfaces/IBSH.sol"; +import "../interfaces/IBMCPeriphery.sol"; +import "../interfaces/IBMV.sol"; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/EncodeBase64.sol"; +import "../libraries/DecodeBase64.sol"; +import "../libraries/Types.sol"; + +contract BMC is IBMCPeriphery { + using ParseAddress for address; + using ParseAddress for string; + using RLPDecodeStruct for bytes; + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.EventMessage; + using RLPEncodeStruct for Types.Response; + using String for string; + + uint256 internal constant RC_OK = 0; + uint256 internal constant RC_ERR = 1; + + event Message( + string _next, // an address of the next BMC (it could be a destination BMC) + uint256 _seq, // a sequence number of BMC (NOT sequence number of BSH) + bytes _msg + ); + + // emit EVENT to announce link/unlink service + event Event(string _next, uint256 _seq, bytes _msg); + + event ErrorOnBTPError( + string _svc, + int256 _sn, + uint256 _code, + string _errMsg, + uint256 _svcErrCode, + string _svcErrMsg + ); + + mapping(address => bool) private _owners; + uint256 private numOfOwner; + + mapping(string => address) internal bshServices; + mapping(string => address) private bmvServices; + mapping(string => string) private connectedBMC; + mapping(address => Types.RelayStats) private relayStats; + mapping(string => string) private routes; + mapping(string => Types.Link) internal links; // should be private, temporarily set internal for testing + mapping(string => string[]) private reachable; + string[] private listBMVNames; + string[] private listBSHNames; + string[] private listRouteKeys; + string[] private listLinkNames; + address[] private addrs; + + string public bmcAddress; // a network address BMV, i.e. btp://1234.pra + uint256 private numOfBMVService; + uint256 private numOfBSHService; + uint256 private numOfLinks; + uint256 private numOfRoutes; + + uint256 private constant BLOCK_INTERVAL_MSEC = 1000; + uint256 internal constant UNKNOWN_ERR = 0; + uint256 internal constant BMC_ERR = 10; + uint256 internal constant BMV_ERR = 25; + uint256 internal constant BSH_ERR = 40; + uint256 private constant DECIMAL_PRECISION = 10**6; + + modifier owner { + require(_owners[msg.sender] == true, "BMCRevertUnauthorized"); + _; + } + + constructor(string memory _network) { + bmcAddress = string("btp://").concat(_network).concat("/").concat( + address(this).toString() + ); + _owners[msg.sender] = true; + numOfOwner++; + } + + /***************************************************************************************** + + *****************************************************************************************/ + function getBmcBtpAddress() external view override returns (string memory) { + return bmcAddress; + } + + function handleRelayMessage(string calldata _prev, string calldata _msg) + external + override + {} + + function handleMessage(string calldata _prev, Types.BMCMessage memory _msg) + internal + { + if (_msg.svc.compareTo("bmc")) { + Types.BMCService memory _sm; + try this.tryDecodeBMCService(_msg.message) returns ( + Types.BMCService memory ret + ) { + _sm = ret; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + } + if (_sm.serviceType.compareTo("FeeGathering")) { + Types.GatherFeeMessage memory _gatherFee; + try this.tryDecodeGatherFeeMessage(_sm.payload) returns ( + Types.GatherFeeMessage memory ret + ) { + _gatherFee = ret; + } catch { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertParseFailure"); + } + for (uint256 i = 0; i < _gatherFee.svcs.length; i++) { + // If 'svc' not found, ignore + if (bshServices[_gatherFee.svcs[i]] != address(0)) { + try + IBSH(bshServices[_gatherFee.svcs[i]]) + .handleFeeGathering( + _gatherFee.fa, + _gatherFee.svcs[i] + ) + {} catch { + // If BSH contract throws a revert error, ignore and continue + } + } + } + } + } else { + if (bshServices[_msg.svc] == address(0)) { + _sendError(_prev, _msg, BMC_ERR, "BMCRevertNotExistsBSH"); + return; + } + + if (_msg.sn >= 0) { + (string memory _net, ) = _msg.src.splitBTPAddress(); + try + IBSH(bshServices[_msg.svc]).handleBTPMessage( + _net, + _msg.svc, + uint256(_msg.sn), + _msg.message + ) + {} catch Error(string memory _error) { + _sendError(_prev, _msg, BSH_ERR, _error); + } + } else { + Types.Response memory _errMsg = _msg.message.decodeResponse(); + try + IBSH(bshServices[_msg.svc]).handleBTPError( + _msg.src, + _msg.svc, + uint256(int256(_msg.sn) * -1), + _errMsg.code, + _errMsg.message + ) + {} catch Error(string memory _error) { + emit ErrorOnBTPError( + _msg.svc, + int256(_msg.sn) * -1, + _errMsg.code, + _errMsg.message, + BSH_ERR, + _error + ); + } catch (bytes memory _error) { + emit ErrorOnBTPError( + _msg.svc, + int256(_msg.sn) * -1, + _errMsg.code, + _errMsg.message, + UNKNOWN_ERR, + string(_error) + ); + } + } + } + } + + // @dev Solidity does not allow using try_catch with internal function + // Thus, work-around solution is the followings + // If there is any error throwing, BMC contract can catch it, then reply back a RC_ERR Response + function tryDecodeBMCService(bytes calldata _msg) + external + pure + returns (Types.BMCService memory) + { + return _msg.decodeBMCService(); + } + + function tryDecodeGatherFeeMessage(bytes calldata _msg) + external + pure + returns (Types.GatherFeeMessage memory) + { + return _msg.decodeGatherFeeMessage(); + } + + function _sendMessage(string memory _to, bytes memory _serializedMsg) + internal + { + links[_to].txSeq += 1; + emit Message(_to, links[_to].txSeq, _serializedMsg); + } + + function _sendError( + string calldata _prev, + Types.BMCMessage memory _message, + uint256 _errCode, + string memory _errMsg + ) internal { + if (_message.sn > 0) { + bytes memory _serializedMsg = + Types + .BMCMessage( + bmcAddress, + _message + .src, + _message + .svc, + int256(_message.sn) * -1, + Types + .ServiceMessage( + Types + .ServiceType + .REPONSE_HANDLE_SERVICE, + Types.Response(_errCode, _errMsg).encodeResponse() + ) + .encodeServiceMessage() + ) + .encodeBMCMessage(); + _sendMessage(_prev, _serializedMsg); + } + } + + /** + @notice Send the message to a specific network. + @dev Caller must be an registered BSH. + @param _to Network Address of destination network + @param _svc Name of the service + @param _sn Serial number of the message, it should be positive + @param _msg Serialized bytes of Service Message + */ + function sendMessage( + string memory _to, + string memory _svc, + uint256 _sn, + bytes memory _msg + ) external override { + require( + msg.sender == address(this) || bshServices[_svc] == msg.sender, + "BMCRevertUnauthorized" + ); + require(_sn >= 0, "BMCRevertInvalidSN"); + // In case BSH sends a REQUEST_COIN_TRANSFER, + // but '_to' is a network which is not supported by BMC + // revert() therein + if (bmvServices[_to] == address(0)) { + revert("BMCRevertNotExistsBMV"); + } + string memory _toBMC = connectedBMC[_to]; + bytes memory _rlp = + Types + .BMCMessage(bmcAddress, _toBMC, _svc, int256(_sn), _msg) + .encodeBMCMessage(); + if (_svc.compareTo("_EVENT")) { + links[_toBMC].txSeq += 1; + emit Event(_toBMC, links[_toBMC].txSeq, _rlp); + } else { + links[_toBMC].txSeq += 1; + emit Message(_toBMC, links[_toBMC].txSeq, _rlp); + } + } + + /** + @notice Add the smart contract for the service. + @dev Caller must be an operator of BTP network. + @param _svc Name of the service + @param _addr Service's contract address + */ + function addService(string memory _svc, address _addr) external owner { + require(_addr != address(0), "BMCRevertInvalidAddress"); + require(bshServices[_svc] == address(0), "BMCRevertAlreadyExistsBSH"); + bshServices[_svc] = _addr; + listBSHNames.push(_svc); + } + + /** + @notice Registers BMV for the network. + @dev Caller must be an operator of BTP network. + @param _net Network Address of the blockchain + @param _addr Address of BMV + */ + function addVerifier(string memory _net, address _addr) external owner { + require(bmvServices[_net] == address(0), "BMCRevertAlreadyExistsBMV"); + bmvServices[_net] = _addr; + listBMVNames.push(_net); + numOfBMVService++; + } + + /** + @notice Initializes status information for the link. + @dev Caller must be an operator of BTP network. + @param _link BTP Address of connected BMC + */ + function addLink(string calldata _link) external owner { + string memory _net; + (_net, ) = _link.splitBTPAddress(); + require(bmvServices[_net] != address(0), "BMCRevertNotExistsBMV"); + require( + links[_link].isConnected == false, + "BMCRevertAlreadyExistsLink" + ); + links[_link] = Types.Link( + new address[](0), + 0, + 0, + BLOCK_INTERVAL_MSEC, + 0, + 10, + 3, + 0, + 0, + 0, + 0, + true + ); + listLinkNames.push(_link); + connectedBMC[_net] = _link; + numOfLinks++; + + // propagate an event "LINK" + propagateEvent("Link", _link); + } + + function propagateEvent(string memory _eventType, string calldata _link) + private + {} + + /* + @notice Get status of BMC. + @param _link BTP Address of the connected BMC. + @return tx_seq Next sequence number of the next sending message. + @return rx_seq Next sequence number of the message to receive. + @return verifier VerifierStatus Object contains status information of the BMV. + */ + function getStatus(string calldata _link) + external + view + override + returns (Types.LinkStats memory _linkStats) + { + _linkStats.txSeq = links[_link].txSeq; + } +} diff --git a/solidity/nativecoinERC20/contracts/test/CheckParseAddress.sol b/solidity/nativecoinERC20/contracts/test/CheckParseAddress.sol new file mode 100644 index 00000000..b319482e --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/CheckParseAddress.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "../libraries/ParseAddress.sol"; + +contract CheckParseAddress { + using ParseAddress for address; + using ParseAddress for string; + + function convertAddressToString(address _addr) + external + pure + returns (string memory strAddr) + { + strAddr = _addr.toString(); + } + + function convertStringToAddress(string calldata _addr) + external + pure + returns (address addr) + { + addr = _addr.parseAddress(); + } +} diff --git a/solidity/nativecoinERC20/contracts/test/EncodeMessage.sol b/solidity/nativecoinERC20/contracts/test/EncodeMessage.sol new file mode 100644 index 00000000..2dc4a2b2 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/EncodeMessage.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../libraries/ParseAddress.sol"; +import "../libraries/RLPEncodeStruct.sol"; +import "../libraries/RLPDecodeStruct.sol"; +import "../libraries/String.sol"; +import "../libraries/Types.sol"; + +contract EncodeMessage { + using RLPEncodeStruct for Types.BMCMessage; + using RLPEncodeStruct for Types.ServiceMessage; + using RLPEncodeStruct for Types.TransferCoin; + using RLPEncodeStruct for Types.Response; + using RLPEncodeStruct for Types.GatherFeeMessage; + using RLPEncodeStruct for Types.BMCService; + using RLPEncodeStruct for string[]; + using ParseAddress for address; + using ParseAddress for string; + using String for string; + + function encodeTransferFeesBMCMessage( + string memory _fromBMC, + string memory _toBMC, + string memory _toAddress, + string memory _svc, + int256 _sn, + address _bsh, + Types.Asset[] memory _fees + ) external pure returns (bytes memory) { + (, string memory _to) = _toAddress.splitBTPAddress(); + return + Types + .BMCMessage( + _fromBMC, + _toBMC, + _svc, + _sn, + encodeServiceMessage(_bsh.toString(), _to, _fees) + ) + .encodeBMCMessage(); + } + + function encodeBMCService(string calldata _fa, string[] memory _svcs) + external + pure + returns (bytes memory) + { + return + Types + .BMCService( + "FeeGathering", + Types.GatherFeeMessage(_fa, _svcs).encodeGatherFeeMessage() + ) + .encodeBMCService(); + } + + function encodeResponseBMCMessage( + string memory _from, + string memory _to, + string memory _svc, + int256 _sn, + uint256 _code, + string memory _msg + ) external view returns (bytes memory) { + return + Types + .BMCMessage( + _from, + _to, + _svc, + _sn, + this.encodeResponseMsg( + Types.ServiceType.REPONSE_HANDLE_SERVICE, + _code, + _msg + ) + ) + .encodeBMCMessage(); + } + + function hashCoinName(string memory _coinName) + external + pure + returns (uint256) + { + return uint256(keccak256(abi.encodePacked(_coinName))); + } + + function encodeResponseMsg( + Types.ServiceType _serviceType, + uint256 _code, + string calldata _msg + ) external pure returns (bytes memory) { + return + Types + .ServiceMessage( + _serviceType, + Types.Response(_code, _msg).encodeResponse() + ) + .encodeServiceMessage(); + } + + function encodeBatchTransferMsgWithAddress( + string calldata _from, + address _to, + Types.Asset[] memory _assets + ) external pure returns (bytes memory) { + return encodeServiceMessage(_from, _to.toString(), _assets); + } + + function encodeBatchTransferMsgWithStringAddress( + string calldata _from, + string calldata _to, + Types.Asset[] memory _assets + ) external pure returns (bytes memory) { + return encodeServiceMessage(_from, _to, _assets); + } + + function encodeTransferMsgWithAddress( + string calldata _from, + address _to, + string memory _coinName, + uint256 _value + ) external pure returns (bytes memory) { + Types.Asset[] memory asset = new Types.Asset[](1); + asset[0] = Types.Asset(_coinName, _value); + return encodeServiceMessage(_from, _to.toString(), asset); + } + + function encodeTransferMsgWithStringAddress( + string calldata _from, + string calldata _to, + string calldata _coinName, + uint256 _value + ) external pure returns (bytes memory) { + Types.Asset[] memory asset = new Types.Asset[](1); + asset[0] = Types.Asset(_coinName, _value); + return encodeServiceMessage(_from, _to, asset); + } + + function encodeServiceMessage( + string memory _from, + string memory _to, + Types.Asset[] memory _asset + ) private pure returns (bytes memory) { + return + Types + .ServiceMessage( + Types + .ServiceType + .REQUEST_COIN_TRANSFER, + Types.TransferCoin(_from, _to, _asset).encodeTransferCoinMsg() + ) + .encodeServiceMessage(); + } +} diff --git a/solidity/nativecoinERC20/contracts/test/Holder.sol b/solidity/nativecoinERC20/contracts/test/Holder.sol new file mode 100644 index 00000000..5e8f77ee --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/Holder.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155Holder.sol"; +import "../interfaces/IBSHPeriphery.sol"; +import "../interfaces/IBSHCore.sol"; +import "../libraries/String.sol"; + +contract Holder is ERC1155Holder { + IBSHPeriphery private bshp; + IBSHCore private bshc; + using String for string; + + function deposit() external payable {} + + function addBSHContract(address _bshp, address _bshc) external { + bshp = IBSHPeriphery(_bshp); + bshc = IBSHCore(_bshc); + } + + function callTransfer( + string calldata _coinName, + uint256 _value, + string calldata _to + ) external { + bshc.transferWrappedCoin(_coinName, _value, _to); + } + + // function isSendingNative(string[] memory _coinNames) + // private + // pure + // returns (int256) + // { + // for (uint256 i = 0; i < _coinNames.length; i++) { + // if (_coinNames[i].compareTo("PARA")) { + // return int256(i); + // } + // } + // return -1; + // } +} diff --git a/solidity/nativecoinERC20/contracts/test/MockBMC.sol b/solidity/nativecoinERC20/contracts/test/MockBMC.sol new file mode 100644 index 00000000..9f9eff1d --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/MockBMC.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "./BMC.sol"; + +contract MockBMC is BMC { + constructor(string memory _network) BMC(_network) {} + + function receiveRequest( + string calldata _src, + string memory _dst, + string memory _svc, + uint256 _sn, + bytes calldata _msg + ) external { + handleMessage( + _src, + Types.BMCMessage(_src, _dst, _svc, int256(_sn), _msg) + ); + } + + function receiveResponse( + string calldata _from, + string memory _svc, + uint256 _sn, + bytes calldata _msg + ) external { + IBSH(bshServices[_svc]).handleBTPMessage(_from, _svc, _sn, _msg); + } + + function getBalance(address _addr) external view returns (uint256) { + return _addr.balance; + } +} diff --git a/solidity/nativecoinERC20/contracts/test/MockBSHCore.sol b/solidity/nativecoinERC20/contracts/test/MockBSHCore.sol new file mode 100644 index 00000000..848f4806 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/MockBSHCore.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../BSHCore.sol"; + +contract MockBSHCore is BSHCore { + function mintMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _mint(_acc, _value); + } + + function burnMock( + address _acc, + uint256 _id, + uint256 _value + ) external { + _burn(_acc, _value); + } + + function setAggregationFee(string calldata _coinName, uint256 _value) + external + { + aggregationFee[_coinName] += _value; + } + + function clearAggregationFee() external { + for (uint256 i = 0; i < coinsName.length; i++) { + delete aggregationFee[coinsName[i]]; + } + } + + function clearBSHPerifSetting() external { + bshPeriphery = IBSHPeriphery(address(0)); + } + + function setRefundableBalance( + address _acc, + string calldata _coinName, + uint256 _value + ) external { + balances[_acc][_coinName].refundableBalance += _value; + } +} diff --git a/solidity/nativecoinERC20/contracts/test/MockBSHPerif.sol b/solidity/nativecoinERC20/contracts/test/MockBSHPerif.sol new file mode 100644 index 00000000..8fe1a50f --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/MockBSHPerif.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; +pragma experimental ABIEncoderV2; +import "../BSHPeriphery.sol"; +import "../BSHCore.sol"; + +contract MockBSHPeriphery is BSHPeriphery { + using String for string; + + function getFees(uint256 _sn) + external + view + returns (Types.PendingTransferCoin memory) + { + return requests[_sn]; + } + + function getAggregationFeeOf(string calldata _coinName) + external + view + returns (uint256 _fee) + { + Types.Asset[] memory _fees = bshCore.getAccumulatedFees(); + for (uint256 i = 0; i < _fees.length; i++) { + if (_coinName.compareTo(_fees[i].coinName)) return _fees[i].value; + } + } +} diff --git a/solidity/nativecoinERC20/contracts/test/NonRefundable.sol b/solidity/nativecoinERC20/contracts/test/NonRefundable.sol new file mode 100644 index 00000000..27308a66 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/NonRefundable.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +// Instead, it has a method deposit() payable to receive coins +contract NonRefundable { + function deposit() external payable {} + + function transfer( + address _bsh, + string calldata _to, + uint256 _amt + ) external { + (bool success, bytes memory err) = + _bsh.call{value: _amt}( + abi.encodeWithSignature("transferNativeCoin(string)", _to) + ); + require(success, string(err)); + } +} diff --git a/solidity/nativecoinERC20/contracts/test/NotPayable.sol b/solidity/nativecoinERC20/contracts/test/NotPayable.sol new file mode 100644 index 00000000..cb4d6d77 --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/NotPayable.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +contract NotPayable { + +} diff --git a/solidity/nativecoinERC20/contracts/test/Refundable.sol b/solidity/nativecoinERC20/contracts/test/Refundable.sol new file mode 100644 index 00000000..79db438a --- /dev/null +++ b/solidity/nativecoinERC20/contracts/test/Refundable.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity >=0.5.0 <0.8.0; + +// This contract does not have any method +// that allows to receives coins, i.e. receive() external payable/fallback() external payable +// Instead, it has a method deposit() payable to receive coins +contract Refundable { + function deposit() external payable {} + + function transfer( + address _bsh, + string calldata _to, + uint256 _amt + ) external { + (bool success, bytes memory err) = + _bsh.call{value: _amt}( + abi.encodeWithSignature("transferNativeCoin(string)", _to) + ); + require(success, string(err)); + } + + receive() external payable {} +} diff --git a/solidity/nativecoinERC20/migrations/1_deploy_bsh.js b/solidity/nativecoinERC20/migrations/1_deploy_bsh.js new file mode 100644 index 00000000..10023126 --- /dev/null +++ b/solidity/nativecoinERC20/migrations/1_deploy_bsh.js @@ -0,0 +1,13 @@ +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const { deployProxy } = require('@openzeppelin/truffle-upgrades'); + +module.exports = async function (deployer, network) { + if (network !== "development" && network !== "dev") { + let INITIAL_SUPPLY = web3.utils.toBN(web3.utils.toWei(""+process.env.BSH_INITIAL_SUPPLY,"ether")) + await deployProxy(BSHCore, [ process.env.BSH_COIN_NAME, parseInt(process.env.BSH_COIN_FEE), parseInt(process.env.BSH_FIXED_FEE), process.env.BSH_TOKEN_NAME, process.env.BSH_TOKEN_SYMBOL, INITIAL_SUPPLY], { deployer }); + await deployProxy(BSHPeriphery, [process.env.BMC_PERIPHERY_ADDRESS, BSHCore.address, process.env.BSH_SERVICE], { deployer }); + const bshCore = await BSHCore.deployed(); + await bshCore.updateBSHPeriphery(BSHPeriphery.address); + } +}; diff --git a/solidity/nativecoinERC20/package-lock.json b/solidity/nativecoinERC20/package-lock.json new file mode 100644 index 00000000..36272240 --- /dev/null +++ b/solidity/nativecoinERC20/package-lock.json @@ -0,0 +1,10598 @@ +{ + "name": "nativecoin-erc20", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.0.tgz", + "integrity": "sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==", + "requires": { + "@babel/highlight": "^7.16.0" + } + }, + "@babel/compat-data": { + "version": "7.16.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz", + "integrity": "sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==" + }, + "@babel/generator": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.5.tgz", + "integrity": "sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA==", + "requires": { + "@babel/types": "^7.16.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.16.3", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz", + "integrity": "sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA==", + "requires": { + "@babel/compat-data": "^7.16.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.17.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz", + "integrity": "sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg==", + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz", + "integrity": "sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg==", + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-function-name": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz", + "integrity": "sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==", + "requires": { + "@babel/helper-get-function-arity": "^7.16.0", + "@babel/template": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz", + "integrity": "sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==", + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz", + "integrity": "sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==", + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz", + "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==", + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz", + "integrity": "sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ==" + }, + "@babel/helper-split-export-declaration": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz", + "integrity": "sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==", + "requires": { + "@babel/types": "^7.16.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==" + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==" + }, + "@babel/highlight": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.0.tgz", + "integrity": "sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==", + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.16.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.6.tgz", + "integrity": "sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ==" + }, + "@babel/plugin-transform-runtime": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.5.tgz", + "integrity": "sha512-gxpfS8XQWDbQ8oP5NcmpXxtEgCJkbO+W9VhZlOhr0xPyVaRjAQPOv7ZDj9fg0d5s9+NiVvMCE6gbkEkcsxwGRw==", + "requires": { + "@babel/helper-module-imports": "^7.16.0", + "@babel/helper-plugin-utils": "^7.16.5", + "babel-plugin-polyfill-corejs2": "^0.3.0", + "babel-plugin-polyfill-corejs3": "^0.4.0", + "babel-plugin-polyfill-regenerator": "^0.3.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "@babel/runtime": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.5.tgz", + "integrity": "sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.16.0.tgz", + "integrity": "sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==", + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/parser": "^7.16.0", + "@babel/types": "^7.16.0" + } + }, + "@babel/traverse": { + "version": "7.16.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.5.tgz", + "integrity": "sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ==", + "requires": { + "@babel/code-frame": "^7.16.0", + "@babel/generator": "^7.16.5", + "@babel/helper-environment-visitor": "^7.16.5", + "@babel/helper-function-name": "^7.16.0", + "@babel/helper-hoist-variables": "^7.16.0", + "@babel/helper-split-export-declaration": "^7.16.0", + "@babel/parser": "^7.16.5", + "@babel/types": "^7.16.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz", + "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==", + "requires": { + "@babel/helper-validator-identifier": "^7.15.7", + "to-fast-properties": "^2.0.0" + } + }, + "@ensdomains/address-encoder": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz", + "integrity": "sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg==", + "requires": { + "bech32": "^1.1.3", + "blakejs": "^1.1.0", + "bn.js": "^4.11.8", + "bs58": "^4.0.1", + "crypto-addr-codec": "^0.1.7", + "nano-base32": "^1.0.1", + "ripemd160": "^2.0.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "@ensdomains/ens": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@ensdomains/ens/-/ens-0.4.3.tgz", + "integrity": "sha512-btC+fGze//ml8SMNCx5DgwM8+kG2t+qDCZrqlL/2+PV4CNxnRIpR3egZ49D9FqS52PFoYLmz6MaQfl7AO3pUMA==", + "requires": { + "bluebird": "^3.5.2", + "eth-ens-namehash": "^2.0.8", + "ethereumjs-testrpc": "^6.0.3", + "ganache-cli": "^6.1.0", + "solc": "^0.4.20", + "testrpc": "0.0.1", + "web3-utils": "^1.0.0-beta.31" + } + }, + "@ensdomains/ensjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@ensdomains/ensjs/-/ensjs-2.0.1.tgz", + "integrity": "sha512-gZLntzE1xqPNkPvaHdJlV5DXHms8JbHBwrXc2xNrL1AylERK01Lj/txCCZyVQqFd3TvUO1laDbfUv8VII0qrjg==", + "requires": { + "@babel/runtime": "^7.4.4", + "@ensdomains/address-encoder": "^0.1.7", + "@ensdomains/ens": "0.4.3", + "@ensdomains/resolver": "0.2.4", + "content-hash": "^2.5.2", + "eth-ens-namehash": "^2.0.8", + "ethers": "^5.0.13", + "js-sha3": "^0.8.0" + }, + "dependencies": { + "ethers": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.5.2.tgz", + "integrity": "sha512-EF5W+6Wwcu6BqVwpgmyR5U2+L4c1FQzlM/02dkZOugN3KF0cG9bzHZP+TDJglmPm2/IzCEJDT7KBxzayk7SAHw==", + "requires": { + "@ethersproject/abi": "5.5.0", + "@ethersproject/abstract-provider": "5.5.1", + "@ethersproject/abstract-signer": "5.5.0", + "@ethersproject/address": "5.5.0", + "@ethersproject/base64": "5.5.0", + "@ethersproject/basex": "5.5.0", + "@ethersproject/bignumber": "5.5.0", + "@ethersproject/bytes": "5.5.0", + "@ethersproject/constants": "5.5.0", + "@ethersproject/contracts": "5.5.0", + "@ethersproject/hash": "5.5.0", + "@ethersproject/hdnode": "5.5.0", + "@ethersproject/json-wallets": "5.5.0", + "@ethersproject/keccak256": "5.5.0", + "@ethersproject/logger": "5.5.0", + "@ethersproject/networks": "5.5.1", + "@ethersproject/pbkdf2": "5.5.0", + "@ethersproject/properties": "5.5.0", + "@ethersproject/providers": "5.5.1", + "@ethersproject/random": "5.5.0", + "@ethersproject/rlp": "5.5.0", + "@ethersproject/sha2": "5.5.0", + "@ethersproject/signing-key": "5.5.0", + "@ethersproject/solidity": "5.5.0", + "@ethersproject/strings": "5.5.0", + "@ethersproject/transactions": "5.5.0", + "@ethersproject/units": "5.5.0", + "@ethersproject/wallet": "5.5.0", + "@ethersproject/web": "5.5.1", + "@ethersproject/wordlists": "5.5.0" + } + } + } + }, + "@ensdomains/resolver": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@ensdomains/resolver/-/resolver-0.2.4.tgz", + "integrity": "sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA==" + }, + "@ethereumjs/common": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.0.tgz", + "integrity": "sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA==", + "requires": { + "crc-32": "^1.2.0", + "ethereumjs-util": "^7.1.3" + } + }, + "@ethereumjs/tx": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.4.0.tgz", + "integrity": "sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw==", + "requires": { + "@ethereumjs/common": "^2.6.0", + "ethereumjs-util": "^7.1.3" + } + }, + "@ethersproject/abi": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.5.0.tgz", + "integrity": "sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/abstract-provider": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz", + "integrity": "sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0" + } + }, + "@ethersproject/abstract-signer": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz", + "integrity": "sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/address": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.5.0.tgz", + "integrity": "sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/rlp": "^5.5.0" + } + }, + "@ethersproject/base64": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.5.0.tgz", + "integrity": "sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA==", + "requires": { + "@ethersproject/bytes": "^5.5.0" + } + }, + "@ethersproject/basex": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.5.0.tgz", + "integrity": "sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/properties": "^5.5.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.5.0.tgz", + "integrity": "sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "bn.js": "^4.11.9" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "@ethersproject/bytes": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.5.0.tgz", + "integrity": "sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/constants": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.5.0.tgz", + "integrity": "sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ==", + "requires": { + "@ethersproject/bignumber": "^5.5.0" + } + }, + "@ethersproject/contracts": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.5.0.tgz", + "integrity": "sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg==", + "requires": { + "@ethersproject/abi": "^5.5.0", + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/transactions": "^5.5.0" + } + }, + "@ethersproject/hash": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.5.0.tgz", + "integrity": "sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/hdnode": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.5.0.tgz", + "integrity": "sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "@ethersproject/json-wallets": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz", + "integrity": "sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ==", + "requires": { + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/pbkdf2": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "@ethersproject/keccak256": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.5.0.tgz", + "integrity": "sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.5.0.tgz", + "integrity": "sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg==" + }, + "@ethersproject/networks": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.5.1.tgz", + "integrity": "sha512-tYRDM4zZtSUcKnD4UMuAlj7SeXH/k5WC4SP2u1Pn57++JdXHkRu2zwNkgNogZoxHzhm9Q6qqurDBVptHOsW49Q==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz", + "integrity": "sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/sha2": "^5.5.0" + } + }, + "@ethersproject/properties": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.5.0.tgz", + "integrity": "sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA==", + "requires": { + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/providers": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.5.1.tgz", + "integrity": "sha512-2zdD5sltACDWhjUE12Kucg2PcgM6V2q9JMyVvObtVGnzJu+QSmibbP+BHQyLWZUBfLApx2942+7DC5D+n4wBQQ==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/basex": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/networks": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/web": "^5.5.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "@ethersproject/random": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.5.0.tgz", + "integrity": "sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/rlp": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.5.0.tgz", + "integrity": "sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/sha2": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.5.0.tgz", + "integrity": "sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "hash.js": "1.1.7" + } + }, + "@ethersproject/signing-key": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.5.0.tgz", + "integrity": "sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "@ethersproject/solidity": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.5.0.tgz", + "integrity": "sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/sha2": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/strings": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.5.0.tgz", + "integrity": "sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/transactions": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.5.0.tgz", + "integrity": "sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA==", + "requires": { + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/rlp": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0" + } + }, + "@ethersproject/units": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.5.0.tgz", + "integrity": "sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag==", + "requires": { + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/constants": "^5.5.0", + "@ethersproject/logger": "^5.5.0" + } + }, + "@ethersproject/wallet": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.5.0.tgz", + "integrity": "sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q==", + "requires": { + "@ethersproject/abstract-provider": "^5.5.0", + "@ethersproject/abstract-signer": "^5.5.0", + "@ethersproject/address": "^5.5.0", + "@ethersproject/bignumber": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/hdnode": "^5.5.0", + "@ethersproject/json-wallets": "^5.5.0", + "@ethersproject/keccak256": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/random": "^5.5.0", + "@ethersproject/signing-key": "^5.5.0", + "@ethersproject/transactions": "^5.5.0", + "@ethersproject/wordlists": "^5.5.0" + } + }, + "@ethersproject/web": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.5.1.tgz", + "integrity": "sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg==", + "requires": { + "@ethersproject/base64": "^5.5.0", + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@ethersproject/wordlists": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.5.0.tgz", + "integrity": "sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q==", + "requires": { + "@ethersproject/bytes": "^5.5.0", + "@ethersproject/hash": "^5.5.0", + "@ethersproject/logger": "^5.5.0", + "@ethersproject/properties": "^5.5.0", + "@ethersproject/strings": "^5.5.0" + } + }, + "@openzeppelin/contracts": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.4.2.tgz", + "integrity": "sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA==" + }, + "@openzeppelin/contracts-upgradeable": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.2.tgz", + "integrity": "sha512-mDlBS17ymb2wpaLcrqRYdnBAmP1EwqhOXMvqWk2c5Q1N1pm5TkiCtXM9Xzznh4bYsQBq0aIWEkFFE2+iLSN1Tw==" + }, + "@openzeppelin/truffle-upgrades": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.11.0.tgz", + "integrity": "sha512-ee9QWWbpbi3wS6FWpgFjgcj0oUUQ0iUmv81jNrKB73R2WHtP+2ChHpFd/0qsLfQWa7bMAeLCkGhLcEy/WoQLjw==", + "requires": { + "@openzeppelin/upgrades-core": "^1.10.0", + "@truffle/contract": "^4.3.26", + "solidity-ast": "^0.4.15" + } + }, + "@openzeppelin/upgrades-core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.10.0.tgz", + "integrity": "sha512-N20t1i1wlHrVmu3etVZLiaRxT6XLkCrO9gIo4mUZNpsaVftl8V+WBu8o940AjoYXvzTEqj7O0re2DSFzjpkRBw==", + "requires": { + "bn.js": "^5.1.2", + "cbor": "^8.0.0", + "chalk": "^4.1.0", + "compare-versions": "^3.6.0", + "debug": "^4.1.1", + "ethereumjs-util": "^7.0.3", + "proper-lockfile": "^4.1.1", + "solidity-ast": "^0.4.15" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@solidity-parser/parser": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.0.tgz", + "integrity": "sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@truffle/abi-utils": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@truffle/abi-utils/-/abi-utils-0.2.5.tgz", + "integrity": "sha512-eKDIn9LqUFP8MnHVohe8ncuza4p9bszz1NtJWc+sr5zUogtmWnnf8Ajyj7JJpNKhLNDVZVbLowVEVxWzSSpMHw==", + "requires": { + "change-case": "3.0.2", + "faker": "^5.3.1", + "fast-check": "^2.12.1" + } + }, + "@truffle/blockchain-utils": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@truffle/blockchain-utils/-/blockchain-utils-0.0.31.tgz", + "integrity": "sha512-BFo/nyxwhoHqPrqBQA1EAmSxeNnspGLiOCMa9pAL7WYSjyNBlrHaqCMO/F2O87G+NUK/u06E70DiSP2BFP0ZZw==" + }, + "@truffle/codec": { + "version": "0.11.21", + "resolved": "https://registry.npmjs.org/@truffle/codec/-/codec-0.11.21.tgz", + "integrity": "sha512-ZDzaEPCUFWmQuFsXA3KzXmU4w4lpxSWZTGUcEDHxn6IqAmL7FY8mmdbR6LQ1wTRAa9oPf84PcehMpTNY47HVcg==", + "requires": { + "@truffle/abi-utils": "^0.2.5", + "@truffle/compile-common": "^0.7.23", + "big.js": "^5.2.2", + "bn.js": "^5.1.3", + "cbor": "^5.1.0", + "debug": "^4.3.1", + "lodash.clonedeep": "^4.5.0", + "lodash.escaperegexp": "^4.1.2", + "lodash.partition": "^4.6.0", + "lodash.sum": "^4.0.2", + "semver": "^7.3.4", + "utf8": "^3.0.0", + "web3-utils": "1.5.3" + }, + "dependencies": { + "bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + }, + "cbor": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz", + "integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==", + "requires": { + "bignumber.js": "^9.0.1", + "nofilter": "^1.0.4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "nofilter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz", + "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==" + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, + "@truffle/compile-common": { + "version": "0.7.23", + "resolved": "https://registry.npmjs.org/@truffle/compile-common/-/compile-common-0.7.23.tgz", + "integrity": "sha512-LWzeboJ9HmSZVgx5DMmKArOo96V4QZhS/+8beDOfeNT1W4QeKfkuVbAM0R77cXjiLnUsNjjFVXehnco6HiF8ww==", + "requires": { + "@truffle/error": "^0.0.14", + "colors": "^1.4.0" + } + }, + "@truffle/contract": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@truffle/contract/-/contract-4.4.1.tgz", + "integrity": "sha512-KVpG9alKxdNzWOcRN97crZXXmmnnShq1SkM9hQN2fOckszzrmy6ctOhnZKNAb8tzfHBgODDCmiGQbTqaYizcrA==", + "requires": { + "@ensdomains/ensjs": "^2.0.1", + "@truffle/blockchain-utils": "^0.0.31", + "@truffle/contract-schema": "^3.4.4", + "@truffle/debug-utils": "^6.0.2", + "@truffle/error": "^0.0.14", + "@truffle/interface-adapter": "^0.5.8", + "bignumber.js": "^7.2.1", + "debug": "^4.3.1", + "ethers": "^4.0.32", + "web3": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-promievent": "1.5.3", + "web3-eth-abi": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "@truffle/contract-schema": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@truffle/contract-schema/-/contract-schema-3.4.4.tgz", + "integrity": "sha512-xWgrm6WRM2jmT04w7dP7aVbS2qyP9XPmH/mybQtFXMjJ/8BZlp0yltC8QOs8sGl6q8Ws7acp19YtRkLdK6SsmQ==", + "requires": { + "ajv": "^6.10.0", + "debug": "^4.3.1" + } + }, + "@truffle/debug-utils": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@truffle/debug-utils/-/debug-utils-6.0.2.tgz", + "integrity": "sha512-gMZ2IHPS9cvWGinUwVMoZedOYJz4sSekUXGC5FQkBnR0XDVriPmuja4rdgXhkA9EFSqZdXu4JAL8IiEHp/1YIw==", + "requires": { + "@truffle/codec": "^0.11.21", + "@trufflesuite/chromafi": "^2.2.2", + "bn.js": "^5.1.3", + "chalk": "^2.4.2", + "debug": "^4.3.1", + "highlightjs-solidity": "^2.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@truffle/error": { + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@truffle/error/-/error-0.0.14.tgz", + "integrity": "sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA==" + }, + "@truffle/hdwallet-provider": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@truffle/hdwallet-provider/-/hdwallet-provider-1.7.0.tgz", + "integrity": "sha512-nT7BPJJ2jPCLJc5uZdVtRnRMny5he5d3kO9Hi80ZSqe5xlnK905grBptM/+CwOfbeqHKQirI1btwm6r3wIBM8A==", + "requires": { + "@ethereumjs/common": "^2.4.0", + "@ethereumjs/tx": "^3.3.0", + "@trufflesuite/web3-provider-engine": "15.0.14", + "eth-sig-util": "^3.0.1", + "ethereum-cryptography": "^0.1.3", + "ethereum-protocol": "^1.0.1", + "ethereumjs-util": "^6.1.0", + "ethereumjs-wallet": "^1.0.1" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "@truffle/interface-adapter": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@truffle/interface-adapter/-/interface-adapter-0.5.8.tgz", + "integrity": "sha512-vvy3xpq36oLgjjy8KE9l2Jabg3WcGPOt18tIyMfTQX9MFnbHoQA2Ne2i8xsd4p6KfxIqSjAB53Q9/nScAqY0UQ==", + "requires": { + "bn.js": "^5.1.3", + "ethers": "^4.0.32", + "web3": "1.5.3" + } + }, + "@trufflesuite/chromafi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz", + "integrity": "sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA==", + "requires": { + "ansi-mark": "^1.0.0", + "ansi-regex": "^3.0.0", + "array-uniq": "^1.0.3", + "camelcase": "^4.1.0", + "chalk": "^2.3.2", + "cheerio": "^1.0.0-rc.2", + "detect-indent": "^5.0.0", + "he": "^1.1.1", + "highlight.js": "^10.4.1", + "lodash.merge": "^4.6.2", + "min-indent": "^1.0.0", + "strip-ansi": "^4.0.0", + "strip-indent": "^2.0.0", + "super-split": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@trufflesuite/eth-json-rpc-filters": { + "version": "4.1.2-1", + "resolved": "https://registry.npmjs.org/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz", + "integrity": "sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ==", + "requires": { + "@trufflesuite/eth-json-rpc-middleware": "^4.4.2-0", + "await-semaphore": "^0.1.3", + "eth-query": "^2.1.2", + "json-rpc-engine": "^5.1.3", + "lodash.flatmap": "^4.5.0", + "safe-event-emitter": "^1.0.1" + } + }, + "@trufflesuite/eth-json-rpc-infura": { + "version": "4.0.3-0", + "resolved": "https://registry.npmjs.org/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz", + "integrity": "sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw==", + "requires": { + "@trufflesuite/eth-json-rpc-middleware": "^4.4.2-1", + "cross-fetch": "^2.1.1", + "eth-json-rpc-errors": "^1.0.1", + "json-rpc-engine": "^5.1.3" + }, + "dependencies": { + "eth-json-rpc-errors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", + "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + } + } + }, + "@trufflesuite/eth-json-rpc-middleware": { + "version": "4.4.2-1", + "resolved": "https://registry.npmjs.org/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz", + "integrity": "sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA==", + "requires": { + "@trufflesuite/eth-sig-util": "^1.4.2", + "btoa": "^1.2.1", + "clone": "^2.1.1", + "eth-json-rpc-errors": "^1.0.1", + "eth-query": "^2.1.2", + "ethereumjs-block": "^1.6.0", + "ethereumjs-tx": "^1.3.7", + "ethereumjs-util": "^5.1.2", + "ethereumjs-vm": "^2.6.0", + "fetch-ponyfill": "^4.0.0", + "json-rpc-engine": "^5.1.3", + "json-stable-stringify": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "eth-json-rpc-errors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", + "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "@trufflesuite/eth-sig-util": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz", + "integrity": "sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA==", + "requires": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^5.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "@trufflesuite/web3-provider-engine": { + "version": "15.0.14", + "resolved": "https://registry.npmjs.org/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.14.tgz", + "integrity": "sha512-6/LoWvNMxYf0oaYzJldK2a9AdnkAdIeJhHW4nuUBAeO29eK9xezEaEYQ0ph1QRTaICxGxvn+1Azp4u8bQ8NEZw==", + "requires": { + "@ethereumjs/tx": "^3.3.0", + "@trufflesuite/eth-json-rpc-filters": "^4.1.2-1", + "@trufflesuite/eth-json-rpc-infura": "^4.0.3-0", + "@trufflesuite/eth-json-rpc-middleware": "^4.4.2-1", + "@trufflesuite/eth-sig-util": "^1.4.2", + "async": "^2.5.0", + "backoff": "^2.5.0", + "clone": "^2.0.0", + "cross-fetch": "^2.1.0", + "eth-block-tracker": "^4.4.2", + "eth-json-rpc-errors": "^2.0.2", + "ethereumjs-block": "^1.2.2", + "ethereumjs-util": "^5.1.5", + "ethereumjs-vm": "^2.3.4", + "json-stable-stringify": "^1.0.1", + "promise-to-callback": "^1.0.0", + "readable-stream": "^2.2.9", + "request": "^2.85.0", + "semaphore": "^1.0.3", + "ws": "^5.1.1", + "xhr": "^2.2.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "ws": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", + "integrity": "sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.2.tgz", + "integrity": "sha512-JepeIUPFDARgIs0zD/SKPgFsJEAF0X5/qO80llx59gOxFTboS9Amv3S+QfB7lqBId5sFXJ99BN0J6zFRvL9dDA==" + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "requires": { + "@types/node": "*" + } + }, + "abstract-leveldown": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", + "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", + "requires": { + "xtend": "~4.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==" + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "requires": { + "acorn": "^4.0.3" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + } + } + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-mark": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ansi-mark/-/ansi-mark-1.0.4.tgz", + "integrity": "sha1-HNS6jVfxXxCdaq9uycqXhsik7mw=", + "requires": { + "ansi-regex": "^3.0.0", + "array-uniq": "^1.0.3", + "chalk": "^2.3.2", + "strip-ansi": "^4.0.0", + "super-split": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "antlr4": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.7.1.tgz", + "integrity": "sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ==", + "dev": true + }, + "antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "optional": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "optional": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "optional": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "optional": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "optional": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "optional": true + }, + "ast-parents": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/ast-parents/-/ast-parents-0.0.1.tgz", + "integrity": "sha1-UI/Q8F0MSHddnszaLhdEIyYejdM=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "optional": true + }, + "async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "requires": { + "async": "^2.4.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "optional": true + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" + }, + "await-semaphore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/await-semaphore/-/await-semaphore-0.1.3.tgz", + "integrity": "sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz", + "integrity": "sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA==", + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.3.0", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz", + "integrity": "sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.0", + "core-js-compat": "^3.18.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz", + "integrity": "sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg==", + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.0" + } + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "optional": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + } + } + }, + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "big-integer": { + "version": "1.6.36", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz", + "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "bignumber.js": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-7.2.1.tgz", + "integrity": "sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "optional": true + }, + "blakejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.1.tgz", + "integrity": "sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg==" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, + "body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA==", + "requires": { + "bytes": "3.1.1", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.6", + "raw-body": "2.4.2", + "type-is": "~1.6.18" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "optional": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz", + "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==", + "requires": { + "caniuse-lite": "^1.0.30001286", + "electron-to-chromium": "^1.4.17", + "escalade": "^3.1.1", + "node-releases": "^2.0.1", + "picocolors": "^1.0.0" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz", + "integrity": "sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "optional": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" + }, + "caniuse-lite": { + "version": "1.0.30001292", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001292.tgz", + "integrity": "sha512-jnT4Tq0Q4ma+6nncYQVe7d73kmDmE9C3OGTx3MvW7lBM/eY1S1DZTMBON7dqV481RhNiS5OxD7k9JQvmDOTirw==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "requires": { + "nofilter": "^3.1.0" + } + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "change-case": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.0.2.tgz", + "integrity": "sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA==", + "requires": { + "camel-case": "^3.0.0", + "constant-case": "^2.0.0", + "dot-case": "^2.1.0", + "header-case": "^1.0.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "no-case": "^2.3.2", + "param-case": "^2.1.0", + "pascal-case": "^2.0.0", + "path-case": "^2.1.0", + "sentence-case": "^2.1.0", + "snake-case": "^2.1.0", + "swap-case": "^1.1.0", + "title-case": "^2.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "checkpoint-store": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", + "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", + "requires": { + "functional-red-black-tree": "^1.0.1" + } + }, + "cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "requires": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + } + }, + "cheerio-select": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.5.0.tgz", + "integrity": "sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==", + "requires": { + "css-select": "^4.1.3", + "css-what": "^5.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0", + "domutils": "^2.7.0" + } + }, + "chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "optional": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "requires": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "requires": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + } + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "optional": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", + "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constant-case": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", + "requires": { + "snake-case": "^2.1.0", + "upper-case": "^1.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-hash": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/content-hash/-/content-hash-2.5.2.tgz", + "integrity": "sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw==", + "requires": { + "cids": "^0.7.1", + "multicodec": "^0.5.5", + "multihashes": "^0.4.15" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "optional": true + }, + "core-js-compat": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.20.0.tgz", + "integrity": "sha512-relrah5h+sslXssTTOkvqcC/6RURifB0W5yhYBdBkaPYa5/2KBMiog3XiD+s3TwEHWxInWVv4Jx2/Lw0vng+IQ==", + "requires": { + "browserslist": "^4.19.1", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "crc-32": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz", + "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.1.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-fetch": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.5.tgz", + "integrity": "sha512-xqYAhQb4NhCJSRym03dwxpP1bYXpK3y7UN83Bo2WFi3x1Zmzn0SL/6xGoPr+gpt4WmNrgCCX3HPysvOwFOW36w==", + "requires": { + "node-fetch": "2.6.1", + "whatwg-fetch": "2.0.4" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + } + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "requires": { + "base-x": "^3.0.8", + "big-integer": "1.6.36", + "blakejs": "^1.1.0", + "bs58": "^4.0.1", + "ripemd160-min": "0.0.6", + "safe-buffer": "^5.2.0", + "sha3": "^2.1.1" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-select": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.2.0.tgz", + "integrity": "sha512-6YVG6hsH9yIb/si3Th/is8Pex7qnVHO6t7q7U6TIUnkQASGbS8tnUDBftnPynLNnuUl/r2+PTd0ekiiq7R0zJw==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^5.1.0", + "domhandler": "^4.3.0", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-5.1.0.tgz", + "integrity": "sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "deferred-leveldown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", + "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", + "requires": { + "abstract-leveldown": "~2.6.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "optional": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-indent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", + "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=" + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", + "integrity": "sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==" + }, + "domhandler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.0.tgz", + "integrity": "sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==", + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", + "requires": { + "no-case": "^2.2.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.4.26", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.26.tgz", + "integrity": "sha512-cA1YwlRzO6TGp7yd3+KAqh9Tt6Z4CuuKqsAJP6uF/H5MQryjAGDhMhnY5cEXo8MaRCczpzSBhMPdqRIodkbZYw==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "emoji-regex": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.0.0.tgz", + "integrity": "sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.1.tgz", + "integrity": "sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.1", + "is-string": "^1.0.7", + "is-weakref": "^1.0.1", + "object-inspect": "^1.11.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + }, + "dependencies": { + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + } + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.13.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-block-tracker": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", + "integrity": "sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==", + "requires": { + "@babel/plugin-transform-runtime": "^7.5.5", + "@babel/runtime": "^7.5.5", + "eth-query": "^2.1.0", + "json-rpc-random-id": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + }, + "dependencies": { + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } + } + }, + "eth-json-rpc-errors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz", + "integrity": "sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "eth-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", + "integrity": "sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=", + "requires": { + "json-rpc-random-id": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "eth-rpc-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz", + "integrity": "sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "eth-sig-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-3.0.1.tgz", + "integrity": "sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ==", + "requires": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^5.1.1", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + } + } + }, + "ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "requires": { + "js-sha3": "^0.8.0" + } + }, + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereum-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz", + "integrity": "sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg==" + }, + "ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "requires": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "ethereumjs-account": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", + "integrity": "sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA==", + "requires": { + "ethereumjs-util": "^5.0.0", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-block": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", + "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", + "requires": { + "async": "^2.0.1", + "ethereum-common": "0.2.0", + "ethereumjs-tx": "^1.2.2", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", + "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==" + }, + "ethereumjs-testrpc": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz", + "integrity": "sha512-lAxxsxDKK69Wuwqym2K49VpXtBvLEsXr1sryNG4AkvL5DomMdeCBbu3D87UEevKenLHBiT8GTjARwN6Yj039gA==", + "requires": { + "webpack": "^3.0.0" + } + }, + "ethereumjs-tx": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", + "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", + "requires": { + "ethereum-common": "^0.0.18", + "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-util": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz", + "integrity": "sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw==", + "requires": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + } + }, + "ethereumjs-vm": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", + "integrity": "sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw==", + "requires": { + "async": "^2.1.2", + "async-eventemitter": "^0.2.2", + "ethereumjs-account": "^2.0.3", + "ethereumjs-block": "~2.2.0", + "ethereumjs-common": "^1.1.0", + "ethereumjs-util": "^6.0.0", + "fake-merkle-patricia-tree": "^1.0.1", + "functional-red-black-tree": "^1.0.1", + "merkle-patricia-tree": "^2.3.2", + "rustbn.js": "~0.2.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-block": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", + "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", + "requires": { + "async": "^2.0.1", + "ethereumjs-common": "^1.5.0", + "ethereumjs-tx": "^2.1.1", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-tx": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", + "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", + "requires": { + "ethereumjs-common": "^1.5.0", + "ethereumjs-util": "^6.0.0" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + } + } + }, + "ethereumjs-wallet": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz", + "integrity": "sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA==", + "requires": { + "aes-js": "^3.1.2", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^7.1.2", + "randombytes": "^2.1.0", + "scrypt-js": "^3.0.1", + "utf8": "^3.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + } + } + }, + "ethers": { + "version": "4.0.49", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz", + "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==", + "requires": { + "aes-js": "3.0.0", + "bn.js": "^4.11.9", + "elliptic": "6.5.4", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "optional": true + } + } + }, + "express": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.2.tgz", + "integrity": "sha512-oxlxJxcQlYwqPWKVJJtvQiwHgosH/LrLSPA+H4UxpyvSS6jC5aH+5MoHFM+KABgTOt0APue4w66Ha8jCUo9QGg==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.6", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ext": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", + "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "requires": { + "type": "^2.5.0" + }, + "dependencies": { + "type": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", + "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "optional": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fake-merkle-patricia-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", + "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", + "requires": { + "checkpoint-store": "^1.1.0" + } + }, + "faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g==" + }, + "fast-check": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-2.20.0.tgz", + "integrity": "sha512-tFNjLyPnOUg6iimVxOtoWMJOIyybCo7B8gUGm1yv43jDCQ0hlPUn0fmna/XO/n1yPxn/dxQw3+IygPSbMDiiog==", + "requires": { + "pure-rand": "^5.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fetch-ponyfill": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz", + "integrity": "sha1-rjzl9zLGReq4fkroeTQUcJsjmJM=", + "requires": { + "node-fetch": "~1.7.1" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "optional": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "optional": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "optional": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "ganache-cli": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ganache-cli/-/ganache-cli-6.12.2.tgz", + "integrity": "sha512-bnmwnJDBDsOWBUP8E/BExWf85TsdDEFelQSzihSJm9VChVO1SHp94YXLP5BlA4j/OTxp0wR4R1Tje9OHOuAJVw==", + "requires": { + "ethereumjs-util": "6.2.1", + "source-map-support": "0.5.12", + "yargs": "13.2.4" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "bundled": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.11.2", + "bundled": true + }, + "@types/pbkdf2": { + "version": "3.1.0", + "bundled": true, + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.1", + "bundled": true, + "requires": { + "@types/node": "*" + } + }, + "ansi-regex": { + "version": "4.1.0", + "bundled": true + }, + "ansi-styles": { + "version": "3.2.1", + "bundled": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "base-x": { + "version": "3.0.8", + "bundled": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "blakejs": { + "version": "1.1.0", + "bundled": true + }, + "bn.js": { + "version": "4.11.9", + "bundled": true + }, + "brorand": { + "version": "1.1.0", + "bundled": true + }, + "browserify-aes": { + "version": "1.2.0", + "bundled": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "bs58": { + "version": "4.0.1", + "bundled": true, + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "bundled": true, + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer-from": { + "version": "1.1.1", + "bundled": true + }, + "buffer-xor": { + "version": "1.0.3", + "bundled": true + }, + "camelcase": { + "version": "5.3.1", + "bundled": true + }, + "cipher-base": { + "version": "1.0.4", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "cliui": { + "version": "5.0.0", + "bundled": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "color-convert": { + "version": "1.9.3", + "bundled": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "bundled": true + }, + "create-hash": { + "version": "1.2.0", + "bundled": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "bundled": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "bundled": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "bundled": true + }, + "elliptic": { + "version": "6.5.3", + "bundled": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "bundled": true + }, + "end-of-stream": { + "version": "1.4.4", + "bundled": true, + "requires": { + "once": "^1.4.0" + } + }, + "ethereum-cryptography": { + "version": "0.1.3", + "bundled": true, + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "bundled": true, + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "ethjs-util": { + "version": "0.1.6", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "bundled": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "bundled": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "bundled": true + }, + "get-stream": { + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "^3.0.0" + } + }, + "hash-base": { + "version": "3.1.0", + "bundled": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true + }, + "invert-kv": { + "version": "2.0.0", + "bundled": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true + }, + "is-hex-prefixed": { + "version": "1.0.0", + "bundled": true + }, + "is-stream": { + "version": "1.1.0", + "bundled": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true + }, + "keccak": { + "version": "3.0.1", + "bundled": true, + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "lcid": { + "version": "2.0.0", + "bundled": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "bundled": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mem": { + "version": "4.3.0", + "bundled": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "bundled": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "bundled": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "bundled": true + }, + "nice-try": { + "version": "1.0.5", + "bundled": true + }, + "node-addon-api": { + "version": "2.0.2", + "bundled": true + }, + "node-gyp-build": { + "version": "4.2.3", + "bundled": true + }, + "npm-run-path": { + "version": "2.0.2", + "bundled": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "once": { + "version": "1.4.0", + "bundled": true, + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "bundled": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "bundled": true + }, + "p-finally": { + "version": "1.0.0", + "bundled": true + }, + "p-is-promise": { + "version": "2.1.0", + "bundled": true + }, + "p-limit": { + "version": "2.3.0", + "bundled": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + }, + "path-exists": { + "version": "3.0.0", + "bundled": true + }, + "path-key": { + "version": "2.0.1", + "bundled": true + }, + "pbkdf2": { + "version": "3.1.1", + "bundled": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pump": { + "version": "3.0.0", + "bundled": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "randombytes": { + "version": "2.1.0", + "bundled": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "require-directory": { + "version": "2.1.1", + "bundled": true + }, + "require-main-filename": { + "version": "2.0.0", + "bundled": true + }, + "ripemd160": { + "version": "2.0.2", + "bundled": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.6", + "bundled": true, + "requires": { + "bn.js": "^4.11.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "bundled": true + }, + "scrypt-js": { + "version": "3.0.1", + "bundled": true + }, + "secp256k1": { + "version": "4.0.2", + "bundled": true, + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semver": { + "version": "5.7.1", + "bundled": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true + }, + "setimmediate": { + "version": "1.0.5", + "bundled": true + }, + "sha.js": { + "version": "2.4.11", + "bundled": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "bundled": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "bundled": true + }, + "signal-exit": { + "version": "3.0.3", + "bundled": true + }, + "source-map": { + "version": "0.6.1", + "bundled": true + }, + "source-map-support": { + "version": "0.5.12", + "bundled": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "string-width": { + "version": "3.1.0", + "bundled": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "bundled": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "bundled": true + }, + "strip-hex-prefix": { + "version": "1.0.0", + "bundled": true, + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true + }, + "which": { + "version": "1.3.1", + "bundled": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "bundled": true + }, + "wrap-ansi": { + "version": "5.1.0", + "bundled": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true + }, + "y18n": { + "version": "4.0.0", + "bundled": true + }, + "yargs": { + "version": "13.2.4", + "bundled": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + } + }, + "yargs-parser": { + "version": "13.1.2", + "bundled": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "optional": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "optional": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "requires": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "optional": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "header-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.3" + } + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==" + }, + "highlightjs-solidity": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/highlightjs-solidity/-/highlightjs-solidity-2.0.3.tgz", + "integrity": "sha512-tjFm5dtIE61VQBzjlZmkCtY5fLs3CaEABbVuUNyXeW+UuOCsxMg3MsPFy0kCelHP74hPpkoqDejLrbnV1axAIw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "husky": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz", + "integrity": "sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "optional": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "optional": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "optional": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "optional": true + }, + "is-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", + "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", + "requires": { + "lower-case": "^1.1.0" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "optional": true + }, + "is-number-object": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.6.tgz", + "integrity": "sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz", + "integrity": "sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA==" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-shared-array-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz", + "integrity": "sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.8.tgz", + "integrity": "sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", + "requires": { + "upper-case": "^1.1.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "optional": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-rpc-engine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz", + "integrity": "sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g==", + "requires": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "json-rpc-random-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", + "integrity": "sha1-uknZat7RRE27jaPSA3SKy7zeyMg=" + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=" + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "requires": { + "invert-kv": "^1.0.0" + } + }, + "level-codec": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", + "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" + }, + "level-errors": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", + "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", + "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", + "requires": { + "inherits": "^2.0.1", + "level-errors": "^1.0.3", + "readable-stream": "^1.0.33", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "level-ws": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", + "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", + "requires": { + "readable-stream": "~1.0.15", + "xtend": "~2.1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "levelup": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", + "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "requires": { + "deferred-leveldown": "~1.2.1", + "level-codec": "~7.0.0", + "level-errors": "~1.0.3", + "level-iterator-stream": "~1.3.0", + "prr": "~1.0.1", + "semver": "~5.4.1", + "xtend": "~4.0.0" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + } + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.assign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz", + "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=" + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=" + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c=" + }, + "lodash.flatmap": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", + "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.partition": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.partition/-/lodash.partition-4.6.0.tgz", + "integrity": "sha1-o45GtzRp4EILDaEhLmbUFL42S6Q=" + }, + "lodash.sum": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lodash.sum/-/lodash.sum-4.0.2.tgz", + "integrity": "sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s=" + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", + "requires": { + "lower-case": "^1.1.2" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "optional": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "optional": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "requires": { + "xtend": "~4.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merkle-patricia-tree": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", + "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", + "requires": { + "async": "^1.4.2", + "ethereumjs-util": "^5.0.0", + "level-ws": "0.0.0", + "levelup": "^1.2.1", + "memdown": "^1.0.0", + "readable-stream": "^2.0.0", + "rlp": "^2.0.0", + "semaphore": ">=1.0.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", + "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" + }, + "mime-types": { + "version": "2.1.34", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", + "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", + "requires": { + "mime-db": "1.51.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "optional": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz", + "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, + "multicodec": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-0.5.7.tgz", + "integrity": "sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA==", + "requires": { + "varint": "^5.0.0" + } + }, + "multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "requires": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "requires": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + } + } + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nano-base32": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/nano-base32/-/nano-base32-1.0.1.tgz", + "integrity": "sha1-ulSMh578+5DaHE2eCX20pGySVe8=" + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "node-releases": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz", + "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==" + }, + "nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "optional": true + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "requires": { + "boolbase": "^1.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "optional": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-inspect": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", + "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "optional": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "oboe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.5.tgz", + "integrity": "sha1-VVQoTFQ6ImbXo48X4HOCH73jk80=", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.4.tgz", + "integrity": "sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw==" + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "requires": { + "parse5": "^6.0.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", + "requires": { + "camel-case": "^3.0.0", + "upper-case-first": "^1.1.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "optional": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", + "requires": { + "no-case": "^2.2.0" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "optional": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "optional": true + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "prettier-plugin-solidity": { + "version": "1.0.0-beta.19", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz", + "integrity": "sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.14.0", + "emoji-regex": "^10.0.0", + "escape-string-regexp": "^4.0.0", + "semver": "^7.3.5", + "solidity-comments-extractor": "^0.0.7", + "string-width": "^4.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "printj": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz", + "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise-to-callback": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz", + "integrity": "sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=", + "requires": { + "is-fn": "^1.0.0", + "set-immediate-shim": "^1.0.1" + } + }, + "proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "requires": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + }, + "pure-rand": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-5.0.0.tgz", + "integrity": "sha512-lD2/y78q+7HqBx2SaT6OT4UcwtvXNRfEpzYEzl0EQ+9gZq2Qi3fa0HDnYPeqQwhlHJFBUhT7AO3mLU3+8bynHA==" + }, + "qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz", + "integrity": "sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==", + "requires": { + "bytes": "3.1.1", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "optional": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "optional": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "optional": true + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "optional": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "optional": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "optional": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "ripemd160-min": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz", + "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==" + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "requires": { + "bn.js": "^5.2.0" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-event-emitter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz", + "integrity": "sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg==", + "requires": { + "events": "^3.0.0" + } + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "optional": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "sentence-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", + "requires": { + "no-case": "^2.2.0", + "upper-case-first": "^1.1.2" + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "sha3": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", + "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", + "requires": { + "buffer": "6.0.3" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", + "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==" + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, + "snake-case": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", + "requires": { + "no-case": "^2.2.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "optional": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "optional": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "optional": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "optional": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "optional": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "solc": { + "version": "0.4.26", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.4.26.tgz", + "integrity": "sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA==", + "requires": { + "fs-extra": "^0.30.0", + "memorystream": "^0.3.1", + "require-from-string": "^1.1.0", + "semver": "^5.3.0", + "yargs": "^4.7.1" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "requires": { + "lcid": "^1.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=" + }, + "window-size": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.2.0.tgz", + "integrity": "sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU=" + }, + "yargs": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-4.8.1.tgz", + "integrity": "sha1-wMQpJMpKqmsObaFznfshZDn53cA=", + "requires": { + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "lodash.assign": "^4.0.3", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.1", + "which-module": "^1.0.0", + "window-size": "^0.2.0", + "y18n": "^3.2.1", + "yargs-parser": "^2.4.1" + } + }, + "yargs-parser": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-2.4.1.tgz", + "integrity": "sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ=", + "requires": { + "camelcase": "^3.0.0", + "lodash.assign": "^4.0.6" + } + } + } + }, + "solhint": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/solhint/-/solhint-3.3.6.tgz", + "integrity": "sha512-HWUxTAv2h7hx3s3hAab3ifnlwb02ZWhwFU/wSudUHqteMS3ll9c+m1FlGn9V8ztE2rf3Z82fQZA005Wv7KpcFA==", + "dev": true, + "requires": { + "@solidity-parser/parser": "^0.13.2", + "ajv": "^6.6.1", + "antlr4": "4.7.1", + "ast-parents": "0.0.1", + "chalk": "^2.4.2", + "commander": "2.18.0", + "cosmiconfig": "^5.0.7", + "eslint": "^5.6.0", + "fast-diff": "^1.1.2", + "glob": "^7.1.3", + "ignore": "^4.0.6", + "js-yaml": "^3.12.0", + "lodash": "^4.17.11", + "prettier": "^1.14.3", + "semver": "^6.3.0" + }, + "dependencies": { + "@solidity-parser/parser": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.13.2.tgz", + "integrity": "sha512-RwHnpRnfrnD2MSPveYoPh8nhofEvX7fgjHk1Oq+NNvCcLx4r1js91CO9o+F/F3fBzOCyvm8kKRTriFICX/odWw==", + "dev": true, + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true, + "optional": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "solhint-plugin-prettier": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz", + "integrity": "sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "solidity-ast": { + "version": "0.4.28", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.28.tgz", + "integrity": "sha512-RtZCP5tSvZMadVtg9/IfLmAMKDOnQEvG2HA6VnPuoTMxqxsbbn4lQy8jgH3RVbqW0eO1hd7cSCKecb72/OeOIw==" + }, + "solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==", + "dev": true + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "optional": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "optional": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==" + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "optional": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "optional": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=" + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "super-split": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/super-split/-/super-split-1.1.0.tgz", + "integrity": "sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "swarm-js": { + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.40.tgz", + "integrity": "sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + } + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==" + }, + "tar": { + "version": "4.4.19", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", + "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", + "requires": { + "chownr": "^1.1.4", + "fs-minipass": "^1.2.7", + "minipass": "^2.9.0", + "minizlib": "^1.3.3", + "mkdirp": "^0.5.5", + "safe-buffer": "^5.2.1", + "yallist": "^3.1.1" + }, + "dependencies": { + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "testrpc": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/testrpc/-/testrpc-0.0.1.tgz", + "integrity": "sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "optional": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "optional": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "dependencies": { + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + } + } + }, + "truffle-assertions": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/truffle-assertions/-/truffle-assertions-0.9.2.tgz", + "integrity": "sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "lodash.isequal": "^4.5.0" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", + "requires": { + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "optional": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "optional": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "optional": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "optional": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "optional": true + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "requires": { + "upper-case": "^1.1.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "optional": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "optional": true + }, + "utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + } + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "web3": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.5.3.tgz", + "integrity": "sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w==", + "requires": { + "web3-bzz": "1.5.3", + "web3-core": "1.5.3", + "web3-eth": "1.5.3", + "web3-eth-personal": "1.5.3", + "web3-net": "1.5.3", + "web3-shh": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-bzz": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.5.3.tgz", + "integrity": "sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg==", + "requires": { + "@types/node": "^12.12.6", + "got": "9.6.0", + "swarm-js": "^0.1.40" + }, + "dependencies": { + "@types/node": { + "version": "12.20.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.38.tgz", + "integrity": "sha512-NxmtBRGipjx1B225OeMdI+CQmLbYqvvmYbukDTJGDgzIDgPQ1EcjGmYxGhOk5hTBqeB558S6RgHSpq2iiqifAQ==" + } + } + }, + "web3-core": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.5.3.tgz", + "integrity": "sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ==", + "requires": { + "@types/bn.js": "^4.11.5", + "@types/node": "^12.12.6", + "bignumber.js": "^9.0.0", + "web3-core-helpers": "1.5.3", + "web3-core-method": "1.5.3", + "web3-core-requestmanager": "1.5.3", + "web3-utils": "1.5.3" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "12.20.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.38.tgz", + "integrity": "sha512-NxmtBRGipjx1B225OeMdI+CQmLbYqvvmYbukDTJGDgzIDgPQ1EcjGmYxGhOk5hTBqeB558S6RgHSpq2iiqifAQ==" + }, + "bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + } + } + }, + "web3-core-helpers": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz", + "integrity": "sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw==", + "requires": { + "web3-eth-iban": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-core-method": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.5.3.tgz", + "integrity": "sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg==", + "requires": { + "@ethereumjs/common": "^2.4.0", + "@ethersproject/transactions": "^5.0.0-beta.135", + "web3-core-helpers": "1.5.3", + "web3-core-promievent": "1.5.3", + "web3-core-subscriptions": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-core-promievent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz", + "integrity": "sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg==", + "requires": { + "eventemitter3": "4.0.4" + } + }, + "web3-core-requestmanager": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz", + "integrity": "sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg==", + "requires": { + "util": "^0.12.0", + "web3-core-helpers": "1.5.3", + "web3-providers-http": "1.5.3", + "web3-providers-ipc": "1.5.3", + "web3-providers-ws": "1.5.3" + }, + "dependencies": { + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + } + } + }, + "web3-core-subscriptions": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz", + "integrity": "sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.5.3" + } + }, + "web3-eth": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.5.3.tgz", + "integrity": "sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q==", + "requires": { + "web3-core": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-method": "1.5.3", + "web3-core-subscriptions": "1.5.3", + "web3-eth-abi": "1.5.3", + "web3-eth-accounts": "1.5.3", + "web3-eth-contract": "1.5.3", + "web3-eth-ens": "1.5.3", + "web3-eth-iban": "1.5.3", + "web3-eth-personal": "1.5.3", + "web3-net": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-eth-abi": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz", + "integrity": "sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg==", + "requires": { + "@ethersproject/abi": "5.0.7", + "web3-utils": "1.5.3" + }, + "dependencies": { + "@ethersproject/abi": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.0.7.tgz", + "integrity": "sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw==", + "requires": { + "@ethersproject/address": "^5.0.4", + "@ethersproject/bignumber": "^5.0.7", + "@ethersproject/bytes": "^5.0.4", + "@ethersproject/constants": "^5.0.4", + "@ethersproject/hash": "^5.0.4", + "@ethersproject/keccak256": "^5.0.3", + "@ethersproject/logger": "^5.0.5", + "@ethersproject/properties": "^5.0.3", + "@ethersproject/strings": "^5.0.4" + } + } + } + }, + "web3-eth-accounts": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz", + "integrity": "sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw==", + "requires": { + "@ethereumjs/common": "^2.3.0", + "@ethereumjs/tx": "^3.2.1", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.8", + "ethereumjs-util": "^7.0.10", + "scrypt-js": "^3.0.1", + "uuid": "3.3.2", + "web3-core": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-method": "1.5.3", + "web3-utils": "1.5.3" + }, + "dependencies": { + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz", + "integrity": "sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg==", + "requires": { + "@types/bn.js": "^4.11.5", + "web3-core": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-method": "1.5.3", + "web3-core-promievent": "1.5.3", + "web3-core-subscriptions": "1.5.3", + "web3-eth-abi": "1.5.3", + "web3-utils": "1.5.3" + }, + "dependencies": { + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + } + } + }, + "web3-eth-ens": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz", + "integrity": "sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw==", + "requires": { + "content-hash": "^2.5.2", + "eth-ens-namehash": "2.0.8", + "web3-core": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-promievent": "1.5.3", + "web3-eth-abi": "1.5.3", + "web3-eth-contract": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-eth-iban": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz", + "integrity": "sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw==", + "requires": { + "bn.js": "^4.11.9", + "web3-utils": "1.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "web3-eth-personal": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz", + "integrity": "sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew==", + "requires": { + "@types/node": "^12.12.6", + "web3-core": "1.5.3", + "web3-core-helpers": "1.5.3", + "web3-core-method": "1.5.3", + "web3-net": "1.5.3", + "web3-utils": "1.5.3" + }, + "dependencies": { + "@types/node": { + "version": "12.20.38", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.38.tgz", + "integrity": "sha512-NxmtBRGipjx1B225OeMdI+CQmLbYqvvmYbukDTJGDgzIDgPQ1EcjGmYxGhOk5hTBqeB558S6RgHSpq2iiqifAQ==" + } + } + }, + "web3-net": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.5.3.tgz", + "integrity": "sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ==", + "requires": { + "web3-core": "1.5.3", + "web3-core-method": "1.5.3", + "web3-utils": "1.5.3" + } + }, + "web3-providers-http": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.5.3.tgz", + "integrity": "sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw==", + "requires": { + "web3-core-helpers": "1.5.3", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz", + "integrity": "sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg==", + "requires": { + "oboe": "2.1.5", + "web3-core-helpers": "1.5.3" + } + }, + "web3-providers-ws": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz", + "integrity": "sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg==", + "requires": { + "eventemitter3": "4.0.4", + "web3-core-helpers": "1.5.3", + "websocket": "^1.0.32" + } + }, + "web3-shh": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.5.3.tgz", + "integrity": "sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q==", + "requires": { + "web3-core": "1.5.3", + "web3-core-method": "1.5.3", + "web3-core-subscriptions": "1.5.3", + "web3-net": "1.5.3" + } + }, + "web3-utils": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.5.3.tgz", + "integrity": "sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q==", + "requires": { + "bn.js": "^4.11.9", + "eth-lib": "0.2.8", + "ethereum-bloom-filters": "^1.0.6", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, + "webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "requires": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "requires": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "which-typed-array": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.7.tgz", + "integrity": "sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==", + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.18.5", + "foreach": "^2.0.5", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.7" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + }, + "xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "requires": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "requires": { + "xhr-request": "^1.1.0" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + } + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" + } + } + } + } +} diff --git a/solidity/nativecoinERC20/package.json b/solidity/nativecoinERC20/package.json new file mode 100644 index 00000000..d6a1930c --- /dev/null +++ b/solidity/nativecoinERC20/package.json @@ -0,0 +1,34 @@ +{ + "name": "nativecoin-erc20", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@openzeppelin/contracts": "^3.4.1-solc-0.7-2", + "@openzeppelin/contracts-upgradeable": "^3.4.1-solc-0.7-2", + "@openzeppelin/truffle-upgrades": "^1.7.0", + "@truffle/hdwallet-provider": "^1.4.1" + }, + "devDependencies": { + "chai": "^4.3.4", + "husky": "^6.0.0", + "prettier": "^2.2.1", + "prettier-plugin-solidity": "^1.0.0-beta.7", + "rlp": "^2.2.6", + "solhint": "^3.3.4", + "solhint-plugin-prettier": "^0.0.5", + "truffle-assertions": "^0.9.2" + }, + "scripts": { + "linter": "./node_modules/.bin/solhint -f table ./contracts/**/*.sol -f table ./contracts/*.sol", + "prettier": "./node_modules/.bin/prettier --write ./contracts -l", + "contract:compile": "truffle compile --all", + "test": "yarn test:unit && yarn test:integration", + "test:unit": "rm -rf .openzeppelin && truffle test test/unit/*.js", + "test:integration": "rm -rf .openzeppelin && truffle test test/integration/*.js" + }, + "husky": { + "hooks": { + "pre-push": "yarn linter && yarn prettier" + } + } +} diff --git a/solidity/nativecoinERC20/test/integration/2-native-coin-bsh.js b/solidity/nativecoinERC20/test/integration/2-native-coin-bsh.js new file mode 100644 index 00000000..3ebdf280 --- /dev/null +++ b/solidity/nativecoinERC20/test/integration/2-native-coin-bsh.js @@ -0,0 +1,333 @@ +const MockBSHPeriphery = artifacts.require("MockBSHPeriphery"); +const BSHPeriphery = artifacts.require("BSHPeriphery"); +const BSHCore = artifacts.require("BSHCore"); +const BMC = artifacts.require("MockBMC"); +const Holder = artifacts.require("Holder"); +const NotPayable = artifacts.require("NotPayable"); +const NonRefundable = artifacts.require("NonRefundable"); +const Refundable = artifacts.require("Refundable"); +const EncodeMsg = artifacts.require("EncodeMessage"); +const { assert, AssertionError } = require('chai'); +const truffleAssert = require('truffle-assertions'); +const rlp = require('rlp'); + +let toHex = (buf) => { + buf = buf.toString('hex'); + if (buf.substring(0, 2) == '0x') + return buf; + return '0x' + buf.toString('hex'); +}; + +contract('As a user, I want to send MOVR to ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, nonrefundable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let RC_OK = 0; let RC_ERR = 1; + let _native = 'MOVR'; let deposit = 1000000000000; + let _fee = 10; let _fixed_fee = 500000; + let REPONSE_HANDLE_SERVICE = 2; let _uri = 'https://github.com/icon-project/btp'; + let _tokenName = 'ICX'; + let _tokenSymbol = 'ICX'; + let _initialSupply = 1; + + let DECIMALS = 18; + let INITIAL_SUPPLY = web3.utils.toBN(100000) // 100000 tokens + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.movr'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_native, _fee, _fixed_fee, _tokenName, _tokenSymbol, INITIAL_SUPPLY); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + nonrefundable = await NonRefundable.new(); + refundable = await Refundable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + }); + + it('Scenario 5: Account client transfers a valid native coin to a side chain', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let tx = await bsh_core.transferNativeCoin(_to, { from: accounts[0], value: amount }); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let bsh_coin_balance = await bsh_core.getBalanceOf(bsh_core.address, _native); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[0]); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, 'MOVR'); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _native); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert( + web3.utils.BN(bsh_coin_balance._usableBalance).toNumber() === amount && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === 0 && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === amount + ); + }); + + it('Scenario 6: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let account_balanceBefore = await bsh_core.getBalanceOf(accounts[0], _native); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let account_balanceAfter = await bsh_core.getBalanceOf(accounts[0], _native); + let fees = await bsh_core.getAccumulatedFees(); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[0]); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert( + fees[0].coinName === _native && + Number(fees[0].value) === (Math.floor(amount / 1000) + _fixed_fee) && + web3.utils.BN(account_balanceBefore._lockedBalance).toNumber() === amount && + web3.utils.BN(account_balanceAfter._lockedBalance).toNumber() === 0 + ); + }); +}); + +contract('As a user, I want to send ERC20_ICX to ICON blockchain', (accounts) => { + let bsh_perif, bsh_core, bmc, holder; + let service = 'Coin/WrappedCoin'; + let _native = 'MOVR'; let _fee = 10; let _fixed_fee = 500000; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; let _value = 999999999999999; + let REPONSE_HANDLE_SERVICE = 2; let RC_OK = 0; let RC_ERR = 1; + let _tokenName = 'ICX'; + let _tokenSymbol = 'ICX'; + let INITIAL_SUPPLY = web3.utils.toBN(10000000000000) // 100000 tokens + + before(async () => { + console.log(accounts) + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.movr'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_native, _fee, _fixed_fee, _tokenName, _tokenSymbol, INITIAL_SUPPLY); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, holder.address, _tokenName, _value); + await bmc.receiveRequest(_bmcICON, "", service, 0, _msg); + let balanceBefore = await bsh_core.balanceOf(accounts[0]); + }); + + it('Scenario 8: User sends a valid transferring request', async () => { + let _to = 'btp://1234.iconee/0x12345678'; + let amount = 600000; + //funding account 1 with some ERC20_ICX tokens + const data1 = await bsh_core.contract.methods["transfer(address,uint256)"]("0x2BB06D5F4cF1740C96BD1f77316aE89Dca704140", web3.utils.toWei("1","ether")).encodeABI(); + console.log(data1) + + await bsh_core.transfer(accounts[1], amount); + let balanceBefore = await bsh_core.getBalanceOf(accounts[1], _tokenName); + await bsh_core.approve(bsh_core.address, amount, { from: accounts[1] }); + const data = await bsh_core.contract.methods["transferWrappedCoin(string,uint256,string)"](_tokenName, amount, _to).encodeABI(); + let tx = await bsh_core.sendTransaction({ data, from: accounts[1] }); + //let tx = await bsh_core.transfer(_tokenName, amount, _to, {from: accounts[1]}); + let balanceAfter = await bsh_core.getBalanceOf(accounts[1], _tokenName); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _tokenName); + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + + const transferEvents = await bsh_perif.getPastEvents('TransferStart', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + assert.equal(event._from, accounts[1]); + assert.equal(event._to, _to); + assert.equal(event._sn, 1); + assert.equal(event._assetDetails.length, 1); + assert.equal(event._assetDetails[0].coinName, _tokenName); + assert.equal(event._assetDetails[0].value, amount - chargedFee); + assert.equal(event._assetDetails[0].fee, chargedFee); + + const linkStatus = await bmc.getStatus(_bmcICON); + const bmcBtpAddress = await bmc.getBmcBtpAddress(); + + const messageEvents = await bmc.getPastEvents('Message', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + event = messageEvents[0].returnValues; + assert.equal(event._next, _bmcICON); + assert.equal(event._seq, linkStatus.txSeq); + + const bmcMsg = rlp.decode(event._msg); + + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[0])), bmcBtpAddress); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[1])), _bmcICON); + assert.equal(web3.utils.hexToUtf8(toHex(bmcMsg[2])), service); + assert.equal(web3.utils.hexToNumber(toHex(bmcMsg[3])), 1); + + const ServiceMsg = rlp.decode(bmcMsg[4]); + assert.equal(web3.utils.hexToUtf8(toHex(ServiceMsg[0])), 0); + + const coinTransferMsg = rlp.decode(ServiceMsg[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[0])), accounts[1]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[1])), _to.split('/').slice(-1)[0]); + assert.equal(web3.utils.hexToUtf8(toHex(coinTransferMsg[2][0][0])), _tokenName); + assert.equal(web3.utils.hexToNumber(toHex(coinTransferMsg[2][0][1])), amount - chargedFee); + + assert.equal(web3.utils.BN(balanceBefore._lockedBalance).toNumber(), 0); + assert.equal(web3.utils.BN(balanceAfter._lockedBalance).toNumber(), amount); + assert.equal( + web3.utils.BN(balanceAfter._usableBalance).toNumber(), + web3.utils.BN(balanceBefore._usableBalance).toNumber() - amount + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), amount); + }); + + it('Scenario 9: BSHPeriphery receives a successful response of a recent request', async () => { + let amount = 600000; + let chargedFee = Math.floor(amount / 1000) + _fixed_fee; + let contract_balanceBefore = await bsh_core.getBalanceOf(accounts[1], _tokenName); + let _msg = await encode_msg.encodeResponseMsg(REPONSE_HANDLE_SERVICE, RC_OK, ""); + let tx = await bmc.receiveResponse(_net, service, 1, _msg); + let contract_balanceAfter = await bsh_core.getBalanceOf(accounts[1], _tokenName); + let fees = await bsh_core.getAccumulatedFees(); + let bsh_core_balance = await bsh_core.getBalanceOf(bsh_core.address, _tokenName); + + const transferEvents = await bsh_perif.getPastEvents('TransferEnd', { fromBlock: tx.receipt.blockNumber, toBlock: 'latest' }); + let event = transferEvents[0].returnValues; + + assert.equal(event._from, accounts[1]); + assert.equal(event._sn, 1); + assert.equal(event._code, 0); + assert.equal(event._response, ''); + + assert.equal(web3.utils.BN(contract_balanceBefore._lockedBalance).toNumber(), amount); + assert.equal(web3.utils.BN(contract_balanceAfter._lockedBalance).toNumber(), 0); + assert.equal( + web3.utils.BN(contract_balanceBefore._usableBalance).toNumber(), + web3.utils.BN(contract_balanceAfter._usableBalance).toNumber() + ); + assert.equal(web3.utils.BN(bsh_core_balance._usableBalance).toNumber(), chargedFee); + assert.equal(fees[1].coinName, _tokenName);//todo: check this + assert.equal(Number(fees[1].value), chargedFee) + }); +}); + +contract('As a user, I want to receive MOVR from ICON blockchain', (accounts) => { + let bmc, bsh_perif, bsh_core, notpayable, refundable; + let service = 'Coin/WrappedCoin'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _to = 'btp://1234.iconee/0x12345678'; + let _native = 'MOVR'; let _fee = 10; let _fixed_fee = 500000; + let RC_ERR = 1; let RC_OK = 0; + let _uri = 'https://github.com/icon-project/btp'; + let _tokenName = 'ICX'; + let _tokenSymbol = 'ICX'; + let INITIAL_SUPPLY = web3.utils.toBN(10000000000000) // 100000 tokens + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.movr'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_native, _fee, _fixed_fee, _tokenName, _tokenSymbol, INITIAL_SUPPLY); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + notpayable = await NotPayable.new(); + refundable = await Refundable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await bsh_core.transferNativeCoin(_to, { from: accounts[0], value: 100000000 }); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 4: BSHPeriphery receives a request of transferring coins', async () => { + let _from = '0x12345678'; + let _value = 12345; + let balanceBefore = await bmc.getBalance(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _native, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bmc.getBalance(accounts[1]); + + assert.equal( + web3.utils.BN(balanceAfter).toString(), + web3.utils.BN(balanceBefore).add(new web3.utils.BN(_value)).toString() + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); +}); + +contract('As a user, I want to receive ERC20_ICX from ICON blockchain', (accounts) => { + let bmc, bsh_perif, bsh_core, holder, notpayable; + let service = 'Coin/WrappedCoin'; let _uri = 'https://github.com/icon-project/btp'; + let _native = 'MOVR'; let _fee = 10; let _fixed_fee = 500000; + let _name = 'ICON'; let _bmcICON = 'btp://1234.iconee/0x1234567812345678'; + let _net = '1234.iconee'; let _from = '0x12345678'; + let RC_ERR = 1; let RC_OK = 0; + let _tokenName = 'ICX'; + let _tokenSymbol = 'ICX'; + let INITIAL_SUPPLY = web3.utils.toBN(10000000000000) // 100000 tokens + + + before(async () => { + bsh_perif = await BSHPeriphery.new(); + bsh_core = await BSHCore.new(); + bmc = await BMC.new('1234.movr'); + encode_msg = await EncodeMsg.new(); + await bsh_perif.initialize(bmc.address, bsh_core.address, service); + await bsh_core.initialize(_native, _fee, _fixed_fee, _tokenName, _tokenSymbol, INITIAL_SUPPLY); + await bsh_core.updateBSHPeriphery(bsh_perif.address); + holder = await Holder.new(); + notpayable = await NotPayable.new(); + await bmc.addService(service, bsh_perif.address); + await bmc.addVerifier(_net, accounts[1]); + await bmc.addLink(_bmcICON); + await holder.addBSHContract(bsh_perif.address, bsh_core.address); + btpAddr = await bmc.bmcAddress(); + }); + + it('Scenario 5: Receiver is an account client', async () => { + let _value = 5500; + let balanceBefore = await bsh_core.balanceOf(accounts[1]); + let _eventMsg = await encode_msg.encodeResponseBMCMessage(btpAddr, _bmcICON, service, 10, RC_OK, ''); + let _msg = await encode_msg.encodeTransferMsgWithAddress(_from, accounts[1], _tokenName, _value); + let output = await bmc.receiveRequest(_bmcICON, '', service, 10, _msg); + let balanceAfter = await bsh_core.balanceOf(accounts[1]); + + assert.equal( + web3.utils.BN(balanceAfter).toNumber(), + web3.utils.BN(balanceBefore).toNumber() + _value + ); + assert.equal(output.logs[0].args._next, _bmcICON); + assert.equal(output.logs[0].args._msg, _eventMsg); + }); +}); \ No newline at end of file diff --git a/solidity/nativecoinERC20/test/integration/native-coin-bsh b/solidity/nativecoinERC20/test/integration/native-coin-bsh new file mode 100644 index 00000000..19ba07a8 --- /dev/null +++ b/solidity/nativecoinERC20/test/integration/native-coin-bsh @@ -0,0 +1,33 @@ +const BSHPerif = artifacts.require("BSHPeriphery"); +const MockBSHCore = artifacts.require("MockBSHCore"); +const CheckParseAddress = artifacts.require("CheckParseAddress"); +const { assert } = require('chai'); +const truffleAssert = require('truffle-assertions'); + + +// BSHPeriphery is being used for communications among BSHCore and BMCPeriphery contract +// Thus, all tests relating to BSHPeriphery will be moved to Integration Test +// This part just covers some basic feature which is checking an authorization +contract('BSHPeriphery Unit Tests', (accounts) => { + let bsh_perif; + + before(async () => { + bsh_perif = await BSHPerif.new(); + }); + + + it('Scenario 40: Should succeed when a client, which owns a refundable, tries to reclaim', async () => { + let _coin = 'ICON'; let _value = 10000; + let _id = await bsh_core.coinId(_coin); + await bsh_core.mintMock(bsh_core.address, _id, _value); + let balanceBefore = await bsh_core.getBalanceOf(accounts[2], _coin); + await bsh_core.reclaim(_coin, _value, {from: accounts[2]}); + let balanceAfter = await bsh_core.getBalanceOf(accounts[2], _coin); + assert( + web3.utils.BN(balanceAfter._usableBalance).toNumber() === + web3.utils.BN(balanceBefore._usableBalance).toNumber() + _value + ); + }); + +}); + \ No newline at end of file diff --git a/solidity/nativecoinERC20/test3.js b/solidity/nativecoinERC20/test3.js new file mode 100644 index 00000000..235ea907 --- /dev/null +++ b/solidity/nativecoinERC20/test3.js @@ -0,0 +1,59 @@ +const Web3 = require('web3') +var Personal = require('web3-eth-personal') +const ethers = require('ethers') + +const web3 = new Web3('ws://localhost:9944') +const personal = new Personal('http://34.125.31.100:8545') +const provider = new ethers.providers.JsonRpcProvider('http://localhost:9933') + +function hex_to_ascii(str1) { + var hex = str1.toString(); + var str = ''; + for (var n = 0; n < hex.length; n += 2) { + str += String.fromCharCode(parseInt(hex.substr(n, 2), 16)); + } + return str; +} + +async function reason(hash) { + console.log('tx hash:', hash) + console.log('provider:', process.env.WEB3_URL) + + let tx = await provider.getTransaction(hash) + if (!tx) { + console.log('tx not found') + } else { + let code = await provider.call(tx, tx.blockNumber) + let reason = hex_to_ascii(code.substr(138)) + console.log('revert reason:', reason) + } +} + +let owner = "0xf24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + +async function reason(hash) { + console.log('tx hash:', hash) + let tx = await provider.getTransaction(hash) + if (!tx) { + console.log('tx not found') + } else { + let code = await provider.call(tx, tx.blockNumber) + console.log(code) + let reason = hex_to_ascii(code.substr(138)) + console.log('revert reason:', reason) + } +} + +async function main() { + //const accounts = await web3.eth.getAccounts() + //await personal.unlockAccount(owner, "Perlia0", 60*60*12) + let balance = await web3.eth.getBalance(owner) + console.log("Owner Balance: ", web3.utils.fromWei(balance ,"ether")) + + console.log(await web3.eth.getTransactionReceipt("0x5702521ec3e6f67c1c5a4ef74e11c62b3a75552c1116fc06aa6af0f7b36bb1aa")) + reason("0x5702521ec3e6f67c1c5a4ef74e11c62b3a75552c1116fc06aa6af0f7b36bb1aa") + +} + +main().then(r => console + .log("Done.")) \ No newline at end of file diff --git a/solidity/nativecoinERC20/truffle-config.js b/solidity/nativecoinERC20/truffle-config.js new file mode 100644 index 00000000..f4f09d79 --- /dev/null +++ b/solidity/nativecoinERC20/truffle-config.js @@ -0,0 +1,82 @@ +const HDWalletProvider = require('@truffle/hdwallet-provider'); +const web3 = require("web3") + +const privKeys = (process.env.PRIVATE_KEYS) ? process.env.PRIVATE_KEYS.split(',') : + [ + '0x5fb92d6e98884f76de468fa3f6278f8807c48bebc13595d45af5bdc4da702133', // Alith + '0x8075991ce870b93a8870eca0c0f91913d12f47948ca0fd25b49c6fa7cdbeee8b', // Baltathar + '0x0b6e18cafb6ed99687ec547bd28139cafdd2bffe70e6b688025de6b445aa5c5b', // Charleth + '0x39539ab1876910bbf3a223d84a29e28f1cb4e2e456503e7e91ed39b2e7223d68', // Dorothy + '0x7dce9bc8babb68fec1409be38c8e1a52650206a7ed90ff956ae8a6d15eeaaef4', // Ethan + '0xb9d2ea9a615f3165812e8d44de0d24da9bbd164b65c4f0573e1ce2c8dbd9c8df', // Faith + '0x96b8a38e12e1a31dee1eab2fffdf9d9990045f5b37e44d8cc27766ef294acf18', // Goliath + '0x0d6dcaaef49272a5411896be8ad16c01c35d6f8c18873387b71fbc734759b0ab', // Heath + '0x4c42532034540267bf568198ccec4cb822a025da542861fcb146a5fab6433ff8', // Ida + '0x94c49300a58d576011096bcb006aa06f5a91b34b4383891e8029c21dc39fbb8b' // Judith + ]; + +module.exports = { + networks: { + dev: { + host: "localhost", + port: 9545, + network_id: "*", + }, + development: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbeamlocal: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "http://localhost:9933", + }), + network_id: 1281 + }, + moonbase: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.testnet.moonbeam.network", + }), + network_id: 1287, + networkCheckTimeout: 100000, + // Make deploy faster for deployment + gasPrice: web3.utils.toWei("2", "Gwei"), + }, + moonriver: { + provider: () => new HDWalletProvider({ + privateKeys: privKeys, + providerOrUrl: "https://rpc.moonriver.moonbeam.network", + }), + network_id: 1285, + networkCheckTimeout: 100000, + }, + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.7.6", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 10 + }, + evmVersion: "petersburg" + } + } + }, + + db: { + enabled: false + } +}; diff --git a/solidity/nativecoinERC20/yarn.lock b/solidity/nativecoinERC20/yarn.lock new file mode 100644 index 00000000..5d5376a6 --- /dev/null +++ b/solidity/nativecoinERC20/yarn.lock @@ -0,0 +1,7776 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" + integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== + dependencies: + "@babel/highlight" "^7.16.0" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.0": + version "7.16.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" + integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== + +"@babel/generator@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.5.tgz#26e1192eb8f78e0a3acaf3eede3c6fc96d22bedf" + integrity sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA== + dependencies: + "@babel/types" "^7.16.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.13.0": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" + integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== + dependencies: + "@babel/compat-data" "^7.16.0" + "@babel/helper-validator-option" "^7.14.5" + browserslist "^4.17.5" + semver "^6.3.0" + +"@babel/helper-define-polyfill-provider@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.0.tgz#c5b10cf4b324ff840140bb07e05b8564af2ae971" + integrity sha512-7hfT8lUljl/tM3h+izTX/pO3W3frz2ok6Pk+gzys8iJqDfZrZy2pXjRTZAvG2YmfHun1X4q8/UZRLatMfqc5Tg== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz#f6a7f38b3c6d8b07c88faea083c46c09ef5451b8" + integrity sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-function-name@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" + integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== + dependencies: + "@babel/helper-get-function-arity" "^7.16.0" + "@babel/template" "^7.16.0" + "@babel/types" "^7.16.0" + +"@babel/helper-get-function-arity@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" + integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-hoist-variables@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" + integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" + integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz#afe37a45f39fce44a3d50a7958129ea5b1a5c074" + integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== + +"@babel/helper-split-export-declaration@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" + integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== + dependencies: + "@babel/types" "^7.16.0" + +"@babel/helper-validator-identifier@^7.15.7": + version "7.15.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" + integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== + +"@babel/helper-validator-option@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" + integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== + +"@babel/highlight@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" + integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== + dependencies: + "@babel/helper-validator-identifier" "^7.15.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.16.0", "@babel/parser@^7.16.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.5.tgz#beb3af702e54d24796341ab9420fb329131ad658" + integrity sha512-+Ce7T5iPNWzfu9C1aB5tN3Lyafs5xb3Ic7vBWyZL2KXT3QSdD1dD3CvgOzPmQKoNNRt6uauc0XwNJTQtXC2/Mw== + +"@babel/plugin-transform-runtime@^7.5.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.5.tgz#0cc3f01d69f299d5a42cd9ec43b92ea7a777b8db" + integrity sha512-gxpfS8XQWDbQ8oP5NcmpXxtEgCJkbO+W9VhZlOhr0xPyVaRjAQPOv7ZDj9fg0d5s9+NiVvMCE6gbkEkcsxwGRw== + dependencies: + "@babel/helper-module-imports" "^7.16.0" + "@babel/helper-plugin-utils" "^7.16.5" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.4.0" + babel-plugin-polyfill-regenerator "^0.3.0" + semver "^6.3.0" + +"@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a" + integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/template@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" + integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/parser" "^7.16.0" + "@babel/types" "^7.16.0" + +"@babel/traverse@^7.13.0": + version "7.16.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.5.tgz#d7d400a8229c714a59b87624fc67b0f1fbd4b2b3" + integrity sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ== + dependencies: + "@babel/code-frame" "^7.16.0" + "@babel/generator" "^7.16.5" + "@babel/helper-environment-visitor" "^7.16.5" + "@babel/helper-function-name" "^7.16.0" + "@babel/helper-hoist-variables" "^7.16.0" + "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/parser" "^7.16.5" + "@babel/types" "^7.16.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.16.0": + version "7.16.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" + integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== + dependencies: + "@babel/helper-validator-identifier" "^7.15.7" + to-fast-properties "^2.0.0" + +"@ensdomains/address-encoder@^0.1.7": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@ensdomains/address-encoder/-/address-encoder-0.1.9.tgz#f948c485443d9ef7ed2c0c4790e931c33334d02d" + integrity sha512-E2d2gP4uxJQnDu2Kfg1tHNspefzbLT8Tyjrm5sEuim32UkU2sm5xL4VXtgc2X33fmPEw9+jUMpGs4veMbf+PYg== + dependencies: + bech32 "^1.1.3" + blakejs "^1.1.0" + bn.js "^4.11.8" + bs58 "^4.0.1" + crypto-addr-codec "^0.1.7" + nano-base32 "^1.0.1" + ripemd160 "^2.0.2" + +"@ensdomains/ens@0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@ensdomains/ens/-/ens-0.4.3.tgz#f4a6b55146fe526c9a50e13f373bf90d36ca94dc" + integrity sha512-btC+fGze//ml8SMNCx5DgwM8+kG2t+qDCZrqlL/2+PV4CNxnRIpR3egZ49D9FqS52PFoYLmz6MaQfl7AO3pUMA== + dependencies: + bluebird "^3.5.2" + eth-ens-namehash "^2.0.8" + ethereumjs-testrpc "^6.0.3" + ganache-cli "^6.1.0" + solc "^0.4.20" + testrpc "0.0.1" + web3-utils "^1.0.0-beta.31" + +"@ensdomains/ensjs@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@ensdomains/ensjs/-/ensjs-2.0.1.tgz#c27438f9ca074825ddb08430988c7decf2062a91" + integrity sha512-gZLntzE1xqPNkPvaHdJlV5DXHms8JbHBwrXc2xNrL1AylERK01Lj/txCCZyVQqFd3TvUO1laDbfUv8VII0qrjg== + dependencies: + "@babel/runtime" "^7.4.4" + "@ensdomains/address-encoder" "^0.1.7" + "@ensdomains/ens" "0.4.3" + "@ensdomains/resolver" "0.2.4" + content-hash "^2.5.2" + eth-ens-namehash "^2.0.8" + ethers "^5.0.13" + js-sha3 "^0.8.0" + +"@ensdomains/resolver@0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" + integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== + +"@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.0.tgz#feb96fb154da41ee2cc2c5df667621a440f36348" + integrity sha512-Cq2qS0FTu6O2VU1sgg+WyU9Ps0M6j/BEMHN+hRaECXCV/r0aI78u4N6p52QW/BDVhwWZpCdrvG8X7NJdzlpNUA== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.3" + +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.4.0.tgz#7eb1947eefa55eb9cf05b3ca116fb7a3dbd0bce7" + integrity sha512-WWUwg1PdjHKZZxPPo274ZuPsJCWV3SqATrEKQP1n2DrVYVP1aZIYpo/mFaA0BDoE0tIQmBeimRCEA0Lgil+yYw== + dependencies: + "@ethereumjs/common" "^2.6.0" + ethereumjs-util "^7.1.3" + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@5.5.0", "@ethersproject/abi@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" + integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" + integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + +"@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" + integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/address@5.5.0", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" + integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + +"@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" + integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + +"@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" + integrity sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + +"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" + integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + bn.js "^4.11.9" + +"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" + integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/constants@5.5.0", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" + integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + +"@ethersproject/contracts@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" + integrity sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg== + dependencies: + "@ethersproject/abi" "^5.5.0" + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + +"@ethersproject/hash@5.5.0", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" + integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" + integrity sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + +"@ethersproject/json-wallets@5.5.0", "@ethersproject/json-wallets@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" + integrity sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/pbkdf2" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" + integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + js-sha3 "0.8.0" + +"@ethersproject/logger@5.5.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" + integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== + +"@ethersproject/networks@5.5.1", "@ethersproject/networks@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.1.tgz#b7f7b9fb88dec1ea48f739b7fb9621311aa8ce6c" + integrity sha512-tYRDM4zZtSUcKnD4UMuAlj7SeXH/k5WC4SP2u1Pn57++JdXHkRu2zwNkgNogZoxHzhm9Q6qqurDBVptHOsW49Q== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" + integrity sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + +"@ethersproject/properties@5.5.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" + integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== + dependencies: + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/providers@5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.1.tgz#ba87e3c93219bbd2e2edf8b369873aee774abf04" + integrity sha512-2zdD5sltACDWhjUE12Kucg2PcgM6V2q9JMyVvObtVGnzJu+QSmibbP+BHQyLWZUBfLApx2942+7DC5D+n4wBQQ== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/basex" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/networks" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/web" "^5.5.0" + bech32 "1.1.4" + ws "7.4.6" + +"@ethersproject/random@5.5.0", "@ethersproject/random@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.0.tgz#305ed9e033ca537735365ac12eed88580b0f81f9" + integrity sha512-egGYZwZ/YIFKMHcoBUo8t3a8Hb/TKYX8BCBoLjudVCZh892welR3jOxgOmb48xznc9bTcMm7Tpwc1gHC1PFNFQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/rlp@5.5.0", "@ethersproject/rlp@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" + integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" + integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.5.0", "@ethersproject/signing-key@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" + integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/solidity@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" + integrity sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/sha2" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/strings@5.5.0", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" + integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" + integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== + dependencies: + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/rlp" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + +"@ethersproject/units@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" + integrity sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag== + dependencies: + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/constants" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + +"@ethersproject/wallet@5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" + integrity sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q== + dependencies: + "@ethersproject/abstract-provider" "^5.5.0" + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/address" "^5.5.0" + "@ethersproject/bignumber" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/hdnode" "^5.5.0" + "@ethersproject/json-wallets" "^5.5.0" + "@ethersproject/keccak256" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/random" "^5.5.0" + "@ethersproject/signing-key" "^5.5.0" + "@ethersproject/transactions" "^5.5.0" + "@ethersproject/wordlists" "^5.5.0" + +"@ethersproject/web@5.5.1", "@ethersproject/web@^5.5.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316" + integrity sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg== + dependencies: + "@ethersproject/base64" "^5.5.0" + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": + version "5.5.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" + integrity sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q== + dependencies: + "@ethersproject/bytes" "^5.5.0" + "@ethersproject/hash" "^5.5.0" + "@ethersproject/logger" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + "@ethersproject/strings" "^5.5.0" + +"@openzeppelin/contracts-upgradeable@^3.4.1-solc-0.7-2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-3.4.2.tgz#2c2a1b0fa748235a1f495b6489349776365c51b3" + integrity sha512-mDlBS17ymb2wpaLcrqRYdnBAmP1EwqhOXMvqWk2c5Q1N1pm5TkiCtXM9Xzznh4bYsQBq0aIWEkFFE2+iLSN1Tw== + +"@openzeppelin/contracts@^3.4.1-solc-0.7-2": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.2.tgz#d81f786fda2871d1eb8a8c5a73e455753ba53527" + integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== + +"@openzeppelin/truffle-upgrades@^1.7.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/truffle-upgrades/-/truffle-upgrades-1.11.0.tgz#9b0bfe75dbb98f5c7dcf5e4480de7349d21d5a73" + integrity sha512-ee9QWWbpbi3wS6FWpgFjgcj0oUUQ0iUmv81jNrKB73R2WHtP+2ChHpFd/0qsLfQWa7bMAeLCkGhLcEy/WoQLjw== + dependencies: + "@openzeppelin/upgrades-core" "^1.10.0" + "@truffle/contract" "^4.3.26" + solidity-ast "^0.4.15" + +"@openzeppelin/upgrades-core@^1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.10.0.tgz#d3aa72b7a23827e0e6daff08ddfb8dcd75171abb" + integrity sha512-N20t1i1wlHrVmu3etVZLiaRxT6XLkCrO9gIo4mUZNpsaVftl8V+WBu8o940AjoYXvzTEqj7O0re2DSFzjpkRBw== + dependencies: + bn.js "^5.1.2" + cbor "^8.0.0" + chalk "^4.1.0" + compare-versions "^3.6.0" + debug "^4.1.1" + ethereumjs-util "^7.0.3" + proper-lockfile "^4.1.1" + solidity-ast "^0.4.15" + +"@sindresorhus/is@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" + integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== + +"@solidity-parser/parser@^0.13.2": + version "0.13.2" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.13.2.tgz#b6c71d8ca0b382d90a7bbed241f9bc110af65cbe" + integrity sha512-RwHnpRnfrnD2MSPveYoPh8nhofEvX7fgjHk1Oq+NNvCcLx4r1js91CO9o+F/F3fBzOCyvm8kKRTriFICX/odWw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@solidity-parser/parser@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.0.tgz#d51f074efb0acce0e953ec48133561ed710cebc0" + integrity sha512-cX0JJRcmPtNUJpzD2K7FdA7qQsTOk1UZnFx2k7qAg9ZRvuaH5NBe5IEdBMXGlmf2+FmjhqbygJ26H8l2SV7aKQ== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@szmarczak/http-timer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" + integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== + dependencies: + defer-to-connect "^1.0.1" + +"@truffle/abi-utils@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@truffle/abi-utils/-/abi-utils-0.2.4.tgz#9fc8bfc95bbe29a33cca3ab9028865b078e2f051" + integrity sha512-ICr5Sger6r5uj2G5GN9Zp9OQDCaCqe2ZyAEyvavDoFB+jX0zZFUCfDnv5jllGRhgzdYJ3mec2390mjUyz9jSZA== + dependencies: + change-case "3.0.2" + faker "^5.3.1" + fast-check "^2.12.1" + +"@truffle/blockchain-utils@^0.0.31": + version "0.0.31" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.31.tgz#0503d9fb2ce3e05c167c27294927f2f88d70a24d" + integrity sha512-BFo/nyxwhoHqPrqBQA1EAmSxeNnspGLiOCMa9pAL7WYSjyNBlrHaqCMO/F2O87G+NUK/u06E70DiSP2BFP0ZZw== + +"@truffle/codec@^0.11.20": + version "0.11.20" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.11.20.tgz#dfd74d16b739837415f42590b7a73ffa79b8af71" + integrity sha512-OJtP/AEaYH0QNoq/Z0Pyo4muN0piJDTXQlWPSTb+SI+ypgzd0RT5QOcpmtc6Q24mfeWRB3QbJxQP0ZSeyo1MVA== + dependencies: + "@truffle/abi-utils" "^0.2.4" + "@truffle/compile-common" "^0.7.22" + big.js "^5.2.2" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" + lodash.clonedeep "^4.5.0" + lodash.escaperegexp "^4.1.2" + lodash.partition "^4.6.0" + lodash.sum "^4.0.2" + semver "^7.3.4" + utf8 "^3.0.0" + web3-utils "1.5.3" + +"@truffle/compile-common@^0.7.22": + version "0.7.22" + resolved "https://registry.yarnpkg.com/@truffle/compile-common/-/compile-common-0.7.22.tgz#c376eea36f59dc770ece3bc8cbb7132f49352846" + integrity sha512-afFKh0Wphn8JrCSjOORKjO8/E1X0EtQv6GpFJpQCAWo3/i4VGcSVKR1rjkknnExtjEGe9PJH/Ym/opGH3pQyDw== + dependencies: + "@truffle/error" "^0.0.14" + colors "^1.4.0" + +"@truffle/contract-schema@^3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.3.tgz#c1bcde343f70b9438314202e103a7d77d684603c" + integrity sha512-pgaTgF4CKIpkqVYZVr2qGTxZZQOkNCWOXW9VQpKvLd4G0SNF2Y1gyhrFbBhoOUtYlbbSty+IEFFHsoAqpqlvpQ== + dependencies: + ajv "^6.10.0" + debug "^4.3.1" + +"@truffle/contract@^4.3.26": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.4.0.tgz#f49c50292e07cd147001899914dc58f08c351355" + integrity sha512-k/YbwIOJsRAn3+VseG/lOlFiqjValHYwXdEyCT5D1HRQ0MQZ7JPabR05+dAlY88BK5o9ejUXCh+TDc0CRDn/eQ== + dependencies: + "@ensdomains/ensjs" "^2.0.1" + "@truffle/blockchain-utils" "^0.0.31" + "@truffle/contract-schema" "^3.4.3" + "@truffle/debug-utils" "^6.0.1" + "@truffle/error" "^0.0.14" + "@truffle/interface-adapter" "^0.5.8" + bignumber.js "^7.2.1" + debug "^4.3.1" + ethers "^4.0.32" + web3 "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" + +"@truffle/debug-utils@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-6.0.1.tgz#8935a316f3a59bcee37bab0e4c43eea7d1283fc3" + integrity sha512-ntuLPYwKNOIeWfeb2emO9Tu2v1aH6mCLGBrKRrDX4ofMUbPyN3ujxiCg/FCxbkL9cpkbH9zOHLmu2ubNlZ3gvA== + dependencies: + "@truffle/codec" "^0.11.20" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" + chalk "^2.4.2" + debug "^4.3.1" + highlightjs-solidity "^2.0.2" + +"@truffle/error@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.14.tgz#59683b5407bede7bddf16d80dc5592f9c5e5fa05" + integrity sha512-utJx+SZYoMqk8wldQG4gCVKhV8GwMJbWY7sLXFT/D8wWZTnE2peX7URFJh/cxkjTRCO328z1s2qewkhyVsu2HA== + +"@truffle/hdwallet-provider@^1.4.1": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.7.0.tgz#5cfa8bc67c2a30b3943d3dab78f74c6a191cde02" + integrity sha512-nT7BPJJ2jPCLJc5uZdVtRnRMny5he5d3kO9Hi80ZSqe5xlnK905grBptM/+CwOfbeqHKQirI1btwm6r3wIBM8A== + dependencies: + "@ethereumjs/common" "^2.4.0" + "@ethereumjs/tx" "^3.3.0" + "@trufflesuite/web3-provider-engine" "15.0.14" + eth-sig-util "^3.0.1" + ethereum-cryptography "^0.1.3" + ethereum-protocol "^1.0.1" + ethereumjs-util "^6.1.0" + ethereumjs-wallet "^1.0.1" + +"@truffle/interface-adapter@^0.5.8": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.8.tgz#76cfd34374d85849e1164de1a3d5a3dce0dc5d01" + integrity sha512-vvy3xpq36oLgjjy8KE9l2Jabg3WcGPOt18tIyMfTQX9MFnbHoQA2Ne2i8xsd4p6KfxIqSjAB53Q9/nScAqY0UQ== + dependencies: + bn.js "^5.1.3" + ethers "^4.0.32" + web3 "1.5.3" + +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== + dependencies: + ansi-mark "^1.0.0" + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + camelcase "^4.1.0" + chalk "^2.3.2" + cheerio "^1.0.0-rc.2" + detect-indent "^5.0.0" + he "^1.1.1" + highlight.js "^10.4.1" + lodash.merge "^4.6.2" + min-indent "^1.0.0" + strip-ansi "^4.0.0" + strip-indent "^2.0.0" + super-split "^1.1.0" + +"@trufflesuite/eth-json-rpc-filters@^4.1.2-1": + version "4.1.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.2-1.tgz#61ab78c52e98a883e5cf086925b34a30297b1824" + integrity sha512-/MChvC5dw2ck9NU1cZmdovCz2VKbOeIyR4tcxDvA5sT+NaL0rA2/R5U0yI7zsbo1zD+pgqav77rQHTzpUdDNJQ== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-0" + await-semaphore "^0.1.3" + eth-query "^2.1.2" + json-rpc-engine "^5.1.3" + lodash.flatmap "^4.5.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-json-rpc-infura@^4.0.3-0": + version "4.0.3-0" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-infura/-/eth-json-rpc-infura-4.0.3-0.tgz#6d22122937cf60ec9d21a02351c101fdc608c4fe" + integrity sha512-xaUanOmo0YLqRsL0SfXpFienhdw5bpQ1WEXxMTRi57az4lwpZBv4tFUDvcerdwJrxX9wQqNmgUgd1BrR01dumw== + dependencies: + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + cross-fetch "^2.1.1" + eth-json-rpc-errors "^1.0.1" + json-rpc-engine "^5.1.3" + +"@trufflesuite/eth-json-rpc-middleware@^4.4.2-0", "@trufflesuite/eth-json-rpc-middleware@^4.4.2-1": + version "4.4.2-1" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.2-1.tgz#8c3638ed8a7ed89a1e5e71407de068a65bef0df2" + integrity sha512-iEy9H8ja7/8aYES5HfrepGBKU9n/Y4OabBJEklVd/zIBlhCCBAWBqkIZgXt11nBXO/rYAeKwYuE3puH3ByYnLA== + dependencies: + "@trufflesuite/eth-sig-util" "^1.4.2" + btoa "^1.2.1" + clone "^2.1.1" + eth-json-rpc-errors "^1.0.1" + eth-query "^2.1.2" + ethereumjs-block "^1.6.0" + ethereumjs-tx "^1.3.7" + ethereumjs-util "^5.1.2" + ethereumjs-vm "^2.6.0" + fetch-ponyfill "^4.0.0" + json-rpc-engine "^5.1.3" + json-stable-stringify "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +"@trufflesuite/eth-sig-util@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/eth-sig-util/-/eth-sig-util-1.4.2.tgz#b529e2f38ac08e652116f48981132a26242a4f08" + integrity sha512-+GyfN6b0LNW77hbQlH3ufZ/1eCON7mMrGym6tdYf7xiNw9Vv3jBO72bmmos1EId2NgBvPMhmYYm6DSLQFTmzrA== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + +"@trufflesuite/web3-provider-engine@15.0.14": + version "15.0.14" + resolved "https://registry.yarnpkg.com/@trufflesuite/web3-provider-engine/-/web3-provider-engine-15.0.14.tgz#8f9696f434585cc0ab2e57c312090c1f138bc471" + integrity sha512-6/LoWvNMxYf0oaYzJldK2a9AdnkAdIeJhHW4nuUBAeO29eK9xezEaEYQ0ph1QRTaICxGxvn+1Azp4u8bQ8NEZw== + dependencies: + "@ethereumjs/tx" "^3.3.0" + "@trufflesuite/eth-json-rpc-filters" "^4.1.2-1" + "@trufflesuite/eth-json-rpc-infura" "^4.0.3-0" + "@trufflesuite/eth-json-rpc-middleware" "^4.4.2-1" + "@trufflesuite/eth-sig-util" "^1.4.2" + async "^2.5.0" + backoff "^2.5.0" + clone "^2.0.0" + cross-fetch "^2.1.0" + eth-block-tracker "^4.4.2" + eth-json-rpc-errors "^2.0.2" + ethereumjs-block "^1.2.2" + ethereumjs-util "^5.1.5" + ethereumjs-vm "^2.3.4" + json-stable-stringify "^1.0.1" + promise-to-callback "^1.0.0" + readable-stream "^2.2.9" + request "^2.85.0" + semaphore "^1.0.3" + ws "^5.1.1" + xhr "^2.2.0" + xtend "^4.0.1" + +"@types/bn.js@^4.11.3", "@types/bn.js@^4.11.5": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "16.11.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10" + integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw== + +"@types/node@^12.12.6": + version "12.20.37" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" + integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== + +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + +accepts@~1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn-dynamic-import@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ= + dependencies: + acorn "^4.0.3" + +acorn-jsx@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^4.0.3: + version "4.0.13" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= + +acorn@^5.0.0: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + +acorn@^6.0.7: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +aes-js@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" + integrity sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0= + +aes-js@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + +ajv-keywords@^3.1.0: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.6.1, ajv@^6.9.1: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +ansi-escapes@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" + integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== + +ansi-mark@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" + integrity sha1-HNS6jVfxXxCdaq9uycqXhsik7mw= + dependencies: + ansi-regex "^3.0.0" + array-uniq "^1.0.3" + chalk "^2.3.2" + strip-ansi "^4.0.0" + super-split "^1.1.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +antlr4@4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/antlr4/-/antlr4-4.7.1.tgz#69984014f096e9e775f53dd9744bf994d8959773" + integrity sha512-haHyTW7Y9joE5MVs37P2lNYfU2RWBLfcRDD8OWldcdZm5TiCE91B5Xl1oWSwiDUSd4rlExpt2pu1fksYQjRBYQ== + +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-uniq@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +ast-parents@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" + integrity sha1-UI/Q8F0MSHddnszaLhdEIyYejdM= + +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +await-semaphore@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/await-semaphore/-/await-semaphore-0.1.3.tgz#2b88018cc8c28e06167ae1cdff02504f1f9688d3" + integrity sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +babel-plugin-polyfill-corejs2@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz#407082d0d355ba565af24126fb6cb8e9115251fd" + integrity sha512-wMDoBJ6uG4u4PNFh72Ty6t3EgfA91puCuAwKIazbQlci+ENb/UU9A3xG5lutjUIiXCIn1CY5L15r9LimiJyrSA== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.0" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz#0b571f4cf3d67f911512f5c04842a7b8e8263087" + integrity sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + core-js-compat "^3.18.0" + +babel-plugin-polyfill-regenerator@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.0.tgz#9ebbcd7186e1a33e21c5e20cae4e7983949533be" + integrity sha512-dhAPTDLGoMW5/84wkgwiLRwMnio2i1fUe53EuvtKMv0pn2p3S8OCoV1xAzfJPl0KOX7IB89s2ib85vbYiea3jg== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.0" + +backoff@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" + integrity sha1-9hbtqdPktmuMp/ynn2lXIsX44m8= + dependencies: + precond "0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2, base-x@^3.0.8: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bech32@1.1.4, bech32@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + +big-integer@1.6.36: + version "1.6.36" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.36.tgz#78631076265d4ae3555c04f85e7d9d2f3a071a36" + integrity sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +bignumber.js@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" + integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== + +bignumber.js@^9.0.0, bignumber.js@^9.0.1: + version "9.0.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" + integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +blakejs@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" + integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== + +bluebird@^3.5.0, bluebird@^3.5.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha1-UzRK2xRhehP26N0s4okF0cC6MhU= + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + +body-parser@1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" + integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + dependencies: + bytes "3.1.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.7.2" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.7.0" + raw-body "2.4.0" + type-is "~1.6.17" + +body-parser@^1.16.0: + version "1.19.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.1.tgz#1499abbaa9274af3ecc9f6f10396c995943e31d4" + integrity sha512-8ljfQi5eBk8EJfECMrgqNGWPEY5jWP+1IzkzkGdFFEwFQZZyaZ21UqdaHktgiMlH0xLHqIFtE/u2OYE5dOtViA== + dependencies: + bytes "3.1.1" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.9.6" + raw-body "2.4.2" + type-is "~1.6.18" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.17.5, browserslist@^4.18.1: + version "4.19.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.0.tgz#5f02742ac2b86dde56ae4cef7be2b003e47b1ee0" + integrity sha512-JGHzm73ei2OnAcobcQ61GXNnN6vDCg5Oz5MayudL+FyzjoLnCzUWnuLtDLMIYw8aXgQzzdCZMVky+fftD5jbtA== + dependencies: + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + +btoa@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73" + integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-to-arraybuffer@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz#6064a40fa76eb43c723aba9ef8f6e1216d10511a" + integrity sha1-YGSkD6dutDxyOrqe+PbhIW0QURo= + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.0.5, buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +bufferutil@^4.0.1: + version "4.0.5" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" + integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== + dependencies: + node-gyp-build "^4.3.0" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" + integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + +bytes@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.1.tgz#3f018291cb4cbad9accb6e6970bca9c8889e879a" + integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" + integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^3.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^1.0.2" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +caller-callsite@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" + integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= + dependencies: + callsites "^2.0.0" + +caller-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" + integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= + dependencies: + caller-callsite "^2.0.0" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" + integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + +camelcase@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +caniuse-lite@^1.0.30001286: + version "1.0.30001286" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001286.tgz#3e9debad420419618cfdf52dc9b6572b28a8fff6" + integrity sha512-zaEMRH6xg8ESMi2eQ3R4eZ5qw/hJiVsO/HlLwniIwErij0JDr9P+8V4dtx1l+kLq6j3yy8l8W4fst1lBnat5wQ== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + +cbor@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== + dependencies: + nofilter "^3.1.0" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chai@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" + integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.1" + type-detect "^4.0.5" + +chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +change-case@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.2.tgz#fd48746cce02f03f0a672577d1d3a8dc2eceb037" + integrity sha512-Mww+SLF6MZ0U6kdg11algyKd5BARbyM4TbFBepwowYSR5ClfQGCGtxNXgykpN0uF/bstWeaGDT4JWaDh8zWAHA== + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.3.2" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= + dependencies: + functional-red-black-tree "^1.0.1" + +cheerio-select@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823" + integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg== + dependencies: + css-select "^4.1.3" + css-what "^5.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils "^2.7.0" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" + integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +cids@^0.7.1: + version "0.7.5" + resolved "https://registry.yarnpkg.com/cids/-/cids-0.7.5.tgz#60a08138a99bfb69b6be4ceb63bfef7a396b28b2" + integrity sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA== + dependencies: + buffer "^5.5.0" + class-is "^1.1.0" + multibase "~0.6.0" + multicodec "^1.0.0" + multihashes "~0.4.15" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +class-is@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/class-is/-/class-is-1.1.0.tgz#9d3c0fba0440d211d843cec3dedfa48055005825" + integrity sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +cliui@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" + integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== + dependencies: + string-width "^3.1.0" + strip-ansi "^5.2.0" + wrap-ansi "^5.1.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@^2.0.0, clone@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" + integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== + +compare-versions@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62" + integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + integrity sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY= + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" + integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + dependencies: + safe-buffer "5.1.2" + +content-hash@^2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/content-hash/-/content-hash-2.5.2.tgz#bbc2655e7c21f14fd3bfc7b7d4bfe6e454c9e211" + integrity sha512-FvIQKy0S1JaWV10sMsA7TRx8bpU+pqPkhbsfvOJAdjRXvYxEckAwQWGwtRjiaJfh+E0DvcWUGqcdjwMGFjsSdw== + dependencies: + cids "^0.7.1" + multicodec "^0.5.5" + multihashes "^0.4.15" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" + integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + +cookiejar@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" + integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +core-js-compat@^3.18.0: + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.3.tgz#de75e5821c5ce924a0a1e7b7d5c2cb973ff388aa" + integrity sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA== + dependencies: + browserslist "^4.18.1" + semver "7.0.0" + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.1: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cosmiconfig@^5.0.7: + version "5.2.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" + integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== + dependencies: + import-fresh "^2.0.0" + is-directory "^0.3.1" + js-yaml "^3.13.1" + parse-json "^4.0.0" + +crc-32@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" + integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== + dependencies: + exit-on-epipe "~1.0.1" + printj "~1.1.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-fetch@^2.1.0, cross-fetch@^2.1.1: + version "2.2.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-2.2.5.tgz#afaf5729f3b6c78d89c9296115c9f142541a5705" + integrity sha512-xqYAhQb4NhCJSRym03dwxpP1bYXpK3y7UN83Bo2WFi3x1Zmzn0SL/6xGoPr+gpt4WmNrgCCX3HPysvOwFOW36w== + dependencies: + node-fetch "2.6.1" + whatwg-fetch "2.0.4" + +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.0, cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-addr-codec@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz#e16cea892730178fe25a38f6d15b680cab3124ae" + integrity sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg== + dependencies: + base-x "^3.0.8" + big-integer "1.6.36" + blakejs "^1.1.0" + bs58 "^4.0.1" + ripemd160-min "0.0.6" + safe-buffer "^5.2.0" + sha3 "^2.1.1" + +crypto-browserify@3.12.0, crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-select@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067" + integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA== + dependencies: + boolbase "^1.0.0" + css-what "^5.0.0" + domhandler "^4.2.0" + domutils "^2.6.0" + nth-check "^2.0.0" + +css-what@^5.0.0, css-what@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +decompress-response@^3.2.0, decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= + dependencies: + mimic-response "^1.0.0" + +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + +deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +defer-to-connect@^1.0.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" + integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== + +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-indent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-serializer@^1.0.1, dom-serializer@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" + integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + +domhandler@^4.0.0, domhandler@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" + integrity sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4= + dependencies: + no-case "^2.2.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.4.17: + version "1.4.17" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.17.tgz#16ec40f61005582d5d41fac08400a254dccfb85f" + integrity sha512-zhk1MravPtq/KBhmGB7TLBILmXTgRG9TFSI3qS3DbgyfHzIl72iiTE37r/BHIbPCJJlWIo5rySyxiH4vWhu2ZA== + +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emoji-regex@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.0.0.tgz#96559e19f82231b436403e059571241d627c42b8" + integrity sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw== + +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +encoding@^0.1.11: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +enhanced-resolve@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24= + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + object-assign "^4.0.1" + tapable "^0.2.7" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +errno@^0.1.3, errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.18.5: + version "1.19.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" + integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.2" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.1" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.1" + object-inspect "^1.11.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@~0.10.14: + version "0.10.53" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" + integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.3" + next-tick "~1.0.0" + +es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-map@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-set "~0.1.5" + es6-symbol "~3.1.1" + event-emitter "~0.3.5" + +es6-set@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= + dependencies: + d "1" + es5-ext "~0.10.14" + es6-iterator "~2.0.1" + es6-symbol "3.1.1" + event-emitter "~0.3.5" + +es6-symbol@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-symbol@^3.1.1, es6-symbol@~3.1.1, es6-symbol@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +es6-weak-map@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint@^5.6.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.0.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eth-block-tracker@^4.4.2: + version "4.4.3" + resolved "https://registry.yarnpkg.com/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz#766a0a0eb4a52c867a28328e9ae21353812cf626" + integrity sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw== + dependencies: + "@babel/plugin-transform-runtime" "^7.5.5" + "@babel/runtime" "^7.5.5" + eth-query "^2.1.0" + json-rpc-random-id "^1.0.1" + pify "^3.0.0" + safe-event-emitter "^1.0.1" + +eth-ens-namehash@2.0.8, eth-ens-namehash@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz#229ac46eca86d52e0c991e7cb2aef83ff0f68bcf" + integrity sha1-IprEbsqG1S4MmR58sq74P/D2i88= + dependencies: + idna-uts46-hx "^2.3.1" + js-sha3 "^0.5.7" + +eth-json-rpc-errors@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" + integrity sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-json-rpc-errors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-2.0.2.tgz#c1965de0301fe941c058e928bebaba2e1285e3c4" + integrity sha512-uBCRM2w2ewusRHGxN8JhcuOb2RN3ueAOYH/0BhqdFmQkZx5lj5+fLKTz0mIVOzd4FG5/kUksCzCD7eTEim6gaA== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-lib@0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8" + integrity sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + xhr-request-promise "^0.1.2" + +eth-lib@^0.1.26: + version "0.1.29" + resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.1.29.tgz#0c11f5060d42da9f931eab6199084734f4dbd1d9" + integrity sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ== + dependencies: + bn.js "^4.11.6" + elliptic "^6.4.0" + nano-json-stream-parser "^0.1.2" + servify "^0.1.12" + ws "^3.0.0" + xhr-request-promise "^0.1.2" + +eth-query@^2.1.0, eth-query@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eth-query/-/eth-query-2.1.2.tgz#d6741d9000106b51510c72db92d6365456a6da5e" + integrity sha1-1nQdkAAQa1FRDHLbktY2VFam2l4= + dependencies: + json-rpc-random-id "^1.0.0" + xtend "^4.0.1" + +eth-rpc-errors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz#d7b22653c70dbf9defd4ef490fd08fe70608ca10" + integrity sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg== + dependencies: + fast-safe-stringify "^2.0.6" + +eth-sig-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-3.0.1.tgz#8753297c83a3f58346bd13547b59c4b2cd110c96" + integrity sha512-0Us50HiGGvZgjtWTyAI/+qTzYPMLy5Q451D0Xy68bxq1QMWdoOddDwGvsqcFT27uohKgalM9z/yxplyt+mY2iQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.0" + +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + +ethereum-common@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.2.0.tgz#13bf966131cce1eeade62a1b434249bb4cb120ca" + integrity sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA== + +ethereum-common@^0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" + integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= + +ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereum-protocol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz#b7d68142f4105e0ae7b5e178cf42f8d4dc4b93cf" + integrity sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg== + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz#eeafc62de544cb07b0ee44b10f572c9c49e00a84" + integrity sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA== + dependencies: + ethereumjs-util "^5.0.0" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-block@^1.2.2, ethereumjs-block@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz#78b88e6cc56de29a6b4884ee75379b6860333c3f" + integrity sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg== + dependencies: + async "^2.0.1" + ethereum-common "0.2.0" + ethereumjs-tx "^1.2.2" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-block@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-common@^1.1.0, ethereumjs-common@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" + integrity sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA== + +ethereumjs-testrpc@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ethereumjs-testrpc/-/ethereumjs-testrpc-6.0.3.tgz#7a0b87bf3670f92f607f98fa6a78801d9741b124" + integrity sha512-lAxxsxDKK69Wuwqym2K49VpXtBvLEsXr1sryNG4AkvL5DomMdeCBbu3D87UEevKenLHBiT8GTjARwN6Yj039gA== + dependencies: + webpack "^3.0.0" + +ethereumjs-tx@^1.2.2, ethereumjs-tx@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz#88323a2d875b10549b8347e09f4862b546f3d89a" + integrity sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA== + dependencies: + ethereum-common "^0.0.18" + ethereumjs-util "^5.0.0" + +ethereumjs-tx@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" + integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== + dependencies: + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.0.0" + +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.1.2, ethereumjs-util@^5.1.5: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" + integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== + dependencies: + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "^0.1.3" + rlp "^2.0.0" + safe-buffer "^5.1.1" + +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.2, ethereumjs-util@^7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.3.tgz#b55d7b64dde3e3e45749e4c41288238edec32d23" + integrity sha512-y+82tEbyASO0K0X1/SRhbJJoAlfcvq8JbrG4a5cjrOks7HS/36efU/0j2flxCPOUM++HFahk33kr/ZxyC4vNuw== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-vm@^2.3.4, ethereumjs-vm@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz#76243ed8de031b408793ac33907fb3407fe400c6" + integrity sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + ethereumjs-account "^2.0.3" + ethereumjs-block "~2.2.0" + ethereumjs-common "^1.1.0" + ethereumjs-util "^6.0.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "^2.3.2" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + +ethereumjs-wallet@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ethereumjs-wallet/-/ethereumjs-wallet-1.0.2.tgz#2c000504b4c71e8f3782dabe1113d192522e99b6" + integrity sha512-CCWV4RESJgRdHIvFciVQFnCHfqyhXWchTPlkfp28Qc53ufs+doi5I/cV2+xeK9+qEo25XCWfP9MiL+WEPAZfdA== + dependencies: + aes-js "^3.1.2" + bs58check "^2.1.2" + ethereum-cryptography "^0.1.3" + ethereumjs-util "^7.1.2" + randombytes "^2.1.0" + scrypt-js "^3.0.1" + utf8 "^3.0.0" + uuid "^8.3.2" + +ethers@^4.0.32: + version "4.0.49" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.49.tgz#0eb0e9161a0c8b4761be547396bbe2fb121a8894" + integrity sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg== + dependencies: + aes-js "3.0.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.3" + js-sha3 "0.5.7" + scrypt-js "2.0.4" + setimmediate "1.0.4" + uuid "2.0.1" + xmlhttprequest "1.8.0" + +ethers@^5.0.13: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.2.tgz#cd2e508c7342c44fa70392f722e8de8f2416489f" + integrity sha512-EF5W+6Wwcu6BqVwpgmyR5U2+L4c1FQzlM/02dkZOugN3KF0cG9bzHZP+TDJglmPm2/IzCEJDT7KBxzayk7SAHw== + dependencies: + "@ethersproject/abi" "5.5.0" + "@ethersproject/abstract-provider" "5.5.1" + "@ethersproject/abstract-signer" "5.5.0" + "@ethersproject/address" "5.5.0" + "@ethersproject/base64" "5.5.0" + "@ethersproject/basex" "5.5.0" + "@ethersproject/bignumber" "5.5.0" + "@ethersproject/bytes" "5.5.0" + "@ethersproject/constants" "5.5.0" + "@ethersproject/contracts" "5.5.0" + "@ethersproject/hash" "5.5.0" + "@ethersproject/hdnode" "5.5.0" + "@ethersproject/json-wallets" "5.5.0" + "@ethersproject/keccak256" "5.5.0" + "@ethersproject/logger" "5.5.0" + "@ethersproject/networks" "5.5.1" + "@ethersproject/pbkdf2" "5.5.0" + "@ethersproject/properties" "5.5.0" + "@ethersproject/providers" "5.5.1" + "@ethersproject/random" "5.5.0" + "@ethersproject/rlp" "5.5.0" + "@ethersproject/sha2" "5.5.0" + "@ethersproject/signing-key" "5.5.0" + "@ethersproject/solidity" "5.5.0" + "@ethersproject/strings" "5.5.0" + "@ethersproject/transactions" "5.5.0" + "@ethersproject/units" "5.5.0" + "@ethersproject/wallet" "5.5.0" + "@ethersproject/web" "5.5.1" + "@ethersproject/wordlists" "5.5.0" + +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk= + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.3: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + +event-emitter@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= + dependencies: + d "1" + es5-ext "~0.10.14" + +eventemitter3@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384" + integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ== + +events@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +exit-on-epipe@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692" + integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +express@^4.14.0: + version "4.17.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" + integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + dependencies: + accepts "~1.3.7" + array-flatten "1.1.1" + body-parser "1.19.0" + content-disposition "0.5.3" + content-type "~1.0.4" + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.5" + qs "6.7.0" + range-parser "~1.2.1" + safe-buffer "5.1.2" + send "0.17.1" + serve-static "1.14.1" + setprototypeof "1.1.1" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +ext@^1.1.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" + integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== + dependencies: + type "^2.5.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + +faker@^5.3.1: + version "5.5.3" + resolved "https://registry.yarnpkg.com/faker/-/faker-5.5.3.tgz#c57974ee484431b25205c2c8dc09fda861e51e0e" + integrity sha512-wLTv2a28wjUyWkbnX7u/ABZBkUkIF2fCd73V6P2oFqEGEktDfzWx4UxrSqtPRw0xPRAcjeAOIiJWqZm3pP4u3g== + +fast-check@^2.12.1: + version "2.20.0" + resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-2.20.0.tgz#0c88d8640649e981adb501ef92f90a26dc8bd628" + integrity sha512-tFNjLyPnOUg6iimVxOtoWMJOIyybCo7B8gUGm1yv43jDCQ0hlPUn0fmna/XO/n1yPxn/dxQw3+IygPSbMDiiog== + dependencies: + pure-rand "^5.0.0" + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.6: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fetch-ponyfill@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz#ae3ce5f732c645eab87e4ae8793414709b239893" + integrity sha1-rjzl9zLGReq4fkroeTQUcJsjmJM= + dependencies: + node-fetch "~1.7.1" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-minipass@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" + integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== + dependencies: + minipass "^2.6.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +ganache-cli@^6.1.0: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ganache-cli/-/ganache-cli-6.12.2.tgz#c0920f7db0d4ac062ffe2375cb004089806f627a" + integrity sha512-bnmwnJDBDsOWBUP8E/BExWf85TsdDEFelQSzihSJm9VChVO1SHp94YXLP5BlA4j/OTxp0wR4R1Tje9OHOuAJVw== + dependencies: + ethereumjs-util "6.2.1" + source-map-support "0.5.12" + yargs "13.2.4" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== + +get-caller-file@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + +get-stream@^4.0.0, get-stream@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.2, glob@^7.1.3: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0, globals@^11.7.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +got@9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" + integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== + dependencies: + "@sindresorhus/is" "^0.14.0" + "@szmarczak/http-timer" "^1.1.2" + cacheable-request "^6.0.0" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^4.1.0" + lowercase-keys "^1.0.1" + mimic-response "^1.0.1" + p-cancelable "^1.0.0" + to-readable-stream "^1.0.0" + url-parse-lax "^3.0.0" + +got@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" + integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== + dependencies: + decompress-response "^3.2.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-plain-obj "^1.1.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + p-cancelable "^0.3.0" + p-timeout "^1.1.1" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + url-parse-lax "^1.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.4: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.3: + version "5.1.5" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" + integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== + dependencies: + ajv "^6.12.3" + har-schema "^2.0.0" + +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== + +has-symbols@^1.0.1, has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== + dependencies: + has-symbol-support-x "^1.4.1" + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +he@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +header-case@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.1.tgz#9535973197c144b09613cd65d317ef19963bd02d" + integrity sha1-lTWXMZfBRLCWE81l0xfvGZY70C0= + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + +highlight.js@^10.4.1: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +highlightjs-solidity@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-2.0.2.tgz#87ffdec3c51ae8b6def42d50f9a40b4676f57e4e" + integrity sha512-q0aYUKiZ9MPQg41qx/KpXKaCpqql50qTvmwGYyLFfcjt9AE/+C9CwjVIdJZc7EYj6NGgJuFJ4im1gfgrzUU1fQ== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-errors@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" + integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-errors@~1.7.2: + version "1.7.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" + integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.1.1" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.0" + +http-https@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" + integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +husky@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +idna-uts46-hx@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz#a1dc5c4df37eee522bf66d969cc980e00e8711f9" + integrity sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA== + dependencies: + punycode "2.1.0" + +ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +import-fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" + integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= + dependencies: + caller-path "^2.0.0" + resolve-from "^3.0.0" + +import-fresh@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + +inquirer@^6.2.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" + integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== + dependencies: + ansi-escapes "^3.2.0" + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^2.0.0" + lodash "^4.17.12" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.4.0" + string-width "^2.1.0" + strip-ansi "^5.1.0" + through "^2.3.6" + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= + +invert-kv@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" + integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-core-module@^2.2.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" + integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fn/-/is-fn-1.0.0.tgz#9543d5de7bcf5b08a22ec8a20bae6e286d510d8c" + integrity sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw= + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + +is-negative-zero@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" + integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-retry-allowed@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" + integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== + +is-shared-array-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" + integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== + +is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= + +is-weakref@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-sha3@0.5.7, js-sha3@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" + integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= + +js-sha3@0.8.0, js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.12.0, js-yaml@^3.13.0, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= + +json-loader@^0.5.4: + version "0.5.7" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-rpc-engine@^5.1.3: + version "5.4.0" + resolved "https://registry.yarnpkg.com/json-rpc-engine/-/json-rpc-engine-5.4.0.tgz#75758609d849e1dba1e09021ae473f3ab63161e5" + integrity sha512-rAffKbPoNDjuRnXkecTjnsE3xLLrb00rEkdgalINhaYVYIxDwWtvYBr9UFbhTvPB1B2qUOLoFd/cV6f4Q7mh7g== + dependencies: + eth-rpc-errors "^3.0.0" + safe-event-emitter "^1.0.1" + +json-rpc-random-id@^1.0.0, json-rpc-random-id@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz#ba49d96aded1444dbb8da3d203748acbbcdec8c8" + integrity sha1-uknZat7RRE27jaPSA3SKy7zeyMg= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + +jsprim@^1.2.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +keccak@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" + integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= + dependencies: + invert-kv "^1.0.0" + +lcid@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" + integrity sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA== + dependencies: + invert-kv "^2.0.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== + dependencies: + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + +loader-runner@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash.assign@^4.0.3, lodash.assign@^4.0.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= + +lodash.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha1-ZHYsSGGAglGKw99Mz11YhtriA0c= + +lodash.flatmap@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz#ef8cbf408f6e48268663345305c6acc0b778702e" + integrity sha1-74y/QI9uSCaGYzRTBcaswLd4cC4= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha1-o45GtzRp4EILDaEhLmbUFL42S6Q= + +lodash.sum@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" + integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= + +lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.14: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +map-age-cleaner@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= + dependencies: + mimic-fn "^1.0.0" + +mem@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" + integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== + dependencies: + map-age-cleaner "^0.1.1" + mimic-fn "^2.0.0" + p-is-promise "^2.0.0" + +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== + +mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + dependencies: + mime-db "1.51.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + +mimic-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-response@^1.0.0, mimic-response@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU= + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minipass@^2.6.0, minipass@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" + integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" + integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== + dependencies: + minipass "^2.9.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp-promise@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" + integrity sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE= + dependencies: + mkdirp "*" + +mkdirp@*: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mock-fs@^4.1.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" + integrity sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +multibase@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" + integrity sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multibase@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.6.1.tgz#b76df6298536cc17b9f6a6db53ec88f85f8cc12b" + integrity sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw== + dependencies: + base-x "^3.0.8" + buffer "^5.5.0" + +multicodec@^0.5.5: + version "0.5.7" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-0.5.7.tgz#1fb3f9dd866a10a55d226e194abba2dcc1ee9ffd" + integrity sha512-PscoRxm3f+88fAtELwUnZxGDkduE2HD9Q6GHUOywQLjOGT/HAdhjLDYNZ1e7VR0s0TP0EwZ16LNUTFpoBGivOA== + dependencies: + varint "^5.0.0" + +multicodec@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-1.0.4.tgz#46ac064657c40380c28367c90304d8ed175a714f" + integrity sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg== + dependencies: + buffer "^5.6.0" + varint "^5.0.0" + +multihashes@^0.4.15, multihashes@~0.4.15: + version "0.4.21" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-0.4.21.tgz#dc02d525579f334a7909ade8a122dabb58ccfcb5" + integrity sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw== + dependencies: + buffer "^5.5.0" + multibase "^0.7.0" + varint "^5.0.0" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + +nan@^2.12.1: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + +nano-base32@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nano-base32/-/nano-base32-1.0.1.tgz#ba548c879efcfb90da1c4d9e097db4a46c9255ef" + integrity sha1-ulSMh578+5DaHE2eCX20pGySVe8= + +nano-json-stream-parser@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" + integrity sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18= + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +neo-async@^2.5.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +next-tick@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^2.2.0, no-case@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" + integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + +node-fetch@~1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" + integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + +node-libs-browser@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" + integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== + +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== + +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-url@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +nth-check@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" + integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== + dependencies: + boolbase "^1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= + +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA= + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.11.0, object-inspect@^1.9.0: + version "1.11.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" + integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== + +object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +oboe@2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/oboe/-/oboe-2.1.5.tgz#5554284c543a2266d7a38f17e073821fbde393cd" + integrity sha1-VVQoTFQ6ImbXo48X4HOCH73jk80= + dependencies: + http-https "^1.0.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= + dependencies: + lcid "^1.0.0" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-locale@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a" + integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q== + dependencies: + execa "^1.0.0" + lcid "^2.0.0" + mem "^4.0.0" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + +p-cancelable@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" + integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== + +p-cancelable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" + integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +p-is-promise@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" + integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-timeout@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +param-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= + dependencies: + no-case "^2.2.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-headers@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.4.tgz#9eaf2d02bed2d1eff494331ce3df36d7924760bf" + integrity sha512-psZ9iZoCNFLrgRjZ1d8mn0h9WRqJwFxM9q3x7iUjN/YT2OksthDJ5TiPCu2F38kS4zutqfW+YdVVkBZZx3/1aw== + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.1.tgz#2d578d3455f660da65eca18ef95b4e0de912761e" + integrity sha1-LVeNNFX2YNpl7KGO+VtODekSdh4= + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.1.tgz#94b8037c372d3fe2906e465bb45e25d226e8eea5" + integrity sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU= + dependencies: + no-case "^2.2.0" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +path-parse@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +pbkdf2@^3.0.17, pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +precond@0.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac" + integrity sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier-plugin-solidity@^1.0.0-beta.7: + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== + dependencies: + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" + escape-string-regexp "^4.0.0" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.3" + +prettier@^1.14.3: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== + +prettier@^2.2.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + +printj@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" + integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + +promise-to-callback@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/promise-to-callback/-/promise-to-callback-1.0.0.tgz#5d2a749010bfb67d963598fcd3960746a68feef7" + integrity sha1-XSp0kBC/tn2WNZj805YHRqaP7vc= + dependencies: + is-fn "^1.0.0" + set-immediate-shim "^1.0.1" + +proper-lockfile@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +proxy-addr@~2.0.5: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" + integrity sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0= + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +pure-rand@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-5.0.0.tgz#87f5bdabeadbd8904e316913a5c0b8caac517b37" + integrity sha512-lD2/y78q+7HqBx2SaT6OT4UcwtvXNRfEpzYEzl0EQ+9gZq2Qi3fa0HDnYPeqQwhlHJFBUhT7AO3mLU3+8bynHA== + +qs@6.7.0: + version "6.7.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" + integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + +qs@6.9.6: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" + integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + dependencies: + bytes "3.1.0" + http-errors "1.7.2" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.2.tgz#baf3e9c21eebced59dd6533ac872b71f7b61cb32" + integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ== + dependencies: + bytes "3.1.1" + http-errors "1.8.1" + iconv-lite "0.4.24" + unpipe "1.0.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.9, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.2, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +request@^2.79.0, request@^2.85.0: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.14.2: + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= + dependencies: + lowercase-keys "^1.0.0" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= + dependencies: + align-text "^0.1.1" + +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160-min@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/ripemd160-min/-/ripemd160-min-0.0.6.tgz#a904b77658114474d02503e819dcc55853b67e62" + integrity sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A== + +ripemd160@^2.0.0, ripemd160@^2.0.1, ripemd160@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4, rlp@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + +run-async@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + +rxjs@^6.4.0: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-event-emitter@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz#5b692ef22329ed8f69fdce607e50ca734f6f20af" + integrity sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg== + dependencies: + events "^3.0.0" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +scrypt-js@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16" + integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw== + +scrypt-js@3.0.1, scrypt-js@^3.0.0, scrypt-js@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" + integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== + +secp256k1@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" + integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== + dependencies: + elliptic "^6.5.2" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore@>=1.0.1, semaphore@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.4, semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== + +send@0.17.1: + version "0.17.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" + integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.7.2" + mime "1.6.0" + ms "2.1.1" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +sentence-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" + integrity sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ= + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + +serve-static@1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" + integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.1" + +servify@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" + integrity sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw== + dependencies: + body-parser "^1.16.0" + cors "^2.8.1" + express "^4.14.0" + request "^2.79.0" + xhr "^2.3.3" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.4.tgz#20e81de622d4a02588ce0c8da8973cbcf1d3138f" + integrity sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48= + +setimmediate@^1.0.4, setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" + integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +sha3@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/sha3/-/sha3-2.1.4.tgz#000fac0fe7c2feac1f48a25e7a31b52a6492cc8f" + integrity sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg== + dependencies: + buffer "6.0.3" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" + integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== + dependencies: + decompress-response "^3.3.0" + once "^1.3.1" + simple-concat "^1.0.0" + +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + integrity sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8= + dependencies: + no-case "^2.2.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +solc@^0.4.20: + version "0.4.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.4.26.tgz#5390a62a99f40806b86258c737c1cf653cc35cb5" + integrity sha512-o+c6FpkiHd+HPjmjEVpQgH7fqZ14tJpXhho+/bQXlXbliLIS/xjXb42Vxh+qQY1WCSTMQ0+a5vR9vi0MfhU6mA== + dependencies: + fs-extra "^0.30.0" + memorystream "^0.3.1" + require-from-string "^1.1.0" + semver "^5.3.0" + yargs "^4.7.1" + +solhint-plugin-prettier@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/solhint-plugin-prettier/-/solhint-plugin-prettier-0.0.5.tgz#e3b22800ba435cd640a9eca805a7f8bc3e3e6a6b" + integrity sha512-7jmWcnVshIrO2FFinIvDQmhQpfpS2rRRn3RejiYgnjIE68xO2bvrYvjqVNfrio4xH9ghOqn83tKuTzLjEbmGIA== + dependencies: + prettier-linter-helpers "^1.0.0" + +solhint@^3.3.4: + version "3.3.6" + resolved "https://registry.yarnpkg.com/solhint/-/solhint-3.3.6.tgz#abe9af185a9a7defefba480047b3e42cbe9a1210" + integrity sha512-HWUxTAv2h7hx3s3hAab3ifnlwb02ZWhwFU/wSudUHqteMS3ll9c+m1FlGn9V8ztE2rf3Z82fQZA005Wv7KpcFA== + dependencies: + "@solidity-parser/parser" "^0.13.2" + ajv "^6.6.1" + antlr4 "4.7.1" + ast-parents "0.0.1" + chalk "^2.4.2" + commander "2.18.0" + cosmiconfig "^5.0.7" + eslint "^5.6.0" + fast-diff "^1.1.2" + glob "^7.1.3" + ignore "^4.0.6" + js-yaml "^3.12.0" + lodash "^4.17.11" + semver "^6.3.0" + optionalDependencies: + prettier "^1.14.3" + +solidity-ast@^0.4.15: + version "0.4.28" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.28.tgz#5589998512b9a3602e6ba612cbe7fed7401294f4" + integrity sha512-RtZCP5tSvZMadVtg9/IfLmAMKDOnQEvG2HA6VnPuoTMxqxsbbn4lQy8jgH3RVbqW0eO1hd7cSCKecb72/OeOIw== + +solidity-comments-extractor@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz#99d8f1361438f84019795d928b931f4e5c39ca19" + integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +source-map@^0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0, string-width@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string-width@^3.0.0, string-width@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= + dependencies: + ansi-regex "^3.0.0" + +strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + +strip-json-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + +super-split@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" + integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== + +supports-color@^4.2.1: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s= + dependencies: + has-flag "^2.0.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +swarm-js@^0.1.40: + version "0.1.40" + resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" + integrity sha512-yqiOCEoA4/IShXkY3WKwP5PvZhmoOOD8clsKA7EEcRILMkTEYHCQ21HDCAcVpmIxZq4LyZvWeRJ6quIyHk1caA== + dependencies: + bluebird "^3.5.0" + buffer "^5.0.5" + eth-lib "^0.1.26" + fs-extra "^4.0.2" + got "^7.1.0" + mime-types "^2.1.16" + mkdirp-promise "^5.0.1" + mock-fs "^4.1.0" + setimmediate "^1.0.5" + tar "^4.0.2" + xhr-request "^1.0.1" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + +tapable@^0.2.7: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.9.tgz#af2d8bbc9b04f74ee17af2b4d9048f807acd18a8" + integrity sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A== + +tar@^4.0.2: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== + dependencies: + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" + +testrpc@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/testrpc/-/testrpc-0.0.1.tgz#83e2195b1f5873aec7be1af8cbe6dcf39edb7aed" + integrity sha512-afH1hO+SQ/VPlmaLUFj2636QMeDvPCeQMc/9RBMW0IfjNe9gFD9Ra3ShqYkB7py0do1ZcCna/9acHyzTJ+GcNA== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +title-case@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.1.tgz#3e127216da58d2bc5becf137ab91dae3a7cd8faa" + integrity sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o= + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-readable-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" + integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" + integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +truffle-assertions@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/truffle-assertions/-/truffle-assertions-0.9.2.tgz#0f8360f53ad92b6d8fdb8ceb5dce54c1fc392e23" + integrity sha512-9g2RhaxU2F8DeWhqoGQvL/bV8QVoSnQ6PY+ZPvYRP5eF7+/8LExb4mjLx/FeliLTjc3Tv1SABG05Gu5qQ/ErmA== + dependencies: + assertion-error "^1.1.0" + lodash.isequal "^4.5.0" + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + dependencies: + prelude-ls "~1.1.2" + +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.17, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" + integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +uglify-js@^2.8.29: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +uglifyjs-webpack-plugin@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + integrity sha1-uVH0q7a9YX5m9j64kUmOORdj4wk= + dependencies: + source-map "^0.5.6" + uglify-js "^2.8.29" + webpack-sources "^1.0.1" + +ultron@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" + integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== + +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= + dependencies: + prepend-http "^2.0.0" + +url-set-query@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339" + integrity sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk= + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +utf-8-validate@^5.0.2: + version "5.0.7" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" + integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== + dependencies: + node-gyp-build "^4.3.0" + +utf8@3.0.0, utf8@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +util@^0.12.0: + version "0.12.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" + integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac" + integrity sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w= + +uuid@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +varint@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.4.0: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" + +web3-bzz@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.5.3.tgz#e36456905ce051138f9c3ce3623cbc73da088c2b" + integrity sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg== + dependencies: + "@types/node" "^12.12.6" + got "9.6.0" + swarm-js "^0.1.40" + +web3-core-helpers@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz#099030235c477aadf39a94199ef40092151d563c" + integrity sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw== + dependencies: + web3-eth-iban "1.5.3" + web3-utils "1.5.3" + +web3-core-method@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.5.3.tgz#6cff97ed19fe4ea2e9183d6f703823a079f5132c" + integrity sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg== + dependencies: + "@ethereumjs/common" "^2.4.0" + "@ethersproject/transactions" "^5.0.0-beta.135" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-utils "1.5.3" + +web3-core-promievent@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz#3f11833c3dc6495577c274350b61144e0a4dba01" + integrity sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg== + dependencies: + eventemitter3 "4.0.4" + +web3-core-requestmanager@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz#b339525815fd40e3a2a81813c864ddc413f7b6f7" + integrity sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg== + dependencies: + util "^0.12.0" + web3-core-helpers "1.5.3" + web3-providers-http "1.5.3" + web3-providers-ipc "1.5.3" + web3-providers-ws "1.5.3" + +web3-core-subscriptions@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz#d7d69c4caad65074212028656e9dc56ca5c2159d" + integrity sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.5.3" + +web3-core@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.5.3.tgz#59f8728b27c8305b349051326aa262b9b7e907bf" + integrity sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ== + dependencies: + "@types/bn.js" "^4.11.5" + "@types/node" "^12.12.6" + bignumber.js "^9.0.0" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-requestmanager "1.5.3" + web3-utils "1.5.3" + +web3-eth-abi@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz#5aea9394d797f99ca0d9bd40c3417eb07241c96c" + integrity sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg== + dependencies: + "@ethersproject/abi" "5.0.7" + web3-utils "1.5.3" + +web3-eth-accounts@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz#076c816ff4d68c9dffebdc7fd2bfaddcfc163d77" + integrity sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw== + dependencies: + "@ethereumjs/common" "^2.3.0" + "@ethereumjs/tx" "^3.2.1" + crypto-browserify "3.12.0" + eth-lib "0.2.8" + ethereumjs-util "^7.0.10" + scrypt-js "^3.0.1" + uuid "3.3.2" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" + +web3-eth-contract@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz#12b03a4a16ce583a945f874bea2ff2fb4c5b81ad" + integrity sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg== + dependencies: + "@types/bn.js" "^4.11.5" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" + +web3-eth-ens@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz#ef6eee1ddf32b1ff9536fc7c599a74f2656bafe1" + integrity sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw== + dependencies: + content-hash "^2.5.2" + eth-ens-namehash "2.0.8" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-contract "1.5.3" + web3-utils "1.5.3" + +web3-eth-iban@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz#91b1475893a877b10eac1de5cce6eb379fb81b5d" + integrity sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw== + dependencies: + bn.js "^4.11.9" + web3-utils "1.5.3" + +web3-eth-personal@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz#4ebe09e9a77dd49d23d93b36b36cfbf4a6dae713" + integrity sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew== + dependencies: + "@types/node" "^12.12.6" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" + +web3-eth@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.5.3.tgz#d7d1ac7198f816ab8a2088c01e0bf1eda45862fe" + integrity sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q== + dependencies: + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-accounts "1.5.3" + web3-eth-contract "1.5.3" + web3-eth-ens "1.5.3" + web3-eth-iban "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" + +web3-net@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.5.3.tgz#545fee49b8e213b0c55cbe74ffd0295766057463" + integrity sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ== + dependencies: + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" + +web3-providers-http@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.5.3.tgz#74f170fc3d79eb7941d9fbc34e2a067d61ced0b2" + integrity sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw== + dependencies: + web3-core-helpers "1.5.3" + xhr2-cookies "1.1.0" + +web3-providers-ipc@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz#4bd7f5e445c2f3c2595fce0929c72bb879320a3f" + integrity sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg== + dependencies: + oboe "2.1.5" + web3-core-helpers "1.5.3" + +web3-providers-ws@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz#eec6cfb32bb928a4106de506f13a49070a21eabf" + integrity sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg== + dependencies: + eventemitter3 "4.0.4" + web3-core-helpers "1.5.3" + websocket "^1.0.32" + +web3-shh@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.5.3.tgz#3c04aa4cda9ba0b746d7225262401160f8e38b13" + integrity sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q== + dependencies: + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-net "1.5.3" + +web3-utils@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.5.3.tgz#e914c9320cd663b2a09a5cb920ede574043eb437" + integrity sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31: + version "1.6.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.6.1.tgz#befcb23922b00603ab56d8c5b4158468dc494aca" + integrity sha512-RidGKv5kOkcerI6jQqDFDoTllQQqV+rPhTzZHhmbqtFObbYpU93uc+yG1LHivRTQhA6llIx67iudc/vzisgO+w== + dependencies: + bn.js "^4.11.9" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.5.3.tgz#11882679453c645bf33620fbc255a243343075aa" + integrity sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w== + dependencies: + web3-bzz "1.5.3" + web3-core "1.5.3" + web3-eth "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-shh "1.5.3" + web3-utils "1.5.3" + +webpack-sources@^1.0.1: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^3.0.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.12.0.tgz#3f9e34360370602fcf639e97939db486f4ec0d74" + integrity sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ== + dependencies: + acorn "^5.0.0" + acorn-dynamic-import "^2.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + async "^2.1.2" + enhanced-resolve "^3.4.0" + escope "^3.6.0" + interpret "^1.0.0" + json-loader "^0.5.4" + json5 "^0.5.1" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + mkdirp "~0.5.0" + node-libs-browser "^2.0.0" + source-map "^0.5.3" + supports-color "^4.2.1" + tapable "^0.2.7" + uglifyjs-webpack-plugin "^0.4.6" + watchpack "^1.4.0" + webpack-sources "^1.0.1" + yargs "^8.0.2" + +websocket@^1.0.32: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-fetch@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f" + integrity sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng== + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.2: + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-abstract "^1.18.5" + foreach "^2.0.5" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.7" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrap-ansi@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" + integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== + dependencies: + ansi-styles "^3.2.0" + string-width "^3.0.0" + strip-ansi "^5.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + +ws@7.4.6: + version "7.4.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" + integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== + +ws@^3.0.0: + version "3.3.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== + dependencies: + async-limiter "~1.0.0" + safe-buffer "~5.1.0" + ultron "~1.1.0" + +ws@^5.1.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d" + integrity sha512-jZArVERrMsKUatIdnLzqvcfydI85dvd/Fp1u/VOpfdDWQ4c9qWXe+VIeAbQ5FrDwciAkr+lzofXLz3Kuf26AOA== + dependencies: + async-limiter "~1.0.0" + +xhr-request-promise@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz#2d5f4b16d8c6c893be97f1a62b0ed4cf3ca5f96c" + integrity sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg== + dependencies: + xhr-request "^1.1.0" + +xhr-request@^1.0.1, xhr-request@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr-request/-/xhr-request-1.1.0.tgz#f4a7c1868b9f198723444d82dcae317643f2e2ed" + integrity sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA== + dependencies: + buffer-to-arraybuffer "^0.0.5" + object-assign "^4.1.1" + query-string "^5.0.1" + simple-get "^2.7.0" + timed-out "^4.0.1" + url-set-query "^1.0.0" + xhr "^2.0.4" + +xhr2-cookies@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz#7d77449d0999197f155cb73b23df72505ed89d48" + integrity sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg= + dependencies: + cookiejar "^2.1.1" + +xhr@^2.0.4, xhr@^2.2.0, xhr@^2.3.3: + version "2.6.0" + resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" + integrity sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA== + dependencies: + global "~4.4.0" + is-function "^1.0.1" + parse-headers "^2.0.0" + xtend "^4.0.0" + +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: + version "3.2.2" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" + integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.0, yallist@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^13.1.0: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" + integrity sha1-hVaN488VD/SfpRgl8DqMiA3cxcQ= + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.0.6" + +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= + dependencies: + camelcase "^4.1.0" + +yargs@13.2.4: + version "13.2.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" + integrity sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + os-locale "^3.1.0" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.0" + +yargs@^4.7.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" + integrity sha1-wMQpJMpKqmsObaFznfshZDn53cA= + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.0.3" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.1" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^2.4.1" + +yargs@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A= + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0"